746 lines
15 KiB
Go
746 lines
15 KiB
Go
package structs
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
// PacketStruct represents a runtime packet structure that can be filled with data
|
|
type PacketStruct struct {
|
|
definition *PacketVersion
|
|
data map[string]interface{}
|
|
arrays map[string][]interface{}
|
|
}
|
|
|
|
// NewPacketStruct creates a new packet struct from a definition
|
|
func NewPacketStruct(def *PacketVersion) *PacketStruct {
|
|
ps := &PacketStruct{
|
|
definition: def,
|
|
data: make(map[string]interface{}),
|
|
arrays: make(map[string][]interface{}),
|
|
}
|
|
|
|
// Initialize with default values
|
|
for _, field := range def.Fields {
|
|
if field.Default != nil {
|
|
ps.data[field.Name] = field.Default
|
|
}
|
|
}
|
|
|
|
return ps
|
|
}
|
|
|
|
// Set sets a field value
|
|
func (ps *PacketStruct) Set(name string, value interface{}) error {
|
|
// Check if field exists in definition
|
|
found := false
|
|
for _, field := range ps.definition.Fields {
|
|
if field.Name == name {
|
|
found = true
|
|
// Validate type if needed
|
|
ps.data[name] = value
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return fmt.Errorf("field %s not found in packet definition", name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Get retrieves a field value
|
|
func (ps *PacketStruct) Get(name string) (interface{}, bool) {
|
|
value, exists := ps.data[name]
|
|
return value, exists
|
|
}
|
|
|
|
// SetArray sets an array field
|
|
func (ps *PacketStruct) SetArray(name string, values []interface{}) error {
|
|
ps.arrays[name] = values
|
|
return nil
|
|
}
|
|
|
|
// GetArray retrieves an array field
|
|
func (ps *PacketStruct) GetArray(name string) ([]interface{}, bool) {
|
|
values, exists := ps.arrays[name]
|
|
return values, exists
|
|
}
|
|
|
|
// Serialize converts the packet struct to bytes
|
|
func (ps *PacketStruct) Serialize() ([]byte, error) {
|
|
buf := &bytes.Buffer{}
|
|
|
|
for _, field := range ps.definition.Fields {
|
|
// Check conditionals
|
|
if !ps.checkCondition(field) {
|
|
continue
|
|
}
|
|
|
|
// Handle arrays
|
|
if field.Type == FieldTypeArray {
|
|
if err := ps.serializeArray(buf, field); err != nil {
|
|
return nil, err
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Get value
|
|
value, exists := ps.data[field.Name]
|
|
if !exists && !field.Optional {
|
|
// Use zero value if not set
|
|
value = ps.getZeroValue(field.Type)
|
|
}
|
|
|
|
// Serialize based on type
|
|
if err := ps.serializeField(buf, field, value); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
// Deserialize reads data from bytes into the packet struct
|
|
func (ps *PacketStruct) Deserialize(data []byte) error {
|
|
buf := bytes.NewReader(data)
|
|
|
|
for _, field := range ps.definition.Fields {
|
|
// Check conditionals
|
|
if !ps.checkCondition(field) {
|
|
continue
|
|
}
|
|
|
|
// Handle arrays
|
|
if field.Type == FieldTypeArray {
|
|
if err := ps.deserializeArray(buf, field); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Deserialize based on type
|
|
value, err := ps.deserializeField(buf, field)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ps.data[field.Name] = value
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// serializeField writes a single field to the buffer
|
|
func (ps *PacketStruct) serializeField(buf *bytes.Buffer, field Field, value interface{}) error {
|
|
switch field.Type {
|
|
case FieldTypeUInt8:
|
|
v := ps.toUint8(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeUInt16:
|
|
v := ps.toUint16(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeUInt32:
|
|
v := ps.toUint32(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeUInt64:
|
|
v := ps.toUint64(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeInt8:
|
|
v := ps.toInt8(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeInt16:
|
|
v := ps.toInt16(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeInt32:
|
|
v := ps.toInt32(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeInt64:
|
|
v := ps.toInt64(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeFloat:
|
|
v := ps.toFloat32(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeDouble:
|
|
v := ps.toFloat64(value)
|
|
return binary.Write(buf, binary.LittleEndian, v)
|
|
|
|
case FieldTypeChar:
|
|
return ps.serializeChar(buf, field, value)
|
|
|
|
case FieldTypeString8:
|
|
return ps.serializeString8(buf, value)
|
|
|
|
case FieldTypeString16:
|
|
return ps.serializeString16(buf, value)
|
|
|
|
case FieldTypeString32:
|
|
return ps.serializeString32(buf, value)
|
|
|
|
case FieldTypeColor, FieldTypeEQ2Color:
|
|
return ps.serializeColor(buf, value)
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported field type: %v", field.Type)
|
|
}
|
|
}
|
|
|
|
// deserializeField reads a single field from the buffer
|
|
func (ps *PacketStruct) deserializeField(buf *bytes.Reader, field Field) (interface{}, error) {
|
|
switch field.Type {
|
|
case FieldTypeUInt8:
|
|
var v uint8
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeUInt16:
|
|
var v uint16
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeUInt32:
|
|
var v uint32
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeUInt64:
|
|
var v uint64
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeInt8:
|
|
var v int8
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeInt16:
|
|
var v int16
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeInt32:
|
|
var v int32
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeInt64:
|
|
var v int64
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeFloat:
|
|
var v float32
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeDouble:
|
|
var v float64
|
|
err := binary.Read(buf, binary.LittleEndian, &v)
|
|
return v, err
|
|
|
|
case FieldTypeChar:
|
|
return ps.deserializeChar(buf, field)
|
|
|
|
case FieldTypeString8:
|
|
return ps.deserializeString8(buf)
|
|
|
|
case FieldTypeString16:
|
|
return ps.deserializeString16(buf)
|
|
|
|
case FieldTypeString32:
|
|
return ps.deserializeString32(buf)
|
|
|
|
case FieldTypeColor, FieldTypeEQ2Color:
|
|
return ps.deserializeColor(buf)
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported field type: %v", field.Type)
|
|
}
|
|
}
|
|
|
|
// String serialization helpers
|
|
func (ps *PacketStruct) serializeString8(buf *bytes.Buffer, value interface{}) error {
|
|
str := ps.toString(value)
|
|
if err := buf.WriteByte(uint8(len(str))); err != nil {
|
|
return err
|
|
}
|
|
_, err := buf.WriteString(str)
|
|
return err
|
|
}
|
|
|
|
func (ps *PacketStruct) serializeString16(buf *bytes.Buffer, value interface{}) error {
|
|
str := ps.toString(value)
|
|
if err := binary.Write(buf, binary.LittleEndian, uint16(len(str))); err != nil {
|
|
return err
|
|
}
|
|
_, err := buf.WriteString(str)
|
|
return err
|
|
}
|
|
|
|
func (ps *PacketStruct) serializeString32(buf *bytes.Buffer, value interface{}) error {
|
|
str := ps.toString(value)
|
|
if err := binary.Write(buf, binary.LittleEndian, uint32(len(str))); err != nil {
|
|
return err
|
|
}
|
|
_, err := buf.WriteString(str)
|
|
return err
|
|
}
|
|
|
|
func (ps *PacketStruct) deserializeString8(buf *bytes.Reader) (string, error) {
|
|
length, err := buf.ReadByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
data := make([]byte, length)
|
|
if _, err := buf.Read(data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|
|
|
|
func (ps *PacketStruct) deserializeString16(buf *bytes.Reader) (string, error) {
|
|
var length uint16
|
|
if err := binary.Read(buf, binary.LittleEndian, &length); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
data := make([]byte, length)
|
|
if _, err := buf.Read(data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|
|
|
|
func (ps *PacketStruct) deserializeString32(buf *bytes.Reader) (string, error) {
|
|
var length uint32
|
|
if err := binary.Read(buf, binary.LittleEndian, &length); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
data := make([]byte, length)
|
|
if _, err := buf.Read(data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|
|
|
|
// Char field serialization (fixed-size string)
|
|
func (ps *PacketStruct) serializeChar(buf *bytes.Buffer, field Field, value interface{}) error {
|
|
str := ps.toString(value)
|
|
size := field.Size
|
|
if size == 0 {
|
|
size = len(str) + 1 // Null-terminated if no size specified
|
|
}
|
|
|
|
// Write string up to size
|
|
written := 0
|
|
for i := 0; i < len(str) && i < size; i++ {
|
|
buf.WriteByte(str[i])
|
|
written++
|
|
}
|
|
|
|
// Pad with zeros
|
|
for written < size {
|
|
buf.WriteByte(0)
|
|
written++
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ps *PacketStruct) deserializeChar(buf *bytes.Reader, field Field) (string, error) {
|
|
size := field.Size
|
|
if size == 0 {
|
|
// Read until null terminator
|
|
var result []byte
|
|
for {
|
|
b, err := buf.ReadByte()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if b == 0 {
|
|
break
|
|
}
|
|
result = append(result, b)
|
|
}
|
|
return string(result), nil
|
|
}
|
|
|
|
// Read fixed size
|
|
data := make([]byte, size)
|
|
if _, err := buf.Read(data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Find null terminator
|
|
for i, b := range data {
|
|
if b == 0 {
|
|
return string(data[:i]), nil
|
|
}
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|
|
|
|
// Color serialization
|
|
func (ps *PacketStruct) serializeColor(buf *bytes.Buffer, value interface{}) error {
|
|
// Assume color is represented as uint32 (RGBA)
|
|
color := ps.toUint32(value)
|
|
return binary.Write(buf, binary.LittleEndian, color)
|
|
}
|
|
|
|
func (ps *PacketStruct) deserializeColor(buf *bytes.Reader) (uint32, error) {
|
|
var color uint32
|
|
err := binary.Read(buf, binary.LittleEndian, &color)
|
|
return color, err
|
|
}
|
|
|
|
// Array handling
|
|
func (ps *PacketStruct) serializeArray(buf *bytes.Buffer, field Field) error {
|
|
values, exists := ps.arrays[field.Name]
|
|
if !exists {
|
|
values = []interface{}{}
|
|
}
|
|
|
|
// Write each element
|
|
for _, value := range values {
|
|
// Create a temporary field for the element
|
|
elemField := Field{
|
|
Name: field.Name + "_elem",
|
|
Type: field.Type, // This should be the element type, not array
|
|
}
|
|
if err := ps.serializeField(buf, elemField, value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ps *PacketStruct) deserializeArray(buf *bytes.Reader, field Field) error {
|
|
// Get array size
|
|
size := field.Size
|
|
if field.SizeVar != "" {
|
|
// Size is stored in another field
|
|
if sizeVal, ok := ps.data[field.SizeVar]; ok {
|
|
size = int(ps.toUint32(sizeVal))
|
|
}
|
|
}
|
|
|
|
values := make([]interface{}, 0, size)
|
|
for i := 0; i < size; i++ {
|
|
// Create a temporary field for the element
|
|
elemField := Field{
|
|
Name: field.Name + "_elem",
|
|
Type: field.Type, // This should be the element type, not array
|
|
}
|
|
value, err := ps.deserializeField(buf, elemField)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
values = append(values, value)
|
|
}
|
|
|
|
ps.arrays[field.Name] = values
|
|
return nil
|
|
}
|
|
|
|
// Condition checking
|
|
func (ps *PacketStruct) checkCondition(field Field) bool {
|
|
// Check IfSet condition
|
|
if field.IfSet != "" {
|
|
if val, exists := ps.data[field.IfSet]; !exists || val == nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Check IfNotSet condition
|
|
if field.IfNotSet != "" {
|
|
if val, exists := ps.data[field.IfNotSet]; exists && val != nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Type conversion helpers
|
|
func (ps *PacketStruct) toUint8(value interface{}) uint8 {
|
|
switch v := value.(type) {
|
|
case uint8:
|
|
return v
|
|
case int:
|
|
return uint8(v)
|
|
case uint:
|
|
return uint8(v)
|
|
case uint16:
|
|
return uint8(v)
|
|
case uint32:
|
|
return uint8(v)
|
|
case uint64:
|
|
return uint8(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toUint16(value interface{}) uint16 {
|
|
switch v := value.(type) {
|
|
case uint16:
|
|
return v
|
|
case int:
|
|
return uint16(v)
|
|
case uint:
|
|
return uint16(v)
|
|
case uint8:
|
|
return uint16(v)
|
|
case uint32:
|
|
return uint16(v)
|
|
case uint64:
|
|
return uint16(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toUint32(value interface{}) uint32 {
|
|
switch v := value.(type) {
|
|
case uint32:
|
|
return v
|
|
case int:
|
|
return uint32(v)
|
|
case uint:
|
|
return uint32(v)
|
|
case uint8:
|
|
return uint32(v)
|
|
case uint16:
|
|
return uint32(v)
|
|
case uint64:
|
|
return uint32(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toUint64(value interface{}) uint64 {
|
|
switch v := value.(type) {
|
|
case uint64:
|
|
return v
|
|
case int:
|
|
return uint64(v)
|
|
case uint:
|
|
return uint64(v)
|
|
case uint8:
|
|
return uint64(v)
|
|
case uint16:
|
|
return uint64(v)
|
|
case uint32:
|
|
return uint64(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toInt8(value interface{}) int8 {
|
|
switch v := value.(type) {
|
|
case int8:
|
|
return v
|
|
case int:
|
|
return int8(v)
|
|
case int16:
|
|
return int8(v)
|
|
case int32:
|
|
return int8(v)
|
|
case int64:
|
|
return int8(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toInt16(value interface{}) int16 {
|
|
switch v := value.(type) {
|
|
case int16:
|
|
return v
|
|
case int:
|
|
return int16(v)
|
|
case int8:
|
|
return int16(v)
|
|
case int32:
|
|
return int16(v)
|
|
case int64:
|
|
return int16(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toInt32(value interface{}) int32 {
|
|
switch v := value.(type) {
|
|
case int32:
|
|
return v
|
|
case int:
|
|
return int32(v)
|
|
case int8:
|
|
return int32(v)
|
|
case int16:
|
|
return int32(v)
|
|
case int64:
|
|
return int32(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toInt64(value interface{}) int64 {
|
|
switch v := value.(type) {
|
|
case int64:
|
|
return v
|
|
case int:
|
|
return int64(v)
|
|
case int8:
|
|
return int64(v)
|
|
case int16:
|
|
return int64(v)
|
|
case int32:
|
|
return int64(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toFloat32(value interface{}) float32 {
|
|
switch v := value.(type) {
|
|
case float32:
|
|
return v
|
|
case float64:
|
|
return float32(v)
|
|
case int:
|
|
return float32(v)
|
|
case uint:
|
|
return float32(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toFloat64(value interface{}) float64 {
|
|
switch v := value.(type) {
|
|
case float64:
|
|
return v
|
|
case float32:
|
|
return float64(v)
|
|
case int:
|
|
return float64(v)
|
|
case uint:
|
|
return float64(v)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func (ps *PacketStruct) toString(value interface{}) string {
|
|
switch v := value.(type) {
|
|
case string:
|
|
return v
|
|
case []byte:
|
|
return string(v)
|
|
default:
|
|
return fmt.Sprintf("%v", v)
|
|
}
|
|
}
|
|
|
|
// getZeroValue returns the zero value for a field type
|
|
func (ps *PacketStruct) getZeroValue(ft FieldType) interface{} {
|
|
switch ft {
|
|
case FieldTypeUInt8:
|
|
return uint8(0)
|
|
case FieldTypeUInt16:
|
|
return uint16(0)
|
|
case FieldTypeUInt32:
|
|
return uint32(0)
|
|
case FieldTypeUInt64:
|
|
return uint64(0)
|
|
case FieldTypeInt8:
|
|
return int8(0)
|
|
case FieldTypeInt16:
|
|
return int16(0)
|
|
case FieldTypeInt32:
|
|
return int32(0)
|
|
case FieldTypeInt64:
|
|
return int64(0)
|
|
case FieldTypeFloat:
|
|
return float32(0)
|
|
case FieldTypeDouble:
|
|
return float64(0)
|
|
case FieldTypeChar, FieldTypeString8, FieldTypeString16, FieldTypeString32:
|
|
return ""
|
|
case FieldTypeColor, FieldTypeEQ2Color:
|
|
return uint32(0)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// GetSize returns the serialized size of the packet struct
|
|
func (ps *PacketStruct) GetSize() int {
|
|
size := 0
|
|
|
|
for _, field := range ps.definition.Fields {
|
|
if !ps.checkCondition(field) {
|
|
continue
|
|
}
|
|
|
|
fieldSize := GetFieldSize(field.Type)
|
|
if fieldSize > 0 {
|
|
size += fieldSize
|
|
} else {
|
|
// Variable size field
|
|
switch field.Type {
|
|
case FieldTypeChar:
|
|
if field.Size > 0 {
|
|
size += field.Size
|
|
} else {
|
|
// Null-terminated string
|
|
if val, ok := ps.data[field.Name]; ok {
|
|
size += len(ps.toString(val)) + 1
|
|
} else {
|
|
size += 1 // Just null terminator
|
|
}
|
|
}
|
|
case FieldTypeString8:
|
|
size += 1 // Length byte
|
|
if val, ok := ps.data[field.Name]; ok {
|
|
size += len(ps.toString(val))
|
|
}
|
|
case FieldTypeString16:
|
|
size += 2 // Length uint16
|
|
if val, ok := ps.data[field.Name]; ok {
|
|
size += len(ps.toString(val))
|
|
}
|
|
case FieldTypeString32:
|
|
size += 4 // Length uint32
|
|
if val, ok := ps.data[field.Name]; ok {
|
|
size += len(ps.toString(val))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return size
|
|
} |