eq2go/internal/packets/parser/array_parser.go

215 lines
5.0 KiB
Go

package parser
import (
"fmt"
"reflect"
)
// readArray handles array parsing with full substruct support
func (p *Parser) readArray(field reflect.Value, fieldTag *FieldTag) error {
var arraySize int
if fieldTag.ArraySizeVar != "" {
arraySize = p.getDynamicLength(fieldTag.ArraySizeVar)
} else {
size, err := p.readUint32()
if err != nil {
return err
}
arraySize = int(size)
}
if arraySize == 0 {
return nil
}
if field.Kind() == reflect.Slice {
elemType := field.Type().Elem()
slice := reflect.MakeSlice(field.Type(), arraySize, arraySize)
p.arrayStack = append(p.arrayStack, ArrayContext{
elementType: elemType,
totalSize: arraySize,
sizeVariable: fieldTag.ArraySizeVar,
})
for i := 0; i < arraySize; i++ {
p.arrayStack[len(p.arrayStack)-1].currentIndex = i
elem := slice.Index(i)
if elemType.Kind() == reflect.Struct {
if err := p.parseStructElement(elem, elemType); err != nil {
p.arrayStack = p.arrayStack[:len(p.arrayStack)-1]
return fmt.Errorf("array element %d: %w", i, err)
}
} else {
if err := p.readPrimitiveArrayElement(elem, elemType); err != nil {
p.arrayStack = p.arrayStack[:len(p.arrayStack)-1]
return fmt.Errorf("array element %d: %w", i, err)
}
}
}
p.arrayStack = p.arrayStack[:len(p.arrayStack)-1]
field.Set(slice)
}
return nil
}
// readSubstruct handles substruct parsing
func (p *Parser) readSubstruct(field reflect.Value, length int) error {
if field.Kind() == reflect.Slice {
elemType := field.Type().Elem()
slice := reflect.MakeSlice(field.Type(), length, length)
for i := range length {
elem := slice.Index(i)
if err := p.parseStructElement(elem, elemType); err != nil {
return fmt.Errorf("substruct element %d: %w", i, err)
}
}
field.Set(slice)
return nil
} else if field.Kind() == reflect.Struct {
return p.parseStructElement(field, field.Type())
} else if field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct {
structType := field.Type().Elem()
newStruct := reflect.New(structType)
if err := p.parseStructElement(newStruct.Elem(), structType); err != nil {
return err
}
field.Set(newStruct)
return nil
}
return fmt.Errorf("substruct field must be struct, slice of structs, or pointer to struct")
}
// parseStructElement parses a single struct element
func (p *Parser) parseStructElement(elem reflect.Value, elemType reflect.Type) error {
oldStruct := p.currentStruct
oldCache := p.fieldCache
p.currentStruct = elem
p.fieldCache = make(map[string]any)
p.structStack = append(p.structStack, elem)
for i := range elem.NumField() {
field := elem.Field(i)
fieldType := elemType.Field(i)
if !field.CanSet() {
continue
}
tag := fieldType.Tag.Get("eq2")
if tag == "" || tag == "-" {
continue
}
if err := p.parseField(field, tag); err != nil {
p.structStack = p.structStack[:len(p.structStack)-1]
p.currentStruct = oldStruct
p.fieldCache = oldCache
return fmt.Errorf("field %s: %w", fieldType.Name, err)
}
if field.CanInterface() {
p.fieldCache[fieldType.Name] = field.Interface()
}
}
p.structStack = p.structStack[:len(p.structStack)-1]
p.currentStruct = oldStruct
p.fieldCache = oldCache
return nil
}
// readPrimitiveArrayElement reads primitive array elements
func (p *Parser) readPrimitiveArrayElement(elem reflect.Value, elemType reflect.Type) error {
switch elemType.Kind() {
case reflect.Uint8:
val, err := p.readUint8()
if err != nil {
return err
}
elem.SetUint(uint64(val))
case reflect.Uint16:
val, err := p.readUint16()
if err != nil {
return err
}
elem.SetUint(uint64(val))
case reflect.Uint32:
val, err := p.readUint32()
if err != nil {
return err
}
elem.SetUint(uint64(val))
case reflect.Uint64:
val, err := p.readUint64()
if err != nil {
return err
}
elem.SetUint(val)
case reflect.Int8:
val, err := p.readUint8()
if err != nil {
return err
}
elem.SetInt(int64(int8(val)))
case reflect.Int16:
val, err := p.readUint16()
if err != nil {
return err
}
elem.SetInt(int64(int16(val)))
case reflect.Int32:
val, err := p.readUint32()
if err != nil {
return err
}
elem.SetInt(int64(int32(val)))
case reflect.Int64:
val, err := p.readUint64()
if err != nil {
return err
}
elem.SetInt(int64(val))
case reflect.Float32:
val, err := p.readFloat32()
if err != nil {
return err
}
elem.SetFloat(float64(val))
case reflect.Float64:
val, err := p.readFloat64()
if err != nil {
return err
}
elem.SetFloat(val)
default:
return fmt.Errorf("unsupported primitive array element type: %v", elemType.Kind())
}
return nil
}
// GetCurrentArrayIndex returns current array index for nested parsing
func (p *Parser) GetCurrentArrayIndex() int {
if len(p.arrayStack) > 0 {
return p.arrayStack[len(p.arrayStack)-1].currentIndex
}
return -1
}
// GetCurrentArraySize returns current array size
func (p *Parser) GetCurrentArraySize() int {
if len(p.arrayStack) > 0 {
return p.arrayStack[len(p.arrayStack)-1].totalSize
}
return 0
}