package parser import ( "encoding/binary" "eq2emu/internal/common" "fmt" "io" "reflect" "strconv" "strings" "unsafe" ) // Parser handles reading EQ2 packet data into structs type Parser struct { data []byte offset int endian binary.ByteOrder fieldCache map[string]any // Cache parsed field values for conditions currentStruct reflect.Value // Current struct being parsed } // NewParser creates a new EQ2 packet parser func NewParser(data []byte) *Parser { return &Parser{ data: data, offset: 0, endian: binary.LittleEndian, fieldCache: make(map[string]any), } } // VersionRegistry manages version-specific struct types type VersionRegistry struct { structs map[string]map[string]reflect.Type // [structName][version] = Type } func NewVersionRegistry() *VersionRegistry { return &VersionRegistry{ structs: make(map[string]map[string]reflect.Type), } } // RegisterStruct registers a struct type for a specific version func (vr *VersionRegistry) RegisterStruct(name, version string, structType reflect.Type) { if vr.structs[name] == nil { vr.structs[name] = make(map[string]reflect.Type) } vr.structs[name][version] = structType } // GetStruct returns the appropriate struct type for a version func (vr *VersionRegistry) GetStruct(name, version string) (reflect.Type, bool) { if versions, exists := vr.structs[name]; exists { if structType, exists := versions[version]; exists { return structType, true } // Fallback to nearest lower version return vr.findNearestVersion(name, version) } return nil, false } // findNearestVersion finds the closest version <= requested version func (vr *VersionRegistry) findNearestVersion(name, targetVersion string) (reflect.Type, bool) { versions := vr.structs[name] var bestVersion string var bestType reflect.Type for version, structType := range versions { if version <= targetVersion && version > bestVersion { bestVersion = version bestType = structType } } return bestType, bestVersion != "" } // ParseWithVersion parses using version-specific struct func (p *Parser) ParseWithVersion(registry *VersionRegistry, structName, version string) (any, error) { structType, exists := registry.GetStruct(structName, version) if !exists { return nil, fmt.Errorf("no struct found for %s version %s", structName, version) } // Create new instance ptr := reflect.New(structType) elem := ptr.Elem() // Parse into the struct err := p.ParseStruct(ptr.Interface()) if err != nil { return nil, err } return elem.Interface(), nil } // ParseStruct reads data into a struct using reflection and tags func (p *Parser) ParseStruct(v any) error { val := reflect.ValueOf(v) if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct { return fmt.Errorf("target must be pointer to struct") } elem := val.Elem() typ := elem.Type() p.currentStruct = elem p.fieldCache = make(map[string]any) // Reset cache for new struct for i := 0; i < elem.NumField(); i++ { field := elem.Field(i) fieldType := typ.Field(i) if !field.CanSet() { continue } tag := fieldType.Tag.Get("eq2") if tag == "" || tag == "-" { continue } if err := p.parseField(field, tag); err != nil { return fmt.Errorf("field %s: %w", fieldType.Name, err) } // Cache the field value for conditional evaluation if field.CanInterface() { p.fieldCache[fieldType.Name] = field.Interface() } } return nil } // parseField processes individual struct fields based on their tags func (p *Parser) parseField(field reflect.Value, tag string) error { parts := strings.Split(tag, ",") typeStr := parts[0] var length int = 1 var condition string var maxSize int = -1 var skipOversized bool var dynamicLen string var oversizedValue int = -1 var oversizedByte byte = 127 var type2 string var type2Criteria string // Parse additional tag options for _, part := range parts[1:] { if strings.HasPrefix(part, "len=") { lenVal := part[4:] if l, err := strconv.Atoi(lenVal); err == nil { length = l } else { // Dynamic length reference to another field dynamicLen = lenVal } } else if strings.HasPrefix(part, "if=") { condition = part[3:] } else if strings.HasPrefix(part, "maxsize=") { if m, err := strconv.Atoi(part[8:]); err == nil { maxSize = m } } else if part == "skipoversized" { skipOversized = true } else if strings.HasPrefix(part, "oversized=") { if o, err := strconv.Atoi(part[10:]); err == nil { oversizedValue = o } } else if strings.HasPrefix(part, "oversizedbyte=") { if o, err := strconv.Atoi(part[14:]); err == nil { oversizedByte = byte(o) } } else if strings.HasPrefix(part, "type2=") { type2 = part[6:] } else if strings.HasPrefix(part, "type2criteria=") { type2Criteria = part[14:] } } // Skip field if condition not met if condition != "" && !p.evaluateCondition(condition, field) { return nil } // Handle dynamic length if dynamicLen != "" { if dynLen := p.getDynamicLength(dynamicLen); dynLen > 0 { length = dynLen // Apply max size limit if maxSize > 0 && length > maxSize { if skipOversized { length = maxSize } else { return fmt.Errorf("length %d exceeds maximum %d", length, maxSize) } } } } // Handle oversized values if oversizedValue > 0 && length > oversizedValue { if skipOversized { length = oversizedValue } else { // Use oversized byte value return p.handleOversizedField(field, oversizedByte, length) } } // Choose type based on type2 criteria actualType := typeStr if type2 != "" && type2Criteria != "" && p.evaluateType2Criteria(type2Criteria) { actualType = type2 } switch actualType { case "int8": return p.readInt8(field, length) case "int16": return p.readInt16(field, length) case "int32": return p.readInt32(field, length) case "int64": return p.readInt64(field, length) case "sint8": return p.readSInt8(field, length) case "sint16": return p.readSInt16(field, length) case "sint32": return p.readSInt32(field, length) case "sint64": return p.readSInt64(field, length) case "char": return p.readChar(field, length) case "float": return p.readFloat(field, length) case "double": return p.readDouble(field, length) case "string8", "EQ2_8BitString", "EQ2_8Bit_String": return p.readString8(field) case "string16", "EQ2_16BitString", "EQ2_16Bit_String": return p.readString16(field) case "string32", "EQ2_32BitString", "EQ2_32Bit_String": return p.readString32(field) case "color", "EQ2_Color": return p.readColor(field, length) case "equipment", "EQ2_EquipmentItem": return p.readEquipment(field, length) case "array": return p.readArray(field) case "substruct": return p.readSubstruct(field, length) default: return fmt.Errorf("unknown type: %s", actualType) } } // Type-specific readers func (p *Parser) readInt8(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint8() if err != nil { return err } field.SetUint(uint64(val)) return nil } slice := make([]uint8, length) for i := 0; i < length; i++ { val, err := p.readUint8() if err != nil { return err } slice[i] = val } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readInt16(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint16() if err != nil { return err } field.SetUint(uint64(val)) return nil } slice := make([]uint16, length) for i := 0; i < length; i++ { val, err := p.readUint16() if err != nil { return err } slice[i] = val } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readInt32(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint32() if err != nil { return err } field.SetUint(uint64(val)) return nil } slice := make([]uint32, length) for i := 0; i < length; i++ { val, err := p.readUint32() if err != nil { return err } slice[i] = val } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readInt64(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint64() if err != nil { return err } field.SetUint(val) return nil } slice := make([]uint64, length) for i := 0; i < length; i++ { val, err := p.readUint64() if err != nil { return err } slice[i] = val } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readSInt8(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint8() if err != nil { return err } field.SetInt(int64(int8(val))) return nil } slice := make([]int8, length) for i := 0; i < length; i++ { val, err := p.readUint8() if err != nil { return err } slice[i] = int8(val) } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readSInt16(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint16() if err != nil { return err } field.SetInt(int64(int16(val))) return nil } slice := make([]int16, length) for i := 0; i < length; i++ { val, err := p.readUint16() if err != nil { return err } slice[i] = int16(val) } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readSInt32(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint32() if err != nil { return err } field.SetInt(int64(int32(val))) return nil } slice := make([]int32, length) for i := 0; i < length; i++ { val, err := p.readUint32() if err != nil { return err } slice[i] = int32(val) } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readSInt64(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint64() if err != nil { return err } field.SetInt(int64(val)) return nil } slice := make([]int64, length) for i := 0; i < length; i++ { val, err := p.readUint64() if err != nil { return err } slice[i] = int64(val) } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readChar(field reflect.Value, length int) error { if length == 1 { val, err := p.readUint8() if err != nil { return err } field.SetUint(uint64(val)) return nil } slice := make([]byte, length) err := p.readBytes(slice) if err != nil { return err } field.SetBytes(slice) return nil } func (p *Parser) readFloat(field reflect.Value, length int) error { if length == 1 { val, err := p.readFloat32() if err != nil { return err } field.SetFloat(float64(val)) return nil } slice := make([]float32, length) for i := 0; i < length; i++ { val, err := p.readFloat32() if err != nil { return err } slice[i] = val } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readDouble(field reflect.Value, length int) error { if length == 1 { val, err := p.readFloat64() if err != nil { return err } field.SetFloat(val) return nil } slice := make([]float64, length) for i := 0; i < length; i++ { val, err := p.readFloat64() if err != nil { return err } slice[i] = val } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readString8(field reflect.Value) error { size, err := p.readUint8() if err != nil { return err } data := make([]byte, size) err = p.readBytes(data) if err != nil { return err } str8 := common.EQ2String8{ Size: size, Data: string(data), } field.Set(reflect.ValueOf(str8)) return nil } func (p *Parser) readString16(field reflect.Value) error { size, err := p.readUint16() if err != nil { return err } data := make([]byte, size) err = p.readBytes(data) if err != nil { return err } str16 := common.EQ2String16{ Size: size, Data: string(data), } field.Set(reflect.ValueOf(str16)) return nil } func (p *Parser) readString32(field reflect.Value) error { size, err := p.readUint32() if err != nil { return err } data := make([]byte, size) err = p.readBytes(data) if err != nil { return err } str32 := common.EQ2String32{ Size: size, Data: string(data), } field.Set(reflect.ValueOf(str32)) return nil } func (p *Parser) readColor(field reflect.Value, length int) error { if length == 1 { color := common.EQ2Color{} var err error color.Red, err = p.readUint8() if err != nil { return err } color.Green, err = p.readUint8() if err != nil { return err } color.Blue, err = p.readUint8() if err != nil { return err } field.Set(reflect.ValueOf(color)) return nil } slice := make([]common.EQ2Color, length) for i := 0; i < length; i++ { var err error slice[i].Red, err = p.readUint8() if err != nil { return err } slice[i].Green, err = p.readUint8() if err != nil { return err } slice[i].Blue, err = p.readUint8() if err != nil { return err } } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readEquipment(field reflect.Value, length int) error { if length == 1 { equipment := common.EQ2EquipmentItem{} var err error equipment.Type, err = p.readUint16() if err != nil { return err } equipment.Color.Red, err = p.readUint8() if err != nil { return err } equipment.Color.Green, err = p.readUint8() if err != nil { return err } equipment.Color.Blue, err = p.readUint8() if err != nil { return err } equipment.Highlight.Red, err = p.readUint8() if err != nil { return err } equipment.Highlight.Green, err = p.readUint8() if err != nil { return err } equipment.Highlight.Blue, err = p.readUint8() if err != nil { return err } field.Set(reflect.ValueOf(equipment)) return nil } slice := make([]common.EQ2EquipmentItem, length) for i := 0; i < length; i++ { var err error slice[i].Type, err = p.readUint16() if err != nil { return err } slice[i].Color.Red, err = p.readUint8() if err != nil { return err } slice[i].Color.Green, err = p.readUint8() if err != nil { return err } slice[i].Color.Blue, err = p.readUint8() if err != nil { return err } slice[i].Highlight.Red, err = p.readUint8() if err != nil { return err } slice[i].Highlight.Green, err = p.readUint8() if err != nil { return err } slice[i].Highlight.Blue, err = p.readUint8() if err != nil { return err } } field.Set(reflect.ValueOf(slice)) return nil } func (p *Parser) readArray(field reflect.Value) error { // For arrays, read the size first then parse elements size, err := p.readUint32() if err != nil { return err } // This would need to be implemented based on the specific array type // For now, just skip the specified number of bytes p.offset += int(size) return nil } func (p *Parser) readSubstruct(field reflect.Value, length int) error { if field.Kind() == reflect.Slice { // Create slice for multiple substructs elemType := field.Type().Elem() slice := reflect.MakeSlice(field.Type(), length, length) for i := 0; i < length; i++ { elem := slice.Index(i) if err := p.parseSubstructElement(elem, elemType); err != nil { return fmt.Errorf("substruct element %d: %w", i, err) } } field.Set(slice) return nil } else if field.Kind() == reflect.Struct { // Single substruct return p.parseSubstructElement(field, field.Type()) } else if field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct { // Pointer to struct structType := field.Type().Elem() newStruct := reflect.New(structType) if err := p.parseSubstructElement(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") } func (p *Parser) parseSubstructElement(elem reflect.Value, elemType reflect.Type) error { // Save current state oldStruct := p.currentStruct oldCache := p.fieldCache // Set up for nested parsing p.currentStruct = elem p.fieldCache = make(map[string]any) // Parse all fields of the substruct for i := 0; i < elem.NumField(); i++ { 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 { // Restore state before returning error p.currentStruct = oldStruct p.fieldCache = oldCache return fmt.Errorf("field %s: %w", fieldType.Name, err) } // Cache the field value for conditional evaluation if field.CanInterface() { p.fieldCache[fieldType.Name] = field.Interface() } } // Restore state p.currentStruct = oldStruct p.fieldCache = oldCache return nil } // Low-level read methods func (p *Parser) readUint8() (uint8, error) { if p.offset >= len(p.data) { return 0, io.EOF } val := p.data[p.offset] p.offset++ return val, nil } func (p *Parser) readUint16() (uint16, error) { if p.offset+2 > len(p.data) { return 0, io.EOF } val := p.endian.Uint16(p.data[p.offset:]) p.offset += 2 return val, nil } func (p *Parser) readUint32() (uint32, error) { if p.offset+4 > len(p.data) { return 0, io.EOF } val := p.endian.Uint32(p.data[p.offset:]) p.offset += 4 return val, nil } func (p *Parser) readUint64() (uint64, error) { if p.offset+8 > len(p.data) { return 0, io.EOF } val := p.endian.Uint64(p.data[p.offset:]) p.offset += 8 return val, nil } func (p *Parser) readFloat32() (float32, error) { val, err := p.readUint32() if err != nil { return 0, err } return *(*float32)(unsafe.Pointer(&val)), nil } func (p *Parser) readFloat64() (float64, error) { val, err := p.readUint64() if err != nil { return 0, err } return *(*float64)(unsafe.Pointer(&val)), nil } func (p *Parser) readBytes(buf []byte) error { if p.offset+len(buf) > len(p.data) { return io.EOF } copy(buf, p.data[p.offset:]) p.offset += len(buf) return nil } // evaluateCondition evaluates conditions for conditional fields func (p *Parser) evaluateCondition(condition string, field reflect.Value) bool { // Handle simple comparisons like "Channel==1", "Count>0", "Flag&4" // Parse condition operators operators := []string{"==", "!=", ">=", "<=", ">", "<", "&", "|"} for _, op := range operators { if idx := strings.Index(condition, op); idx > 0 { fieldName := strings.TrimSpace(condition[:idx]) valueStr := strings.TrimSpace(condition[idx+len(op):]) // Get field value from cache cachedValue, exists := p.fieldCache[fieldName] if !exists { // Try to get from current struct if p.currentStruct.IsValid() { if structField := p.currentStruct.FieldByName(fieldName); structField.IsValid() { cachedValue = structField.Interface() } else { return false } } else { return false } } // Convert comparison value compareValue, err := p.convertValue(valueStr, cachedValue) if err != nil { return false } return p.compareValues(cachedValue, compareValue, op) } } // Simple boolean field check if cachedValue, exists := p.fieldCache[condition]; exists { return p.isTruthy(cachedValue) } return true } // getDynamicLength gets length from another field func (p *Parser) getDynamicLength(fieldName string) int { if cachedValue, exists := p.fieldCache[fieldName]; exists { return p.valueToInt(cachedValue) } // Try current struct if p.currentStruct.IsValid() { if field := p.currentStruct.FieldByName(fieldName); field.IsValid() { return p.valueToInt(field.Interface()) } } return 0 } // convertValue converts string to appropriate type for comparison func (p *Parser) convertValue(valueStr string, reference any) (any, error) { switch reference.(type) { case uint8, int8: if val, err := strconv.ParseInt(valueStr, 0, 8); err == nil { return uint8(val), nil } case uint16, int16: if val, err := strconv.ParseInt(valueStr, 0, 16); err == nil { return uint16(val), nil } case uint32, int32: if val, err := strconv.ParseInt(valueStr, 0, 32); err == nil { return uint32(val), nil } case uint64, int64: if val, err := strconv.ParseInt(valueStr, 0, 64); err == nil { return uint64(val), nil } case float32: if val, err := strconv.ParseFloat(valueStr, 32); err == nil { return float32(val), nil } case float64: if val, err := strconv.ParseFloat(valueStr, 64); err == nil { return val, nil } case string: return valueStr, nil } // Try as int for bitwise operations if val, err := strconv.ParseInt(valueStr, 0, 32); err == nil { return int(val), nil } return valueStr, nil } // compareValues performs comparison between two values func (p *Parser) compareValues(a, b any, op string) bool { // Convert to comparable types aVal := p.valueToInt64(a) bVal := p.valueToInt64(b) switch op { case "==": return aVal == bVal case "!=": return aVal != bVal case ">": return aVal > bVal case ">=": return aVal >= bVal case "<": return aVal < bVal case "<=": return aVal <= bVal case "&": return (aVal & bVal) != 0 case "|": return (aVal | bVal) != 0 } return false } // valueToInt converts various types to int func (p *Parser) valueToInt(v any) int { switch val := v.(type) { case uint8: return int(val) case int8: return int(val) case uint16: return int(val) case int16: return int(val) case uint32: return int(val) case int32: return int(val) case uint64: return int(val) case int64: return int(val) case int: return val } return 0 } // valueToInt64 converts various types to int64 for comparison func (p *Parser) valueToInt64(v any) int64 { switch val := v.(type) { case uint8: return int64(val) case int8: return int64(val) case uint16: return int64(val) case int16: return int64(val) case uint32: return int64(val) case int32: return int64(val) case uint64: return int64(val) case int64: return val case int: return int64(val) case float32: return int64(val) case float64: return int64(val) } return 0 } // isTruthy checks if a value is truthy func (p *Parser) isTruthy(v any) bool { switch val := v.(type) { case bool: return val case uint8, int8, uint16, int16, uint32, int32, uint64, int64, int: return p.valueToInt64(val) != 0 case string: return val != "" } return false } // evaluateType2Criteria evaluates type2 criteria for alternative types func (p *Parser) evaluateType2Criteria(criteria string) bool { // Handle criteria like "stat_type!=6" operators := []string{"!=", "==", ">=", "<=", ">", "<"} for _, op := range operators { if idx := strings.Index(criteria, op); idx > 0 { fieldName := strings.TrimSpace(criteria[:idx]) valueStr := strings.TrimSpace(criteria[idx+len(op):]) // Get field value from cache cachedValue, exists := p.fieldCache[fieldName] if !exists { return false } // Convert comparison value compareValue, err := p.convertValue(valueStr, cachedValue) if err != nil { return false } return p.compareValues(cachedValue, compareValue, op) } } return false } // handleOversizedField handles fields that exceed their oversized value func (p *Parser) handleOversizedField(field reflect.Value, oversizedByte byte, length int) error { // For oversized fields, we typically write the oversized byte value // and skip the actual data if field.Kind() == reflect.Slice { // Create slice with oversized byte values slice := reflect.MakeSlice(field.Type(), 1, 1) if slice.Len() > 0 { elem := slice.Index(0) if elem.CanSet() { switch elem.Kind() { case reflect.Uint8: elem.SetUint(uint64(oversizedByte)) case reflect.Int8: elem.SetInt(int64(int8(oversizedByte))) default: elem.SetUint(uint64(oversizedByte)) } } } field.Set(slice) } else { // Single value switch field.Kind() { case reflect.Uint8: field.SetUint(uint64(oversizedByte)) case reflect.Int8: field.SetInt(int64(int8(oversizedByte))) default: field.SetUint(uint64(oversizedByte)) } } return nil } // Position tracking func (p *Parser) Offset() int { return p.offset } func (p *Parser) SetOffset(offset int) { p.offset = offset } func (p *Parser) Remaining() int { return len(p.data) - p.offset }