From 9c35904ecf76553bb2a2cd7b341cd434a3a2b187 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Tue, 29 Jul 2025 22:23:29 -0500 Subject: [PATCH] bring in more common types from emu --- internal/common/types.go | 202 +++++++++++++++++++++++++++-- internal/packets/parser/context.go | 68 +++++++--- internal/packets/parser/parser.go | 34 ++--- internal/packets/parser/structs.go | 19 ++- 4 files changed, 269 insertions(+), 54 deletions(-) diff --git a/internal/common/types.go b/internal/common/types.go index dee72a9..bed6bee 100644 --- a/internal/common/types.go +++ b/internal/common/types.go @@ -2,32 +2,33 @@ package common type EQ2DataType int +// Constants matching data_struct.hpp exactly const ( - TypeUint8 EQ2DataType = iota - TypeUint16 - TypeUint32 - TypeUint64 + TypeNone EQ2DataType = iota TypeInt8 TypeInt16 TypeInt32 TypeInt64 - TypeChar TypeFloat TypeDouble + TypeColor + TypeSInt8 + TypeSInt16 + TypeSInt32 + TypeChar TypeString8 TypeString16 TypeString32 - TypeColor TypeEquipment - TypeItem TypeArray + TypeItem + TypeSInt64 ) -// Core EQ2 structures type EQ2Color struct { - Red uint8 - Green uint8 - Blue uint8 + Red int8 + Green int8 + Blue int8 } type EQ2EquipmentItem struct { @@ -37,16 +38,189 @@ type EQ2EquipmentItem struct { } type EQ2String8 struct { - Size uint8 + Size int8 Data string } type EQ2String16 struct { - Size uint16 + Size int16 Data string } type EQ2String32 struct { - Size uint32 + Size int32 Data string } + +const ( + SlotPrimary = iota + SlotSecondary + SlotHead + SlotChest + SlotShoulders + SlotForearms + SlotHands + SlotLegs + SlotFeet + SlotLeftRing + SlotRightRing + SlotEars + SlotNeck + SlotLeftWrist + SlotRightWrist + SlotRanged + SlotAmmo + SlotWaist + SlotActivate1 + SlotActivate2 + SlotTextures + SlotHair + SlotBeard + SlotNakedChest + SlotNakedLegs +) + +type EQ2Equipment struct { + EquipID [25]int16 // Equipment item IDs for each slot + Color [25]EQ2Color // Primary colors for each equipment slot + Highlight [25]EQ2Color // Highlight colors for each equipment slot +} + +type CharFeatures struct { + HairType int16 // Hair style type + HairFaceType int16 // Facial hair type + WingType int16 // Wing type (for races with wings) + ChestType int16 // Chest appearance type + LegsType int16 // Leg appearance type + EyeType [3]int8 // Eye shape variations + EarType [3]int8 // Ear shape variations + EyeBrowType [3]int8 // Eyebrow shape variations + CheekType [3]int8 // Cheek shape variations + LipType [3]int8 // Lip shape variations + ChinType [3]int8 // Chin shape variations + NoseType [3]int8 // Nose shape variations + BodySize int8 // Body size modifier + BodyAge int8 // Body age appearance + SogaEyeType [3]int8 // SOGA eye shape variations + SogaEarType [3]int8 // SOGA ear shape variations + SogaEyeBrowType [3]int8 // SOGA eyebrow shape variations + SogaCheekType [3]int8 // SOGA cheek shape variations + SogaChestType int16 // SOGA chest appearance type + SogaLegsType int16 // SOGA leg appearance type + SogaLipType [3]int8 // SOGA lip shape variations + SogaChinType [3]int8 // SOGA chin shape variations + SogaNoseType [3]int8 // SOGA nose shape variations + SogaBodySize int8 // SOGA body size modifier + SogaBodyAge int8 // SOGA body age appearance + SogaHairType int16 // SOGA hair style type + SogaHairFaceType int16 // SOGA facial hair type + CombatVoice int16 // Combat voice type + EmoteVoice int16 // Emote voice type + MountModelType int16 // Mount model type + + MountSaddleColor EQ2Color // Mount saddle color + MountColor EQ2Color // Mount body color + SkinColor EQ2Color // Skin color + EyeColor EQ2Color // Eye color + HairTypeColor EQ2Color // Primary hair color + HairTypeHighlightColor EQ2Color // Hair highlight color + HairFaceColor EQ2Color // Facial hair color + HairFaceHighlightColor EQ2Color // Facial hair highlight color + HairHighlightColor EQ2Color // Hair highlight color + WingColor1 EQ2Color // Wing primary color + WingColor2 EQ2Color // Wing secondary color + ShirtColor EQ2Color // Shirt color + PantsColor EQ2Color // Pants color + HairColor1 EQ2Color // Hair primary color + HairColor2 EQ2Color // Hair secondary color + SogaSkinColor EQ2Color // SOGA skin color + SogaEyeColor EQ2Color // SOGA eye color + SogaHairColor1 EQ2Color // SOGA hair primary color + SogaHairColor2 EQ2Color // SOGA hair secondary color + SogaHairTypeColor EQ2Color // SOGA primary hair color + SogaHairTypeHighlightColor EQ2Color // SOGA hair highlight color + SogaHairFaceColor EQ2Color // SOGA facial hair color + SogaHairFaceHighlightColor EQ2Color // SOGA facial hair highlight color + SogaHairHighlightColor EQ2Color // SOGA hair highlight color + ModelColor EQ2Color // Model color + SogaModelColor EQ2Color // SOGA model color +} + +type PositionData struct { + GridID int32 // Current grid identifier + BadGridID int32 // Invalid grid identifier + Speed1 int8 // Primary speed value + Speed2 int8 // Secondary speed value + Dir1 int16 // Primary direction + Dir2 int16 // Secondary direction + Pitch1 int16 // Primary pitch angle + Pitch2 int16 // Secondary pitch angle + Roll int16 // Roll angle + X float32 // X coordinate + Y float32 // Y coordinate + Z float32 // Z coordinate + X2 float32 // Secondary X coordinate + Y2 float32 // Secondary Y coordinate + Z2 float32 // Secondary Z coordinate + X3 float32 // Tertiary X coordinate + Y3 float32 // Tertiary Y coordinate + Z3 float32 // Tertiary Z coordinate + SpawnOrigX float32 // Original spawn X coordinate + SpawnOrigY float32 // Original spawn Y coordinate + SpawnOrigZ float32 // Original spawn Z coordinate + SpawnOrigHeading float32 // Original spawn heading + SpawnOrigPitch float32 // Original spawn pitch + SpawnOrigRoll float32 // Original spawn roll + SpeedX float32 // X-axis speed + SpeedY float32 // Y-axis speed + SpeedZ float32 // Z-axis speed + SideSpeed float32 // Lateral movement speed + VertSpeed float32 // Vertical movement speed + ClientHeading1 float32 // Client-side heading (primary) + ClientHeading2 float32 // Client-side heading (secondary) + ClientPitch float32 // Client-side pitch + CollisionRadius int16 // Collision detection radius + State int16 // Current movement state +} + +type AppearanceData struct { + Pos PositionData // Position and movement data + ModelType int16 // 3D model type identifier + SogaModelType int16 // SOGA model type identifier + ActivityStatus int16 // Current activity status + VisualState int16 // Visual state flags + ActionState int16 // Action state flags + MoodState int16 // Mood/emotion state + EmoteState int16 // Current emote state + Attackable int8 // Whether entity can be attacked + Icon int8 // Icon type to display + HideHood int8 // Whether to hide hood graphics + ShowLevel int8 // Whether to show level + LockedNoLoot int8 // Locked status with no loot + QuestFlag int8 // Quest-related flag + HeroicFlag int8 // Heroic opportunity flag + ShowCommandIcon int8 // Whether to show command icon + DisplayHandIcon int8 // Whether to show hand cursor icon + PlayerFlag int8 // Player character flag + Targetable int8 // Whether entity can be targeted + DisplayName int8 // Whether to display name + SubTitle [255]byte // Subtitle text (Guild name) + DisplayHP int32 // Health percentage (0 = 100%) + PowerLeft int32 // Power remaining (bar hidden if >=100) + AdventureClass int8 // Adventure class identifier + TradeskillClass int8 // Tradeskill class identifier + Level int8 // Character level + TradeskillLevel int8 // Tradeskill level + MinLevel int8 // Minimum level for encounters + MaxLevel int8 // Maximum level for encounters + Difficulty int8 // Encounter difficulty rating + Visible int16 // Visibility state (02 = normal, 15 = shadow) + Name [128]byte // Entity name + LastName [64]byte // Last name (for players) + PrefixTitle [128]byte // Title prefix + SuffixTitle [128]byte // Title suffix + Race int8 // Race identifier + Gender int8 // Gender identifier + Randomize int32 // Randomization seed + LuaRaceID int8 // Lua script race identifier +} diff --git a/internal/packets/parser/context.go b/internal/packets/parser/context.go index e063575..9eeb027 100644 --- a/internal/packets/parser/context.go +++ b/internal/packets/parser/context.go @@ -26,6 +26,7 @@ func NewContext(data []byte, version uint32, flags uint64) *ParseContext { } } +// Unsigned integer readers func (ctx *ParseContext) readUint8() uint8 { val := ctx.data[ctx.offset] ctx.offset++ @@ -50,6 +51,32 @@ func (ctx *ParseContext) readUint64() uint64 { return val } +// Signed integer readers +func (ctx *ParseContext) readSint8() int8 { + val := int8(ctx.data[ctx.offset]) + ctx.offset++ + return val +} + +func (ctx *ParseContext) readSint16() int16 { + val := int16(binary.LittleEndian.Uint16(ctx.data[ctx.offset:])) + ctx.offset += 2 + return val +} + +func (ctx *ParseContext) readSint32() int32 { + val := int32(binary.LittleEndian.Uint32(ctx.data[ctx.offset:])) + ctx.offset += 4 + return val +} + +func (ctx *ParseContext) readSint64() int64 { + val := int64(binary.LittleEndian.Uint64(ctx.data[ctx.offset:])) + ctx.offset += 8 + return val +} + +// Oversized readers func (ctx *ParseContext) readOversizedUint8(threshold int) uint8 { if ctx.data[ctx.offset] == byte(threshold) { ctx.offset++ @@ -74,22 +101,17 @@ func (ctx *ParseContext) readOversizedUint32(threshold int) uint32 { return uint32(ctx.readUint16()) } -func (ctx *ParseContext) readString8() string { - length := ctx.readUint8() - str := string(ctx.data[ctx.offset : ctx.offset+int(length)]) - ctx.offset += int(length) - return str -} - -func (ctx *ParseContext) readString16() string { - length := ctx.readUint16() - str := string(ctx.data[ctx.offset : ctx.offset+int(length)]) - ctx.offset += int(length) - return str +func (ctx *ParseContext) readOversizedSint16(threshold int) int16 { + val := int8(ctx.data[ctx.offset]) + if val == int8(threshold) || val == int8(-threshold) { + ctx.offset++ + return ctx.readSint16() + } + return int16(ctx.readSint8()) } func (ctx *ParseContext) readEQ2String8() common.EQ2String8 { - size := ctx.readUint8() + size := ctx.readSint8() data := string(ctx.data[ctx.offset : ctx.offset+int(size)]) ctx.offset += int(size) return common.EQ2String8{ @@ -99,7 +121,7 @@ func (ctx *ParseContext) readEQ2String8() common.EQ2String8 { } func (ctx *ParseContext) readEQ2String16() common.EQ2String16 { - size := ctx.readUint16() + size := ctx.readSint16() data := string(ctx.data[ctx.offset : ctx.offset+int(size)]) ctx.offset += int(size) return common.EQ2String16{ @@ -109,7 +131,7 @@ func (ctx *ParseContext) readEQ2String16() common.EQ2String16 { } func (ctx *ParseContext) readEQ2String32() common.EQ2String32 { - size := ctx.readUint32() + size := ctx.readSint32() data := string(ctx.data[ctx.offset : ctx.offset+int(size)]) ctx.offset += int(size) return common.EQ2String32{ @@ -120,9 +142,9 @@ func (ctx *ParseContext) readEQ2String32() common.EQ2String32 { func (ctx *ParseContext) readEQ2Color() common.EQ2Color { return common.EQ2Color{ - Red: ctx.readUint8(), - Green: ctx.readUint8(), - Blue: ctx.readUint8(), + Red: ctx.readSint8(), + Green: ctx.readSint8(), + Blue: ctx.readSint8(), } } @@ -326,7 +348,7 @@ func (ctx *ParseContext) evaluateComparison(varName, valueStr, op string) bool { func (ctx *ParseContext) hasVar(name string) bool { if val, exists := ctx.vars[name]; exists { switch v := val.(type) { - case uint8, uint16, uint32, uint64: + case uint8, uint16, uint32, uint64, int8, int16, int32, int64: return ctx.getVarValue(name) != 0 case string: return v != "" @@ -354,6 +376,14 @@ func (ctx *ParseContext) getVarValue(name string) uint64 { return uint64(v) case uint64: return v + case int8: + return uint64(v) + case int16: + return uint64(v) + case int32: + return uint64(v) + case int64: + return uint64(v) } } return 0 diff --git a/internal/packets/parser/parser.go b/internal/packets/parser/parser.go index a58b8a1..67d301c 100644 --- a/internal/packets/parser/parser.go +++ b/internal/packets/parser/parser.go @@ -55,16 +55,16 @@ type Parser struct { fieldNames []string } -// Type mapping for efficient lookup +// Type mapping using corrected type constants var typeMap = map[string]common.EQ2DataType{ - "u8": common.TypeUint8, - "u16": common.TypeUint16, - "u32": common.TypeUint32, - "u64": common.TypeUint64, - "i8": common.TypeInt8, - "i16": common.TypeInt16, - "i32": common.TypeInt32, - "i64": common.TypeInt64, + "u8": common.TypeInt8, + "u16": common.TypeInt16, + "u32": common.TypeInt32, + "u64": common.TypeInt64, + "i8": common.TypeSInt8, + "i16": common.TypeSInt16, + "i32": common.TypeSInt32, + "i64": common.TypeSInt64, "f32": common.TypeFloat, "f64": common.TypeDouble, "double": common.TypeDouble, @@ -151,24 +151,24 @@ func getDataType(tag string) (common.EQ2DataType, bool) { case 'u': switch tag { case "u8": - return common.TypeUint8, true + return common.TypeInt8, true case "u16": - return common.TypeUint16, true + return common.TypeInt16, true case "u32": - return common.TypeUint32, true + return common.TypeInt32, true case "u64": - return common.TypeUint64, true + return common.TypeInt64, true } case 'i': switch tag { case "i8": - return common.TypeInt8, true + return common.TypeSInt8, true case "i16": - return common.TypeInt16, true + return common.TypeSInt16, true case "i32": - return common.TypeInt32, true + return common.TypeSInt32, true case "i64": - return common.TypeInt64, true + return common.TypeSInt64, true } case 's': switch tag { diff --git a/internal/packets/parser/structs.go b/internal/packets/parser/structs.go index 618be07..a5a7741 100644 --- a/internal/packets/parser/structs.go +++ b/internal/packets/parser/structs.go @@ -47,23 +47,34 @@ func (def *PacketDef) parseStruct(ctx *ParseContext) (map[string]any, error) { func (def *PacketDef) parseField(ctx *ParseContext, field FieldDesc, fieldType common.EQ2DataType, fieldName string) any { switch fieldType { - case common.TypeInt8, common.TypeUint8: + case common.TypeInt8: if field.Oversized > 0 { return ctx.readOversizedUint8(field.Oversized) } return ctx.readUint8() - case common.TypeInt16, common.TypeUint16: + case common.TypeInt16: if field.Oversized > 0 { return ctx.readOversizedUint16(field.Oversized) } return ctx.readUint16() - case common.TypeInt32, common.TypeUint32: + case common.TypeInt32: if field.Oversized > 0 { return ctx.readOversizedUint32(field.Oversized) } return ctx.readUint32() - case common.TypeInt64, common.TypeUint64: + case common.TypeInt64: return ctx.readUint64() + case common.TypeSInt8: + return ctx.readSint8() + case common.TypeSInt16: + if field.Oversized > 0 { + return ctx.readOversizedSint16(field.Oversized) + } + return ctx.readSint16() + case common.TypeSInt32: + return ctx.readSint32() + case common.TypeSInt64: + return ctx.readSint64() case common.TypeString8: return ctx.readEQ2String8() case common.TypeString16: