From 2773923ace0e38cdd662d63b7512440565f0fd3f Mon Sep 17 00:00:00 2001 From: CarlPoppa Date: Wed, 22 Oct 2025 10:11:48 +0100 Subject: [PATCH 1/4] Add CharFlags AOB scan --- pkg/memory/offset.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/memory/offset.go b/pkg/memory/offset.go index 4091998..5e22818 100644 --- a/pkg/memory/offset.go +++ b/pkg/memory/offset.go @@ -21,6 +21,7 @@ type Offset struct { Quests uintptr Ping uintptr LegacyGraphics uintptr + CharData uintptr } func calculateOffsets(process *Process) Offset { @@ -112,6 +113,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, @@ -129,5 +136,6 @@ func calculateOffsets(process *Process) Offset { Quests: questDataOffset, Ping: pingOffset, LegacyGraphics: legacyGfxOffset, + CharData: charDataOffset, } } From 90e5f449346fe63073bb5becbbcd7d77eeaa72b1 Mon Sep 17 00:00:00 2001 From: CarlPoppa Date: Wed, 22 Oct 2025 10:12:22 +0100 Subject: [PATCH 2/4] Add GetCharacterFlags func --- pkg/memory/game_reader.go | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/pkg/memory/game_reader.go b/pkg/memory/game_reader.go index 0ce0901..e07210c 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 + Dead bool + Expansion bool + Ladder bool +} + var WidgetStateFlags = map[string]uint64{ "WeaponSwap": 0xF2D7CF8E9CC08212, } @@ -496,3 +504,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, + Dead: (fieldValue & flagDead) != 0, + Expansion: (fieldValue & flagExpansion) != 0, + Ladder: (fieldValue & flagLadder) != 0, + } + + return flags, nil + } + } + + return CharacterFlags{}, fmt.Errorf("character not found: %s", characterName) +} From af6549aac592facec3758c43df97d8fa489c257c Mon Sep 17 00:00:00 2001 From: CarlPoppa Date: Mon, 27 Oct 2025 08:28:33 +0000 Subject: [PATCH 3/4] Rename Dead flag to HasEverDied --- pkg/memory/game_reader.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/memory/game_reader.go b/pkg/memory/game_reader.go index e07210c..d920b8d 100644 --- a/pkg/memory/game_reader.go +++ b/pkg/memory/game_reader.go @@ -37,10 +37,10 @@ type MercOption struct { } type CharacterFlags struct { - Hardcore bool - Dead bool - Expansion bool - Ladder bool + Hardcore bool + HasEverDied bool + Expansion bool + Ladder bool } var WidgetStateFlags = map[string]uint64{ From 73276c57aa5b08e0c16d06b9adb70182e1b6e493 Mon Sep 17 00:00:00 2001 From: guiyomu-dev <22749537+guiyomu-dev@users.noreply.github.com> Date: Mon, 27 Oct 2025 20:24:40 +0100 Subject: [PATCH 4/4] Rename Dead flag to HasEverDied in GetCharacterFlags function --- pkg/memory/game_reader.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/memory/game_reader.go b/pkg/memory/game_reader.go index d920b8d..fe35888 100644 --- a/pkg/memory/game_reader.go +++ b/pkg/memory/game_reader.go @@ -549,10 +549,10 @@ func (gd *GameReader) GetCharacterFlags(characterName string) (CharacterFlags, e fieldValue := uint16(gd.Process.ReadUInt(charStructPtr+charFlagsOffset, Uint16)) flags := CharacterFlags{ - Hardcore: (fieldValue & flagHardcore) != 0, - Dead: (fieldValue & flagDead) != 0, - Expansion: (fieldValue & flagExpansion) != 0, - Ladder: (fieldValue & flagLadder) != 0, + Hardcore: (fieldValue & flagHardcore) != 0, + HasEverDied: (fieldValue & flagDead) != 0, + Expansion: (fieldValue & flagExpansion) != 0, + Ladder: (fieldValue & flagLadder) != 0, } return flags, nil