bring in more common types from emu

This commit is contained in:
Sky Johnson 2025-07-29 22:23:29 -05:00
parent d337309b17
commit 9c35904ecf
4 changed files with 269 additions and 54 deletions

View File

@ -2,32 +2,33 @@ package common
type EQ2DataType int type EQ2DataType int
// Constants matching data_struct.hpp exactly
const ( const (
TypeUint8 EQ2DataType = iota TypeNone EQ2DataType = iota
TypeUint16
TypeUint32
TypeUint64
TypeInt8 TypeInt8
TypeInt16 TypeInt16
TypeInt32 TypeInt32
TypeInt64 TypeInt64
TypeChar
TypeFloat TypeFloat
TypeDouble TypeDouble
TypeColor
TypeSInt8
TypeSInt16
TypeSInt32
TypeChar
TypeString8 TypeString8
TypeString16 TypeString16
TypeString32 TypeString32
TypeColor
TypeEquipment TypeEquipment
TypeItem
TypeArray TypeArray
TypeItem
TypeSInt64
) )
// Core EQ2 structures
type EQ2Color struct { type EQ2Color struct {
Red uint8 Red int8
Green uint8 Green int8
Blue uint8 Blue int8
} }
type EQ2EquipmentItem struct { type EQ2EquipmentItem struct {
@ -37,16 +38,189 @@ type EQ2EquipmentItem struct {
} }
type EQ2String8 struct { type EQ2String8 struct {
Size uint8 Size int8
Data string Data string
} }
type EQ2String16 struct { type EQ2String16 struct {
Size uint16 Size int16
Data string Data string
} }
type EQ2String32 struct { type EQ2String32 struct {
Size uint32 Size int32
Data string 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
}

View File

@ -26,6 +26,7 @@ func NewContext(data []byte, version uint32, flags uint64) *ParseContext {
} }
} }
// Unsigned integer readers
func (ctx *ParseContext) readUint8() uint8 { func (ctx *ParseContext) readUint8() uint8 {
val := ctx.data[ctx.offset] val := ctx.data[ctx.offset]
ctx.offset++ ctx.offset++
@ -50,6 +51,32 @@ func (ctx *ParseContext) readUint64() uint64 {
return val 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 { func (ctx *ParseContext) readOversizedUint8(threshold int) uint8 {
if ctx.data[ctx.offset] == byte(threshold) { if ctx.data[ctx.offset] == byte(threshold) {
ctx.offset++ ctx.offset++
@ -74,22 +101,17 @@ func (ctx *ParseContext) readOversizedUint32(threshold int) uint32 {
return uint32(ctx.readUint16()) return uint32(ctx.readUint16())
} }
func (ctx *ParseContext) readString8() string { func (ctx *ParseContext) readOversizedSint16(threshold int) int16 {
length := ctx.readUint8() val := int8(ctx.data[ctx.offset])
str := string(ctx.data[ctx.offset : ctx.offset+int(length)]) if val == int8(threshold) || val == int8(-threshold) {
ctx.offset += int(length) ctx.offset++
return str return ctx.readSint16()
} }
return int16(ctx.readSint8())
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) readEQ2String8() common.EQ2String8 { func (ctx *ParseContext) readEQ2String8() common.EQ2String8 {
size := ctx.readUint8() size := ctx.readSint8()
data := string(ctx.data[ctx.offset : ctx.offset+int(size)]) data := string(ctx.data[ctx.offset : ctx.offset+int(size)])
ctx.offset += int(size) ctx.offset += int(size)
return common.EQ2String8{ return common.EQ2String8{
@ -99,7 +121,7 @@ func (ctx *ParseContext) readEQ2String8() common.EQ2String8 {
} }
func (ctx *ParseContext) readEQ2String16() common.EQ2String16 { func (ctx *ParseContext) readEQ2String16() common.EQ2String16 {
size := ctx.readUint16() size := ctx.readSint16()
data := string(ctx.data[ctx.offset : ctx.offset+int(size)]) data := string(ctx.data[ctx.offset : ctx.offset+int(size)])
ctx.offset += int(size) ctx.offset += int(size)
return common.EQ2String16{ return common.EQ2String16{
@ -109,7 +131,7 @@ func (ctx *ParseContext) readEQ2String16() common.EQ2String16 {
} }
func (ctx *ParseContext) readEQ2String32() common.EQ2String32 { func (ctx *ParseContext) readEQ2String32() common.EQ2String32 {
size := ctx.readUint32() size := ctx.readSint32()
data := string(ctx.data[ctx.offset : ctx.offset+int(size)]) data := string(ctx.data[ctx.offset : ctx.offset+int(size)])
ctx.offset += int(size) ctx.offset += int(size)
return common.EQ2String32{ return common.EQ2String32{
@ -120,9 +142,9 @@ func (ctx *ParseContext) readEQ2String32() common.EQ2String32 {
func (ctx *ParseContext) readEQ2Color() common.EQ2Color { func (ctx *ParseContext) readEQ2Color() common.EQ2Color {
return common.EQ2Color{ return common.EQ2Color{
Red: ctx.readUint8(), Red: ctx.readSint8(),
Green: ctx.readUint8(), Green: ctx.readSint8(),
Blue: ctx.readUint8(), Blue: ctx.readSint8(),
} }
} }
@ -326,7 +348,7 @@ func (ctx *ParseContext) evaluateComparison(varName, valueStr, op string) bool {
func (ctx *ParseContext) hasVar(name string) bool { func (ctx *ParseContext) hasVar(name string) bool {
if val, exists := ctx.vars[name]; exists { if val, exists := ctx.vars[name]; exists {
switch v := val.(type) { switch v := val.(type) {
case uint8, uint16, uint32, uint64: case uint8, uint16, uint32, uint64, int8, int16, int32, int64:
return ctx.getVarValue(name) != 0 return ctx.getVarValue(name) != 0
case string: case string:
return v != "" return v != ""
@ -354,6 +376,14 @@ func (ctx *ParseContext) getVarValue(name string) uint64 {
return uint64(v) return uint64(v)
case uint64: case uint64:
return v return v
case int8:
return uint64(v)
case int16:
return uint64(v)
case int32:
return uint64(v)
case int64:
return uint64(v)
} }
} }
return 0 return 0

View File

@ -55,16 +55,16 @@ type Parser struct {
fieldNames []string fieldNames []string
} }
// Type mapping for efficient lookup // Type mapping using corrected type constants
var typeMap = map[string]common.EQ2DataType{ var typeMap = map[string]common.EQ2DataType{
"u8": common.TypeUint8, "u8": common.TypeInt8,
"u16": common.TypeUint16, "u16": common.TypeInt16,
"u32": common.TypeUint32, "u32": common.TypeInt32,
"u64": common.TypeUint64, "u64": common.TypeInt64,
"i8": common.TypeInt8, "i8": common.TypeSInt8,
"i16": common.TypeInt16, "i16": common.TypeSInt16,
"i32": common.TypeInt32, "i32": common.TypeSInt32,
"i64": common.TypeInt64, "i64": common.TypeSInt64,
"f32": common.TypeFloat, "f32": common.TypeFloat,
"f64": common.TypeDouble, "f64": common.TypeDouble,
"double": common.TypeDouble, "double": common.TypeDouble,
@ -151,24 +151,24 @@ func getDataType(tag string) (common.EQ2DataType, bool) {
case 'u': case 'u':
switch tag { switch tag {
case "u8": case "u8":
return common.TypeUint8, true return common.TypeInt8, true
case "u16": case "u16":
return common.TypeUint16, true return common.TypeInt16, true
case "u32": case "u32":
return common.TypeUint32, true return common.TypeInt32, true
case "u64": case "u64":
return common.TypeUint64, true return common.TypeInt64, true
} }
case 'i': case 'i':
switch tag { switch tag {
case "i8": case "i8":
return common.TypeInt8, true return common.TypeSInt8, true
case "i16": case "i16":
return common.TypeInt16, true return common.TypeSInt16, true
case "i32": case "i32":
return common.TypeInt32, true return common.TypeSInt32, true
case "i64": case "i64":
return common.TypeInt64, true return common.TypeSInt64, true
} }
case 's': case 's':
switch tag { switch tag {

View File

@ -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 { func (def *PacketDef) parseField(ctx *ParseContext, field FieldDesc, fieldType common.EQ2DataType, fieldName string) any {
switch fieldType { switch fieldType {
case common.TypeInt8, common.TypeUint8: case common.TypeInt8:
if field.Oversized > 0 { if field.Oversized > 0 {
return ctx.readOversizedUint8(field.Oversized) return ctx.readOversizedUint8(field.Oversized)
} }
return ctx.readUint8() return ctx.readUint8()
case common.TypeInt16, common.TypeUint16: case common.TypeInt16:
if field.Oversized > 0 { if field.Oversized > 0 {
return ctx.readOversizedUint16(field.Oversized) return ctx.readOversizedUint16(field.Oversized)
} }
return ctx.readUint16() return ctx.readUint16()
case common.TypeInt32, common.TypeUint32: case common.TypeInt32:
if field.Oversized > 0 { if field.Oversized > 0 {
return ctx.readOversizedUint32(field.Oversized) return ctx.readOversizedUint32(field.Oversized)
} }
return ctx.readUint32() return ctx.readUint32()
case common.TypeInt64, common.TypeUint64: case common.TypeInt64:
return ctx.readUint64() 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: case common.TypeString8:
return ctx.readEQ2String8() return ctx.readEQ2String8()
case common.TypeString16: case common.TypeString16: