1103 lines
24 KiB
Go
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
|
|
}
|