286 lines
6.5 KiB
Go
286 lines
6.5 KiB
Go
package cps
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
)
|
|
|
|
// Serializer handles packet serialization/deserialization
|
|
type Serializer struct {
|
|
manager *Manager
|
|
}
|
|
|
|
// NewSerializer creates a packet serializer
|
|
func NewSerializer(manager *Manager) *Serializer {
|
|
return &Serializer{manager: manager}
|
|
}
|
|
|
|
// Serialize converts packet data to bytes
|
|
func (s *Serializer) Serialize(packetName string, version int, data map[string]any) ([]byte, error) {
|
|
packet, err := s.manager.GetPacket(packetName, version)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var buf []byte
|
|
|
|
for _, field := range packet.Fields {
|
|
value, exists := data[field.Name]
|
|
if !exists {
|
|
value = s.getZeroValue(field)
|
|
}
|
|
|
|
fieldBytes, err := s.serializeField(field, value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("serializing field %s: %w", field.Name, err)
|
|
}
|
|
|
|
buf = append(buf, fieldBytes...)
|
|
}
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
// Deserialize parses bytes into packet data
|
|
func (s *Serializer) Deserialize(packetName string, version int, data []byte) (map[string]any, error) {
|
|
packet, err := s.manager.GetPacket(packetName, version)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[string]any)
|
|
offset := 0
|
|
|
|
for _, field := range packet.Fields {
|
|
value, consumed, err := s.deserializeField(field, data[offset:])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("deserializing field %s: %w", field.Name, err)
|
|
}
|
|
|
|
result[field.Name] = value
|
|
offset += consumed
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// serializeField serializes a single field value
|
|
func (s *Serializer) serializeField(field *CompiledField, value any) ([]byte, error) {
|
|
switch field.Type {
|
|
case TypeInt8:
|
|
v, ok := value.(int8)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected int8, got %T", value)
|
|
}
|
|
return []byte{byte(v)}, nil
|
|
|
|
case TypeInt16:
|
|
v, ok := value.(int16)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected int16, got %T", value)
|
|
}
|
|
buf := make([]byte, 2)
|
|
binary.LittleEndian.PutUint16(buf, uint16(v))
|
|
return buf, nil
|
|
|
|
case TypeInt32:
|
|
v, ok := value.(int32)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected int32, got %T", value)
|
|
}
|
|
buf := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(buf, uint32(v))
|
|
return buf, nil
|
|
|
|
case TypeInt64:
|
|
v, ok := value.(int64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected int64, got %T", value)
|
|
}
|
|
buf := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(buf, uint64(v))
|
|
return buf, nil
|
|
|
|
case TypeString8:
|
|
v, ok := value.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
buf := make([]byte, 1+len(v))
|
|
buf[0] = byte(len(v))
|
|
copy(buf[1:], v)
|
|
return buf, nil
|
|
|
|
case TypeString16:
|
|
v, ok := value.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
buf := make([]byte, 2+len(v))
|
|
binary.LittleEndian.PutUint16(buf[0:2], uint16(len(v)))
|
|
copy(buf[2:], v)
|
|
return buf, nil
|
|
|
|
case TypeString32:
|
|
v, ok := value.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected string, got %T", value)
|
|
}
|
|
buf := make([]byte, 4+len(v))
|
|
binary.LittleEndian.PutUint32(buf[0:4], uint32(len(v)))
|
|
copy(buf[4:], v)
|
|
return buf, nil
|
|
|
|
case TypeColor:
|
|
v, ok := value.(uint32)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected uint32, got %T", value)
|
|
}
|
|
buf := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(buf, v)
|
|
return buf, nil
|
|
|
|
case TypeFloat32:
|
|
if field.Size > 0 {
|
|
// Array of floats
|
|
v, ok := value.([]float32)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected []float32, got %T", value)
|
|
}
|
|
buf := make([]byte, len(v)*4)
|
|
for i, f := range v {
|
|
binary.LittleEndian.PutUint32(buf[i*4:], math.Float32bits(f))
|
|
}
|
|
return buf, nil
|
|
} else {
|
|
// Single float
|
|
v, ok := value.(float32)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected float32, got %T", value)
|
|
}
|
|
buf := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(buf, math.Float32bits(v))
|
|
return buf, nil
|
|
}
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported field type: %v", field.Type)
|
|
}
|
|
}
|
|
|
|
// deserializeField deserializes a single field value
|
|
func (s *Serializer) deserializeField(field *CompiledField, data []byte) (any, int, error) {
|
|
switch field.Type {
|
|
case TypeInt8:
|
|
if len(data) < 1 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return int8(data[0]), 1, nil
|
|
|
|
case TypeInt16:
|
|
if len(data) < 2 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return int16(binary.LittleEndian.Uint16(data[0:2])), 2, nil
|
|
|
|
case TypeInt32:
|
|
if len(data) < 4 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return int32(binary.LittleEndian.Uint32(data[0:4])), 4, nil
|
|
|
|
case TypeInt64:
|
|
if len(data) < 8 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return int64(binary.LittleEndian.Uint64(data[0:8])), 8, nil
|
|
|
|
case TypeString8:
|
|
if len(data) < 1 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
strlen := int(data[0])
|
|
if len(data) < 1+strlen {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return string(data[1 : 1+strlen]), 1 + strlen, nil
|
|
|
|
case TypeString16:
|
|
if len(data) < 2 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
strlen := int(binary.LittleEndian.Uint16(data[0:2]))
|
|
if len(data) < 2+strlen {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return string(data[2 : 2+strlen]), 2 + strlen, nil
|
|
|
|
case TypeString32:
|
|
if len(data) < 4 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
strlen := int(binary.LittleEndian.Uint32(data[0:4]))
|
|
if len(data) < 4+strlen {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return string(data[4 : 4+strlen]), 4 + strlen, nil
|
|
|
|
case TypeColor:
|
|
if len(data) < 4 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return binary.LittleEndian.Uint32(data[0:4]), 4, nil
|
|
|
|
case TypeFloat32:
|
|
if field.Size > 0 {
|
|
// Array of floats
|
|
if len(data) < field.Size*4 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
floats := make([]float32, field.Size)
|
|
for i := 0; i < field.Size; i++ {
|
|
bits := binary.LittleEndian.Uint32(data[i*4:])
|
|
floats[i] = math.Float32frombits(bits)
|
|
}
|
|
return floats, field.Size * 4, nil
|
|
} else {
|
|
// Single float
|
|
if len(data) < 4 {
|
|
return nil, 0, io.ErrUnexpectedEOF
|
|
}
|
|
bits := binary.LittleEndian.Uint32(data[0:4])
|
|
return math.Float32frombits(bits), 4, nil
|
|
}
|
|
|
|
default:
|
|
return nil, 0, fmt.Errorf("unsupported field type: %v", field.Type)
|
|
}
|
|
}
|
|
|
|
// getZeroValue returns appropriate zero value for field type
|
|
func (s *Serializer) getZeroValue(field *CompiledField) any {
|
|
switch field.Type {
|
|
case TypeInt8:
|
|
return int8(0)
|
|
case TypeInt16:
|
|
return int16(0)
|
|
case TypeInt32:
|
|
return int32(0)
|
|
case TypeInt64:
|
|
return int64(0)
|
|
case TypeString8, TypeString16, TypeString32:
|
|
return ""
|
|
case TypeColor:
|
|
return uint32(0)
|
|
case TypeFloat32:
|
|
if field.Size > 0 {
|
|
return make([]float32, field.Size)
|
|
}
|
|
return float32(0)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|