125 lines
3.8 KiB
Go

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, common.TypeUint8:
if field.Oversized > 0 {
return ctx.readOversizedUint8(field.Oversized)
}
return ctx.readUint8()
case common.TypeInt16, common.TypeUint16:
if field.Oversized > 0 {
return ctx.readOversizedUint16(field.Oversized)
}
return ctx.readUint16()
case common.TypeInt32, common.TypeUint32:
if field.Oversized > 0 {
return ctx.readOversizedUint32(field.Oversized)
}
return ctx.readUint32()
case common.TypeInt64, common.TypeUint64:
return ctx.readUint64()
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
}