1
0
Protocol/structs/packet_struct.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
}