mirror of
https://github.com/thunderbrewhq/binana.git
synced 2026-02-03 16:39:07 +00:00
feat(core): CheatEngine item container coprse loaders
This commit is contained in:
parent
52bd97bc47
commit
89a8691959
14 changed files with 2787 additions and 106 deletions
|
|
@ -716,55 +716,45 @@ function GetCGObjectAddr(guidValue)
|
|||
return nil
|
||||
end
|
||||
|
||||
local CGUNIT_C_VTABLE = 0xa34d90
|
||||
local CGPLAYER_C_VTABLE = 0xa326c8
|
||||
local VTABLE_TYPES = {
|
||||
[0xA34D90] = "unit",
|
||||
[0xA326C8] = "player",
|
||||
[0xA33428] = "item",
|
||||
[0xA332D0] = "container",
|
||||
[0xA331C8] = "corpse",
|
||||
}
|
||||
|
||||
local memScan = createMemScan()
|
||||
local foundList = createFoundList(memScan)
|
||||
|
||||
local function cleanup()
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
end
|
||||
|
||||
memScan.firstScan(
|
||||
soExactValue,
|
||||
vtQword,
|
||||
rtRounded,
|
||||
soExactValue, vtQword, rtRounded,
|
||||
string.format("%016X", guidValue),
|
||||
nil,
|
||||
0x0,
|
||||
0x7FFFFFFFFFFFFFFF,
|
||||
"",
|
||||
fsmNotAligned,
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
nil, 0x0, 0x7FFFFFFFFFFFFFFF,
|
||||
"", fsmNotAligned, nil,
|
||||
true, false, false, false
|
||||
)
|
||||
|
||||
memScan.waitTillDone()
|
||||
foundList.initialize()
|
||||
|
||||
local resultAddr, resultType = nil, nil
|
||||
|
||||
for i = 0, foundList.Count - 1 do
|
||||
local addrGUIDvalue = tonumber(foundList.Address[i], 16)
|
||||
if addrGUIDvalue then
|
||||
local base = addrGUIDvalue - 0x30
|
||||
local vtbl = readInteger(base)
|
||||
|
||||
if vtbl == CGPLAYER_C_VTABLE then
|
||||
resultAddr, resultType = base, "player"
|
||||
break
|
||||
elseif vtbl == CGUNIT_C_VTABLE then
|
||||
resultAddr, resultType = base, "unit"
|
||||
break
|
||||
local addr = tonumber(foundList.Address[i], 16)
|
||||
if addr then
|
||||
local base = addr - 0x30
|
||||
local objType = VTABLE_TYPES[readInteger(base)]
|
||||
if objType then
|
||||
cleanup()
|
||||
return base, objType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
|
||||
if resultAddr then
|
||||
return resultAddr, resultType
|
||||
end
|
||||
cleanup()
|
||||
return nil
|
||||
end
|
||||
48
cheatengine/cgcontainer.lua
Normal file
48
cheatengine/cgcontainer.lua
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
-- #include "cgobject.lua"
|
||||
|
||||
local ObjectFields = Struct("ObjectFields")
|
||||
:hex("GUID", "uint64")
|
||||
:field("type", "uint32")
|
||||
:field("unk", "uint32")
|
||||
:field("scale", "float")
|
||||
:field("pad", "uint32")
|
||||
|
||||
local ContainerSlot = Struct("ContainerSlot")
|
||||
:hex("guid", "uint64")
|
||||
|
||||
local ContainerFields = Struct("ContainerFields")
|
||||
:field("NumSlots", "int32")
|
||||
:field("Pad", "int32")
|
||||
:structArray("Slot", ContainerSlot, 36)
|
||||
|
||||
local ItemEnchantment = Struct("ItemEnchantment")
|
||||
:array("id", "uint32", 3)
|
||||
|
||||
local ItemFields = Struct("ItemFields")
|
||||
:hex("Owner", "uint64")
|
||||
:hex("Contained", "uint64")
|
||||
:hex("Creator", "uint64")
|
||||
:hex("GiftCreator", "uint64")
|
||||
:field("StackCount", "int32")
|
||||
:field("Duration", "int32")
|
||||
:array("SpellCharges", "int32", 5)
|
||||
:hex("Flags", "uint32")
|
||||
:structArray("Enchantment", ItemEnchantment, 12)
|
||||
:field("PropertySeed", "int32")
|
||||
:field("RandomPropertiesID", "int32")
|
||||
:field("Durability", "int32")
|
||||
:field("MaxDurability", "int32")
|
||||
:field("CreatePlayedTime", "int32")
|
||||
:field("Pad", "int32")
|
||||
|
||||
local CGContainer = Struct("CGContainer", CGObject)
|
||||
:paddingTo(0x778)
|
||||
:embed("ObjectFields", ObjectFields) -- 0x778
|
||||
:embed("ItemFields", ItemFields) -- 0x790
|
||||
:embed("ContainerFields", ContainerFields) -- 0x878
|
||||
:field("itemId", "int32") -- 0x9A0
|
||||
:field("scale", "float") -- 0x9A4
|
||||
:paddingTo(0xB88)
|
||||
|
||||
|
||||
-- #include "load_player_container.lua"
|
||||
39
cheatengine/cgcorpse.lua
Normal file
39
cheatengine/cgcorpse.lua
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
-- #include "cgobject.lua"
|
||||
|
||||
local Vector3 = Struct("Vector3")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local ObjectFields = Struct("ObjectFields")
|
||||
:hex("GUID", "uint64")
|
||||
:field("type", "uint32")
|
||||
:field("unk", "uint32")
|
||||
:field("scale", "float")
|
||||
:field("pad", "uint32")
|
||||
|
||||
local CorpseFields = Struct("CorpseFields")
|
||||
:hex("Owner", "uint64")
|
||||
:hex("Party", "uint64")
|
||||
:field("DisplayId", "int32")
|
||||
:array("Item", "int32", 19)
|
||||
:hex("Bytes1", "uint32")
|
||||
:hex("Bytes2", "uint32")
|
||||
:field("Guild", "int32")
|
||||
:field("Flags", "int32")
|
||||
:field("DynamicFlags", "int32")
|
||||
:field("Pad", "int32")
|
||||
|
||||
local CGCorpse = Struct("CGCorpse", CGObject)
|
||||
:paddingTo(0xE8)
|
||||
:embed("Position", Vector3) -- 0x0E8
|
||||
:paddingTo(0xF8)
|
||||
:field("FacingAngle", "float") -- 0x0F8
|
||||
:paddingTo(0x274)
|
||||
:embed("Scale", Vector3) -- 0x274
|
||||
:paddingTo(0x290)
|
||||
:embed("ObjectFields", ObjectFields) -- 0x290
|
||||
:embed("CorpseFields", CorpseFields) -- 0x2A8
|
||||
:paddingTo(0x338)
|
||||
|
||||
-- #include "load_mouseover_corpse.lua"
|
||||
44
cheatengine/cgitem.lua
Normal file
44
cheatengine/cgitem.lua
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
-- #include "cgobject.lua"
|
||||
|
||||
local ObjectFields = Struct("ObjectFields")
|
||||
:hex("GUID", "uint64")
|
||||
:field("type", "uint32")
|
||||
:field("unk", "uint32")
|
||||
:field("scale", "float")
|
||||
:field("pad", "uint32")
|
||||
|
||||
local ItemEnchantment = Struct("ItemEnchantment")
|
||||
:array("id", "uint32", 3)
|
||||
|
||||
local ItemFields = Struct("ItemFields")
|
||||
:hex("Owner", "uint64")
|
||||
:hex("Contained", "uint64")
|
||||
:hex("Creator", "uint64")
|
||||
:hex("GiftCreator", "uint64")
|
||||
:field("StackCount", "int32")
|
||||
:field("Duration", "int32")
|
||||
:array("SpellCharges", "int32", 5)
|
||||
:hex("Flags", "uint32")
|
||||
:structArray("Enchantment", ItemEnchantment, 12)
|
||||
:field("PropertySeed", "int32")
|
||||
:field("RandomPropertiesID", "int32")
|
||||
:field("Durability", "int32")
|
||||
:field("MaxDurability", "int32")
|
||||
:field("CreatePlayedTime", "int32")
|
||||
:field("Pad", "int32")
|
||||
|
||||
local CGItem = Struct("CGItem", CGObject)
|
||||
:paddingTo(0x3E0)
|
||||
:embed("ObjectFields", ObjectFields) -- 0x3E0
|
||||
:embed("ItemFields", ItemFields) -- 0x3F8
|
||||
:field("itemId", "int32") -- 0x4E0
|
||||
:field("scale", "float") -- 0x4E4
|
||||
:paddingTo(0x4F4)
|
||||
:field("StackCount", "int32") -- 0x4F4
|
||||
:array("SpellCharges", "int32", 5) -- 0x4F8
|
||||
:hex("Flags", "uint32") -- 0x50C
|
||||
:structArray("Enchantment", ItemEnchantment, 12) -- 0x510
|
||||
:field("Durability", "int32") -- 0x5A0
|
||||
:paddingTo(0x5A8)
|
||||
|
||||
-- #include "load_bagpack_item.lua"
|
||||
|
|
@ -18,7 +18,7 @@ CGObject:field("m_objectScalingEndMS", "int32") -- 0x00A0
|
|||
CGObject:field("m_objectLastScale", "float") -- 0x00A4
|
||||
CGObject:ptr("specialEffectPtr") -- 0x00A8
|
||||
CGObject:field("objectHeight", "float") -- 0x00AC
|
||||
CGObject:ptr("unkPlayerNamePtr", "int32") -- 0x00B0
|
||||
CGObject:ptr("unkPlayerNamePtr") -- 0x00B0
|
||||
CGObject:ptr("CM2Model", "m_model") -- 0x00B4
|
||||
CGObject:ptr("cmapEntityPtr") -- 0x00B8
|
||||
CGObject:hex("unkMovementFlags", "int32") -- 0x00BC
|
||||
|
|
|
|||
12
cheatengine/load_bagpack_item.lua
Normal file
12
cheatengine/load_bagpack_item.lua
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
local addr, typ = GetCGObjectAddr(readQword(0xC79D10)) -- local player GUID from game connection class
|
||||
if addr then
|
||||
if typ == "player" then
|
||||
local bagpackOffset = 0x1F20
|
||||
for i = 0, 15, 1 do
|
||||
local addr, typ = GetCGObjectAddr(readQword(addr + bagpackOffset + i * 8))
|
||||
if addr ~= 0 and typ == "item" then
|
||||
loadStructToTable(CGItem, addr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
17
cheatengine/load_mouseover_corpse.lua
Normal file
17
cheatengine/load_mouseover_corpse.lua
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
local address = 0x51f838 -- at CGGameUI__HandleObjectTrackChange
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
local addr, typ = GetCGObjectAddr(readQword(0x00bd07a0)) -- mouseover guid
|
||||
if addr then
|
||||
if typ == "corpse" then
|
||||
loadStructToTable(CGCorpse, addr)
|
||||
end
|
||||
end
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
12
cheatengine/load_player_container.lua
Normal file
12
cheatengine/load_player_container.lua
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
local addr, typ = GetCGObjectAddr(readQword(0xC79D10)) -- local player GUID from game connection class
|
||||
if addr then
|
||||
if typ == "player" then
|
||||
local bagsOffset = 0x1F00
|
||||
for i = 0, 4, 1 do
|
||||
local addr, typ = GetCGObjectAddr(readQword(addr + bagsOffset + i * 8))
|
||||
if addr ~= 0 and typ == "container" then
|
||||
loadStructToTable(CGContainer, addr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
848
profile/3.3.5a-windows-386/cheatengine/Load_CGContainer.lua
Normal file
848
profile/3.3.5a-windows-386/cheatengine/Load_CGContainer.lua
Normal file
|
|
@ -0,0 +1,848 @@
|
|||
local CE = {
|
||||
vtByte = 0,
|
||||
vtWord = 1,
|
||||
vtDword = 2,
|
||||
vtQword = 3,
|
||||
vtSingle = 4,
|
||||
vtDouble = 5,
|
||||
vtString = 6,
|
||||
vtGrouped = 14,
|
||||
}
|
||||
|
||||
local Types = {
|
||||
-- Unsigned integers
|
||||
uint8 = { ce = CE.vtByte, size = 1 },
|
||||
uint16 = { ce = CE.vtWord, size = 2 },
|
||||
uint32 = { ce = CE.vtDword, size = 4 },
|
||||
uint64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Signed integers
|
||||
int8 = { ce = CE.vtByte, size = 1 },
|
||||
int16 = { ce = CE.vtWord, size = 2 },
|
||||
int32 = { ce = CE.vtDword, size = 4 },
|
||||
int64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Floating point
|
||||
float = { ce = CE.vtSingle, size = 4 },
|
||||
double = { ce = CE.vtDouble, size = 8 },
|
||||
|
||||
-- Pointers
|
||||
ptr = { ce = CE.vtDword, size = 4 },
|
||||
ptr32 = { ce = CE.vtDword, size = 4 },
|
||||
ptr64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Aliases
|
||||
bool = { ce = CE.vtByte, size = 1 },
|
||||
bool32 = { ce = CE.vtDword, size = 4 },
|
||||
char = { ce = CE.vtByte, size = 1 },
|
||||
byte = { ce = CE.vtByte, size = 1 },
|
||||
word = { ce = CE.vtWord, size = 2 },
|
||||
dword = { ce = CE.vtDword, size = 4 },
|
||||
qword = { ce = CE.vtQword, size = 8 },
|
||||
}
|
||||
|
||||
function SetPointerSize(bits)
|
||||
if bits == 64 then
|
||||
Types.ptr = { ce = CE.vtQword, size = 8 }
|
||||
else
|
||||
Types.ptr = { ce = CE.vtDword, size = 4 }
|
||||
end
|
||||
end
|
||||
|
||||
local StructDef = {}
|
||||
StructDef.__index = StructDef
|
||||
|
||||
-- @param name
|
||||
-- @param parent
|
||||
function StructDef.new(name, parent)
|
||||
local self = setmetatable({}, StructDef)
|
||||
self.name = name
|
||||
self.parent = parent
|
||||
self.ownFields = {}
|
||||
self.embeddings = {}
|
||||
|
||||
if parent then
|
||||
self._offset = parent:totalSize()
|
||||
else
|
||||
self._offset = 0
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param typeName (uint32, float, ptr etc)
|
||||
-- @param opts {hex=bool, color=number}
|
||||
function StructDef:field(name, typeName, opts)
|
||||
opts = opts or {}
|
||||
|
||||
local typeInfo = Types[typeName]
|
||||
if not typeInfo then
|
||||
error(string.format("Unknown type '%s' for field '%s'", typeName, name))
|
||||
end
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = name,
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = opts.hex,
|
||||
})
|
||||
|
||||
self._offset = self._offset + typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
StructDef.f = StructDef.field
|
||||
|
||||
--- Add hex-field
|
||||
function StructDef:hex(name, typeName, opts)
|
||||
opts = opts or {}
|
||||
opts.hex = true
|
||||
return self:field(name, typeName, opts)
|
||||
end
|
||||
|
||||
--- Add string
|
||||
function StructDef:string(name, size, opts)
|
||||
opts = opts or {}
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = name,
|
||||
type = CE.vtString,
|
||||
string_size = size,
|
||||
size = size,
|
||||
color = opts.color,
|
||||
})
|
||||
|
||||
self._offset = self._offset + size
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add array
|
||||
function StructDef:array(name, typeName, count, opts)
|
||||
opts = opts or {}
|
||||
|
||||
local typeInfo = Types[typeName]
|
||||
if not typeInfo then
|
||||
error(string.format("Unknown type '%s' for array '%s'", typeName, name))
|
||||
end
|
||||
|
||||
for i = 0, count - 1 do
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset + i * typeInfo.size,
|
||||
name = string.format("%s[%d]", name, i),
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = opts.hex,
|
||||
})
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
--- skip to X bytes
|
||||
function StructDef:paddingTo(targetOffset, opts)
|
||||
local size = targetOffset - self._offset
|
||||
if size <= 0 then
|
||||
return self
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
local remaining = size
|
||||
|
||||
while remaining >= 4 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint32", opts)
|
||||
remaining = remaining - 4
|
||||
end
|
||||
|
||||
while remaining >= 1 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint8", opts)
|
||||
remaining = remaining - 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- skip N bytes
|
||||
function StructDef:padding(size, opts)
|
||||
opts = opts or {}
|
||||
local remaining = size
|
||||
|
||||
while remaining >= 4 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint32", opts)
|
||||
remaining = remaining - 4
|
||||
end
|
||||
|
||||
while remaining >= 1 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint8", opts)
|
||||
remaining = remaining - 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:skip(size)
|
||||
self._offset = self._offset + size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:unk(size, opts)
|
||||
opts = opts or {}
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
|
||||
if size == 1 then
|
||||
return self:field(name, "uint8", opts)
|
||||
elseif size == 2 then
|
||||
return self:field(name, "uint16", opts)
|
||||
elseif size == 4 then
|
||||
return self:field(name, "uint32", opts)
|
||||
elseif size == 8 then
|
||||
return self:field(name, "uint64", opts)
|
||||
else
|
||||
return self:array(name, "uint8", size, opts)
|
||||
end
|
||||
end
|
||||
|
||||
function StructDef:alignTo(alignment)
|
||||
local rem = self._offset % alignment
|
||||
if rem ~= 0 then
|
||||
self._offset = self._offset + (alignment - rem)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:at(offset)
|
||||
self._offset = offset
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:currentOffset()
|
||||
return self._offset
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param otherStruct
|
||||
-- @param opts {expand=bool}
|
||||
function StructDef:embed(name, otherStruct, opts)
|
||||
opts = opts or {}
|
||||
local baseOffset = self._offset
|
||||
|
||||
table.insert(self.embeddings, {
|
||||
type = "embed",
|
||||
name = name,
|
||||
struct = otherStruct,
|
||||
offset = baseOffset,
|
||||
size = otherStruct:totalSize(),
|
||||
expand = opts.expand or false,
|
||||
})
|
||||
|
||||
for _, f in ipairs(otherStruct:getAllFields()) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.fieldOffset = baseOffset + f.fieldOffset
|
||||
newField.name = name .. "." .. f.name
|
||||
newField.structName = nil
|
||||
newField._embeddedIn = name
|
||||
table.insert(self.ownFields, newField)
|
||||
end
|
||||
|
||||
self._offset = self._offset + otherStruct:totalSize()
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param otherStruct
|
||||
-- @param count
|
||||
-- @param opts {expand=bool}
|
||||
function StructDef:structArray(name, otherStruct, count, opts)
|
||||
opts = opts or {}
|
||||
local structSize = otherStruct:totalSize()
|
||||
local baseOffset = self._offset
|
||||
|
||||
table.insert(self.embeddings, {
|
||||
type = "structArray",
|
||||
name = name,
|
||||
struct = otherStruct,
|
||||
offset = baseOffset,
|
||||
count = count,
|
||||
size = count * structSize,
|
||||
elemSize = structSize,
|
||||
expand = opts.expand or false,
|
||||
})
|
||||
|
||||
for i = 0, count - 1 do
|
||||
local elemOffset = self._offset + i * structSize
|
||||
|
||||
for _, f in ipairs(otherStruct:getAllFields()) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.fieldOffset = elemOffset + f.fieldOffset
|
||||
newField.name = string.format("%s[%d].%s", name, i, f.name)
|
||||
newField._embeddedIn = name
|
||||
newField._arrayIndex = i
|
||||
table.insert(self.ownFields, newField)
|
||||
end
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * structSize
|
||||
return self
|
||||
end
|
||||
|
||||
-- :ptrArray("fieldName", count) -- void*[]
|
||||
-- :ptrArray("fieldName", count, opts) -- void*[] with opts
|
||||
-- :ptrArray("TypeName", "fieldName", count) -- TypeName*[]
|
||||
-- :ptrArray("TypeName", "fieldName", count, opts) -- TypeName*[] with opts
|
||||
function StructDef:ptrArray(arg1, arg2, arg3, arg4)
|
||||
local typeName, fieldName, count, opts
|
||||
|
||||
if type(arg2) == "number" then
|
||||
-- ptrArray("fieldName", count) or ptrArray("fieldName", count, opts)
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
count = arg2
|
||||
opts = arg3 or {}
|
||||
elseif type(arg2) == "string" then
|
||||
-- ptrArray("TypeName", "fieldName", count) or ptrArray("TypeName", "fieldName", count, opts)
|
||||
typeName = arg1
|
||||
fieldName = arg2
|
||||
count = arg3
|
||||
opts = arg4 or {}
|
||||
else
|
||||
error("Invalid arguments for ptrArray()")
|
||||
end
|
||||
|
||||
local typeInfo = Types.ptr
|
||||
|
||||
for i = 0, count - 1 do
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset + i * typeInfo.size,
|
||||
name = string.format("%s[%d]", fieldName, i),
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = true,
|
||||
isPointer = true,
|
||||
ptrType = typeName,
|
||||
_ptrArrayBase = fieldName,
|
||||
})
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:ptr(arg1, arg2, arg3)
|
||||
local typeName, fieldName, opts
|
||||
|
||||
if arg2 == nil then
|
||||
-- ptr("fieldName")
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
opts = {}
|
||||
elseif type(arg2) == "table" then
|
||||
-- ptr("fieldName", opts)
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
opts = arg2
|
||||
elseif type(arg2) == "string" then
|
||||
-- ptr("TypeName", "fieldName") or ptr("TypeName", "fieldName", opts)
|
||||
typeName = arg1
|
||||
fieldName = arg2
|
||||
opts = arg3 or {}
|
||||
else
|
||||
error("Invalid arguments for ptr()")
|
||||
end
|
||||
|
||||
local typeInfo = Types.ptr
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = fieldName,
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = true,
|
||||
isPointer = true,
|
||||
ptrType = typeName,
|
||||
})
|
||||
|
||||
self._offset = self._offset + typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:toCStruct(options)
|
||||
options = options or {}
|
||||
local indent = options.indent or " "
|
||||
local collapsePadding = options.collapsePadding ~= false
|
||||
local flattenInheritance = options.flattenInheritance or false
|
||||
local lines = {}
|
||||
|
||||
local ceToC = {
|
||||
[CE.vtByte] = "uint8_t",
|
||||
[CE.vtWord] = "uint16_t",
|
||||
[CE.vtDword] = "uint32_t",
|
||||
[CE.vtQword] = "uint64_t",
|
||||
[CE.vtSingle] = "float",
|
||||
[CE.vtDouble] = "double",
|
||||
[CE.vtString] = "char",
|
||||
}
|
||||
|
||||
local function getPtrTypeName(field)
|
||||
return field.ptrType or "void"
|
||||
end
|
||||
|
||||
local function sanitizeName(name)
|
||||
return name:gsub("%.", "_"):gsub("%[", "_"):gsub("%]", "")
|
||||
end
|
||||
|
||||
local function isUnkField(name)
|
||||
return name:match("^unk_[%dA-Fa-f]+$") ~= nil
|
||||
end
|
||||
|
||||
local function isUnkArray(name)
|
||||
return name:match("^unk_[%dA-Fa-f]+%[%d+%]$") ~= nil
|
||||
end
|
||||
|
||||
local function isAnyUnk(name)
|
||||
return isUnkField(name) or isUnkArray(name)
|
||||
end
|
||||
|
||||
local fields
|
||||
local embeddingMap = {}
|
||||
|
||||
if flattenInheritance then
|
||||
fields = self:getAllFields()
|
||||
for _, emb in ipairs(self:getAllEmbeddings()) do
|
||||
embeddingMap[emb.offset] = emb
|
||||
end
|
||||
else
|
||||
fields = self.ownFields
|
||||
for _, emb in ipairs(self.embeddings) do
|
||||
embeddingMap[emb.offset] = emb
|
||||
end
|
||||
end
|
||||
|
||||
if flattenInheritance and self.parent then
|
||||
table.insert(lines, string.format("// Inherits from: %s (size: 0x%X)", self.parent.name, self.parent:totalSize()))
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("struct %s {", self.name))
|
||||
|
||||
if not flattenInheritance and self.parent then
|
||||
table.insert(lines, string.format("%s%s base; // 0x%04X", indent, self.parent.name, 0))
|
||||
end
|
||||
|
||||
local i = 1
|
||||
local processedEmbeddings = {}
|
||||
|
||||
while i <= #fields do
|
||||
local field = fields[i]
|
||||
local cType = ceToC[field.type] or "uint8_t"
|
||||
|
||||
local emb = embeddingMap[field.fieldOffset]
|
||||
|
||||
if emb and not processedEmbeddings[emb.name] then
|
||||
processedEmbeddings[emb.name] = true
|
||||
|
||||
if emb.type == "embed" then
|
||||
if emb.expand then
|
||||
table.insert(lines, string.format("%sstruct { // %s::%s", indent, emb.struct.name, emb.name))
|
||||
|
||||
for _, sf in ipairs(emb.struct:getAllFields()) do
|
||||
local subType = ceToC[sf.type] or "uint8_t"
|
||||
if sf.type == CE.vtString and sf.string_size then
|
||||
table.insert(lines, string.format("%s%schar %s[%d]; // +0x%02X", indent, indent, sanitizeName(sf.name), sf.string_size, sf.fieldOffset))
|
||||
elseif sf.isPointer then
|
||||
table.insert(lines, string.format("%s%s%s* %s; // +0x%02X", indent, indent, getPtrTypeName(sf), sanitizeName(sf.name), sf.fieldOffset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s%s %s; // +0x%02X", indent, indent, subType, sanitizeName(sf.name), sf.fieldOffset))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s} %s; // 0x%04X",
|
||||
indent, emb.name, emb.offset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s; // 0x%04X",
|
||||
indent, emb.struct.name, emb.name, emb.offset))
|
||||
end
|
||||
|
||||
while i <= #fields and fields[i]._embeddedIn == emb.name do
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
elseif emb.type == "structArray" then
|
||||
if emb.expand then
|
||||
table.insert(lines, string.format("%sstruct { // %s element", indent, emb.struct.name))
|
||||
|
||||
for _, sf in ipairs(emb.struct:getAllFields()) do
|
||||
local subType = ceToC[sf.type] or "uint8_t"
|
||||
if sf.isPointer then
|
||||
table.insert(lines, string.format("%s%s%s* %s; // +0x%02X", indent, indent, getPtrTypeName(sf), sanitizeName(sf.name), sf.fieldOffset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s%s %s; // +0x%02X", indent, indent, subType, sanitizeName(sf.name), sf.fieldOffset))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s} %s[%d]; // 0x%04X (0x%X bytes)", indent, emb.name, emb.count, emb.offset, emb.size))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s[%d]; // 0x%04X (0x%X bytes)", indent, emb.struct.name, emb.name, emb.count, emb.offset, emb.size))
|
||||
end
|
||||
|
||||
while i <= #fields and fields[i]._embeddedIn == emb.name do
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
elseif collapsePadding and isAnyUnk(field.name) then
|
||||
local startOffset = field.fieldOffset
|
||||
local totalSize = 0
|
||||
local j = i
|
||||
|
||||
while j <= #fields do
|
||||
local f = fields[j]
|
||||
local expectedOffset = startOffset + totalSize
|
||||
|
||||
if isAnyUnk(f.name) and f.fieldOffset == expectedOffset then
|
||||
totalSize = totalSize + f.size
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if totalSize > 0 then
|
||||
table.insert(lines, string.format("%suint8_t pad_%04X[0x%X]; // 0x%04X", indent, startOffset, totalSize, startOffset))
|
||||
end
|
||||
|
||||
i = j
|
||||
|
||||
elseif field.isPointer and field._ptrArrayBase and field.name:match("%[0%]$") then
|
||||
local baseName = field._ptrArrayBase
|
||||
local j = i + 1
|
||||
local count = 1
|
||||
|
||||
while j <= #fields do
|
||||
if fields[j]._ptrArrayBase == baseName then
|
||||
count = count + 1
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s%s* %s[%d]; // 0x%04X", indent, getPtrTypeName(field), baseName, count, field.fieldOffset))
|
||||
i = j
|
||||
|
||||
elseif field.name:match("%[0%]$") and not field.name:find("%.") then
|
||||
local baseName = field.name:match("^([^%[]+)")
|
||||
local j = i + 1
|
||||
local count = 1
|
||||
|
||||
while j <= #fields do
|
||||
local bn, ci = fields[j].name:match("^([^%[%.]+)%[(%d+)%]$")
|
||||
if bn == baseName and tonumber(ci) == count then
|
||||
count = count + 1
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s%s %s[%d]; // 0x%04X", indent, cType, baseName, count, field.fieldOffset))
|
||||
i = j
|
||||
|
||||
elseif field.type == CE.vtString and field.string_size then
|
||||
table.insert(lines, string.format("%schar %s[%d]; // 0x%04X", indent, sanitizeName(field.name), field.string_size, field.fieldOffset))
|
||||
i = i + 1
|
||||
|
||||
elseif field.isPointer then
|
||||
table.insert(lines, string.format("%s%s* %s; // 0x%04X", indent, getPtrTypeName(field), sanitizeName(field.name), field.fieldOffset))
|
||||
i = i + 1
|
||||
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s; // 0x%04X", indent, cType, sanitizeName(field.name), field.fieldOffset))
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("}; // sizeof: 0x%X (%d bytes)", self:totalSize(), self:totalSize()))
|
||||
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
function StructDef:getDependencies()
|
||||
local deps = {}
|
||||
local seen = {}
|
||||
|
||||
local function collect(struct)
|
||||
if seen[struct.name] then return end
|
||||
seen[struct.name] = true
|
||||
|
||||
if struct.parent then
|
||||
collect(struct.parent)
|
||||
end
|
||||
|
||||
for _, emb in ipairs(struct.embeddings) do
|
||||
collect(emb.struct)
|
||||
end
|
||||
|
||||
table.insert(deps, struct)
|
||||
end
|
||||
|
||||
collect(self)
|
||||
return deps
|
||||
end
|
||||
|
||||
function StructDef:toCStructWithDeps(options)
|
||||
local deps = self:getDependencies()
|
||||
local parts = {}
|
||||
|
||||
for _, struct in ipairs(deps) do
|
||||
table.insert(parts, struct:toCStruct(options))
|
||||
end
|
||||
|
||||
return table.concat(parts, "\n\n")
|
||||
end
|
||||
|
||||
function StructDef:getAllEmbeddings()
|
||||
local result = {}
|
||||
|
||||
if self.parent then
|
||||
for _, e in ipairs(self.parent:getAllEmbeddings()) do
|
||||
table.insert(result, e)
|
||||
end
|
||||
end
|
||||
|
||||
for _, e in ipairs(self.embeddings) do
|
||||
table.insert(result, e)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function StructDef:totalSize()
|
||||
return self._offset
|
||||
end
|
||||
|
||||
function StructDef:getOwnFields()
|
||||
return self.ownFields
|
||||
end
|
||||
|
||||
function StructDef:getAllFields()
|
||||
local fields = {}
|
||||
|
||||
if self.parent then
|
||||
for _, f in ipairs(self.parent:getAllFields()) do
|
||||
table.insert(fields, f)
|
||||
end
|
||||
end
|
||||
|
||||
for _, f in ipairs(self.ownFields) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.structName = self.name
|
||||
table.insert(fields, newField)
|
||||
end
|
||||
|
||||
return fields
|
||||
end
|
||||
|
||||
-- @param struct
|
||||
-- @param baseAddress
|
||||
-- @param options
|
||||
function loadStructToTable(struct, baseAddress, options)
|
||||
options = options or {}
|
||||
local showStructName = options.showStructName ~= false
|
||||
local parentRecord = options.parentRecord
|
||||
|
||||
local fields = struct:getAllFields()
|
||||
|
||||
for _, field in ipairs(fields) do
|
||||
local memrec = AddressList.createMemoryRecord()
|
||||
|
||||
if type(baseAddress) == "string" then
|
||||
memrec.Address = string.format("%s+0x%X", baseAddress, field.fieldOffset)
|
||||
else
|
||||
memrec.Address = string.format("0x%X", field.fieldOffset + baseAddress)
|
||||
end
|
||||
|
||||
if showStructName and field.structName then
|
||||
memrec.Description = string.format("0x%03X %s::%s", field.fieldOffset, field.structName, field.name)
|
||||
else
|
||||
memrec.Description = string.format("0x%03X %s", field.fieldOffset, field.name)
|
||||
end
|
||||
|
||||
memrec.Type = field.type
|
||||
|
||||
if field.string_size then
|
||||
memrec.String.Size = field.string_size
|
||||
end
|
||||
if field.color then
|
||||
memrec.Color = field.color
|
||||
end
|
||||
if field.hex then
|
||||
memrec.ShowAsHex = true
|
||||
end
|
||||
|
||||
if parentRecord then
|
||||
memrec.appendToEntry(parentRecord)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Struct(name, parent)
|
||||
return StructDef.new(name, parent)
|
||||
end
|
||||
|
||||
function GetCGObjectAddr(guidValue)
|
||||
if type(guidValue) == "string" then
|
||||
guidValue = tonumber(guidValue, 16)
|
||||
end
|
||||
if not guidValue or guidValue == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local VTABLE_TYPES = {
|
||||
[0xA34D90] = "unit",
|
||||
[0xA326C8] = "player",
|
||||
[0xA33428] = "item",
|
||||
[0xA332D0] = "container",
|
||||
[0xA331C8] = "corpse",
|
||||
}
|
||||
|
||||
local memScan = createMemScan()
|
||||
local foundList = createFoundList(memScan)
|
||||
|
||||
local function cleanup()
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
end
|
||||
|
||||
memScan.firstScan(
|
||||
soExactValue, vtQword, rtRounded,
|
||||
string.format("%016X", guidValue),
|
||||
nil, 0x0, 0x7FFFFFFFFFFFFFFF,
|
||||
"", fsmNotAligned, nil,
|
||||
true, false, false, false
|
||||
)
|
||||
|
||||
memScan.waitTillDone()
|
||||
foundList.initialize()
|
||||
|
||||
for i = 0, foundList.Count - 1 do
|
||||
local addr = tonumber(foundList.Address[i], 16)
|
||||
if addr then
|
||||
local base = addr - 0x30
|
||||
local objType = VTABLE_TYPES[readInteger(base)]
|
||||
if objType then
|
||||
cleanup()
|
||||
return base, objType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cleanup()
|
||||
return nil
|
||||
end
|
||||
|
||||
local CGObject = Struct("CGObject")
|
||||
|
||||
CGObject:ptr("void*", "VtablePtr") -- 0x0000
|
||||
CGObject:unk(4) -- 0x0004
|
||||
CGObject:ptr("dataBeginPtr") -- 0x0008
|
||||
CGObject:ptr("dataEndPtr") -- 0x000C
|
||||
CGObject:hex("unkFlag", "int32") -- 0x0010
|
||||
CGObject:field("TypeID", "int32") -- 0x0014
|
||||
CGObject:hex("low_GUID", "uint32") -- 0x0018
|
||||
CGObject:paddingTo(0x30)
|
||||
CGObject:hex("ObjectGuid", "uint64") -- 0x0030
|
||||
CGObject:paddingTo(0x98)
|
||||
CGObject:field("m_objectSacle1", "float") -- 0x0098
|
||||
CGObject:field("m_objectSacle2", "float") -- 0x009C
|
||||
CGObject:field("m_objectScalingEndMS", "int32") -- 0x00A0
|
||||
CGObject:field("m_objectLastScale", "float") -- 0x00A4
|
||||
CGObject:ptr("specialEffectPtr") -- 0x00A8
|
||||
CGObject:field("objectHeight", "float") -- 0x00AC
|
||||
CGObject:ptr("unkPlayerNamePtr") -- 0x00B0
|
||||
CGObject:ptr("CM2Model", "m_model") -- 0x00B4
|
||||
CGObject:ptr("cmapEntityPtr") -- 0x00B8
|
||||
CGObject:hex("unkMovementFlags", "int32") -- 0x00BC
|
||||
CGObject:field("unk_00C0", "int32") -- 0x00C0
|
||||
CGObject:field("unk_00C4", "int32") -- 0x00C4
|
||||
CGObject:field("m_alpha", "uint8") -- 0x00C8
|
||||
CGObject:field("m_startAlpha", "uint8") -- 0x00C9
|
||||
CGObject:field("m_endAlpha", "uint8") -- 0x00CA
|
||||
CGObject:field("m_maxAlpha", "uint8") -- 0x00CB
|
||||
CGObject:ptr("effectManagerPtr") -- 0x00CC
|
||||
|
||||
local ObjectFields = Struct("ObjectFields")
|
||||
:hex("GUID", "uint64")
|
||||
:field("type", "uint32")
|
||||
:field("unk", "uint32")
|
||||
:field("scale", "float")
|
||||
:field("pad", "uint32")
|
||||
|
||||
local ContainerSlot = Struct("ContainerSlot")
|
||||
:hex("guid", "uint64")
|
||||
|
||||
local ContainerFields = Struct("ContainerFields")
|
||||
:field("NumSlots", "int32")
|
||||
:field("Pad", "int32")
|
||||
:structArray("Slot", ContainerSlot, 36)
|
||||
|
||||
local ItemEnchantment = Struct("ItemEnchantment")
|
||||
:array("id", "uint32", 3)
|
||||
|
||||
local ItemFields = Struct("ItemFields")
|
||||
:hex("Owner", "uint64")
|
||||
:hex("Contained", "uint64")
|
||||
:hex("Creator", "uint64")
|
||||
:hex("GiftCreator", "uint64")
|
||||
:field("StackCount", "int32")
|
||||
:field("Duration", "int32")
|
||||
:array("SpellCharges", "int32", 5)
|
||||
:hex("Flags", "uint32")
|
||||
:structArray("Enchantment", ItemEnchantment, 12)
|
||||
:field("PropertySeed", "int32")
|
||||
:field("RandomPropertiesID", "int32")
|
||||
:field("Durability", "int32")
|
||||
:field("MaxDurability", "int32")
|
||||
:field("CreatePlayedTime", "int32")
|
||||
:field("Pad", "int32")
|
||||
|
||||
local CGContainer = Struct("CGContainer", CGObject)
|
||||
:paddingTo(0x778)
|
||||
:embed("ObjectFields", ObjectFields) -- 0x778
|
||||
:embed("ItemFields", ItemFields) -- 0x790
|
||||
:embed("ContainerFields", ContainerFields) -- 0x878
|
||||
:field("itemId", "int32") -- 0x9A0
|
||||
:field("scale", "float") -- 0x9A4
|
||||
:paddingTo(0xB88)
|
||||
|
||||
|
||||
local addr, typ = GetCGObjectAddr(readQword(0xC79D10)) -- local player GUID from game connection class
|
||||
if addr then
|
||||
if typ == "player" then
|
||||
local bagsOffset = 0x1F00
|
||||
for i = 0, 4, 1 do
|
||||
local addr, typ = GetCGObjectAddr(readQword(addr + bagsOffset + i * 8))
|
||||
if addr ~= 0 and typ == "container" then
|
||||
loadStructToTable(CGContainer, addr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
844
profile/3.3.5a-windows-386/cheatengine/Load_CGCorpse.lua
Normal file
844
profile/3.3.5a-windows-386/cheatengine/Load_CGCorpse.lua
Normal file
|
|
@ -0,0 +1,844 @@
|
|||
local CE = {
|
||||
vtByte = 0,
|
||||
vtWord = 1,
|
||||
vtDword = 2,
|
||||
vtQword = 3,
|
||||
vtSingle = 4,
|
||||
vtDouble = 5,
|
||||
vtString = 6,
|
||||
vtGrouped = 14,
|
||||
}
|
||||
|
||||
local Types = {
|
||||
-- Unsigned integers
|
||||
uint8 = { ce = CE.vtByte, size = 1 },
|
||||
uint16 = { ce = CE.vtWord, size = 2 },
|
||||
uint32 = { ce = CE.vtDword, size = 4 },
|
||||
uint64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Signed integers
|
||||
int8 = { ce = CE.vtByte, size = 1 },
|
||||
int16 = { ce = CE.vtWord, size = 2 },
|
||||
int32 = { ce = CE.vtDword, size = 4 },
|
||||
int64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Floating point
|
||||
float = { ce = CE.vtSingle, size = 4 },
|
||||
double = { ce = CE.vtDouble, size = 8 },
|
||||
|
||||
-- Pointers
|
||||
ptr = { ce = CE.vtDword, size = 4 },
|
||||
ptr32 = { ce = CE.vtDword, size = 4 },
|
||||
ptr64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Aliases
|
||||
bool = { ce = CE.vtByte, size = 1 },
|
||||
bool32 = { ce = CE.vtDword, size = 4 },
|
||||
char = { ce = CE.vtByte, size = 1 },
|
||||
byte = { ce = CE.vtByte, size = 1 },
|
||||
word = { ce = CE.vtWord, size = 2 },
|
||||
dword = { ce = CE.vtDword, size = 4 },
|
||||
qword = { ce = CE.vtQword, size = 8 },
|
||||
}
|
||||
|
||||
function SetPointerSize(bits)
|
||||
if bits == 64 then
|
||||
Types.ptr = { ce = CE.vtQword, size = 8 }
|
||||
else
|
||||
Types.ptr = { ce = CE.vtDword, size = 4 }
|
||||
end
|
||||
end
|
||||
|
||||
local StructDef = {}
|
||||
StructDef.__index = StructDef
|
||||
|
||||
-- @param name
|
||||
-- @param parent
|
||||
function StructDef.new(name, parent)
|
||||
local self = setmetatable({}, StructDef)
|
||||
self.name = name
|
||||
self.parent = parent
|
||||
self.ownFields = {}
|
||||
self.embeddings = {}
|
||||
|
||||
if parent then
|
||||
self._offset = parent:totalSize()
|
||||
else
|
||||
self._offset = 0
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param typeName (uint32, float, ptr etc)
|
||||
-- @param opts {hex=bool, color=number}
|
||||
function StructDef:field(name, typeName, opts)
|
||||
opts = opts or {}
|
||||
|
||||
local typeInfo = Types[typeName]
|
||||
if not typeInfo then
|
||||
error(string.format("Unknown type '%s' for field '%s'", typeName, name))
|
||||
end
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = name,
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = opts.hex,
|
||||
})
|
||||
|
||||
self._offset = self._offset + typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
StructDef.f = StructDef.field
|
||||
|
||||
--- Add hex-field
|
||||
function StructDef:hex(name, typeName, opts)
|
||||
opts = opts or {}
|
||||
opts.hex = true
|
||||
return self:field(name, typeName, opts)
|
||||
end
|
||||
|
||||
--- Add string
|
||||
function StructDef:string(name, size, opts)
|
||||
opts = opts or {}
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = name,
|
||||
type = CE.vtString,
|
||||
string_size = size,
|
||||
size = size,
|
||||
color = opts.color,
|
||||
})
|
||||
|
||||
self._offset = self._offset + size
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add array
|
||||
function StructDef:array(name, typeName, count, opts)
|
||||
opts = opts or {}
|
||||
|
||||
local typeInfo = Types[typeName]
|
||||
if not typeInfo then
|
||||
error(string.format("Unknown type '%s' for array '%s'", typeName, name))
|
||||
end
|
||||
|
||||
for i = 0, count - 1 do
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset + i * typeInfo.size,
|
||||
name = string.format("%s[%d]", name, i),
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = opts.hex,
|
||||
})
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
--- skip to X bytes
|
||||
function StructDef:paddingTo(targetOffset, opts)
|
||||
local size = targetOffset - self._offset
|
||||
if size <= 0 then
|
||||
return self
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
local remaining = size
|
||||
|
||||
while remaining >= 4 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint32", opts)
|
||||
remaining = remaining - 4
|
||||
end
|
||||
|
||||
while remaining >= 1 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint8", opts)
|
||||
remaining = remaining - 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- skip N bytes
|
||||
function StructDef:padding(size, opts)
|
||||
opts = opts or {}
|
||||
local remaining = size
|
||||
|
||||
while remaining >= 4 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint32", opts)
|
||||
remaining = remaining - 4
|
||||
end
|
||||
|
||||
while remaining >= 1 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint8", opts)
|
||||
remaining = remaining - 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:skip(size)
|
||||
self._offset = self._offset + size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:unk(size, opts)
|
||||
opts = opts or {}
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
|
||||
if size == 1 then
|
||||
return self:field(name, "uint8", opts)
|
||||
elseif size == 2 then
|
||||
return self:field(name, "uint16", opts)
|
||||
elseif size == 4 then
|
||||
return self:field(name, "uint32", opts)
|
||||
elseif size == 8 then
|
||||
return self:field(name, "uint64", opts)
|
||||
else
|
||||
return self:array(name, "uint8", size, opts)
|
||||
end
|
||||
end
|
||||
|
||||
function StructDef:alignTo(alignment)
|
||||
local rem = self._offset % alignment
|
||||
if rem ~= 0 then
|
||||
self._offset = self._offset + (alignment - rem)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:at(offset)
|
||||
self._offset = offset
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:currentOffset()
|
||||
return self._offset
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param otherStruct
|
||||
-- @param opts {expand=bool}
|
||||
function StructDef:embed(name, otherStruct, opts)
|
||||
opts = opts or {}
|
||||
local baseOffset = self._offset
|
||||
|
||||
table.insert(self.embeddings, {
|
||||
type = "embed",
|
||||
name = name,
|
||||
struct = otherStruct,
|
||||
offset = baseOffset,
|
||||
size = otherStruct:totalSize(),
|
||||
expand = opts.expand or false,
|
||||
})
|
||||
|
||||
for _, f in ipairs(otherStruct:getAllFields()) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.fieldOffset = baseOffset + f.fieldOffset
|
||||
newField.name = name .. "." .. f.name
|
||||
newField.structName = nil
|
||||
newField._embeddedIn = name
|
||||
table.insert(self.ownFields, newField)
|
||||
end
|
||||
|
||||
self._offset = self._offset + otherStruct:totalSize()
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param otherStruct
|
||||
-- @param count
|
||||
-- @param opts {expand=bool}
|
||||
function StructDef:structArray(name, otherStruct, count, opts)
|
||||
opts = opts or {}
|
||||
local structSize = otherStruct:totalSize()
|
||||
local baseOffset = self._offset
|
||||
|
||||
table.insert(self.embeddings, {
|
||||
type = "structArray",
|
||||
name = name,
|
||||
struct = otherStruct,
|
||||
offset = baseOffset,
|
||||
count = count,
|
||||
size = count * structSize,
|
||||
elemSize = structSize,
|
||||
expand = opts.expand or false,
|
||||
})
|
||||
|
||||
for i = 0, count - 1 do
|
||||
local elemOffset = self._offset + i * structSize
|
||||
|
||||
for _, f in ipairs(otherStruct:getAllFields()) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.fieldOffset = elemOffset + f.fieldOffset
|
||||
newField.name = string.format("%s[%d].%s", name, i, f.name)
|
||||
newField._embeddedIn = name
|
||||
newField._arrayIndex = i
|
||||
table.insert(self.ownFields, newField)
|
||||
end
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * structSize
|
||||
return self
|
||||
end
|
||||
|
||||
-- :ptrArray("fieldName", count) -- void*[]
|
||||
-- :ptrArray("fieldName", count, opts) -- void*[] with opts
|
||||
-- :ptrArray("TypeName", "fieldName", count) -- TypeName*[]
|
||||
-- :ptrArray("TypeName", "fieldName", count, opts) -- TypeName*[] with opts
|
||||
function StructDef:ptrArray(arg1, arg2, arg3, arg4)
|
||||
local typeName, fieldName, count, opts
|
||||
|
||||
if type(arg2) == "number" then
|
||||
-- ptrArray("fieldName", count) or ptrArray("fieldName", count, opts)
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
count = arg2
|
||||
opts = arg3 or {}
|
||||
elseif type(arg2) == "string" then
|
||||
-- ptrArray("TypeName", "fieldName", count) or ptrArray("TypeName", "fieldName", count, opts)
|
||||
typeName = arg1
|
||||
fieldName = arg2
|
||||
count = arg3
|
||||
opts = arg4 or {}
|
||||
else
|
||||
error("Invalid arguments for ptrArray()")
|
||||
end
|
||||
|
||||
local typeInfo = Types.ptr
|
||||
|
||||
for i = 0, count - 1 do
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset + i * typeInfo.size,
|
||||
name = string.format("%s[%d]", fieldName, i),
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = true,
|
||||
isPointer = true,
|
||||
ptrType = typeName,
|
||||
_ptrArrayBase = fieldName,
|
||||
})
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:ptr(arg1, arg2, arg3)
|
||||
local typeName, fieldName, opts
|
||||
|
||||
if arg2 == nil then
|
||||
-- ptr("fieldName")
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
opts = {}
|
||||
elseif type(arg2) == "table" then
|
||||
-- ptr("fieldName", opts)
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
opts = arg2
|
||||
elseif type(arg2) == "string" then
|
||||
-- ptr("TypeName", "fieldName") or ptr("TypeName", "fieldName", opts)
|
||||
typeName = arg1
|
||||
fieldName = arg2
|
||||
opts = arg3 or {}
|
||||
else
|
||||
error("Invalid arguments for ptr()")
|
||||
end
|
||||
|
||||
local typeInfo = Types.ptr
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = fieldName,
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = true,
|
||||
isPointer = true,
|
||||
ptrType = typeName,
|
||||
})
|
||||
|
||||
self._offset = self._offset + typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:toCStruct(options)
|
||||
options = options or {}
|
||||
local indent = options.indent or " "
|
||||
local collapsePadding = options.collapsePadding ~= false
|
||||
local flattenInheritance = options.flattenInheritance or false
|
||||
local lines = {}
|
||||
|
||||
local ceToC = {
|
||||
[CE.vtByte] = "uint8_t",
|
||||
[CE.vtWord] = "uint16_t",
|
||||
[CE.vtDword] = "uint32_t",
|
||||
[CE.vtQword] = "uint64_t",
|
||||
[CE.vtSingle] = "float",
|
||||
[CE.vtDouble] = "double",
|
||||
[CE.vtString] = "char",
|
||||
}
|
||||
|
||||
local function getPtrTypeName(field)
|
||||
return field.ptrType or "void"
|
||||
end
|
||||
|
||||
local function sanitizeName(name)
|
||||
return name:gsub("%.", "_"):gsub("%[", "_"):gsub("%]", "")
|
||||
end
|
||||
|
||||
local function isUnkField(name)
|
||||
return name:match("^unk_[%dA-Fa-f]+$") ~= nil
|
||||
end
|
||||
|
||||
local function isUnkArray(name)
|
||||
return name:match("^unk_[%dA-Fa-f]+%[%d+%]$") ~= nil
|
||||
end
|
||||
|
||||
local function isAnyUnk(name)
|
||||
return isUnkField(name) or isUnkArray(name)
|
||||
end
|
||||
|
||||
local fields
|
||||
local embeddingMap = {}
|
||||
|
||||
if flattenInheritance then
|
||||
fields = self:getAllFields()
|
||||
for _, emb in ipairs(self:getAllEmbeddings()) do
|
||||
embeddingMap[emb.offset] = emb
|
||||
end
|
||||
else
|
||||
fields = self.ownFields
|
||||
for _, emb in ipairs(self.embeddings) do
|
||||
embeddingMap[emb.offset] = emb
|
||||
end
|
||||
end
|
||||
|
||||
if flattenInheritance and self.parent then
|
||||
table.insert(lines, string.format("// Inherits from: %s (size: 0x%X)", self.parent.name, self.parent:totalSize()))
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("struct %s {", self.name))
|
||||
|
||||
if not flattenInheritance and self.parent then
|
||||
table.insert(lines, string.format("%s%s base; // 0x%04X", indent, self.parent.name, 0))
|
||||
end
|
||||
|
||||
local i = 1
|
||||
local processedEmbeddings = {}
|
||||
|
||||
while i <= #fields do
|
||||
local field = fields[i]
|
||||
local cType = ceToC[field.type] or "uint8_t"
|
||||
|
||||
local emb = embeddingMap[field.fieldOffset]
|
||||
|
||||
if emb and not processedEmbeddings[emb.name] then
|
||||
processedEmbeddings[emb.name] = true
|
||||
|
||||
if emb.type == "embed" then
|
||||
if emb.expand then
|
||||
table.insert(lines, string.format("%sstruct { // %s::%s", indent, emb.struct.name, emb.name))
|
||||
|
||||
for _, sf in ipairs(emb.struct:getAllFields()) do
|
||||
local subType = ceToC[sf.type] or "uint8_t"
|
||||
if sf.type == CE.vtString and sf.string_size then
|
||||
table.insert(lines, string.format("%s%schar %s[%d]; // +0x%02X", indent, indent, sanitizeName(sf.name), sf.string_size, sf.fieldOffset))
|
||||
elseif sf.isPointer then
|
||||
table.insert(lines, string.format("%s%s%s* %s; // +0x%02X", indent, indent, getPtrTypeName(sf), sanitizeName(sf.name), sf.fieldOffset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s%s %s; // +0x%02X", indent, indent, subType, sanitizeName(sf.name), sf.fieldOffset))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s} %s; // 0x%04X",
|
||||
indent, emb.name, emb.offset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s; // 0x%04X",
|
||||
indent, emb.struct.name, emb.name, emb.offset))
|
||||
end
|
||||
|
||||
while i <= #fields and fields[i]._embeddedIn == emb.name do
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
elseif emb.type == "structArray" then
|
||||
if emb.expand then
|
||||
table.insert(lines, string.format("%sstruct { // %s element", indent, emb.struct.name))
|
||||
|
||||
for _, sf in ipairs(emb.struct:getAllFields()) do
|
||||
local subType = ceToC[sf.type] or "uint8_t"
|
||||
if sf.isPointer then
|
||||
table.insert(lines, string.format("%s%s%s* %s; // +0x%02X", indent, indent, getPtrTypeName(sf), sanitizeName(sf.name), sf.fieldOffset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s%s %s; // +0x%02X", indent, indent, subType, sanitizeName(sf.name), sf.fieldOffset))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s} %s[%d]; // 0x%04X (0x%X bytes)", indent, emb.name, emb.count, emb.offset, emb.size))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s[%d]; // 0x%04X (0x%X bytes)", indent, emb.struct.name, emb.name, emb.count, emb.offset, emb.size))
|
||||
end
|
||||
|
||||
while i <= #fields and fields[i]._embeddedIn == emb.name do
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
elseif collapsePadding and isAnyUnk(field.name) then
|
||||
local startOffset = field.fieldOffset
|
||||
local totalSize = 0
|
||||
local j = i
|
||||
|
||||
while j <= #fields do
|
||||
local f = fields[j]
|
||||
local expectedOffset = startOffset + totalSize
|
||||
|
||||
if isAnyUnk(f.name) and f.fieldOffset == expectedOffset then
|
||||
totalSize = totalSize + f.size
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if totalSize > 0 then
|
||||
table.insert(lines, string.format("%suint8_t pad_%04X[0x%X]; // 0x%04X", indent, startOffset, totalSize, startOffset))
|
||||
end
|
||||
|
||||
i = j
|
||||
|
||||
elseif field.isPointer and field._ptrArrayBase and field.name:match("%[0%]$") then
|
||||
local baseName = field._ptrArrayBase
|
||||
local j = i + 1
|
||||
local count = 1
|
||||
|
||||
while j <= #fields do
|
||||
if fields[j]._ptrArrayBase == baseName then
|
||||
count = count + 1
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s%s* %s[%d]; // 0x%04X", indent, getPtrTypeName(field), baseName, count, field.fieldOffset))
|
||||
i = j
|
||||
|
||||
elseif field.name:match("%[0%]$") and not field.name:find("%.") then
|
||||
local baseName = field.name:match("^([^%[]+)")
|
||||
local j = i + 1
|
||||
local count = 1
|
||||
|
||||
while j <= #fields do
|
||||
local bn, ci = fields[j].name:match("^([^%[%.]+)%[(%d+)%]$")
|
||||
if bn == baseName and tonumber(ci) == count then
|
||||
count = count + 1
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s%s %s[%d]; // 0x%04X", indent, cType, baseName, count, field.fieldOffset))
|
||||
i = j
|
||||
|
||||
elseif field.type == CE.vtString and field.string_size then
|
||||
table.insert(lines, string.format("%schar %s[%d]; // 0x%04X", indent, sanitizeName(field.name), field.string_size, field.fieldOffset))
|
||||
i = i + 1
|
||||
|
||||
elseif field.isPointer then
|
||||
table.insert(lines, string.format("%s%s* %s; // 0x%04X", indent, getPtrTypeName(field), sanitizeName(field.name), field.fieldOffset))
|
||||
i = i + 1
|
||||
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s; // 0x%04X", indent, cType, sanitizeName(field.name), field.fieldOffset))
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("}; // sizeof: 0x%X (%d bytes)", self:totalSize(), self:totalSize()))
|
||||
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
function StructDef:getDependencies()
|
||||
local deps = {}
|
||||
local seen = {}
|
||||
|
||||
local function collect(struct)
|
||||
if seen[struct.name] then return end
|
||||
seen[struct.name] = true
|
||||
|
||||
if struct.parent then
|
||||
collect(struct.parent)
|
||||
end
|
||||
|
||||
for _, emb in ipairs(struct.embeddings) do
|
||||
collect(emb.struct)
|
||||
end
|
||||
|
||||
table.insert(deps, struct)
|
||||
end
|
||||
|
||||
collect(self)
|
||||
return deps
|
||||
end
|
||||
|
||||
function StructDef:toCStructWithDeps(options)
|
||||
local deps = self:getDependencies()
|
||||
local parts = {}
|
||||
|
||||
for _, struct in ipairs(deps) do
|
||||
table.insert(parts, struct:toCStruct(options))
|
||||
end
|
||||
|
||||
return table.concat(parts, "\n\n")
|
||||
end
|
||||
|
||||
function StructDef:getAllEmbeddings()
|
||||
local result = {}
|
||||
|
||||
if self.parent then
|
||||
for _, e in ipairs(self.parent:getAllEmbeddings()) do
|
||||
table.insert(result, e)
|
||||
end
|
||||
end
|
||||
|
||||
for _, e in ipairs(self.embeddings) do
|
||||
table.insert(result, e)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function StructDef:totalSize()
|
||||
return self._offset
|
||||
end
|
||||
|
||||
function StructDef:getOwnFields()
|
||||
return self.ownFields
|
||||
end
|
||||
|
||||
function StructDef:getAllFields()
|
||||
local fields = {}
|
||||
|
||||
if self.parent then
|
||||
for _, f in ipairs(self.parent:getAllFields()) do
|
||||
table.insert(fields, f)
|
||||
end
|
||||
end
|
||||
|
||||
for _, f in ipairs(self.ownFields) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.structName = self.name
|
||||
table.insert(fields, newField)
|
||||
end
|
||||
|
||||
return fields
|
||||
end
|
||||
|
||||
-- @param struct
|
||||
-- @param baseAddress
|
||||
-- @param options
|
||||
function loadStructToTable(struct, baseAddress, options)
|
||||
options = options or {}
|
||||
local showStructName = options.showStructName ~= false
|
||||
local parentRecord = options.parentRecord
|
||||
|
||||
local fields = struct:getAllFields()
|
||||
|
||||
for _, field in ipairs(fields) do
|
||||
local memrec = AddressList.createMemoryRecord()
|
||||
|
||||
if type(baseAddress) == "string" then
|
||||
memrec.Address = string.format("%s+0x%X", baseAddress, field.fieldOffset)
|
||||
else
|
||||
memrec.Address = string.format("0x%X", field.fieldOffset + baseAddress)
|
||||
end
|
||||
|
||||
if showStructName and field.structName then
|
||||
memrec.Description = string.format("0x%03X %s::%s", field.fieldOffset, field.structName, field.name)
|
||||
else
|
||||
memrec.Description = string.format("0x%03X %s", field.fieldOffset, field.name)
|
||||
end
|
||||
|
||||
memrec.Type = field.type
|
||||
|
||||
if field.string_size then
|
||||
memrec.String.Size = field.string_size
|
||||
end
|
||||
if field.color then
|
||||
memrec.Color = field.color
|
||||
end
|
||||
if field.hex then
|
||||
memrec.ShowAsHex = true
|
||||
end
|
||||
|
||||
if parentRecord then
|
||||
memrec.appendToEntry(parentRecord)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Struct(name, parent)
|
||||
return StructDef.new(name, parent)
|
||||
end
|
||||
|
||||
function GetCGObjectAddr(guidValue)
|
||||
if type(guidValue) == "string" then
|
||||
guidValue = tonumber(guidValue, 16)
|
||||
end
|
||||
if not guidValue or guidValue == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local VTABLE_TYPES = {
|
||||
[0xA34D90] = "unit",
|
||||
[0xA326C8] = "player",
|
||||
[0xA33428] = "item",
|
||||
[0xA332D0] = "container",
|
||||
[0xA331C8] = "corpse",
|
||||
}
|
||||
|
||||
local memScan = createMemScan()
|
||||
local foundList = createFoundList(memScan)
|
||||
|
||||
local function cleanup()
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
end
|
||||
|
||||
memScan.firstScan(
|
||||
soExactValue, vtQword, rtRounded,
|
||||
string.format("%016X", guidValue),
|
||||
nil, 0x0, 0x7FFFFFFFFFFFFFFF,
|
||||
"", fsmNotAligned, nil,
|
||||
true, false, false, false
|
||||
)
|
||||
|
||||
memScan.waitTillDone()
|
||||
foundList.initialize()
|
||||
|
||||
for i = 0, foundList.Count - 1 do
|
||||
local addr = tonumber(foundList.Address[i], 16)
|
||||
if addr then
|
||||
local base = addr - 0x30
|
||||
local objType = VTABLE_TYPES[readInteger(base)]
|
||||
if objType then
|
||||
cleanup()
|
||||
return base, objType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cleanup()
|
||||
return nil
|
||||
end
|
||||
|
||||
local CGObject = Struct("CGObject")
|
||||
|
||||
CGObject:ptr("void*", "VtablePtr") -- 0x0000
|
||||
CGObject:unk(4) -- 0x0004
|
||||
CGObject:ptr("dataBeginPtr") -- 0x0008
|
||||
CGObject:ptr("dataEndPtr") -- 0x000C
|
||||
CGObject:hex("unkFlag", "int32") -- 0x0010
|
||||
CGObject:field("TypeID", "int32") -- 0x0014
|
||||
CGObject:hex("low_GUID", "uint32") -- 0x0018
|
||||
CGObject:paddingTo(0x30)
|
||||
CGObject:hex("ObjectGuid", "uint64") -- 0x0030
|
||||
CGObject:paddingTo(0x98)
|
||||
CGObject:field("m_objectSacle1", "float") -- 0x0098
|
||||
CGObject:field("m_objectSacle2", "float") -- 0x009C
|
||||
CGObject:field("m_objectScalingEndMS", "int32") -- 0x00A0
|
||||
CGObject:field("m_objectLastScale", "float") -- 0x00A4
|
||||
CGObject:ptr("specialEffectPtr") -- 0x00A8
|
||||
CGObject:field("objectHeight", "float") -- 0x00AC
|
||||
CGObject:ptr("unkPlayerNamePtr") -- 0x00B0
|
||||
CGObject:ptr("CM2Model", "m_model") -- 0x00B4
|
||||
CGObject:ptr("cmapEntityPtr") -- 0x00B8
|
||||
CGObject:hex("unkMovementFlags", "int32") -- 0x00BC
|
||||
CGObject:field("unk_00C0", "int32") -- 0x00C0
|
||||
CGObject:field("unk_00C4", "int32") -- 0x00C4
|
||||
CGObject:field("m_alpha", "uint8") -- 0x00C8
|
||||
CGObject:field("m_startAlpha", "uint8") -- 0x00C9
|
||||
CGObject:field("m_endAlpha", "uint8") -- 0x00CA
|
||||
CGObject:field("m_maxAlpha", "uint8") -- 0x00CB
|
||||
CGObject:ptr("effectManagerPtr") -- 0x00CC
|
||||
|
||||
local Vector3 = Struct("Vector3")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local ObjectFields = Struct("ObjectFields")
|
||||
:hex("GUID", "uint64")
|
||||
:field("type", "uint32")
|
||||
:field("unk", "uint32")
|
||||
:field("scale", "float")
|
||||
:field("pad", "uint32")
|
||||
|
||||
local CorpseFields = Struct("CorpseFields")
|
||||
:hex("Owner", "uint64")
|
||||
:hex("Party", "uint64")
|
||||
:field("DisplayId", "int32")
|
||||
:array("Item", "int32", 19)
|
||||
:hex("Bytes1", "uint32")
|
||||
:hex("Bytes2", "uint32")
|
||||
:field("Guild", "int32")
|
||||
:field("Flags", "int32")
|
||||
:field("DynamicFlags", "int32")
|
||||
:field("Pad", "int32")
|
||||
|
||||
local CGCorpse = Struct("CGCorpse", CGObject)
|
||||
:paddingTo(0xE8)
|
||||
:embed("Position", Vector3) -- 0x0E8
|
||||
:paddingTo(0xF8)
|
||||
:field("FacingAngle", "float") -- 0x0F8
|
||||
:paddingTo(0x274)
|
||||
:embed("Scale", Vector3) -- 0x274
|
||||
:paddingTo(0x290)
|
||||
:embed("ObjectFields", ObjectFields) -- 0x290
|
||||
:embed("CorpseFields", CorpseFields) -- 0x2A8
|
||||
:paddingTo(0x338)
|
||||
|
||||
local address = 0x51f838 -- at CGGameUI__HandleObjectTrackChange
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
local addr, typ = GetCGObjectAddr(readQword(0x00bd07a0)) -- mouseover guid
|
||||
if addr then
|
||||
if typ == "corpse" then
|
||||
loadStructToTable(CGCorpse, addr)
|
||||
end
|
||||
end
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
844
profile/3.3.5a-windows-386/cheatengine/Load_CGItem.lua
Normal file
844
profile/3.3.5a-windows-386/cheatengine/Load_CGItem.lua
Normal file
|
|
@ -0,0 +1,844 @@
|
|||
local CE = {
|
||||
vtByte = 0,
|
||||
vtWord = 1,
|
||||
vtDword = 2,
|
||||
vtQword = 3,
|
||||
vtSingle = 4,
|
||||
vtDouble = 5,
|
||||
vtString = 6,
|
||||
vtGrouped = 14,
|
||||
}
|
||||
|
||||
local Types = {
|
||||
-- Unsigned integers
|
||||
uint8 = { ce = CE.vtByte, size = 1 },
|
||||
uint16 = { ce = CE.vtWord, size = 2 },
|
||||
uint32 = { ce = CE.vtDword, size = 4 },
|
||||
uint64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Signed integers
|
||||
int8 = { ce = CE.vtByte, size = 1 },
|
||||
int16 = { ce = CE.vtWord, size = 2 },
|
||||
int32 = { ce = CE.vtDword, size = 4 },
|
||||
int64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Floating point
|
||||
float = { ce = CE.vtSingle, size = 4 },
|
||||
double = { ce = CE.vtDouble, size = 8 },
|
||||
|
||||
-- Pointers
|
||||
ptr = { ce = CE.vtDword, size = 4 },
|
||||
ptr32 = { ce = CE.vtDword, size = 4 },
|
||||
ptr64 = { ce = CE.vtQword, size = 8 },
|
||||
|
||||
-- Aliases
|
||||
bool = { ce = CE.vtByte, size = 1 },
|
||||
bool32 = { ce = CE.vtDword, size = 4 },
|
||||
char = { ce = CE.vtByte, size = 1 },
|
||||
byte = { ce = CE.vtByte, size = 1 },
|
||||
word = { ce = CE.vtWord, size = 2 },
|
||||
dword = { ce = CE.vtDword, size = 4 },
|
||||
qword = { ce = CE.vtQword, size = 8 },
|
||||
}
|
||||
|
||||
function SetPointerSize(bits)
|
||||
if bits == 64 then
|
||||
Types.ptr = { ce = CE.vtQword, size = 8 }
|
||||
else
|
||||
Types.ptr = { ce = CE.vtDword, size = 4 }
|
||||
end
|
||||
end
|
||||
|
||||
local StructDef = {}
|
||||
StructDef.__index = StructDef
|
||||
|
||||
-- @param name
|
||||
-- @param parent
|
||||
function StructDef.new(name, parent)
|
||||
local self = setmetatable({}, StructDef)
|
||||
self.name = name
|
||||
self.parent = parent
|
||||
self.ownFields = {}
|
||||
self.embeddings = {}
|
||||
|
||||
if parent then
|
||||
self._offset = parent:totalSize()
|
||||
else
|
||||
self._offset = 0
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param typeName (uint32, float, ptr etc)
|
||||
-- @param opts {hex=bool, color=number}
|
||||
function StructDef:field(name, typeName, opts)
|
||||
opts = opts or {}
|
||||
|
||||
local typeInfo = Types[typeName]
|
||||
if not typeInfo then
|
||||
error(string.format("Unknown type '%s' for field '%s'", typeName, name))
|
||||
end
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = name,
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = opts.hex,
|
||||
})
|
||||
|
||||
self._offset = self._offset + typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
StructDef.f = StructDef.field
|
||||
|
||||
--- Add hex-field
|
||||
function StructDef:hex(name, typeName, opts)
|
||||
opts = opts or {}
|
||||
opts.hex = true
|
||||
return self:field(name, typeName, opts)
|
||||
end
|
||||
|
||||
--- Add string
|
||||
function StructDef:string(name, size, opts)
|
||||
opts = opts or {}
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = name,
|
||||
type = CE.vtString,
|
||||
string_size = size,
|
||||
size = size,
|
||||
color = opts.color,
|
||||
})
|
||||
|
||||
self._offset = self._offset + size
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add array
|
||||
function StructDef:array(name, typeName, count, opts)
|
||||
opts = opts or {}
|
||||
|
||||
local typeInfo = Types[typeName]
|
||||
if not typeInfo then
|
||||
error(string.format("Unknown type '%s' for array '%s'", typeName, name))
|
||||
end
|
||||
|
||||
for i = 0, count - 1 do
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset + i * typeInfo.size,
|
||||
name = string.format("%s[%d]", name, i),
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = opts.hex,
|
||||
})
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
--- skip to X bytes
|
||||
function StructDef:paddingTo(targetOffset, opts)
|
||||
local size = targetOffset - self._offset
|
||||
if size <= 0 then
|
||||
return self
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
local remaining = size
|
||||
|
||||
while remaining >= 4 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint32", opts)
|
||||
remaining = remaining - 4
|
||||
end
|
||||
|
||||
while remaining >= 1 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint8", opts)
|
||||
remaining = remaining - 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- skip N bytes
|
||||
function StructDef:padding(size, opts)
|
||||
opts = opts or {}
|
||||
local remaining = size
|
||||
|
||||
while remaining >= 4 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint32", opts)
|
||||
remaining = remaining - 4
|
||||
end
|
||||
|
||||
while remaining >= 1 do
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
self:field(name, "uint8", opts)
|
||||
remaining = remaining - 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:skip(size)
|
||||
self._offset = self._offset + size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:unk(size, opts)
|
||||
opts = opts or {}
|
||||
local name = string.format("unk_%04X", self._offset)
|
||||
|
||||
if size == 1 then
|
||||
return self:field(name, "uint8", opts)
|
||||
elseif size == 2 then
|
||||
return self:field(name, "uint16", opts)
|
||||
elseif size == 4 then
|
||||
return self:field(name, "uint32", opts)
|
||||
elseif size == 8 then
|
||||
return self:field(name, "uint64", opts)
|
||||
else
|
||||
return self:array(name, "uint8", size, opts)
|
||||
end
|
||||
end
|
||||
|
||||
function StructDef:alignTo(alignment)
|
||||
local rem = self._offset % alignment
|
||||
if rem ~= 0 then
|
||||
self._offset = self._offset + (alignment - rem)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:at(offset)
|
||||
self._offset = offset
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:currentOffset()
|
||||
return self._offset
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param otherStruct
|
||||
-- @param opts {expand=bool}
|
||||
function StructDef:embed(name, otherStruct, opts)
|
||||
opts = opts or {}
|
||||
local baseOffset = self._offset
|
||||
|
||||
table.insert(self.embeddings, {
|
||||
type = "embed",
|
||||
name = name,
|
||||
struct = otherStruct,
|
||||
offset = baseOffset,
|
||||
size = otherStruct:totalSize(),
|
||||
expand = opts.expand or false,
|
||||
})
|
||||
|
||||
for _, f in ipairs(otherStruct:getAllFields()) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.fieldOffset = baseOffset + f.fieldOffset
|
||||
newField.name = name .. "." .. f.name
|
||||
newField.structName = nil
|
||||
newField._embeddedIn = name
|
||||
table.insert(self.ownFields, newField)
|
||||
end
|
||||
|
||||
self._offset = self._offset + otherStruct:totalSize()
|
||||
return self
|
||||
end
|
||||
|
||||
-- @param name
|
||||
-- @param otherStruct
|
||||
-- @param count
|
||||
-- @param opts {expand=bool}
|
||||
function StructDef:structArray(name, otherStruct, count, opts)
|
||||
opts = opts or {}
|
||||
local structSize = otherStruct:totalSize()
|
||||
local baseOffset = self._offset
|
||||
|
||||
table.insert(self.embeddings, {
|
||||
type = "structArray",
|
||||
name = name,
|
||||
struct = otherStruct,
|
||||
offset = baseOffset,
|
||||
count = count,
|
||||
size = count * structSize,
|
||||
elemSize = structSize,
|
||||
expand = opts.expand or false,
|
||||
})
|
||||
|
||||
for i = 0, count - 1 do
|
||||
local elemOffset = self._offset + i * structSize
|
||||
|
||||
for _, f in ipairs(otherStruct:getAllFields()) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.fieldOffset = elemOffset + f.fieldOffset
|
||||
newField.name = string.format("%s[%d].%s", name, i, f.name)
|
||||
newField._embeddedIn = name
|
||||
newField._arrayIndex = i
|
||||
table.insert(self.ownFields, newField)
|
||||
end
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * structSize
|
||||
return self
|
||||
end
|
||||
|
||||
-- :ptrArray("fieldName", count) -- void*[]
|
||||
-- :ptrArray("fieldName", count, opts) -- void*[] with opts
|
||||
-- :ptrArray("TypeName", "fieldName", count) -- TypeName*[]
|
||||
-- :ptrArray("TypeName", "fieldName", count, opts) -- TypeName*[] with opts
|
||||
function StructDef:ptrArray(arg1, arg2, arg3, arg4)
|
||||
local typeName, fieldName, count, opts
|
||||
|
||||
if type(arg2) == "number" then
|
||||
-- ptrArray("fieldName", count) or ptrArray("fieldName", count, opts)
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
count = arg2
|
||||
opts = arg3 or {}
|
||||
elseif type(arg2) == "string" then
|
||||
-- ptrArray("TypeName", "fieldName", count) or ptrArray("TypeName", "fieldName", count, opts)
|
||||
typeName = arg1
|
||||
fieldName = arg2
|
||||
count = arg3
|
||||
opts = arg4 or {}
|
||||
else
|
||||
error("Invalid arguments for ptrArray()")
|
||||
end
|
||||
|
||||
local typeInfo = Types.ptr
|
||||
|
||||
for i = 0, count - 1 do
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset + i * typeInfo.size,
|
||||
name = string.format("%s[%d]", fieldName, i),
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = true,
|
||||
isPointer = true,
|
||||
ptrType = typeName,
|
||||
_ptrArrayBase = fieldName,
|
||||
})
|
||||
end
|
||||
|
||||
self._offset = self._offset + count * typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:ptr(arg1, arg2, arg3)
|
||||
local typeName, fieldName, opts
|
||||
|
||||
if arg2 == nil then
|
||||
-- ptr("fieldName")
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
opts = {}
|
||||
elseif type(arg2) == "table" then
|
||||
-- ptr("fieldName", opts)
|
||||
typeName = nil
|
||||
fieldName = arg1
|
||||
opts = arg2
|
||||
elseif type(arg2) == "string" then
|
||||
-- ptr("TypeName", "fieldName") or ptr("TypeName", "fieldName", opts)
|
||||
typeName = arg1
|
||||
fieldName = arg2
|
||||
opts = arg3 or {}
|
||||
else
|
||||
error("Invalid arguments for ptr()")
|
||||
end
|
||||
|
||||
local typeInfo = Types.ptr
|
||||
|
||||
table.insert(self.ownFields, {
|
||||
fieldOffset = self._offset,
|
||||
name = fieldName,
|
||||
type = typeInfo.ce,
|
||||
size = typeInfo.size,
|
||||
color = opts.color,
|
||||
hex = true,
|
||||
isPointer = true,
|
||||
ptrType = typeName,
|
||||
})
|
||||
|
||||
self._offset = self._offset + typeInfo.size
|
||||
return self
|
||||
end
|
||||
|
||||
function StructDef:toCStruct(options)
|
||||
options = options or {}
|
||||
local indent = options.indent or " "
|
||||
local collapsePadding = options.collapsePadding ~= false
|
||||
local flattenInheritance = options.flattenInheritance or false
|
||||
local lines = {}
|
||||
|
||||
local ceToC = {
|
||||
[CE.vtByte] = "uint8_t",
|
||||
[CE.vtWord] = "uint16_t",
|
||||
[CE.vtDword] = "uint32_t",
|
||||
[CE.vtQword] = "uint64_t",
|
||||
[CE.vtSingle] = "float",
|
||||
[CE.vtDouble] = "double",
|
||||
[CE.vtString] = "char",
|
||||
}
|
||||
|
||||
local function getPtrTypeName(field)
|
||||
return field.ptrType or "void"
|
||||
end
|
||||
|
||||
local function sanitizeName(name)
|
||||
return name:gsub("%.", "_"):gsub("%[", "_"):gsub("%]", "")
|
||||
end
|
||||
|
||||
local function isUnkField(name)
|
||||
return name:match("^unk_[%dA-Fa-f]+$") ~= nil
|
||||
end
|
||||
|
||||
local function isUnkArray(name)
|
||||
return name:match("^unk_[%dA-Fa-f]+%[%d+%]$") ~= nil
|
||||
end
|
||||
|
||||
local function isAnyUnk(name)
|
||||
return isUnkField(name) or isUnkArray(name)
|
||||
end
|
||||
|
||||
local fields
|
||||
local embeddingMap = {}
|
||||
|
||||
if flattenInheritance then
|
||||
fields = self:getAllFields()
|
||||
for _, emb in ipairs(self:getAllEmbeddings()) do
|
||||
embeddingMap[emb.offset] = emb
|
||||
end
|
||||
else
|
||||
fields = self.ownFields
|
||||
for _, emb in ipairs(self.embeddings) do
|
||||
embeddingMap[emb.offset] = emb
|
||||
end
|
||||
end
|
||||
|
||||
if flattenInheritance and self.parent then
|
||||
table.insert(lines, string.format("// Inherits from: %s (size: 0x%X)", self.parent.name, self.parent:totalSize()))
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("struct %s {", self.name))
|
||||
|
||||
if not flattenInheritance and self.parent then
|
||||
table.insert(lines, string.format("%s%s base; // 0x%04X", indent, self.parent.name, 0))
|
||||
end
|
||||
|
||||
local i = 1
|
||||
local processedEmbeddings = {}
|
||||
|
||||
while i <= #fields do
|
||||
local field = fields[i]
|
||||
local cType = ceToC[field.type] or "uint8_t"
|
||||
|
||||
local emb = embeddingMap[field.fieldOffset]
|
||||
|
||||
if emb and not processedEmbeddings[emb.name] then
|
||||
processedEmbeddings[emb.name] = true
|
||||
|
||||
if emb.type == "embed" then
|
||||
if emb.expand then
|
||||
table.insert(lines, string.format("%sstruct { // %s::%s", indent, emb.struct.name, emb.name))
|
||||
|
||||
for _, sf in ipairs(emb.struct:getAllFields()) do
|
||||
local subType = ceToC[sf.type] or "uint8_t"
|
||||
if sf.type == CE.vtString and sf.string_size then
|
||||
table.insert(lines, string.format("%s%schar %s[%d]; // +0x%02X", indent, indent, sanitizeName(sf.name), sf.string_size, sf.fieldOffset))
|
||||
elseif sf.isPointer then
|
||||
table.insert(lines, string.format("%s%s%s* %s; // +0x%02X", indent, indent, getPtrTypeName(sf), sanitizeName(sf.name), sf.fieldOffset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s%s %s; // +0x%02X", indent, indent, subType, sanitizeName(sf.name), sf.fieldOffset))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s} %s; // 0x%04X",
|
||||
indent, emb.name, emb.offset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s; // 0x%04X",
|
||||
indent, emb.struct.name, emb.name, emb.offset))
|
||||
end
|
||||
|
||||
while i <= #fields and fields[i]._embeddedIn == emb.name do
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
elseif emb.type == "structArray" then
|
||||
if emb.expand then
|
||||
table.insert(lines, string.format("%sstruct { // %s element", indent, emb.struct.name))
|
||||
|
||||
for _, sf in ipairs(emb.struct:getAllFields()) do
|
||||
local subType = ceToC[sf.type] or "uint8_t"
|
||||
if sf.isPointer then
|
||||
table.insert(lines, string.format("%s%s%s* %s; // +0x%02X", indent, indent, getPtrTypeName(sf), sanitizeName(sf.name), sf.fieldOffset))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s%s %s; // +0x%02X", indent, indent, subType, sanitizeName(sf.name), sf.fieldOffset))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s} %s[%d]; // 0x%04X (0x%X bytes)", indent, emb.name, emb.count, emb.offset, emb.size))
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s[%d]; // 0x%04X (0x%X bytes)", indent, emb.struct.name, emb.name, emb.count, emb.offset, emb.size))
|
||||
end
|
||||
|
||||
while i <= #fields and fields[i]._embeddedIn == emb.name do
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
elseif collapsePadding and isAnyUnk(field.name) then
|
||||
local startOffset = field.fieldOffset
|
||||
local totalSize = 0
|
||||
local j = i
|
||||
|
||||
while j <= #fields do
|
||||
local f = fields[j]
|
||||
local expectedOffset = startOffset + totalSize
|
||||
|
||||
if isAnyUnk(f.name) and f.fieldOffset == expectedOffset then
|
||||
totalSize = totalSize + f.size
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if totalSize > 0 then
|
||||
table.insert(lines, string.format("%suint8_t pad_%04X[0x%X]; // 0x%04X", indent, startOffset, totalSize, startOffset))
|
||||
end
|
||||
|
||||
i = j
|
||||
|
||||
elseif field.isPointer and field._ptrArrayBase and field.name:match("%[0%]$") then
|
||||
local baseName = field._ptrArrayBase
|
||||
local j = i + 1
|
||||
local count = 1
|
||||
|
||||
while j <= #fields do
|
||||
if fields[j]._ptrArrayBase == baseName then
|
||||
count = count + 1
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s%s* %s[%d]; // 0x%04X", indent, getPtrTypeName(field), baseName, count, field.fieldOffset))
|
||||
i = j
|
||||
|
||||
elseif field.name:match("%[0%]$") and not field.name:find("%.") then
|
||||
local baseName = field.name:match("^([^%[]+)")
|
||||
local j = i + 1
|
||||
local count = 1
|
||||
|
||||
while j <= #fields do
|
||||
local bn, ci = fields[j].name:match("^([^%[%.]+)%[(%d+)%]$")
|
||||
if bn == baseName and tonumber(ci) == count then
|
||||
count = count + 1
|
||||
j = j + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("%s%s %s[%d]; // 0x%04X", indent, cType, baseName, count, field.fieldOffset))
|
||||
i = j
|
||||
|
||||
elseif field.type == CE.vtString and field.string_size then
|
||||
table.insert(lines, string.format("%schar %s[%d]; // 0x%04X", indent, sanitizeName(field.name), field.string_size, field.fieldOffset))
|
||||
i = i + 1
|
||||
|
||||
elseif field.isPointer then
|
||||
table.insert(lines, string.format("%s%s* %s; // 0x%04X", indent, getPtrTypeName(field), sanitizeName(field.name), field.fieldOffset))
|
||||
i = i + 1
|
||||
|
||||
else
|
||||
table.insert(lines, string.format("%s%s %s; // 0x%04X", indent, cType, sanitizeName(field.name), field.fieldOffset))
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(lines, string.format("}; // sizeof: 0x%X (%d bytes)", self:totalSize(), self:totalSize()))
|
||||
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
function StructDef:getDependencies()
|
||||
local deps = {}
|
||||
local seen = {}
|
||||
|
||||
local function collect(struct)
|
||||
if seen[struct.name] then return end
|
||||
seen[struct.name] = true
|
||||
|
||||
if struct.parent then
|
||||
collect(struct.parent)
|
||||
end
|
||||
|
||||
for _, emb in ipairs(struct.embeddings) do
|
||||
collect(emb.struct)
|
||||
end
|
||||
|
||||
table.insert(deps, struct)
|
||||
end
|
||||
|
||||
collect(self)
|
||||
return deps
|
||||
end
|
||||
|
||||
function StructDef:toCStructWithDeps(options)
|
||||
local deps = self:getDependencies()
|
||||
local parts = {}
|
||||
|
||||
for _, struct in ipairs(deps) do
|
||||
table.insert(parts, struct:toCStruct(options))
|
||||
end
|
||||
|
||||
return table.concat(parts, "\n\n")
|
||||
end
|
||||
|
||||
function StructDef:getAllEmbeddings()
|
||||
local result = {}
|
||||
|
||||
if self.parent then
|
||||
for _, e in ipairs(self.parent:getAllEmbeddings()) do
|
||||
table.insert(result, e)
|
||||
end
|
||||
end
|
||||
|
||||
for _, e in ipairs(self.embeddings) do
|
||||
table.insert(result, e)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function StructDef:totalSize()
|
||||
return self._offset
|
||||
end
|
||||
|
||||
function StructDef:getOwnFields()
|
||||
return self.ownFields
|
||||
end
|
||||
|
||||
function StructDef:getAllFields()
|
||||
local fields = {}
|
||||
|
||||
if self.parent then
|
||||
for _, f in ipairs(self.parent:getAllFields()) do
|
||||
table.insert(fields, f)
|
||||
end
|
||||
end
|
||||
|
||||
for _, f in ipairs(self.ownFields) do
|
||||
local newField = {}
|
||||
for k, v in pairs(f) do
|
||||
newField[k] = v
|
||||
end
|
||||
newField.structName = self.name
|
||||
table.insert(fields, newField)
|
||||
end
|
||||
|
||||
return fields
|
||||
end
|
||||
|
||||
-- @param struct
|
||||
-- @param baseAddress
|
||||
-- @param options
|
||||
function loadStructToTable(struct, baseAddress, options)
|
||||
options = options or {}
|
||||
local showStructName = options.showStructName ~= false
|
||||
local parentRecord = options.parentRecord
|
||||
|
||||
local fields = struct:getAllFields()
|
||||
|
||||
for _, field in ipairs(fields) do
|
||||
local memrec = AddressList.createMemoryRecord()
|
||||
|
||||
if type(baseAddress) == "string" then
|
||||
memrec.Address = string.format("%s+0x%X", baseAddress, field.fieldOffset)
|
||||
else
|
||||
memrec.Address = string.format("0x%X", field.fieldOffset + baseAddress)
|
||||
end
|
||||
|
||||
if showStructName and field.structName then
|
||||
memrec.Description = string.format("0x%03X %s::%s", field.fieldOffset, field.structName, field.name)
|
||||
else
|
||||
memrec.Description = string.format("0x%03X %s", field.fieldOffset, field.name)
|
||||
end
|
||||
|
||||
memrec.Type = field.type
|
||||
|
||||
if field.string_size then
|
||||
memrec.String.Size = field.string_size
|
||||
end
|
||||
if field.color then
|
||||
memrec.Color = field.color
|
||||
end
|
||||
if field.hex then
|
||||
memrec.ShowAsHex = true
|
||||
end
|
||||
|
||||
if parentRecord then
|
||||
memrec.appendToEntry(parentRecord)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Struct(name, parent)
|
||||
return StructDef.new(name, parent)
|
||||
end
|
||||
|
||||
function GetCGObjectAddr(guidValue)
|
||||
if type(guidValue) == "string" then
|
||||
guidValue = tonumber(guidValue, 16)
|
||||
end
|
||||
if not guidValue or guidValue == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local VTABLE_TYPES = {
|
||||
[0xA34D90] = "unit",
|
||||
[0xA326C8] = "player",
|
||||
[0xA33428] = "item",
|
||||
[0xA332D0] = "container",
|
||||
[0xA331C8] = "corpse",
|
||||
}
|
||||
|
||||
local memScan = createMemScan()
|
||||
local foundList = createFoundList(memScan)
|
||||
|
||||
local function cleanup()
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
end
|
||||
|
||||
memScan.firstScan(
|
||||
soExactValue, vtQword, rtRounded,
|
||||
string.format("%016X", guidValue),
|
||||
nil, 0x0, 0x7FFFFFFFFFFFFFFF,
|
||||
"", fsmNotAligned, nil,
|
||||
true, false, false, false
|
||||
)
|
||||
|
||||
memScan.waitTillDone()
|
||||
foundList.initialize()
|
||||
|
||||
for i = 0, foundList.Count - 1 do
|
||||
local addr = tonumber(foundList.Address[i], 16)
|
||||
if addr then
|
||||
local base = addr - 0x30
|
||||
local objType = VTABLE_TYPES[readInteger(base)]
|
||||
if objType then
|
||||
cleanup()
|
||||
return base, objType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cleanup()
|
||||
return nil
|
||||
end
|
||||
|
||||
local CGObject = Struct("CGObject")
|
||||
|
||||
CGObject:ptr("void*", "VtablePtr") -- 0x0000
|
||||
CGObject:unk(4) -- 0x0004
|
||||
CGObject:ptr("dataBeginPtr") -- 0x0008
|
||||
CGObject:ptr("dataEndPtr") -- 0x000C
|
||||
CGObject:hex("unkFlag", "int32") -- 0x0010
|
||||
CGObject:field("TypeID", "int32") -- 0x0014
|
||||
CGObject:hex("low_GUID", "uint32") -- 0x0018
|
||||
CGObject:paddingTo(0x30)
|
||||
CGObject:hex("ObjectGuid", "uint64") -- 0x0030
|
||||
CGObject:paddingTo(0x98)
|
||||
CGObject:field("m_objectSacle1", "float") -- 0x0098
|
||||
CGObject:field("m_objectSacle2", "float") -- 0x009C
|
||||
CGObject:field("m_objectScalingEndMS", "int32") -- 0x00A0
|
||||
CGObject:field("m_objectLastScale", "float") -- 0x00A4
|
||||
CGObject:ptr("specialEffectPtr") -- 0x00A8
|
||||
CGObject:field("objectHeight", "float") -- 0x00AC
|
||||
CGObject:ptr("unkPlayerNamePtr") -- 0x00B0
|
||||
CGObject:ptr("CM2Model", "m_model") -- 0x00B4
|
||||
CGObject:ptr("cmapEntityPtr") -- 0x00B8
|
||||
CGObject:hex("unkMovementFlags", "int32") -- 0x00BC
|
||||
CGObject:field("unk_00C0", "int32") -- 0x00C0
|
||||
CGObject:field("unk_00C4", "int32") -- 0x00C4
|
||||
CGObject:field("m_alpha", "uint8") -- 0x00C8
|
||||
CGObject:field("m_startAlpha", "uint8") -- 0x00C9
|
||||
CGObject:field("m_endAlpha", "uint8") -- 0x00CA
|
||||
CGObject:field("m_maxAlpha", "uint8") -- 0x00CB
|
||||
CGObject:ptr("effectManagerPtr") -- 0x00CC
|
||||
|
||||
local ObjectFields = Struct("ObjectFields")
|
||||
:hex("GUID", "uint64")
|
||||
:field("type", "uint32")
|
||||
:field("unk", "uint32")
|
||||
:field("scale", "float")
|
||||
:field("pad", "uint32")
|
||||
|
||||
local ItemEnchantment = Struct("ItemEnchantment")
|
||||
:array("id", "uint32", 3)
|
||||
|
||||
local ItemFields = Struct("ItemFields")
|
||||
:hex("Owner", "uint64")
|
||||
:hex("Contained", "uint64")
|
||||
:hex("Creator", "uint64")
|
||||
:hex("GiftCreator", "uint64")
|
||||
:field("StackCount", "int32")
|
||||
:field("Duration", "int32")
|
||||
:array("SpellCharges", "int32", 5)
|
||||
:hex("Flags", "uint32")
|
||||
:structArray("Enchantment", ItemEnchantment, 12)
|
||||
:field("PropertySeed", "int32")
|
||||
:field("RandomPropertiesID", "int32")
|
||||
:field("Durability", "int32")
|
||||
:field("MaxDurability", "int32")
|
||||
:field("CreatePlayedTime", "int32")
|
||||
:field("Pad", "int32")
|
||||
|
||||
local CGItem = Struct("CGItem", CGObject)
|
||||
:paddingTo(0x3E0)
|
||||
:embed("ObjectFields", ObjectFields) -- 0x3E0
|
||||
:embed("ItemFields", ItemFields) -- 0x3F8
|
||||
:field("itemId", "int32") -- 0x4E0
|
||||
:field("scale", "float") -- 0x4E4
|
||||
:paddingTo(0x4F4)
|
||||
:field("StackCount", "int32") -- 0x4F4
|
||||
:array("SpellCharges", "int32", 5) -- 0x4F8
|
||||
:hex("Flags", "uint32") -- 0x50C
|
||||
:structArray("Enchantment", ItemEnchantment, 12) -- 0x510
|
||||
:field("Durability", "int32") -- 0x5A0
|
||||
:paddingTo(0x5A8)
|
||||
|
||||
local addr, typ = GetCGObjectAddr(readQword(0xC79D10)) -- local player GUID from game connection class
|
||||
if addr then
|
||||
if typ == "player" then
|
||||
local bagpackOffset = 0x1F20
|
||||
for i = 0, 15, 1 do
|
||||
local addr, typ = GetCGObjectAddr(readQword(addr + bagpackOffset + i * 8))
|
||||
if addr ~= 0 and typ == "item" then
|
||||
loadStructToTable(CGItem, addr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -716,56 +716,46 @@ function GetCGObjectAddr(guidValue)
|
|||
return nil
|
||||
end
|
||||
|
||||
local CGUNIT_C_VTABLE = 0xa34d90
|
||||
local CGPLAYER_C_VTABLE = 0xa326c8
|
||||
local VTABLE_TYPES = {
|
||||
[0xA34D90] = "unit",
|
||||
[0xA326C8] = "player",
|
||||
[0xA33428] = "item",
|
||||
[0xA332D0] = "container",
|
||||
[0xA331C8] = "corpse",
|
||||
}
|
||||
|
||||
local memScan = createMemScan()
|
||||
local foundList = createFoundList(memScan)
|
||||
|
||||
local function cleanup()
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
end
|
||||
|
||||
memScan.firstScan(
|
||||
soExactValue,
|
||||
vtQword,
|
||||
rtRounded,
|
||||
soExactValue, vtQword, rtRounded,
|
||||
string.format("%016X", guidValue),
|
||||
nil,
|
||||
0x0,
|
||||
0x7FFFFFFFFFFFFFFF,
|
||||
"",
|
||||
fsmNotAligned,
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
nil, 0x0, 0x7FFFFFFFFFFFFFFF,
|
||||
"", fsmNotAligned, nil,
|
||||
true, false, false, false
|
||||
)
|
||||
|
||||
memScan.waitTillDone()
|
||||
foundList.initialize()
|
||||
|
||||
local resultAddr, resultType = nil, nil
|
||||
|
||||
for i = 0, foundList.Count - 1 do
|
||||
local addrGUIDvalue = tonumber(foundList.Address[i], 16)
|
||||
if addrGUIDvalue then
|
||||
local base = addrGUIDvalue - 0x30
|
||||
local vtbl = readInteger(base)
|
||||
|
||||
if vtbl == CGPLAYER_C_VTABLE then
|
||||
resultAddr, resultType = base, "player"
|
||||
break
|
||||
elseif vtbl == CGUNIT_C_VTABLE then
|
||||
resultAddr, resultType = base, "unit"
|
||||
break
|
||||
local addr = tonumber(foundList.Address[i], 16)
|
||||
if addr then
|
||||
local base = addr - 0x30
|
||||
local objType = VTABLE_TYPES[readInteger(base)]
|
||||
if objType then
|
||||
cleanup()
|
||||
return base, objType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
|
||||
if resultAddr then
|
||||
return resultAddr, resultType
|
||||
end
|
||||
cleanup()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
|
@ -787,7 +777,7 @@ CGObject:field("m_objectScalingEndMS", "int32") -- 0x00A0
|
|||
CGObject:field("m_objectLastScale", "float") -- 0x00A4
|
||||
CGObject:ptr("specialEffectPtr") -- 0x00A8
|
||||
CGObject:field("objectHeight", "float") -- 0x00AC
|
||||
CGObject:ptr("unkPlayerNamePtr", "int32") -- 0x00B0
|
||||
CGObject:ptr("unkPlayerNamePtr") -- 0x00B0
|
||||
CGObject:ptr("CM2Model", "m_model") -- 0x00B4
|
||||
CGObject:ptr("cmapEntityPtr") -- 0x00B8
|
||||
CGObject:hex("unkMovementFlags", "int32") -- 0x00BC
|
||||
|
|
|
|||
|
|
@ -716,56 +716,46 @@ function GetCGObjectAddr(guidValue)
|
|||
return nil
|
||||
end
|
||||
|
||||
local CGUNIT_C_VTABLE = 0xa34d90
|
||||
local CGPLAYER_C_VTABLE = 0xa326c8
|
||||
local VTABLE_TYPES = {
|
||||
[0xA34D90] = "unit",
|
||||
[0xA326C8] = "player",
|
||||
[0xA33428] = "item",
|
||||
[0xA332D0] = "container",
|
||||
[0xA331C8] = "corpse",
|
||||
}
|
||||
|
||||
local memScan = createMemScan()
|
||||
local foundList = createFoundList(memScan)
|
||||
|
||||
local function cleanup()
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
end
|
||||
|
||||
memScan.firstScan(
|
||||
soExactValue,
|
||||
vtQword,
|
||||
rtRounded,
|
||||
soExactValue, vtQword, rtRounded,
|
||||
string.format("%016X", guidValue),
|
||||
nil,
|
||||
0x0,
|
||||
0x7FFFFFFFFFFFFFFF,
|
||||
"",
|
||||
fsmNotAligned,
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
nil, 0x0, 0x7FFFFFFFFFFFFFFF,
|
||||
"", fsmNotAligned, nil,
|
||||
true, false, false, false
|
||||
)
|
||||
|
||||
memScan.waitTillDone()
|
||||
foundList.initialize()
|
||||
|
||||
local resultAddr, resultType = nil, nil
|
||||
|
||||
for i = 0, foundList.Count - 1 do
|
||||
local addrGUIDvalue = tonumber(foundList.Address[i], 16)
|
||||
if addrGUIDvalue then
|
||||
local base = addrGUIDvalue - 0x30
|
||||
local vtbl = readInteger(base)
|
||||
|
||||
if vtbl == CGPLAYER_C_VTABLE then
|
||||
resultAddr, resultType = base, "player"
|
||||
break
|
||||
elseif vtbl == CGUNIT_C_VTABLE then
|
||||
resultAddr, resultType = base, "unit"
|
||||
break
|
||||
local addr = tonumber(foundList.Address[i], 16)
|
||||
if addr then
|
||||
local base = addr - 0x30
|
||||
local objType = VTABLE_TYPES[readInteger(base)]
|
||||
if objType then
|
||||
cleanup()
|
||||
return base, objType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
foundList.destroy()
|
||||
memScan.destroy()
|
||||
|
||||
if resultAddr then
|
||||
return resultAddr, resultType
|
||||
end
|
||||
cleanup()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
|
@ -787,7 +777,7 @@ CGObject:field("m_objectScalingEndMS", "int32") -- 0x00A0
|
|||
CGObject:field("m_objectLastScale", "float") -- 0x00A4
|
||||
CGObject:ptr("specialEffectPtr") -- 0x00A8
|
||||
CGObject:field("objectHeight", "float") -- 0x00AC
|
||||
CGObject:ptr("unkPlayerNamePtr", "int32") -- 0x00B0
|
||||
CGObject:ptr("unkPlayerNamePtr") -- 0x00B0
|
||||
CGObject:ptr("CM2Model", "m_model") -- 0x00B4
|
||||
CGObject:ptr("cmapEntityPtr") -- 0x00B8
|
||||
CGObject:hex("unkMovementFlags", "int32") -- 0x00BC
|
||||
|
|
|
|||
|
|
@ -7,4 +7,7 @@ if [ $# -eq 0 ]
|
|||
fi
|
||||
|
||||
./script/build-ce-loader-script -i $1/cgunit.lua -o $2/Load_CGUnit.lua
|
||||
./script/build-ce-loader-script -i $1/cgplayer.lua -o $2/Load_CGPlayer.lua
|
||||
./script/build-ce-loader-script -i $1/cgplayer.lua -o $2/Load_CGPlayer.lua
|
||||
./script/build-ce-loader-script -i $1/cgitem.lua -o $2/Load_CGItem.lua
|
||||
./script/build-ce-loader-script -i $1/cgcontainer.lua -o $2/Load_CGContainer.lua
|
||||
./script/build-ce-loader-script -i $1/cgcorpse.lua -o $2/Load_CGCorpse.lua
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue