package parser import ( "eq2emu/internal/common" "reflect" "testing" ) type BasicPacket struct { Channel uint8 `eq2:"int8"` Count uint16 `eq2:"int16"` MessageType uint8 `eq2:"int8,if=Channel==1"` } type ArrayPacket struct { ItemCount uint16 `eq2:"int16"` Items []ItemDescription `eq2:"array,arraysize=ItemCount"` } type ItemDescription struct { InfoHeader WS_ExamineInfoHeader `eq2:"substruct"` Info BaseItemDescription `eq2:"substruct"` ItemType uint8 `eq2:"int8"` } type WS_ExamineInfoHeader struct { Unknown1 uint32 `eq2:"int32"` Unknown2 uint16 `eq2:"int16"` } type BaseItemDescription struct { Name common.EQ2String16 `eq2:"string16"` Category common.EQ2String8 `eq2:"string8"` } type ConditionalPacket struct { HasRewards uint8 `eq2:"int8"` RewardData *RewardInfo `eq2:"substruct,ifvariableset=HasRewards"` CompleteFlag uint8 `eq2:"int8"` ClassicSound uint8 `eq2:"int8,ifvariableset=CompleteFlag"` } type RewardInfo struct { RewardType uint8 `eq2:"int8"` Amount uint32 `eq2:"int32"` } type OversizedPacket struct { LargeDataCount uint16 `eq2:"int16"` LargeData []byte `eq2:"char,len=LargeDataCount,maxsize=10,skipoversized"` } type TypeSwitchPacket struct { StatType uint8 `eq2:"int8"` StatValue any `eq2:"int32,type2=float,type2criteria=StatType!=6"` } type FlagPacket struct { Equipment []common.EQ2EquipmentItem `eq2:"equipment,len=2,ifflag=has_equipment"` Colors []common.EQ2Color `eq2:"color,len=3,ifflagnotset=no_colors"` } type ComplexPacket struct { GroupCount uint8 `eq2:"int8"` Groups []Group `eq2:"array,arraysize=GroupCount"` } type Group struct { MemberCount uint16 `eq2:"int16"` Members []Member `eq2:"array,arraysize=MemberCount"` } type Member struct { Name common.EQ2String16 `eq2:"string16"` Level uint8 `eq2:"int8"` Class uint8 `eq2:"int8"` } // Version-specific structs type PacketV1 struct { Field1 uint8 `eq2:"int8"` Field2 uint16 `eq2:"int16"` } type PacketV2 struct { Field1 uint8 `eq2:"int8"` Field2 uint16 `eq2:"int16"` NewField uint32 `eq2:"int32"` } func TestBasicParsing(t *testing.T) { // Basic packet: Channel=1, Count=100, MessageType=5 data := []byte{0x01, 0x64, 0x00, 0x05} parser := NewParser(data) var packet BasicPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.Channel != 1 { t.Errorf("Expected Channel=1, got %d", packet.Channel) } if packet.Count != 100 { t.Errorf("Expected Count=100, got %d", packet.Count) } if packet.MessageType != 5 { t.Errorf("Expected MessageType=5, got %d", packet.MessageType) } } func TestConditionalSkip(t *testing.T) { // Channel=2 (not 1), so MessageType should be skipped data := []byte{0x02, 0x64, 0x00} parser := NewParser(data) var packet BasicPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.Channel != 2 { t.Errorf("Expected Channel=2, got %d", packet.Channel) } if packet.MessageType != 0 { t.Errorf("Expected MessageType=0 (skipped), got %d", packet.MessageType) } } func TestArrayParsing(t *testing.T) { // ItemCount=2, then 2 items data := []byte{ 0x02, 0x00, // ItemCount = 2 // Item 1 0x01, 0x00, 0x00, 0x00, // InfoHeader.Unknown1 = 1 0x02, 0x00, // InfoHeader.Unknown2 = 2 0x04, 0x00, 't', 'e', 's', 't', // Name: "test" 0x03, 'c', 'a', 't', // Category: "cat" 0x05, // ItemType = 5 // Item 2 0x03, 0x00, 0x00, 0x00, // InfoHeader.Unknown1 = 3 0x04, 0x00, // InfoHeader.Unknown2 = 4 0x05, 0x00, 'i', 't', 'e', 'm', '2', // Name: "item2" 0x04, 't', 'y', 'p', 'e', // Category: "type" 0x06, // ItemType = 6 } parser := NewParser(data) var packet ArrayPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.ItemCount != 2 { t.Errorf("Expected ItemCount=2, got %d", packet.ItemCount) } if len(packet.Items) != 2 { t.Errorf("Expected 2 items, got %d", len(packet.Items)) } if packet.Items[0].Info.Name.Data != "test" { t.Errorf("Expected first item name 'test', got '%s'", packet.Items[0].Info.Name.Data) } if packet.Items[1].ItemType != 6 { t.Errorf("Expected second item type 6, got %d", packet.Items[1].ItemType) } } func TestConditionalFields(t *testing.T) { // HasRewards=1, RewardData present, CompleteFlag=1, ClassicSound present data := []byte{ 0x01, // HasRewards = 1 0x02, // RewardType = 2 0x64, 0x00, 0x00, 0x00, // Amount = 100 0x01, // CompleteFlag = 1 0x05, // ClassicSound = 5 } parser := NewParser(data) var packet ConditionalPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.HasRewards != 1 { t.Errorf("Expected HasRewards=1, got %d", packet.HasRewards) } if packet.RewardData == nil { t.Error("Expected RewardData to be present") } else { if packet.RewardData.RewardType != 2 { t.Errorf("Expected RewardType=2, got %d", packet.RewardData.RewardType) } if packet.RewardData.Amount != 100 { t.Errorf("Expected Amount=100, got %d", packet.RewardData.Amount) } } if packet.ClassicSound != 5 { t.Errorf("Expected ClassicSound=5, got %d", packet.ClassicSound) } } func TestOversizedHandling(t *testing.T) { // LargeDataCount=15 (exceeds maxsize=10), should be truncated data := []byte{ 0x0F, 0x00, // LargeDataCount = 15 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 15 bytes of data } parser := NewParser(data) var packet OversizedPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.LargeDataCount != 15 { t.Errorf("Expected LargeDataCount=15, got %d", packet.LargeDataCount) } if len(packet.LargeData) != 10 { t.Errorf("Expected LargeData length=10 (truncated), got %d", len(packet.LargeData)) } } func TestTypeSwitching(t *testing.T) { // StatType=3 (not 6), so StatValue should be parsed as float data := []byte{ 0x03, // StatType = 3 0x00, 0x00, 0x80, 0x3F, // float32: 1.0 } parser := NewParser(data) var packet TypeSwitchPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.StatType != 3 { t.Errorf("Expected StatType=3, got %d", packet.StatType) } // Should be parsed as float32 since StatType != 6 floatVal, ok := packet.StatValue.(float32) if !ok { t.Errorf("Expected StatValue to be float32, got %T", packet.StatValue) } else if floatVal != 1.0 { t.Errorf("Expected StatValue=1.0, got %f", floatVal) } } func TestFlagBasedConditionals(t *testing.T) { // Equipment data (2 items) data := []byte{ // Equipment item 1 0x01, 0x00, // Type = 1 0xFF, 0x00, 0x00, // Color: Red=255, Green=0, Blue=0 0x00, 0xFF, 0x00, // Highlight: Red=0, Green=255, Blue=0 // Equipment item 2 0x02, 0x00, // Type = 2 0x00, 0x00, 0xFF, // Color: Red=0, Green=0, Blue=255 0xFF, 0xFF, 0x00, // Highlight: Red=255, Green=255, Blue=0 // Colors (3 items) 0x80, 0x80, 0x80, // Color 1: Gray 0x40, 0x40, 0x40, // Color 2: Dark gray 0xC0, 0xC0, 0xC0, // Color 3: Light gray } parser := NewParser(data) parser.SetFlag("has_equipment", true) // no_colors flag is not set, so colors should be parsed var packet FlagPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if len(packet.Equipment) != 2 { t.Errorf("Expected 2 equipment items, got %d", len(packet.Equipment)) } if len(packet.Colors) != 3 { t.Errorf("Expected 3 colors, got %d", len(packet.Colors)) } if packet.Equipment[0].Type != 1 { t.Errorf("Expected first equipment type 1, got %d", packet.Equipment[0].Type) } if packet.Colors[0].Red != 0x80 { t.Errorf("Expected first color red=128, got %d", packet.Colors[0].Red) } } func TestComplexNestedArrays(t *testing.T) { // GroupCount=1, Group has MemberCount=2 data := []byte{ 0x01, // GroupCount = 1 0x02, 0x00, // MemberCount = 2 // Member 1 0x05, 0x00, 'A', 'l', 'i', 'c', 'e', // Name: "Alice" 0x0A, // Level = 10 0x01, // Class = 1 // Member 2 0x03, 0x00, 'B', 'o', 'b', // Name: "Bob" 0x0F, // Level = 15 0x02, // Class = 2 } parser := NewParser(data) var packet ComplexPacket err := parser.ParseStruct(&packet) if err != nil { t.Fatalf("Parse error: %v", err) } if packet.GroupCount != 1 { t.Errorf("Expected GroupCount=1, got %d", packet.GroupCount) } if len(packet.Groups) != 1 { t.Errorf("Expected 1 group, got %d", len(packet.Groups)) } if len(packet.Groups[0].Members) != 2 { t.Errorf("Expected 2 members, got %d", len(packet.Groups[0].Members)) } if packet.Groups[0].Members[0].Name.Data != "Alice" { t.Errorf("Expected first member 'Alice', got '%s'", packet.Groups[0].Members[0].Name.Data) } if packet.Groups[0].Members[1].Level != 15 { t.Errorf("Expected second member level 15, got %d", packet.Groups[0].Members[1].Level) } } func TestVersionRegistry(t *testing.T) { registry := NewVersionRegistry() // Register different versions registry.RegisterStruct("TestPacket", "1.0", reflect.TypeOf(PacketV1{})) registry.RegisterStruct("TestPacket", "2.0", reflect.TypeOf(PacketV2{})) // Test version 1.0 data1 := []byte{0x01, 0x64, 0x00} // Field1=1, Field2=100 parser1 := NewParser(data1) result1, err := parser1.ParseWithVersion(registry, "TestPacket", "1.0") if err != nil { t.Fatalf("Parse v1.0 error: %v", err) } v1Packet, ok := result1.(PacketV1) if !ok { t.Fatal("Expected PacketV1 type") } if v1Packet.Field1 != 1 || v1Packet.Field2 != 100 { t.Errorf("V1 packet values incorrect: Field1=%d, Field2=%d", v1Packet.Field1, v1Packet.Field2) } // Test version 2.0 data2 := []byte{0x02, 0xC8, 0x00, 0x90, 0x01, 0x00, 0x00} // Field1=2, Field2=200, NewField=400 parser2 := NewParser(data2) result2, err := parser2.ParseWithVersion(registry, "TestPacket", "2.0") if err != nil { t.Fatalf("Parse v2.0 error: %v", err) } v2Packet, ok := result2.(PacketV2) if !ok { t.Fatal("Expected PacketV2 type") } if v2Packet.Field1 != 2 || v2Packet.Field2 != 200 || v2Packet.NewField != 400 { t.Errorf("V2 packet values incorrect: Field1=%d, Field2=%d, NewField=%d", v2Packet.Field1, v2Packet.Field2, v2Packet.NewField) } } func TestVersionFallback(t *testing.T) { registry := NewVersionRegistry() registry.RegisterStruct("TestPacket", "1.0", reflect.TypeOf(PacketV1{})) registry.RegisterStruct("TestPacket", "2.0", reflect.TypeOf(PacketV2{})) // Request version 1.5 (doesn't exist), should fall back to 1.0 data := []byte{0x01, 0x64, 0x00} parser := NewParser(data) result, err := parser.ParseWithVersion(registry, "TestPacket", "1.5") if err != nil { t.Fatalf("Parse with fallback error: %v", err) } // Should get v1.0 (nearest lower version) _, ok := result.(PacketV1) if !ok { t.Error("Expected fallback to PacketV1") } } func TestParserPosition(t *testing.T) { data := []byte{0x01, 0x02, 0x03, 0x04} parser := NewParser(data) if parser.Offset() != 0 { t.Errorf("Initial offset should be 0, got %d", parser.Offset()) } parser.readUint8() // Read one byte if parser.Offset() != 1 { t.Errorf("After reading 1 byte, offset should be 1, got %d", parser.Offset()) } if parser.Remaining() != 3 { t.Errorf("After reading 1 byte, remaining should be 3, got %d", parser.Remaining()) } parser.SetOffset(2) if parser.Offset() != 2 { t.Errorf("After SetOffset(2), offset should be 2, got %d", parser.Offset()) } }