package parser import "eq2emu/internal/common" // PacketDef defines a complete packet structure with versioned field ordering type PacketDef struct { Fields map[string]FieldDesc // Field definitions by name Orders map[uint32][]string // Field order by version number } // Creates packet definition with estimated capacity func NewPacketDef(estimatedFields int) *PacketDef { return &PacketDef{ Fields: make(map[string]FieldDesc, estimatedFields), Orders: make(map[uint32][]string, 4), } } func (def *PacketDef) Parse(data []byte, version uint32, flags uint64) (map[string]any, error) { ctx := NewContext(data, version, flags) return def.parseStruct(ctx) } func (def *PacketDef) parseStruct(ctx *ParseContext) (map[string]any, error) { result := make(map[string]any) order := def.getVersionOrder(ctx.version) for _, fieldName := range order { field := def.Fields[fieldName] if !ctx.checkCondition(field.Condition) { continue } fieldType := field.Type if field.Type2 != 0 && ctx.checkCondition(field.Type2Cond) { fieldType = field.Type2 } value := def.parseField(ctx, field, fieldType, fieldName) result[fieldName] = value ctx.setVarWithArrayIndex(fieldName, value) } return result, nil } func (def *PacketDef) parseField(ctx *ParseContext, field FieldDesc, fieldType common.EQ2DataType, fieldName string) any { switch fieldType { case common.TypeInt8: if field.Oversized > 0 { return ctx.readOversizedUint8(field.Oversized) } return ctx.readUint8() case common.TypeInt16: if field.Oversized > 0 { return ctx.readOversizedUint16(field.Oversized) } return ctx.readUint16() case common.TypeInt32: if field.Oversized > 0 { return ctx.readOversizedUint32(field.Oversized) } return ctx.readUint32() case common.TypeInt64: return ctx.readUint64() case common.TypeSInt8: return ctx.readSint8() case common.TypeSInt16: if field.Oversized > 0 { return ctx.readOversizedSint16(field.Oversized) } return ctx.readSint16() case common.TypeSInt32: return ctx.readSint32() case common.TypeSInt64: return ctx.readSint64() case common.TypeString8: return ctx.readEQ2String8() case common.TypeString16: return ctx.readEQ2String16() case common.TypeString32: return ctx.readEQ2String32() case common.TypeChar: return ctx.readBytes(field.Length) case common.TypeFloat: return ctx.readFloat32() case common.TypeDouble: return ctx.readFloat64() case common.TypeColor: return ctx.readEQ2Color() case common.TypeEquipment: return ctx.readEQ2Equipment() case common.TypeArray: size := ctx.getArraySize(field.Condition) if field.MaxArraySize > 0 && size > field.MaxArraySize { size = field.MaxArraySize } result := make([]map[string]any, size) for i := 0; i < size; i++ { ctx.pushArrayIndex(i) item, _ := field.SubDef.parseStruct(ctx) result[i] = item ctx.popArrayIndex() } return result } return nil } func (def *PacketDef) getVersionOrder(version uint32) []string { var bestVersion uint32 for v := range def.Orders { if v <= version && v > bestVersion { bestVersion = v } } return def.Orders[bestVersion] } // FieldDesc describes a single packet field type FieldDesc struct { Type common.EQ2DataType // Primary data type Condition string // Conditional parsing expression Length int // Array length or size for fixed-size fields SubDef *PacketDef // Nested packet definition for arrays Type2 common.EQ2DataType // Alternative data type for conditional parsing Type2Cond string // Condition for using Type2 Oversized int // Threshold for oversized field handling DefaultValue int8 // Default value for initialization MaxArraySize int // Maximum allowed array size Optional bool // Whether this field is optional AddToStruct bool // Whether to include in packet structure AddType common.EQ2DataType // Type to use when adding to packet }