1103 lines
24 KiB
Go

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
}