Add some memory reading stuff, based on many other open source projects
This commit is contained in:
6
go.mod
6
go.mod
@@ -2,7 +2,11 @@ module github.com/hectorgimenez/d2go
|
||||
|
||||
go 1.19
|
||||
|
||||
require github.com/stretchr/testify v1.8.2
|
||||
require (
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/winlabs/gowin32 v0.0.0-20221003142512-0d265587d3c9
|
||||
golang.org/x/sys v0.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -10,6 +10,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/winlabs/gowin32 v0.0.0-20221003142512-0d265587d3c9 h1:U8aCPFEMnxAEyj9IonhMVV1gSL4nzelh8uvoXp0hrq0=
|
||||
github.com/winlabs/gowin32 v0.0.0-20221003142512-0d265587d3c9/go.mod h1:N51TYkG9JGR5sytj0EoPl31Xg2kuB507lxEmrwSNvfQ=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package game
|
||||
package data
|
||||
|
||||
import "strings"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package game
|
||||
package data
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data/area"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package game
|
||||
package data
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data/item"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package game
|
||||
package data
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data/npc"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package game
|
||||
package data
|
||||
|
||||
import "github.com/hectorgimenez/d2go/pkg/data/object"
|
||||
|
||||
|
||||
118
pkg/memory/game_reader.go
Normal file
118
pkg/memory/game_reader.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/stat"
|
||||
)
|
||||
|
||||
type GameReader struct {
|
||||
offset Offset
|
||||
Process
|
||||
}
|
||||
|
||||
func NewGameReader(process Process) *GameReader {
|
||||
return &GameReader{
|
||||
offset: calculateOffsets(process),
|
||||
Process: process,
|
||||
}
|
||||
}
|
||||
|
||||
var previousData *data.Data
|
||||
|
||||
func (gd *GameReader) GetData() data.Data {
|
||||
if gd.offset.UnitTable == 0 {
|
||||
gd.offset = calculateOffsets(gd.Process)
|
||||
}
|
||||
|
||||
roster := gd.getRoster()
|
||||
playerUnitPtr, corpse := gd.getPlayerUnitPtr(roster)
|
||||
|
||||
pu := gd.GetPlayerUnit(playerUnitPtr)
|
||||
|
||||
d := data.Data{
|
||||
Corpse: corpse,
|
||||
Monsters: gd.Monsters(pu.Position),
|
||||
PlayerUnit: pu,
|
||||
Items: gd.Items(pu.Position),
|
||||
Objects: gd.Objects(pu.Position),
|
||||
OpenMenus: gd.openMenus(),
|
||||
Roster: roster,
|
||||
}
|
||||
|
||||
if playerUnitPtr == 0 {
|
||||
return *previousData
|
||||
}
|
||||
|
||||
previousData = &d
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (gd *GameReader) InGame() bool {
|
||||
pu, _ := gd.getPlayerUnitPtr([]data.RosterMember{})
|
||||
|
||||
return pu > 0
|
||||
}
|
||||
|
||||
func (gd *GameReader) openMenus() data.OpenMenus {
|
||||
uiBase := gd.Process.moduleBaseAddressPtr + gd.offset.UI - 0xA
|
||||
|
||||
buffer := gd.Process.ReadBytesFromMemory(uiBase, 0x169)
|
||||
|
||||
isMapShown := gd.Process.ReadUInt(gd.Process.moduleBaseAddressPtr+gd.offset.UI, Uint8)
|
||||
|
||||
return data.OpenMenus{
|
||||
Inventory: buffer[0x01] != 0,
|
||||
LoadingScreen: buffer[0x168] != 0,
|
||||
NPCInteract: buffer[0x08] != 0,
|
||||
NPCShop: buffer[0x0B] != 0,
|
||||
Stash: buffer[0x18] != 0,
|
||||
Waypoint: buffer[0x13] != 0,
|
||||
MapShown: isMapShown != 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (gd *GameReader) hoveredData() (hoveredUnitID uint, hoveredType uint, isHovered bool) {
|
||||
hoverAddressPtr := gd.Process.moduleBaseAddressPtr + gd.offset.Hover
|
||||
hoverBuffer := gd.Process.ReadBytesFromMemory(hoverAddressPtr, 12)
|
||||
isUnitHovered := ReadUIntFromBuffer(hoverBuffer, 0, Uint16)
|
||||
if isUnitHovered > 0 {
|
||||
hoveredType = ReadUIntFromBuffer(hoverBuffer, 0x04, Uint32)
|
||||
hoveredUnitID = ReadUIntFromBuffer(hoverBuffer, 0x08, Uint32)
|
||||
|
||||
return hoveredUnitID, hoveredType, true
|
||||
}
|
||||
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
func getStatData(statEnum, statValue uint) (stat.Stat, int) {
|
||||
value := int(statValue)
|
||||
switch stat.Stat(statEnum) {
|
||||
case stat.Life,
|
||||
stat.MaxLife,
|
||||
stat.Mana,
|
||||
stat.MaxMana,
|
||||
stat.Stamina,
|
||||
stat.LifePerLevel,
|
||||
stat.ManaPerLevel:
|
||||
value = int(statValue >> 8)
|
||||
case stat.ColdLength,
|
||||
stat.PoisonLength:
|
||||
value = int(statValue / 25)
|
||||
}
|
||||
|
||||
return stat.Stat(statEnum), value
|
||||
}
|
||||
|
||||
func setProperties(item *data.Item, flags uint32) {
|
||||
if 0x00400000&flags != 0 {
|
||||
item.Ethereal = true
|
||||
}
|
||||
if 0x00000010&flags != 0 {
|
||||
item.Identified = true
|
||||
}
|
||||
if 0x00002000&flags != 0 {
|
||||
item.IsVendor = true
|
||||
}
|
||||
}
|
||||
131
pkg/memory/item.go
Normal file
131
pkg/memory/item.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/item"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/stat"
|
||||
"github.com/hectorgimenez/d2go/pkg/utils"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func (gd *GameReader) Items(playerPosition data.Position) data.Items {
|
||||
hoveredUnitID, hoveredType, isHovered := gd.hoveredData()
|
||||
|
||||
baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + (4 * 1024)
|
||||
unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8)
|
||||
|
||||
items := data.Items{}
|
||||
for i := 0; i < 128; i++ {
|
||||
itemOffset := 8 * i
|
||||
itemUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(itemOffset), Uint64))
|
||||
for itemUnitPtr > 0 {
|
||||
itemDataBuffer := gd.Process.ReadBytesFromMemory(itemUnitPtr, 144)
|
||||
itemType := ReadUIntFromBuffer(itemDataBuffer, 0x00, Uint32)
|
||||
txtFileNo := ReadUIntFromBuffer(itemDataBuffer, 0x04, Uint32)
|
||||
unitID := ReadUIntFromBuffer(itemDataBuffer, 0x08, Uint32)
|
||||
// itemLoc = 0 in inventory, 1 equipped, 2 in belt, 3 on ground, 4 cursor, 5 dropping, 6 socketed
|
||||
itemLoc := ReadUIntFromBuffer(itemDataBuffer, 0x0C, Uint32)
|
||||
|
||||
if itemType != 4 {
|
||||
itemUnitPtr = uintptr(gd.Process.ReadUInt(itemUnitPtr+0x150, Uint64))
|
||||
continue
|
||||
}
|
||||
|
||||
unitDataPtr := uintptr(ReadUIntFromBuffer(itemDataBuffer, 0x10, Uint64))
|
||||
unitDataBuffer := gd.Process.ReadBytesFromMemory(unitDataPtr, 144)
|
||||
flags := ReadUIntFromBuffer(unitDataBuffer, 0x18, Uint32)
|
||||
invPage := ReadUIntFromBuffer(unitDataBuffer, 0x55, Uint8)
|
||||
itemQuality := ReadUIntFromBuffer(unitDataBuffer, 0x00, Uint32)
|
||||
//itemOwnerNPC := ReadUIntFromBuffer(unitDataBuffer, 0x0C, Uint32)
|
||||
|
||||
// Item coordinates (X, Y)
|
||||
pathPtr := uintptr(ReadUIntFromBuffer(itemDataBuffer, 0x38, Uint64))
|
||||
pathBuffer := gd.Process.ReadBytesFromMemory(pathPtr, 144)
|
||||
itemX := ReadUIntFromBuffer(pathBuffer, 0x10, Uint16)
|
||||
itemY := ReadUIntFromBuffer(pathBuffer, 0x14, Uint16)
|
||||
|
||||
// Item Stats
|
||||
statsListExPtr := uintptr(ReadUIntFromBuffer(itemDataBuffer, 0x88, Uint64))
|
||||
statsListExBuffer := gd.Process.ReadBytesFromMemory(statsListExPtr, 180)
|
||||
statPtr := uintptr(ReadUIntFromBuffer(statsListExBuffer, 0x30, Uint64))
|
||||
statCount := ReadUIntFromBuffer(statsListExBuffer, 0x38, Uint32)
|
||||
statExPtr := uintptr(ReadUIntFromBuffer(statsListExBuffer, 0x88, Uint64))
|
||||
statExCount := ReadUIntFromBuffer(statsListExBuffer, 0x90, Uint32)
|
||||
|
||||
stats := gd.getItemStats(statCount, statPtr, statExCount, statExPtr)
|
||||
|
||||
name := item.GetNameByEnum(txtFileNo)
|
||||
itemHovered := false
|
||||
if isHovered && hoveredType == 4 && hoveredUnitID == unitID {
|
||||
itemHovered = true
|
||||
}
|
||||
|
||||
itm := data.Item{
|
||||
UnitID: data.UnitID(unitID),
|
||||
Name: name,
|
||||
Quality: item.Quality(itemQuality),
|
||||
Position: data.Position{
|
||||
X: int(itemX),
|
||||
Y: int(itemY),
|
||||
},
|
||||
IsHovered: itemHovered,
|
||||
Stats: stats,
|
||||
}
|
||||
setProperties(&itm, uint32(flags))
|
||||
|
||||
switch itemLoc {
|
||||
case 0:
|
||||
if itm.IsVendor {
|
||||
items.Shop = append(items.Shop, itm)
|
||||
} else if invPage == 0 {
|
||||
items.Inventory = append(items.Inventory, itm)
|
||||
}
|
||||
case 1:
|
||||
items.Equipped = append(items.Equipped, itm)
|
||||
case 2:
|
||||
items.Belt = append(items.Belt, itm)
|
||||
case 3, 5:
|
||||
items.Ground = append(items.Ground, itm)
|
||||
}
|
||||
|
||||
itemUnitPtr = uintptr(gd.Process.ReadUInt(itemUnitPtr+0x150, Uint64))
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(items.Ground, func(i, j int) bool {
|
||||
distanceI := utils.DistanceFromPoint(playerPosition, items.Ground[i].Position)
|
||||
distanceJ := utils.DistanceFromPoint(playerPosition, items.Ground[j].Position)
|
||||
|
||||
return distanceI < distanceJ
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
func (gd *GameReader) getItemStats(statCount uint, statPtr uintptr, statExCount uint, statExPtr uintptr) map[stat.Stat]int {
|
||||
stats := map[stat.Stat]int{}
|
||||
|
||||
if statCount < 20 && statCount > 0 {
|
||||
statBuffer := gd.Process.ReadBytesFromMemory(statPtr, statCount*10)
|
||||
for i := 0; i < int(statCount); i++ {
|
||||
offset := uint(i * 8)
|
||||
statEnum := ReadUIntFromBuffer(statBuffer, offset+0x2, Uint16)
|
||||
statValue := ReadUIntFromBuffer(statBuffer, offset+0x4, Uint32)
|
||||
st, value := getStatData(statEnum, statValue)
|
||||
stats[st] = value
|
||||
}
|
||||
}
|
||||
|
||||
if statExCount < 20 && statCount > 0 {
|
||||
statBuffer := gd.Process.ReadBytesFromMemory(statExPtr, statExCount*10)
|
||||
for i := 0; i < int(statExCount); i++ {
|
||||
offset := uint(i * 8)
|
||||
statEnum := ReadUIntFromBuffer(statBuffer, offset+0x2, Uint16)
|
||||
statValue := ReadUIntFromBuffer(statBuffer, offset+0x4, Uint32)
|
||||
st, value := getStatData(statEnum, statValue)
|
||||
stats[st] = value
|
||||
}
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
201
pkg/memory/monsters.go
Normal file
201
pkg/memory/monsters.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/npc"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/stat"
|
||||
"github.com/hectorgimenez/d2go/pkg/utils"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func (gd *GameReader) Monsters(playerPosition data.Position) data.Monsters {
|
||||
hoveredUnitID, hoveredType, isHovered := gd.hoveredData()
|
||||
|
||||
baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + 1024
|
||||
unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8)
|
||||
|
||||
monsters := data.Monsters{}
|
||||
for i := 0; i < 128; i++ {
|
||||
monsterOffset := 8 * i
|
||||
monsterUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(monsterOffset), Uint64))
|
||||
for monsterUnitPtr > 0 {
|
||||
monsterDataBuffer := gd.Process.ReadBytesFromMemory(monsterUnitPtr, 144)
|
||||
|
||||
//monsterType := ReadUIntFromBuffer(monsterDataBuffer, 0x00, Uint32)
|
||||
txtFileNo := ReadUIntFromBuffer(monsterDataBuffer, 0x04, Uint32)
|
||||
if !gd.shouldBeIgnored(txtFileNo) {
|
||||
unitID := ReadUIntFromBuffer(monsterDataBuffer, 0x08, Uint32)
|
||||
|
||||
//mode := ReadUIntFromBuffer(monsterDataBuffer, 0x0C, Uint32)
|
||||
|
||||
unitDataPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x10, Uint64))
|
||||
//isUnique := gd.Process.ReadUInt(unitDataPtr+0x18, Uint16)
|
||||
flag := gd.Process.ReadBytesFromMemory(unitDataPtr+0x1A, Uint8)[0]
|
||||
isCorpse := gd.Process.ReadUInt(monsterUnitPtr+0x1A6, Uint8)
|
||||
|
||||
//unitDataBuffer := gd.Process.ReadBytesFromMemory(unitDataPtr, 144)
|
||||
|
||||
// Coordinates (X, Y)
|
||||
pathPtr := uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x38, Uint64))
|
||||
posX := gd.Process.ReadUInt(pathPtr+0x02, Uint16)
|
||||
posY := gd.Process.ReadUInt(pathPtr+0x06, Uint16)
|
||||
|
||||
hovered := false
|
||||
if isHovered && hoveredType == 1 && hoveredUnitID == unitID {
|
||||
hovered = true
|
||||
}
|
||||
|
||||
statsListExPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x88, Uint64))
|
||||
statPtr := uintptr(gd.Process.ReadUInt(statsListExPtr+0x30, Uint64))
|
||||
statCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
|
||||
|
||||
stats := gd.getMonsterStats(statCount, statPtr)
|
||||
|
||||
m := data.Monster{
|
||||
UnitID: data.UnitID(unitID),
|
||||
Name: npc.ID(int(txtFileNo)),
|
||||
IsHovered: hovered,
|
||||
Position: data.Position{
|
||||
X: int(posX),
|
||||
Y: int(posY),
|
||||
},
|
||||
Stats: stats,
|
||||
Type: getMonsterType(flag),
|
||||
}
|
||||
|
||||
if isCorpse == 0 {
|
||||
monsters = append(monsters, m)
|
||||
}
|
||||
}
|
||||
|
||||
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(monsters, func(i, j int) bool {
|
||||
distanceI := utils.DistanceFromPoint(playerPosition, monsters[i].Position)
|
||||
distanceJ := utils.DistanceFromPoint(playerPosition, monsters[j].Position)
|
||||
|
||||
return distanceI < distanceJ
|
||||
})
|
||||
|
||||
return monsters
|
||||
}
|
||||
|
||||
func getMonsterType(typeFlag byte) data.MonsterType {
|
||||
switch typeFlag {
|
||||
case 10:
|
||||
return data.MonsterTypeSuperUnique
|
||||
case 1 << 2:
|
||||
return data.MonsterTypeChampion
|
||||
case 1 << 3:
|
||||
return data.MonsterTypeUnique
|
||||
case 1 << 4:
|
||||
return data.MonsterTypeMinion
|
||||
}
|
||||
|
||||
return data.MonsterTypeNone
|
||||
}
|
||||
|
||||
func (gd *GameReader) getMonsterStats(statCount uint, statPtr uintptr) map[stat.Stat]int {
|
||||
stats := map[stat.Stat]int{}
|
||||
|
||||
if statCount > 0 {
|
||||
statBuffer := gd.Process.ReadBytesFromMemory(statPtr+0x2, statCount*8)
|
||||
for i := 0; i < int(statCount); i++ {
|
||||
offset := uint(i * 8)
|
||||
statEnum := ReadUIntFromBuffer(statBuffer, offset, Uint16)
|
||||
statValue := ReadUIntFromBuffer(statBuffer, offset+0x2, Uint32)
|
||||
stats[stat.Stat(statEnum)] = int(statValue)
|
||||
}
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
func (gd *GameReader) shouldBeIgnored(txtNo uint) bool {
|
||||
switch npc.ID(txtNo) {
|
||||
case npc.Chicken,
|
||||
npc.Rat,
|
||||
npc.Rogue,
|
||||
npc.HellMeteor,
|
||||
npc.Bird,
|
||||
npc.Bird2,
|
||||
npc.Bat,
|
||||
npc.Act2Male,
|
||||
npc.Act2Female,
|
||||
npc.Act2Child,
|
||||
npc.Cow,
|
||||
npc.Camel,
|
||||
npc.Act2Guard,
|
||||
npc.Act2Vendor,
|
||||
npc.Act2Vendor2,
|
||||
npc.Maggot,
|
||||
npc.Bug,
|
||||
npc.Scorpion,
|
||||
npc.Rogue2,
|
||||
npc.Rogue3,
|
||||
npc.Larva,
|
||||
npc.Familiar,
|
||||
npc.Act3Male,
|
||||
npc.ClayGolem,
|
||||
npc.BloodGolem,
|
||||
npc.IronGolem,
|
||||
npc.FireGolem,
|
||||
npc.Act3Female,
|
||||
npc.Snake,
|
||||
npc.Parrot,
|
||||
npc.Fish,
|
||||
npc.EvilHole,
|
||||
npc.EvilHole2,
|
||||
npc.EvilHole3,
|
||||
npc.EvilHole4,
|
||||
npc.EvilHole5,
|
||||
npc.FireboltTrap,
|
||||
npc.HorzMissileTrap,
|
||||
npc.VertMissileTrap,
|
||||
npc.PoisonCloudTrap,
|
||||
npc.LightningTrap,
|
||||
npc.InvisoSpawner,
|
||||
//338, //Guard
|
||||
npc.MiniSper,
|
||||
npc.BoneWall,
|
||||
npc.Hydra,
|
||||
npc.Hydra2,
|
||||
npc.Hydra3,
|
||||
npc.SevenTombs,
|
||||
npc.Valkyrie,
|
||||
npc.IronWolf,
|
||||
npc.NecroSkeleton,
|
||||
npc.NecroMage,
|
||||
npc.CompellingOrbNpc,
|
||||
npc.SpiritMummy,
|
||||
npc.Act2Guard4,
|
||||
npc.Act2Guard5,
|
||||
npc.Window,
|
||||
npc.Window2,
|
||||
npc.MephistoSpirit,
|
||||
npc.WakeOfDestruction,
|
||||
npc.ChargedBoltSentry,
|
||||
npc.LightningSentry,
|
||||
npc.InvisiblePet,
|
||||
npc.InfernoSentry,
|
||||
npc.DeathSentry,
|
||||
npc.ShadowWarrior,
|
||||
npc.ShadowMaster,
|
||||
npc.DruHawk,
|
||||
npc.DruSpiritWolf,
|
||||
npc.DruFenris,
|
||||
npc.HeartOfWolverine,
|
||||
npc.OakSage,
|
||||
npc.DruBear,
|
||||
npc.BaalThrone,
|
||||
npc.InjuredBarbarian,
|
||||
npc.InjuredBarbarian2,
|
||||
npc.InjuredBarbarian3,
|
||||
npc.DemonHole:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
59
pkg/memory/object.go
Normal file
59
pkg/memory/object.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/object"
|
||||
"github.com/hectorgimenez/d2go/pkg/utils"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func (gd *GameReader) Objects(playerPosition data.Position) []data.Object {
|
||||
hoveredUnitID, hoveredType, isHovered := gd.hoveredData()
|
||||
|
||||
baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + (2 * 1024)
|
||||
unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8)
|
||||
|
||||
var objects []data.Object
|
||||
for i := 0; i < 128; i++ {
|
||||
objectOffset := 8 * i
|
||||
objectUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(objectOffset), Uint64))
|
||||
for objectUnitPtr > 0 {
|
||||
objectType := gd.Process.ReadUInt(objectUnitPtr+0x00, Uint32)
|
||||
if objectType == 2 {
|
||||
txtFileNo := gd.Process.ReadUInt(objectUnitPtr+0x04, Uint32)
|
||||
mode := gd.Process.ReadUInt(objectUnitPtr+0x0c, Uint32)
|
||||
unitID := gd.Process.ReadUInt(objectUnitPtr+0x08, Uint32)
|
||||
|
||||
// Coordinates (X, Y)
|
||||
pathPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x38, Uint64))
|
||||
posX := gd.Process.ReadUInt(pathPtr+0x10, Uint16)
|
||||
posY := gd.Process.ReadUInt(pathPtr+0x14, Uint16)
|
||||
|
||||
unitDataPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x10, Uint64))
|
||||
interactType := gd.Process.ReadUInt(unitDataPtr+0x08, Uint8)
|
||||
|
||||
obj := data.Object{
|
||||
Name: object.Name(int(txtFileNo)),
|
||||
IsHovered: unitID == hoveredUnitID && hoveredType == 2 && isHovered,
|
||||
InteractType: object.InteractType(interactType),
|
||||
Selectable: mode == 0,
|
||||
Position: data.Position{
|
||||
X: int(posX),
|
||||
Y: int(posY),
|
||||
},
|
||||
}
|
||||
objects = append(objects, obj)
|
||||
}
|
||||
objectUnitPtr = uintptr(gd.Process.ReadUInt(objectUnitPtr+0x150, Uint64))
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(objects, func(i, j int) bool {
|
||||
distanceI := utils.DistanceFromPoint(playerPosition, objects[i].Position)
|
||||
distanceJ := utils.DistanceFromPoint(playerPosition, objects[j].Position)
|
||||
|
||||
return distanceI < distanceJ
|
||||
})
|
||||
|
||||
return objects
|
||||
}
|
||||
56
pkg/memory/offset.go
Normal file
56
pkg/memory/offset.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package memory
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
type Offset struct {
|
||||
GameData uintptr
|
||||
UnitTable uintptr
|
||||
UI uintptr
|
||||
Hover uintptr
|
||||
Expansion uintptr
|
||||
RosterOffset uintptr
|
||||
}
|
||||
|
||||
func calculateOffsets(process Process) Offset {
|
||||
// ignoring errors, always best practices
|
||||
memory, _ := process.getProcessMemory()
|
||||
|
||||
// GameReader
|
||||
pattern := process.FindPattern(memory, "\x44\x88\x25\x00\x00\x00\x00\x66\x44\x89\x25\x00\x00\x00\x00", "xxx????xxxx????")
|
||||
bytes := process.ReadBytesFromMemory(pattern+0x3, 4)
|
||||
offsetInt := uintptr(binary.LittleEndian.Uint32(bytes))
|
||||
gameDataOffset := (pattern - process.moduleBaseAddressPtr) - 0x121 + offsetInt
|
||||
|
||||
// UnitTable
|
||||
pattern = process.FindPattern(memory, "\x48\x03\xC7\x49\x8B\x8C\xC6", "xxxxxxx")
|
||||
bytes = process.ReadBytesFromMemory(pattern+7, 4)
|
||||
unitTableOffset := uintptr(binary.LittleEndian.Uint32(bytes))
|
||||
|
||||
// UI
|
||||
pattern = process.FindPattern(memory, "\x40\x84\xed\x0f\x94\x05", "xxxxxx")
|
||||
uiOffset := process.ReadUInt(pattern+6, Uint32)
|
||||
uiOffsetPtr := (pattern - process.moduleBaseAddressPtr) + 10 + uintptr(uiOffset)
|
||||
|
||||
// Hover
|
||||
pattern = process.FindPattern(memory, "\xc6\x84\xc2\x00\x00\x00\x00\x00\x48\x8b\x74\x24\x00", "xxx?????xxxx?")
|
||||
hoverOffset := process.ReadUInt(pattern+3, Uint32) - 1
|
||||
|
||||
// Expansion
|
||||
pattern = process.FindPattern(memory, "\x48\x8B\x05\x00\x00\x00\x00\x48\x8B\xD9\xF3\x0F\x10\x50\x00", "xxx????xxxxxxx?")
|
||||
offsetPtr := uintptr(process.ReadUInt(pattern+3, Uint32))
|
||||
expOffset := pattern - process.moduleBaseAddressPtr + 7 + offsetPtr
|
||||
|
||||
// Party members offset
|
||||
pattern = process.FindPattern(memory, "\x02\x45\x33\xD2\x4D\x8B", "xxxxxx")
|
||||
offsetPtr = uintptr(process.ReadUInt(pattern-3, Uint32))
|
||||
rosterOffset := pattern - process.moduleBaseAddressPtr + 1 + offsetPtr
|
||||
|
||||
return Offset{
|
||||
GameData: gameDataOffset,
|
||||
UnitTable: unitTableOffset,
|
||||
UI: uiOffsetPtr,
|
||||
Hover: uintptr(hoverOffset),
|
||||
Expansion: expOffset,
|
||||
RosterOffset: rosterOffset,
|
||||
}
|
||||
}
|
||||
271
pkg/memory/player.go
Normal file
271
pkg/memory/player.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/area"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/skill"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/stat"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/state"
|
||||
)
|
||||
|
||||
func (gd *GameReader) getPlayerUnitPtr(roster data.Roster) (playerUnitPtr uintptr, corpse data.Corpse) {
|
||||
for i := 0; i < 128; i++ {
|
||||
unitOffset := gd.offset.UnitTable + uintptr(i*8)
|
||||
playerUnitAddr := gd.Process.moduleBaseAddressPtr + unitOffset
|
||||
playerUnit := uintptr(gd.Process.ReadUInt(playerUnitAddr, Uint64))
|
||||
for playerUnit > 0 {
|
||||
pInventory := playerUnit + 0x90
|
||||
inventoryAddr := uintptr(gd.Process.ReadUInt(pInventory, Uint64))
|
||||
|
||||
pPath := playerUnit + 0x38
|
||||
pathAddress := uintptr(gd.Process.ReadUInt(pPath, Uint64))
|
||||
xPos := gd.Process.ReadUInt(pathAddress+0x02, Uint16)
|
||||
yPos := gd.Process.ReadUInt(pathAddress+0x06, Uint16)
|
||||
|
||||
// Only current player has inventory
|
||||
if inventoryAddr > 0 && xPos > 0 && yPos > 0 {
|
||||
expCharPtr := uintptr(gd.Process.ReadUInt(gd.moduleBaseAddressPtr+gd.offset.Expansion, Uint64))
|
||||
expChar := gd.Process.ReadUInt(expCharPtr+0x5C, Uint16)
|
||||
baseCheck := gd.Process.ReadUInt(inventoryAddr+0x30, Uint16)
|
||||
if expChar > 0 {
|
||||
baseCheck = gd.Process.ReadUInt(inventoryAddr+0x70, Uint16)
|
||||
}
|
||||
|
||||
if baseCheck > 0 {
|
||||
isCorpse := gd.Process.ReadUInt(playerUnit+0x1A6, Uint8)
|
||||
|
||||
if isCorpse == 1 {
|
||||
unitID := gd.Process.ReadUInt(playerUnit+0x08, Uint32)
|
||||
hoveredUnitID, hoveredType, isHovered := gd.hoveredData()
|
||||
corpse = data.Corpse{
|
||||
Found: true,
|
||||
IsHovered: isHovered && hoveredUnitID == unitID && hoveredType == 0,
|
||||
Position: data.Position{
|
||||
X: int(xPos),
|
||||
Y: int(yPos),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
playerUnitPtr = playerUnit
|
||||
}
|
||||
} else {
|
||||
pUnitData := playerUnit + 0x10
|
||||
playerNameAddr := uintptr(gd.Process.ReadUInt(pUnitData, Uint64))
|
||||
name := gd.Process.ReadStringFromMemory(playerNameAddr, 0)
|
||||
for k, rm := range roster {
|
||||
if name != rm.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
roster[k] = data.RosterMember{
|
||||
Name: name,
|
||||
Area: rm.Area,
|
||||
Position: data.Position{
|
||||
X: int(xPos),
|
||||
Y: int(yPos),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerUnit = uintptr(gd.Process.ReadUInt(playerUnit+0x150, Uint64))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (gd *GameReader) GetPlayerUnit(playerUnit uintptr) data.PlayerUnit {
|
||||
// Read X and Y Positions
|
||||
pPath := playerUnit + 0x38
|
||||
pathAddress := uintptr(gd.Process.ReadUInt(pPath, Uint64))
|
||||
xPos := gd.Process.ReadUInt(pathAddress+0x02, Uint16)
|
||||
yPos := gd.Process.ReadUInt(pathAddress+0x06, Uint16)
|
||||
|
||||
// Player name
|
||||
pUnitData := playerUnit + 0x10
|
||||
playerNameAddr := uintptr(gd.Process.ReadUInt(pUnitData, Uint64))
|
||||
name := gd.Process.ReadStringFromMemory(playerNameAddr, 0)
|
||||
|
||||
// Get Stats
|
||||
statsListExPtr := uintptr(gd.Process.ReadUInt(playerUnit+0x88, Uint64))
|
||||
statPtr := gd.Process.ReadUInt(statsListExPtr+0x30, Uint64)
|
||||
statCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
|
||||
|
||||
stats := map[stat.Stat]int{}
|
||||
for j := 0; j < int(statCount); j++ {
|
||||
statOffset := uintptr(statPtr) + 0x2 + uintptr(j*8)
|
||||
statNumber := gd.Process.ReadUInt(statOffset, Uint16)
|
||||
statValue := gd.Process.ReadUInt(statOffset+0x02, Uint32)
|
||||
|
||||
switch stat.Stat(statNumber) {
|
||||
case stat.Life,
|
||||
stat.MaxLife,
|
||||
stat.Mana,
|
||||
stat.MaxMana:
|
||||
stats[stat.Stat(statNumber)] = int(uint32(statValue) >> 8)
|
||||
default:
|
||||
stats[stat.Stat(statNumber)] = int(statValue)
|
||||
}
|
||||
}
|
||||
|
||||
// States (Buff, Debuff, Auras)
|
||||
states := gd.getStates(statsListExPtr)
|
||||
|
||||
// Skills
|
||||
skills := gd.getSkills(playerUnit + 0x100)
|
||||
|
||||
// Level number
|
||||
pathPtr := uintptr(gd.Process.ReadUInt(playerUnit+0x38, Uint64))
|
||||
room1Ptr := uintptr(gd.Process.ReadUInt(pathPtr+0x20, Uint64))
|
||||
room2Ptr := uintptr(gd.Process.ReadUInt(room1Ptr+0x18, Uint64))
|
||||
levelPtr := uintptr(gd.Process.ReadUInt(room2Ptr+0x90, Uint64))
|
||||
levelNo := gd.Process.ReadUInt(levelPtr+0x1F8, Uint32)
|
||||
|
||||
return data.PlayerUnit{
|
||||
Name: name,
|
||||
Area: area.Area(levelNo),
|
||||
Position: data.Position{
|
||||
X: int(xPos),
|
||||
Y: int(yPos),
|
||||
},
|
||||
Stats: stats,
|
||||
Skills: skills,
|
||||
States: states,
|
||||
}
|
||||
}
|
||||
|
||||
func (gd *GameReader) getSkills(skillsPtr uintptr) map[skill.Skill]int {
|
||||
skills := make(map[skill.Skill]int)
|
||||
skillListPtr := uintptr(gd.Process.ReadUInt(skillsPtr, Uint64))
|
||||
|
||||
skillPtr := uintptr(gd.Process.ReadUInt(skillListPtr, Uint64))
|
||||
|
||||
for skillPtr != 0 {
|
||||
skillTxtPtr := uintptr(gd.Process.ReadUInt(skillPtr, Uint64))
|
||||
skillTxt := uintptr(gd.Process.ReadUInt(skillTxtPtr, Uint16))
|
||||
skillLvl := gd.Process.ReadUInt(skillTxtPtr+0x34, Uint16)
|
||||
|
||||
skills[skill.Skill(skillTxt)] = int(skillLvl)
|
||||
|
||||
skillPtr = uintptr(gd.Process.ReadUInt(skillPtr+0x08, Uint64))
|
||||
}
|
||||
|
||||
return skills
|
||||
}
|
||||
|
||||
func (gd *GameReader) getStates(statsListExPtr uintptr) []state.State {
|
||||
var states []state.State
|
||||
for i := 0; i < 6; i++ {
|
||||
offset := i * 4
|
||||
stateByte := gd.Process.ReadUInt(statsListExPtr+0xAD0+uintptr(offset), Uint32)
|
||||
|
||||
offset = (32 * i) - 1
|
||||
states = append(states, calculateStates(stateByte, uint(offset))...)
|
||||
}
|
||||
|
||||
return states
|
||||
}
|
||||
|
||||
func calculateStates(stateFlag uint, offset uint) []state.State {
|
||||
var states []state.State
|
||||
if 0x00000001&stateFlag != 0 {
|
||||
states = append(states, state.State(1+offset))
|
||||
}
|
||||
if 0x00000002&stateFlag != 0 {
|
||||
states = append(states, state.State(2+offset))
|
||||
}
|
||||
if 0x00000004&stateFlag != 0 {
|
||||
states = append(states, state.State(3+offset))
|
||||
}
|
||||
if 0x00000008&stateFlag != 0 {
|
||||
states = append(states, state.State(4+offset))
|
||||
}
|
||||
if 0x00000010&stateFlag != 0 {
|
||||
states = append(states, state.State(5+offset))
|
||||
}
|
||||
if 0x00000020&stateFlag != 0 {
|
||||
states = append(states, state.State(6+offset))
|
||||
}
|
||||
if 0x00000040&stateFlag != 0 {
|
||||
states = append(states, state.State(7+offset))
|
||||
}
|
||||
if 0x00000080&stateFlag != 0 {
|
||||
states = append(states, state.State(8+offset))
|
||||
}
|
||||
if 0x00000100&stateFlag != 0 {
|
||||
states = append(states, state.State(9+offset))
|
||||
}
|
||||
if 0x00000200&stateFlag != 0 {
|
||||
states = append(states, state.State(10+offset))
|
||||
}
|
||||
if 0x00000400&stateFlag != 0 {
|
||||
states = append(states, state.State(11+offset))
|
||||
}
|
||||
if 0x00000800&stateFlag != 0 {
|
||||
states = append(states, state.State(12+offset))
|
||||
}
|
||||
if 0x00001000&stateFlag != 0 {
|
||||
states = append(states, state.State(13+offset))
|
||||
}
|
||||
if 0x00002000&stateFlag != 0 {
|
||||
states = append(states, state.State(14+offset))
|
||||
}
|
||||
if 0x00004000&stateFlag != 0 {
|
||||
states = append(states, state.State(15+offset))
|
||||
}
|
||||
if 0x00008000&stateFlag != 0 {
|
||||
states = append(states, state.State(16+offset))
|
||||
}
|
||||
if 0x00010000&stateFlag != 0 {
|
||||
states = append(states, state.State(17+offset))
|
||||
}
|
||||
if 0x00020000&stateFlag != 0 {
|
||||
states = append(states, state.State(18+offset))
|
||||
}
|
||||
if 0x00040000&stateFlag != 0 {
|
||||
states = append(states, state.State(19+offset))
|
||||
}
|
||||
if 0x00080000&stateFlag != 0 {
|
||||
states = append(states, state.State(20+offset))
|
||||
}
|
||||
if 0x00100000&stateFlag != 0 {
|
||||
states = append(states, state.State(21+offset))
|
||||
}
|
||||
if 0x00200000&stateFlag != 0 {
|
||||
states = append(states, state.State(22+offset))
|
||||
}
|
||||
if 0x00400000&stateFlag != 0 {
|
||||
states = append(states, state.State(23+offset))
|
||||
}
|
||||
if 0x00800000&stateFlag != 0 {
|
||||
states = append(states, state.State(24+offset))
|
||||
}
|
||||
if 0x01000000&stateFlag != 0 {
|
||||
states = append(states, state.State(25+offset))
|
||||
}
|
||||
if 0x02000000&stateFlag != 0 {
|
||||
states = append(states, state.State(26+offset))
|
||||
}
|
||||
if 0x04000000&stateFlag != 0 {
|
||||
states = append(states, state.State(27+offset))
|
||||
}
|
||||
if 0x08000000&stateFlag != 0 {
|
||||
states = append(states, state.State(28+offset))
|
||||
}
|
||||
if 0x10000000&stateFlag != 0 {
|
||||
states = append(states, state.State(29+offset))
|
||||
}
|
||||
if 0x20000000&stateFlag != 0 {
|
||||
states = append(states, state.State(30+offset))
|
||||
}
|
||||
if 0x40000000&stateFlag != 0 {
|
||||
states = append(states, state.State(31+offset))
|
||||
}
|
||||
if 0x80000000&stateFlag != 0 {
|
||||
states = append(states, state.State(32+offset))
|
||||
}
|
||||
|
||||
return states
|
||||
}
|
||||
166
pkg/memory/process.go
Normal file
166
pkg/memory/process.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"github.com/winlabs/gowin32"
|
||||
"golang.org/x/sys/windows"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const moduleName = "D2R.exe"
|
||||
|
||||
type Process struct {
|
||||
handler windows.Handle
|
||||
pid uint
|
||||
moduleBaseAddressPtr uintptr
|
||||
moduleBaseSize uint
|
||||
}
|
||||
|
||||
func NewProcess() (Process, error) {
|
||||
module, err := getGameModule()
|
||||
if err != nil {
|
||||
return Process{}, err
|
||||
}
|
||||
|
||||
h, err := windows.OpenProcess(0x0010, false, uint32(module.ProcessID))
|
||||
if err != nil {
|
||||
return Process{}, err
|
||||
}
|
||||
|
||||
return Process{
|
||||
handler: h,
|
||||
pid: module.ProcessID,
|
||||
moduleBaseAddressPtr: uintptr(unsafe.Pointer(module.ModuleBaseAddress)),
|
||||
moduleBaseSize: module.ModuleBaseSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getGameModule() (gowin32.ModuleInfo, error) {
|
||||
processes := make([]uint32, 2048)
|
||||
length := uint32(0)
|
||||
err := windows.EnumProcesses(processes, &length)
|
||||
if err != nil {
|
||||
return gowin32.ModuleInfo{}, err
|
||||
}
|
||||
|
||||
for _, process := range processes {
|
||||
module, _ := getMainModule(process)
|
||||
|
||||
if strings.Contains(module.ExePath, moduleName) {
|
||||
return module, nil
|
||||
}
|
||||
}
|
||||
|
||||
return gowin32.ModuleInfo{}, err
|
||||
}
|
||||
|
||||
func getMainModule(pid uint32) (gowin32.ModuleInfo, error) {
|
||||
mi, err := gowin32.GetProcessModules(pid)
|
||||
if err != nil {
|
||||
return gowin32.ModuleInfo{}, err
|
||||
}
|
||||
for _, m := range mi {
|
||||
if m.ModuleName == moduleName {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
return gowin32.ModuleInfo{}, err
|
||||
}
|
||||
|
||||
func (p Process) getProcessMemory() ([]byte, error) {
|
||||
var data = make([]byte, p.moduleBaseSize)
|
||||
err := windows.ReadProcessMemory(p.handler, p.moduleBaseAddressPtr, &data[0], uintptr(p.moduleBaseSize), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (p Process) ReadBytesFromMemory(address uintptr, size uint) []byte {
|
||||
var data = make([]byte, size)
|
||||
windows.ReadProcessMemory(p.handler, address, &data[0], uintptr(size), nil)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type IntType uint
|
||||
|
||||
const (
|
||||
Uint8 = 1
|
||||
Uint16 = 2
|
||||
Uint32 = 4
|
||||
Uint64 = 8
|
||||
)
|
||||
|
||||
func (p Process) ReadUInt(address uintptr, size IntType) uint {
|
||||
bytes := p.ReadBytesFromMemory(address, uint(size))
|
||||
|
||||
return bytesToUint(bytes, size)
|
||||
}
|
||||
|
||||
func ReadUIntFromBuffer(bytes []byte, offset uint, size IntType) uint {
|
||||
return bytesToUint(bytes[offset:offset+uint(size)], size)
|
||||
}
|
||||
|
||||
func bytesToUint(bytes []byte, size IntType) uint {
|
||||
switch size {
|
||||
case Uint8:
|
||||
return uint(bytes[0])
|
||||
case Uint16:
|
||||
return uint(binary.LittleEndian.Uint16(bytes))
|
||||
case Uint32:
|
||||
return uint(binary.LittleEndian.Uint32(bytes))
|
||||
case Uint64:
|
||||
return uint(binary.LittleEndian.Uint64(bytes))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p Process) ReadStringFromMemory(address uintptr, size uint) string {
|
||||
if size == 0 {
|
||||
for i := 1; true; i++ {
|
||||
data := p.ReadBytesFromMemory(address, uint(i))
|
||||
if data[i-1] == 0 {
|
||||
return string(bytes.Trim(data, "\x00"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string(bytes.Trim(p.ReadBytesFromMemory(address, size), "\x00"))
|
||||
}
|
||||
|
||||
func (p Process) findPattern(memory []byte, pattern, mask string) int {
|
||||
patternLength := len(pattern)
|
||||
for i := 0; i < int(p.moduleBaseSize)-patternLength; i++ {
|
||||
found := true
|
||||
for j := 0; j < patternLength; j++ {
|
||||
if string(mask[j]) != "?" && string(pattern[j]) != string(memory[i+j]) {
|
||||
found = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p Process) FindPattern(memory []byte, pattern, mask string) uintptr {
|
||||
if offset := p.findPattern(memory, pattern, mask); offset != 0 {
|
||||
return p.moduleBaseAddressPtr + uintptr(offset)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p Process) GetPID() uint {
|
||||
return p.pid
|
||||
}
|
||||
27
pkg/memory/roster.go
Normal file
27
pkg/memory/roster.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"github.com/hectorgimenez/d2go/pkg/data/area"
|
||||
)
|
||||
|
||||
func (gd *GameReader) getRoster() (roster []data.RosterMember) {
|
||||
partyStruct := uintptr(gd.Process.ReadUInt(gd.Process.moduleBaseAddressPtr+gd.offset.RosterOffset, Uint64))
|
||||
|
||||
for partyStruct > 0 {
|
||||
name := gd.Process.ReadStringFromMemory(partyStruct, 16)
|
||||
a := gd.Process.ReadUInt(partyStruct+0x5C, Uint32)
|
||||
|
||||
xPos := gd.Process.ReadUInt(partyStruct+0x60, Uint32)
|
||||
yPos := gd.Process.ReadUInt(partyStruct+0x64, Uint32)
|
||||
|
||||
roster = append(roster, data.RosterMember{
|
||||
Name: name,
|
||||
Area: area.Area(a),
|
||||
Position: data.Position{X: int(xPos), Y: int(yPos)},
|
||||
})
|
||||
partyStruct = uintptr(gd.Process.ReadUInt(partyStruct+0x148, Uint64))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
13
pkg/utils/utils.go
Normal file
13
pkg/utils/utils.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/hectorgimenez/d2go/pkg/data"
|
||||
"math"
|
||||
)
|
||||
|
||||
func DistanceFromPoint(from data.Position, to data.Position) int {
|
||||
first := math.Pow(float64(to.X-from.X), 2)
|
||||
second := math.Pow(float64(to.Y-from.Y), 2)
|
||||
|
||||
return int(math.Sqrt(first + second))
|
||||
}
|
||||
Reference in New Issue
Block a user