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 }