diff --git a/pkg/memory/game_reader.go b/pkg/memory/game_reader.go index d178df5..9a2d316 100644 --- a/pkg/memory/game_reader.go +++ b/pkg/memory/game_reader.go @@ -1,6 +1,7 @@ package memory import ( + "errors" "fmt" "log" "math" @@ -35,6 +36,13 @@ type MercOption struct { Cost int } +type CharacterFlags struct { + Hardcore bool + HasEverDied bool + Expansion bool + Ladder bool +} + var WidgetStateFlags = map[string]uint64{ "WeaponSwap": 0xF2D7CF8E9CC08212, } @@ -494,3 +502,60 @@ func (gd *GameReader) GetActiveWeaponSlot() int { } return state } + +func (gd *GameReader) GetCharacterFlags(characterName string) (CharacterFlags, error) { + const ( + charDataHeaderSize = 16 + charNameOffset = 0x010 + charFlagsOffset = 0x122 + maxCharCount = 47 + + flagHardcore = 0x04 + flagDead = 0x08 + flagExpansion = 0x20 + flagLadder = 0x40 + ) + + charDataPtr := gd.moduleBaseAddressPtr + gd.offset.CharData + if charDataPtr == 0 { + return CharacterFlags{}, errors.New("character data pointer is invalid") + } + + headerBuffer := gd.Process.ReadBytesFromMemory(charDataPtr, charDataHeaderSize) + if len(headerBuffer) < charDataHeaderSize { + return CharacterFlags{}, errors.New("failed to read character data header") + } + + charArrayPtr := uintptr(ReadUIntFromBuffer(headerBuffer, 0x00, Uint64)) + charCount := int(ReadIntFromBuffer(headerBuffer, 0x08, Uint64)) + + if charArrayPtr == 0 || charCount <= 0 || charCount > maxCharCount { + return CharacterFlags{}, fmt.Errorf("invalid character metadata: arrayPtr=%v, count=%d", charArrayPtr, charCount) + } + + charPointerArray := gd.Process.ReadBytesFromMemory(charArrayPtr, uint(charCount*8)) + + for i := 0; i < charCount; i++ { + charStructPtr := uintptr(ReadUIntFromBuffer(charPointerArray, uint(i*8), Uint64)) + if charStructPtr == 0 { + continue + } + + charName := gd.Process.ReadStringFromMemory(charStructPtr+charNameOffset, 0) + + if charName == characterName { + fieldValue := uint16(gd.Process.ReadUInt(charStructPtr+charFlagsOffset, Uint16)) + + flags := CharacterFlags{ + Hardcore: (fieldValue & flagHardcore) != 0, + HasEverDied: (fieldValue & flagDead) != 0, + Expansion: (fieldValue & flagExpansion) != 0, + Ladder: (fieldValue & flagLadder) != 0, + } + + return flags, nil + } + } + + return CharacterFlags{}, fmt.Errorf("character not found: %s", characterName) +} diff --git a/pkg/memory/offset.go b/pkg/memory/offset.go index 12b40ec..1503b5b 100644 --- a/pkg/memory/offset.go +++ b/pkg/memory/offset.go @@ -22,6 +22,7 @@ type Offset struct { Quests uintptr Ping uintptr LegacyGraphics uintptr + CharData uintptr } func calculateOffsets(process *Process) Offset { @@ -118,6 +119,12 @@ func calculateOffsets(process *Process) Offset { legacyGfxPtr := uintptr(process.ReadUInt(pattern+2, Uint32)) legacyGfxOffset := pattern - process.moduleBaseAddressPtr + 7 + legacyGfxPtr + // CharData + pattern = process.FindPattern(memory, "\x48\x8D\x05\x00\x00\x00\x00\x89\x93\xF4\x0C\x00\x00", "xxx????xxxxxx") + bytes = process.ReadBytesFromMemory(pattern+3, 4) + relativeOffset = int32(binary.LittleEndian.Uint32(bytes)) + charDataOffset := pattern - process.moduleBaseAddressPtr + 7 + uintptr(relativeOffset) + return Offset{ GameData: gameDataOffset, UnitTable: unitTableOffset, @@ -136,5 +143,6 @@ func calculateOffsets(process *Process) Offset { Quests: questDataOffset, Ping: pingOffset, LegacyGraphics: legacyGfxOffset, + CharData: charDataOffset, } }