mirror of
https://github.com/PretendoNetwork/nex-go.git
synced 2025-04-02 11:02:14 -04:00
556 lines
13 KiB
Go
556 lines
13 KiB
Go
package nex
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// StructureInterface implements all Structure methods
|
|
type StructureInterface interface {
|
|
Hierarchy() []StructureInterface
|
|
ExtractFromStream(*StreamIn) error
|
|
Bytes(*StreamOut) []byte
|
|
}
|
|
|
|
// Structure represents a nex Structure type
|
|
type Structure struct {
|
|
StructureInterface
|
|
}
|
|
|
|
// Hierarchy returns a Structure hierarchy
|
|
func (structure *Structure) Hierarchy() []StructureInterface {
|
|
return make([]StructureInterface, 0)
|
|
}
|
|
|
|
// Data represents a structure with no data
|
|
type Data struct {
|
|
*Structure
|
|
}
|
|
|
|
// ExtractFromStream does nothing for Data
|
|
func (data *Data) ExtractFromStream(stream *StreamIn) error {
|
|
// Basically do nothing. Does a relative seek with 0
|
|
stream.SeekByte(0, true)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Bytes does nothing for Data
|
|
func (data *Data) Bytes(stream *StreamOut) []byte {
|
|
return stream.Bytes()
|
|
}
|
|
|
|
// NewData returns a new Data Structure
|
|
func NewData() *Data {
|
|
structure := &Structure{}
|
|
data := &Data{structure}
|
|
|
|
return data
|
|
}
|
|
|
|
var dataHolderKnownObjects = make(map[string]StructureInterface)
|
|
|
|
func RegisterDataHolderType(structure StructureInterface) {
|
|
name := reflect.TypeOf(structure).Elem().Name()
|
|
dataHolderKnownObjects[name] = structure
|
|
}
|
|
|
|
// DataHolder represents a structure which can hold any other structure
|
|
type DataHolder struct {
|
|
typeName string
|
|
length1 uint32 // length of data including length2
|
|
length2 uint32 // length of the actual structure
|
|
objectData StructureInterface
|
|
}
|
|
|
|
func (dataHolder *DataHolder) TypeName() string {
|
|
return dataHolder.typeName
|
|
}
|
|
|
|
func (dataHolder *DataHolder) SetTypeName(typeName string) {
|
|
dataHolder.typeName = typeName
|
|
}
|
|
|
|
func (dataHolder *DataHolder) ObjectData() StructureInterface {
|
|
return dataHolder.objectData
|
|
}
|
|
|
|
func (dataHolder *DataHolder) SetObjectData(objectData StructureInterface) {
|
|
dataHolder.objectData = objectData
|
|
}
|
|
|
|
// ExtractFromStream extracts a DataHolder structure from a stream
|
|
func (dataHolder *DataHolder) ExtractFromStream(stream *StreamIn) error {
|
|
// TODO: Error checks
|
|
dataHolder.typeName, _ = stream.ReadString()
|
|
dataHolder.length1 = stream.ReadUInt32LE()
|
|
dataHolder.length2 = stream.ReadUInt32LE()
|
|
|
|
newObjectInstance := reflect.New(reflect.TypeOf(dataHolderKnownObjects[dataHolder.typeName]).Elem()).Interface().(StructureInterface)
|
|
|
|
dataHolder.objectData, _ = stream.ReadStructure(newObjectInstance)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (dataHolder *DataHolder) Bytes(stream *StreamOut) []byte {
|
|
content := dataHolder.objectData.Bytes(NewStreamOut(stream.Server))
|
|
|
|
/*
|
|
Technically this way of encoding a DataHolder is "wrong".
|
|
It implies the structure of DataHolder is:
|
|
|
|
- Name (string)
|
|
- Length+4 (uint32)
|
|
- Content (Buffer)
|
|
|
|
However the structure as defined by the official NEX library is:
|
|
|
|
- Name (string)
|
|
- Length+4 (uint32)
|
|
- Length (uint32)
|
|
- Content (bytes)
|
|
|
|
It is convenient to treat the last 2 fields as a Buffer type, but
|
|
it should be noted that this is not actually the case.
|
|
*/
|
|
stream.WriteString(dataHolder.typeName)
|
|
stream.WriteUInt32LE(uint32(len(content) + 4))
|
|
stream.WriteBuffer(content)
|
|
|
|
return stream.Bytes()
|
|
}
|
|
|
|
// NewDataHolder returns a new DataHolder
|
|
func NewDataHolder() *DataHolder {
|
|
return &DataHolder{}
|
|
}
|
|
|
|
// RVConnectionData represents a nex RVConnectionData type
|
|
type RVConnectionData struct {
|
|
stationURL string
|
|
specialProtocols []byte
|
|
stationURLSpecialProtocols string
|
|
time uint64
|
|
|
|
hierarchy []StructureInterface
|
|
Structure
|
|
}
|
|
|
|
// SetStationURL sets the RVConnectionData station URL
|
|
func (rvConnectionData *RVConnectionData) SetStationURL(stationURL string) {
|
|
rvConnectionData.stationURL = stationURL
|
|
}
|
|
|
|
// SetSpecialProtocols sets the RVConnectionData special protocol list (unused by Nintendo)
|
|
func (rvConnectionData *RVConnectionData) SetSpecialProtocols(specialProtocols []byte) {
|
|
rvConnectionData.specialProtocols = specialProtocols
|
|
}
|
|
|
|
// SetStationURLSpecialProtocols sets the RVConnectionData special station URL (unused by Nintendo)
|
|
func (rvConnectionData *RVConnectionData) SetStationURLSpecialProtocols(stationURLSpecialProtocols string) {
|
|
rvConnectionData.stationURLSpecialProtocols = stationURLSpecialProtocols
|
|
}
|
|
|
|
// SetTime sets the RVConnectionData time
|
|
func (rvConnectionData *RVConnectionData) SetTime(time uint64) {
|
|
rvConnectionData.time = time
|
|
}
|
|
|
|
// Bytes encodes the RVConnectionData and returns a byte array
|
|
func (rvConnectionData *RVConnectionData) Bytes(stream *StreamOut) []byte {
|
|
stream.WriteString(rvConnectionData.stationURL)
|
|
stream.WriteUInt32LE(0) // Always 0
|
|
stream.WriteString(rvConnectionData.stationURLSpecialProtocols)
|
|
stream.WriteUInt64LE(rvConnectionData.time)
|
|
|
|
return stream.Bytes()
|
|
}
|
|
|
|
// NewRVConnectionData returns a new RVConnectionData
|
|
func NewRVConnectionData() *RVConnectionData {
|
|
rvConnectionData := &RVConnectionData{}
|
|
|
|
return rvConnectionData
|
|
}
|
|
|
|
// DateTime represents a NEX DateTime type
|
|
type DateTime struct {
|
|
value uint64
|
|
}
|
|
|
|
// Make initilizes a DateTime with the input data
|
|
func (datetime *DateTime) Make(year, month, day, hour, minute, second int) uint64 {
|
|
datetime.value = uint64(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
|
|
|
|
return datetime.value
|
|
}
|
|
|
|
// FromTimestamp converts a Time timestamp into a NEX DateTime
|
|
func (datetime *DateTime) FromTimestamp(timestamp time.Time) uint64 {
|
|
year := timestamp.Year()
|
|
month := int(timestamp.Month())
|
|
day := timestamp.Day()
|
|
hour := timestamp.Hour()
|
|
minute := timestamp.Minute()
|
|
second := timestamp.Second()
|
|
|
|
return datetime.Make(year, month, day, hour, minute, second)
|
|
}
|
|
|
|
// Now converts the current Time timestamp to a NEX DateTime
|
|
func (datetime *DateTime) Now() uint64 {
|
|
return datetime.FromTimestamp(time.Now())
|
|
}
|
|
|
|
// Value returns the stored DateTime time
|
|
func (datetime *DateTime) Value() uint64 {
|
|
return datetime.value
|
|
}
|
|
|
|
// NewDateTime returns a new DateTime instance
|
|
func NewDateTime(value uint64) *DateTime {
|
|
return &DateTime{value: value}
|
|
}
|
|
|
|
// StationURL contains the data for a NEX station URL
|
|
type StationURL struct {
|
|
// Using pointers to check for nil
|
|
scheme string
|
|
address string
|
|
port string
|
|
stream string
|
|
sid string
|
|
cid string
|
|
pid string
|
|
transportType string
|
|
rvcid string
|
|
natm string
|
|
natf string
|
|
upnp string
|
|
pmp string
|
|
probeinit string
|
|
prid string
|
|
}
|
|
|
|
// SetScheme sets the StationURL scheme
|
|
func (station *StationURL) SetScheme(scheme string) {
|
|
station.scheme = scheme
|
|
}
|
|
|
|
// SetAddress sets the StationURL address
|
|
func (station *StationURL) SetAddress(address string) {
|
|
station.address = address
|
|
}
|
|
|
|
// SetPort sets the StationURL port
|
|
func (station *StationURL) SetPort(port string) {
|
|
station.port = port
|
|
}
|
|
|
|
// SetStream sets the StationURL stream
|
|
func (station *StationURL) SetStream(stream string) {
|
|
station.stream = stream
|
|
}
|
|
|
|
// SetSID sets the StationURL SID
|
|
func (station *StationURL) SetSID(sid string) {
|
|
station.sid = sid
|
|
}
|
|
|
|
// SetCID sets the StationURL CID
|
|
func (station *StationURL) SetCID(cid string) {
|
|
station.cid = cid
|
|
}
|
|
|
|
// SetPid sets the StationURL PID
|
|
func (station *StationURL) SetPID(pid string) {
|
|
station.pid = pid
|
|
}
|
|
|
|
// SetType sets the StationURL transportType
|
|
func (station *StationURL) SetType(transportType string) {
|
|
station.transportType = transportType
|
|
}
|
|
|
|
// SetRVCID sets the StationURL RVCID
|
|
func (station *StationURL) SetRVCID(rvcid string) {
|
|
station.rvcid = rvcid
|
|
}
|
|
|
|
// SetNatm sets the StationURL Natm
|
|
func (station *StationURL) SetNatm(natm string) {
|
|
station.natm = natm
|
|
}
|
|
|
|
// SetNatf sets the StationURL Natf
|
|
func (station *StationURL) SetNatf(natf string) {
|
|
station.natf = natf
|
|
}
|
|
|
|
// SetUpnp sets the StationURL Upnp
|
|
func (station *StationURL) SetUpnp(upnp string) {
|
|
station.upnp = upnp
|
|
}
|
|
|
|
// SetPmp sets the StationURL Pmp
|
|
func (station *StationURL) SetPmp(pmp string) {
|
|
station.pmp = pmp
|
|
}
|
|
|
|
// SetProbeInit sets the StationURL ProbeInit
|
|
func (station *StationURL) SetProbeInit(probeinit string) {
|
|
station.probeinit = probeinit
|
|
}
|
|
|
|
// SetPRID sets the StationURL PRID
|
|
func (station *StationURL) SetPRID(prid string) {
|
|
station.prid = prid
|
|
}
|
|
|
|
func (station *StationURL) Scheme() string {
|
|
return station.address
|
|
}
|
|
|
|
func (station *StationURL) Address() string {
|
|
return station.address
|
|
}
|
|
|
|
func (station *StationURL) Port() string {
|
|
return station.port
|
|
}
|
|
|
|
func (station *StationURL) Stream() string {
|
|
return station.stream
|
|
}
|
|
|
|
func (station *StationURL) SID() string {
|
|
return station.sid
|
|
}
|
|
|
|
func (station *StationURL) CID() string {
|
|
return station.cid
|
|
}
|
|
|
|
func (station *StationURL) PID() string {
|
|
return station.pid
|
|
}
|
|
|
|
func (station *StationURL) Type() string {
|
|
return station.transportType
|
|
}
|
|
|
|
func (station *StationURL) RVCID() string {
|
|
return station.rvcid
|
|
}
|
|
|
|
func (station *StationURL) Natm() string {
|
|
return station.natm
|
|
}
|
|
|
|
func (station *StationURL) Natf() string {
|
|
return station.natf
|
|
}
|
|
|
|
func (station *StationURL) Upnp() string {
|
|
return station.upnp
|
|
}
|
|
|
|
func (station *StationURL) Pmp() string {
|
|
return station.pmp
|
|
}
|
|
|
|
func (station *StationURL) ProbeInit() string {
|
|
return station.probeinit
|
|
}
|
|
|
|
func (station *StationURL) PRID() string {
|
|
return station.prid
|
|
}
|
|
|
|
// FromString parses the StationURL data from a string
|
|
func (station *StationURL) FromString(str string) {
|
|
split := strings.Split(str, ":/")
|
|
|
|
station.scheme = split[0]
|
|
fields := split[1]
|
|
|
|
params := strings.Split(fields, ";")
|
|
|
|
for i := 0; i < len(params); i++ {
|
|
param := params[i]
|
|
split = strings.Split(param, "=")
|
|
|
|
name := split[0]
|
|
value := split[1]
|
|
|
|
switch name {
|
|
case "address":
|
|
station.address = value
|
|
case "port":
|
|
station.port = value
|
|
case "stream":
|
|
station.stream = value
|
|
case "sid":
|
|
station.sid = value
|
|
case "CID":
|
|
station.cid = value
|
|
case "PID":
|
|
station.pid = value
|
|
case "type":
|
|
station.transportType = value
|
|
case "RVCID":
|
|
station.rvcid = value
|
|
case "natm":
|
|
station.natm = value
|
|
case "natf":
|
|
station.natf = value
|
|
case "upnp":
|
|
station.upnp = value
|
|
case "pmp":
|
|
station.pmp = value
|
|
case "probeinit":
|
|
station.probeinit = value
|
|
case "PRID":
|
|
station.prid = value
|
|
}
|
|
}
|
|
}
|
|
|
|
// EncodeToString encodes the StationURL into a string
|
|
func (station *StationURL) EncodeToString() string {
|
|
fields := []string{}
|
|
|
|
if station.address != "" {
|
|
fields = append(fields, "address="+station.address)
|
|
}
|
|
|
|
if station.port != "" {
|
|
fields = append(fields, "port="+station.port)
|
|
}
|
|
|
|
if station.stream != "" {
|
|
fields = append(fields, "stream="+station.stream)
|
|
}
|
|
|
|
if station.sid != "" {
|
|
fields = append(fields, "sid="+station.sid)
|
|
}
|
|
|
|
if station.cid != "" {
|
|
fields = append(fields, "CID="+station.cid)
|
|
}
|
|
|
|
if station.pid != "" {
|
|
fields = append(fields, "PID="+station.pid)
|
|
}
|
|
|
|
if station.transportType != "" {
|
|
fields = append(fields, "type="+station.transportType)
|
|
}
|
|
|
|
if station.rvcid != "" {
|
|
fields = append(fields, "RVCID="+station.rvcid)
|
|
}
|
|
|
|
if station.natm != "" {
|
|
fields = append(fields, "natm="+station.natm)
|
|
}
|
|
|
|
if station.natf != "" {
|
|
fields = append(fields, "natf="+station.natf)
|
|
}
|
|
|
|
if station.upnp != "" {
|
|
fields = append(fields, "upnp="+station.upnp)
|
|
}
|
|
|
|
if station.pmp != "" {
|
|
fields = append(fields, "pmp="+station.pmp)
|
|
}
|
|
|
|
if station.probeinit != "" {
|
|
fields = append(fields, "probeinit="+station.probeinit)
|
|
}
|
|
|
|
if station.prid != "" {
|
|
fields = append(fields, "PRID="+station.prid)
|
|
}
|
|
|
|
return station.scheme + ":/" + strings.Join(fields, ";")
|
|
}
|
|
|
|
func NewStationURL(str string) *StationURL {
|
|
station := &StationURL{}
|
|
|
|
if str != "" {
|
|
station.FromString(str)
|
|
}
|
|
|
|
return station
|
|
}
|
|
|
|
// Result is sent in methods which query large objects
|
|
type Result struct {
|
|
code uint32
|
|
}
|
|
|
|
// IsSuccess returns true if the Result is a success
|
|
func (result *Result) IsSuccess() bool {
|
|
return int(result.code)&errorMask == 0
|
|
}
|
|
|
|
// IsError returns true if the Result is a error
|
|
func (result *Result) IsError() bool {
|
|
return int(result.code)&errorMask != 0
|
|
}
|
|
|
|
// ExtractFromStream extracts a Result structure from a stream
|
|
func (result *Result) ExtractFromStream(stream *StreamIn) error {
|
|
result.code = stream.ReadUInt32LE()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Bytes encodes the Result and returns a byte array
|
|
func (result *Result) Bytes(stream *StreamOut) []byte {
|
|
stream.WriteUInt32LE(result.code)
|
|
|
|
return stream.Bytes()
|
|
}
|
|
|
|
// NewResult returns a new Result
|
|
func NewResult(code uint32) *Result {
|
|
return &Result{code}
|
|
}
|
|
|
|
// NewResultSuccess returns a new Result set as a success
|
|
func NewResultSuccess(code uint32) *Result {
|
|
return NewResult(uint32(int(code) & ^errorMask))
|
|
}
|
|
|
|
// NewResultError returns a new Result set as an error
|
|
func NewResultError(code uint32) *Result {
|
|
return NewResult(uint32(int(code) | errorMask))
|
|
}
|
|
|
|
// ResultRange is sent in methods which query large objects
|
|
type ResultRange struct {
|
|
Offset uint32
|
|
Length uint32
|
|
Structure
|
|
}
|
|
|
|
// ExtractFromStream extracts a ResultRange structure from a stream
|
|
func (resultRange *ResultRange) ExtractFromStream(stream *StreamIn) error {
|
|
resultRange.Offset = stream.ReadUInt32LE()
|
|
resultRange.Length = stream.ReadUInt32LE()
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewResultRange returns a new ResultRange
|
|
func NewResultRange() *ResultRange {
|
|
return &ResultRange{}
|
|
}
|