update offset
This commit is contained in:
@@ -245,4 +245,5 @@ const (
|
||||
BindDemonUnderling State = 227
|
||||
Herald State = 228
|
||||
Apocalypse State = 229
|
||||
Missilelimit State = 230
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package memory
|
||||
|
||||
const (
|
||||
tzOffline = 0x29B3208
|
||||
tzOffline = 0x25B2300
|
||||
)
|
||||
|
||||
@@ -76,7 +76,7 @@ func (gd *GameReader) GetData() data.Data {
|
||||
|
||||
// Conditionally update monsters
|
||||
monsters := gd.cachedMonsters
|
||||
if now.Sub(gd.monstersLastUpdate) > 200*time.Millisecond {
|
||||
if now.Sub(gd.monstersLastUpdate) > 100*time.Millisecond {
|
||||
monsters = gd.Monsters(pu.Position, hover)
|
||||
gd.cachedMonsters = monsters
|
||||
gd.monstersLastUpdate = now
|
||||
@@ -88,7 +88,7 @@ func (gd *GameReader) GetData() data.Data {
|
||||
// Conditionally update inventory 500ms
|
||||
// Except when hovering over an item
|
||||
inventory := gd.cachedInventory
|
||||
if now.Sub(gd.inventoryLastUpdate) > 500*time.Millisecond ||
|
||||
if now.Sub(gd.inventoryLastUpdate) > 250*time.Millisecond ||
|
||||
(hover.IsHovered && hover.UnitType == 4) { // 4 = Item type
|
||||
inventory = gd.Inventory(rawPlayerUnits, merc, hover)
|
||||
gd.cachedInventory = inventory
|
||||
@@ -97,7 +97,7 @@ func (gd *GameReader) GetData() data.Data {
|
||||
|
||||
// Conditionally update objects
|
||||
objects := gd.cachedObjects
|
||||
if now.Sub(gd.objectsLastUpdate) > 200*time.Millisecond {
|
||||
if now.Sub(gd.objectsLastUpdate) > 100*time.Millisecond {
|
||||
objects = gd.Objects(pu.Position, hover)
|
||||
gd.cachedObjects = objects
|
||||
gd.objectsLastUpdate = now
|
||||
|
||||
@@ -384,8 +384,11 @@ func (gd *GameReader) Inventory(rawPlayerUnits RawPlayerUnits, merc OwnMercenary
|
||||
|
||||
// Set runeword name if applicable
|
||||
if itm.IsRuneword {
|
||||
if runeword, exists := item.RunewordIDMap[prefixes[0]]; exists {
|
||||
itm.RunewordName = runeword
|
||||
for _, prefixID := range prefixes {
|
||||
if runeword, exists := item.RunewordIDMap[prefixID]; exists {
|
||||
itm.RunewordName = runeword
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if itm.RunewordName == "" || itm.RunewordName == item.RunewordHustle {
|
||||
@@ -546,7 +549,7 @@ func (gd *GameReader) Inventory(rawPlayerUnits RawPlayerUnits, merc OwnMercenary
|
||||
|
||||
func (gd *GameReader) getItemStats(statsListExPtr uintptr) (stat.Stats, stat.Stats) {
|
||||
// Initial full and base stats extraction
|
||||
fullStats := gd.getStatsList(statsListExPtr + 0xA8)
|
||||
fullStats := gd.getStatsList(statsListExPtr + 0xE8)
|
||||
baseStats := gd.getStatsList(statsListExPtr + 0x30)
|
||||
|
||||
// Create empty LevelRequire stat .We will update it from inventory
|
||||
|
||||
@@ -26,61 +26,61 @@ type Offset struct {
|
||||
|
||||
func calculateOffsets(_ *Process) Offset {
|
||||
// UnitTable
|
||||
unitTableOffset := uintptr(0x1EA73D0)
|
||||
unitTableOffset := uintptr(0x1EB9430)
|
||||
|
||||
// UI
|
||||
uiOffsetPtr := uintptr(0x1EB70CA)
|
||||
uiOffsetPtr := uintptr(0x1EC912A)
|
||||
|
||||
// Hover
|
||||
hoverOffset := uintptr(0x1DFB080)
|
||||
hoverOffset := uintptr(0x1E0D0A0)
|
||||
|
||||
// Expansion
|
||||
expOffset := uintptr(0x1DFA4E8)
|
||||
expOffset := uintptr(0x1E0C508)
|
||||
|
||||
// Party members offset
|
||||
rosterOffset := uintptr(0x1EBD6E8)
|
||||
rosterOffset := uintptr(0x1ECF748)
|
||||
|
||||
// PanelManagerContainer
|
||||
panelManagerContainerOffset := uintptr(0x1E11E40)
|
||||
panelManagerContainerOffset := uintptr(0x1E23E60)
|
||||
|
||||
// WidgetStates
|
||||
WidgetStatesOffset := uintptr(0x1EDF700)
|
||||
WidgetStatesOffset := uintptr(0x1EF1760)
|
||||
|
||||
// Waypoints
|
||||
WaypointTableOffset := uintptr(0x1D59440)
|
||||
WaypointTableOffset := uintptr(0x1D6B458)
|
||||
|
||||
// FPS
|
||||
fpsOffset := uintptr(0x1D59414)
|
||||
fpsOffset := uintptr(0x1D6B42C)
|
||||
|
||||
// KeyBindings
|
||||
keyBindingsOffset := uintptr(0x19D25B4)
|
||||
keyBindingsOffset := uintptr(0x19E45F4)
|
||||
|
||||
// KeyBindings Skills
|
||||
keyBindingsSkillsOffset := uintptr(0x1DFB190)
|
||||
keyBindingsSkillsOffset := uintptr(0x1E0D1B0)
|
||||
|
||||
// QuestInfo
|
||||
questInfoOffset := uintptr(0x1EC3D58)
|
||||
// QuestInfo: s_questInfo resolved from live D2R AOB scan.
|
||||
questInfoOffset := uintptr(0x1ED5DB8)
|
||||
|
||||
// Terror Zones
|
||||
tzOffset := uintptr(0x25B1B80)
|
||||
tzOffset := uintptr(0x25B5578)
|
||||
|
||||
// Ping
|
||||
pingOffset := uintptr(0x1DFA4E8)
|
||||
pingOffset := uintptr(0x1E0C508)
|
||||
|
||||
// LegacyGraphics
|
||||
legacyGfxOffset := uintptr(0x1EC3FC6)
|
||||
legacyGfxOffset := uintptr(0x1ED6026)
|
||||
|
||||
// CharData
|
||||
charDataOffset := uintptr(0x1DFE7A0)
|
||||
charDataOffset := uintptr(0x1E10658)
|
||||
|
||||
// Selected Char Name
|
||||
selectedCharNameOffset := uintptr(0x1D50215)
|
||||
selectedCharNameOffset := uintptr(0x1D62215)
|
||||
|
||||
// Last Game Name
|
||||
lastGameNameOffset := uintptr(0x25FA4E0)
|
||||
lastGameNameOffset := uintptr(0x260C4B0)
|
||||
|
||||
// Last Game Password
|
||||
lastGamePasswordOffset := uintptr(0x25FA538)
|
||||
lastGamePasswordOffset := uintptr(0x260C508)
|
||||
|
||||
return Offset{
|
||||
UnitTable: unitTableOffset,
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
)
|
||||
|
||||
var tooltipRenderPriceSnippet = regexp.MustCompile(`[\x20-\x7e\n]{0,512}(?:Sell value|Cost|Price):?\s*\d[\d,.]*[\x20-\x7e\n]{0,160}`)
|
||||
|
||||
func NewPanel(panelPtr uintptr, panelParent string, depth int, gd *GameReader) *data.Panel { //itemUnitPtr = uintptr(gd.Process.ReadUInt(itemUnitPtr+0x158, Uint64))
|
||||
panel := &data.Panel{
|
||||
PanelPtr: panelPtr,
|
||||
@@ -42,6 +48,204 @@ func GetText(p data.Panel) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// VisiblePanelTexts returns readable text from every visible panel in traversal
|
||||
// order. Tooltip text is assembled by the client after hover, so callers can
|
||||
// use this as a live UI source instead of recalculating item values.
|
||||
func (gd *GameReader) VisiblePanelTexts() []string {
|
||||
if gd == nil || gd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
base := gd.Process.moduleBaseAddressPtr + gd.offset.PanelManagerContainerOffset
|
||||
panelStructPtr := uintptr(gd.Process.ReadUInt(base, Uint64))
|
||||
panelPtr := uintptr(gd.Process.ReadUInt(panelStructPtr+0x58, Uint64))
|
||||
numChildren := int(gd.Process.ReadUInt(panelStructPtr+0x60, Uint8))
|
||||
|
||||
out := make([]string, 0, 64)
|
||||
seen := make(map[string]struct{}, 64)
|
||||
collectVisiblePanelTexts(panelPtr, numChildren, &out, seen, gd)
|
||||
return out
|
||||
}
|
||||
|
||||
// TooltipRenderTextsFast scans render/cache pages anchored by visible panel
|
||||
// child arrays. The client keeps item tooltip strings here even when the
|
||||
// regular panel tree has no readable children.
|
||||
func (gd *GameReader) TooltipRenderTextsFast() []string {
|
||||
return gd.tooltipRenderTexts()
|
||||
}
|
||||
|
||||
func (gd *GameReader) tooltipRenderTexts() []string {
|
||||
if gd == nil || gd.Process == nil {
|
||||
return nil
|
||||
}
|
||||
base := gd.Process.moduleBaseAddressPtr + gd.offset.PanelManagerContainerOffset
|
||||
panelStructPtr := uintptr(gd.Process.ReadUInt(base, Uint64))
|
||||
panelPtr := uintptr(gd.Process.ReadUInt(panelStructPtr+0x58, Uint64))
|
||||
numChildren := int(gd.Process.ReadUInt(panelStructPtr+0x60, Uint8))
|
||||
|
||||
childPages := make(map[uintptr]struct{}, 64)
|
||||
collectPanelChildPages(panelPtr, numChildren, childPages, gd)
|
||||
|
||||
scanPages := make(map[uintptr]struct{}, 256)
|
||||
for page := range childPages {
|
||||
for _, delta := range []int{-0x2000, -0x1000, 0, 0x1000, 0x2000} {
|
||||
if addr := pageFromDelta(page, delta); addr != 0 {
|
||||
scanPages[addr] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]string, 0, 16)
|
||||
seen := make(map[string]struct{}, 16)
|
||||
for page := range scanPages {
|
||||
buf := gd.Process.ReadBytesFromMemory(page, 0x1000)
|
||||
if len(buf) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, match := range tooltipRenderPriceSnippet.FindAll(buf, -1) {
|
||||
text := cleanTooltipRenderSnippet(match)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[text]; ok {
|
||||
continue
|
||||
}
|
||||
seen[text] = struct{}{}
|
||||
out = append(out, text)
|
||||
}
|
||||
for _, pageText := range utf16LEPrintablePages(buf) {
|
||||
for _, match := range tooltipRenderPriceSnippet.FindAllString(pageText, -1) {
|
||||
text := cleanTooltipRenderText(match)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[text]; ok {
|
||||
continue
|
||||
}
|
||||
seen[text] = struct{}{}
|
||||
out = append(out, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func collectPanelChildPages(panelPtr uintptr, numChildren int, pages map[uintptr]struct{}, gd *GameReader) {
|
||||
if numChildren <= 0 || numChildren >= 80 {
|
||||
return
|
||||
}
|
||||
for i := 0; i < numChildren; i++ {
|
||||
panelStructPtr := uintptr(gd.Process.ReadUInt(uintptr(uint64(panelPtr)+uint64(i*8)), Uint64))
|
||||
if panelStructPtr == 0 {
|
||||
continue
|
||||
}
|
||||
childPtr := uintptr(gd.Process.ReadUInt(panelStructPtr+0x58, Uint64))
|
||||
childCount := int(gd.Process.ReadUInt(panelStructPtr+0x60, Uint8))
|
||||
if childPtr != 0 {
|
||||
pages[childPtr&^uintptr(0xfff)] = struct{}{}
|
||||
}
|
||||
if childCount > 0 && childCount < 50 {
|
||||
collectPanelChildPages(childPtr, childCount, pages, gd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pageFromDelta(page uintptr, delta int) uintptr {
|
||||
addr := int64(page) + int64(delta)
|
||||
if addr <= 0 {
|
||||
return 0
|
||||
}
|
||||
return uintptr(addr) & ^uintptr(0xfff)
|
||||
}
|
||||
|
||||
func cleanTooltipRenderSnippet(raw []byte) string {
|
||||
raw = bytes.Trim(raw, "\x00")
|
||||
return cleanTooltipRenderText(string(raw))
|
||||
}
|
||||
|
||||
func cleanTooltipRenderText(raw string) string {
|
||||
s := strings.TrimSpace(raw)
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
lines := strings.Split(s, "\n")
|
||||
cleaned := make([]string, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
line = strings.TrimLeft(line, "0123456789")
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
cleaned = append(cleaned, line)
|
||||
}
|
||||
}
|
||||
return strings.Join(cleaned, "\n")
|
||||
}
|
||||
|
||||
func utf16LEPrintablePages(buf []byte) []string {
|
||||
out := make([]string, 0, 2)
|
||||
for align := 0; align < 2; align++ {
|
||||
var b strings.Builder
|
||||
lastWasNL := true
|
||||
for i := align; i+1 < len(buf); i += 2 {
|
||||
lo := buf[i]
|
||||
hi := buf[i+1]
|
||||
if hi == 0 && (lo == '\n' || lo == '\r' || lo == '\t' || (lo >= 0x20 && lo <= 0x7e)) {
|
||||
if lo == '\r' || lo == '\t' {
|
||||
lo = '\n'
|
||||
}
|
||||
if lo == '\n' {
|
||||
if !lastWasNL {
|
||||
b.WriteByte('\n')
|
||||
lastWasNL = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
b.WriteByte(lo)
|
||||
lastWasNL = false
|
||||
continue
|
||||
}
|
||||
if !lastWasNL {
|
||||
b.WriteByte('\n')
|
||||
lastWasNL = true
|
||||
}
|
||||
}
|
||||
text := strings.TrimSpace(b.String())
|
||||
if len(text) >= 16 {
|
||||
out = append(out, text)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func collectVisiblePanelTexts(panelPtr uintptr, numChildren int, out *[]string, seen map[string]struct{}, gd *GameReader) {
|
||||
if numChildren <= 0 || numChildren >= 50 {
|
||||
return
|
||||
}
|
||||
for i := 0; i < numChildren; i++ {
|
||||
panelStructPtr := uintptr(gd.Process.ReadUInt(uintptr(uint64(panelPtr)+uint64(i*8)), Uint64))
|
||||
if panelStructPtr == 0 {
|
||||
continue
|
||||
}
|
||||
p := data.Panel{
|
||||
PanelEnabled: gd.Process.ReadUInt(panelStructPtr+0x50, Uint8) != 0,
|
||||
PanelVisible: gd.Process.ReadUInt(panelStructPtr+0x51, Uint8) != 0,
|
||||
PtrChild: uintptr(gd.Process.ReadUInt(panelStructPtr+0x58, Uint64)),
|
||||
NumChildren: int(gd.Process.ReadUInt(panelStructPtr+0x60, Uint8)),
|
||||
ExtraText: gd.Process.ReadStringFromMemory(panelStructPtr+0xA0, 0),
|
||||
ExtraText2: gd.Process.ReadStringFromMemory(uintptr(gd.Process.ReadUInt(panelStructPtr+0x290, Uint64)), 0),
|
||||
ExtraText3: gd.Process.ReadStringFromMemory(uintptr(gd.Process.ReadUInt(panelStructPtr+0x88, Uint64)), 0),
|
||||
}
|
||||
if p.PanelVisible && p.PanelEnabled {
|
||||
if text := strings.TrimSpace(GetText(p)); text != "" {
|
||||
if _, ok := seen[text]; !ok {
|
||||
seen[text] = struct{}{}
|
||||
*out = append(*out, text)
|
||||
}
|
||||
}
|
||||
collectVisiblePanelTexts(p.PtrChild, p.NumChildren, out, seen, gd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cleanString(input string) string {
|
||||
return input // Replace newlines and carriage returns as needed
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (gd *GameReader) GetRawPlayerUnits() RawPlayerUnits {
|
||||
|
||||
statsListExPtr := uintptr(gd.Process.ReadUInt(playerUnit+0x88, Uint64))
|
||||
baseStats := gd.getStatsList(statsListExPtr + 0x30)
|
||||
stats := gd.getStatsList(statsListExPtr + 0xA8)
|
||||
stats := gd.getStatsList(statsListExPtr + 0xE8)
|
||||
states := gd.GetStates(statsListExPtr)
|
||||
playerMode := mode.PlayerMode(gd.Process.ReadUInt(playerUnit+0x0c, Uint32))
|
||||
|
||||
@@ -184,9 +184,9 @@ func (gd *GameReader) getSkills(skillListPtr uintptr) map[skill.ID]skill.Points
|
||||
for skillPtr != 0 {
|
||||
skillTxtPtr := uintptr(gd.Process.ReadUInt(skillPtr, Uint64))
|
||||
skillTxt := uintptr(gd.Process.ReadUInt(skillTxtPtr, Uint16))
|
||||
lvl := gd.Process.ReadUInt(skillPtr+0x40, Uint16)
|
||||
lvl := gd.Process.ReadUInt(skillPtr+0x38, Uint16)
|
||||
qty := gd.Process.ReadUInt(skillPtr+0x48, Uint16)
|
||||
charges := gd.Process.ReadUInt(skillPtr+0x50, Uint16)
|
||||
charges := gd.Process.ReadUInt(skillPtr+0x4C, Uint16)
|
||||
|
||||
shouldSetSkill := true
|
||||
existingSkill, exists := skills[skill.ID(skillTxt)]
|
||||
@@ -214,7 +214,7 @@ func (gd *GameReader) GetStates(statsListExPtr uintptr) state.States {
|
||||
var states state.States
|
||||
for i := 0; i < 8; i++ {
|
||||
offset := i * 4
|
||||
stateByte := gd.Process.ReadUInt(statsListExPtr+0xAF0+uintptr(offset), Uint32)
|
||||
stateByte := gd.Process.ReadUInt(statsListExPtr+0xB30+uintptr(offset), Uint32)
|
||||
|
||||
offset = (32 * i) - 1
|
||||
states = append(states, calculateStates(stateByte, uint(offset))...)
|
||||
|
||||
Reference in New Issue
Block a user