mirror of
https://github.com/thunderbrewhq/binana.git
synced 2026-02-03 16:39:07 +00:00
feat(core): CheatEngine script loaders
This commit is contained in:
parent
f9fe6ca3e7
commit
4ca5f1bbe5
15 changed files with 4578 additions and 17 deletions
39
CheatEngineScriptsInfo.md
Normal file
39
CheatEngineScriptsInfo.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#### How to execute the script
|
||||
- Click *'Table'* then *'Show Cheat Table Lua Script'* or press Ctrl+Alt+L
|
||||
|
||||
#### CObject_C hierarchy
|
||||
**CGUnit_C**
|
||||
* Select a target unit (it must be of type Unit).
|
||||
* Execute the script: [Load_CGUnit.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGUnit.lua)
|
||||
|
||||
**CGPlayer_C (your character)**
|
||||
* Execute the script: [Load_CGPlayer.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGPlayer.lua)
|
||||
|
||||
**CGCorpse_C**
|
||||
* Execute the script: [Load_CGCorpse.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGCorpse.lua)
|
||||
* Then hover your mouse cursor over the corpse.
|
||||
|
||||
**CGItem_C** (items in the main 16-slot backpack)
|
||||
* Execute the script: [Load_CGItem.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGItem.lua)
|
||||
|
||||
**CGContainers_C** (only for equipped bags)
|
||||
* Execute the script: [Load_CGContainer.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGContainer.lua)
|
||||
|
||||
#### CMapBaseObj hierarchy
|
||||
**CMapArea** (ADT)
|
||||
* Execute the script: [Load_CMapArea.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CMapArea.lua)
|
||||
|
||||
**CMapChunk**
|
||||
* Execute the script: [Load_CMapChunk.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CMapChunk.lua)
|
||||
|
||||
**CMapEntity**
|
||||
* Select a target (unit or player)
|
||||
* Execute the script: [Load_CMapEntity.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CMapEntity.lua)
|
||||
|
||||
**CMapObjDef** (WMO)
|
||||
* Run inside a WMO object (tower, cave, barracks etc)
|
||||
* Execute the script: [Load_CMapObjDef.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CMapObjDef.lua)
|
||||
|
||||
**CMapObjDefGroup**
|
||||
* Run inside a WMO object (tower, cave, barracks etc)
|
||||
* Execute the script: [Load_CMapObjDefGroup.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CMapObjDefGroup.lua)
|
||||
18
README.md
18
README.md
|
|
@ -164,23 +164,7 @@ To load the type information JSON file:
|
|||
make ce-lua
|
||||
```
|
||||
|
||||
**For Units**
|
||||
* Select a target unit (it must be of type Unit).
|
||||
* Execute the script: [Load_CGUnit.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGUnit.lua)
|
||||
*(Menu: Table -> Show Cheat Table Lua Script)*
|
||||
|
||||
**For Player (your character)**
|
||||
* Execute the script: [Load_CGPlayer.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGPlayer.lua)
|
||||
|
||||
**For Corpses**
|
||||
* Execute the script: [Load_CGCorpse.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGCorpse.lua)
|
||||
* Then hover your mouse cursor over the corpse.
|
||||
|
||||
**For Items** (items in the main 16-slot backpack)
|
||||
* Execute the script: [Load_CGItem.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGItem.lua)
|
||||
|
||||
**For Containers** (only for equipped bags)
|
||||
* Execute the script: [Load_CGContainer.lua](https://github.com/thunderbrewhq/binana/blob/master/profile/3.3.5a-windows-386/cheatengine/Load_CGContainer.lua)
|
||||
[Read cheat engine script info](CheatEngineScriptsInfo.md)
|
||||
|
||||
# How to generate a C struct
|
||||
|
||||
|
|
|
|||
47
cheatengine/cmaparea.lua
Normal file
47
cheatengine/cmaparea.lua
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
-- #include "cmapbaseobj.lua"
|
||||
|
||||
local CMapAreaTexture = Struct("CMapAreaTexture")
|
||||
:field("memoryCapacity", "uint32")
|
||||
:field("count", "uint32")
|
||||
:hex("memoryPtr", "uint32")
|
||||
|
||||
local CMapArea = Struct('CMapArea', CMapBaseObj)
|
||||
:embed('bottomRight', C3Vector)
|
||||
:embed('topLeft', C3Vector)
|
||||
:embed('topLeft2', C3Vector)
|
||||
:embed('index', C2iVector)
|
||||
:embed('tileChunkIndex', C2iVector)
|
||||
:embed("textures", TSGrowableArray)
|
||||
:ptr('SMMapHeader', 'header')
|
||||
:field('unk_6C', 'int32')
|
||||
:ptr('CAsyncObject', 'asyncObject')
|
||||
:embed('chunkLinkList', TSExplicitList)
|
||||
:ptr('filePtr')
|
||||
:field('fileSize', 'int32')
|
||||
:ptr('SMChunkInfo', 'chunkInfo')
|
||||
:field('unk_8C', 'int32')
|
||||
:ptr('SMDoodadDef', 'doodadDef')
|
||||
:ptr('SMMapObjDef', 'mapObjDef')
|
||||
:field('doodadDefCount', 'int32')
|
||||
:field('mapObjDefCount', 'int32')
|
||||
:ptr('m2FileNames')
|
||||
:ptr('wmoFileNames')
|
||||
:ptr('modelFilenamesOffsets')
|
||||
:ptr('wmoFilenamesOffsets')
|
||||
:ptr('flyingBbox')
|
||||
:ptr('textureFlags')
|
||||
:ptr('unk_B8')
|
||||
:ptrArray('mapChunk', 256)
|
||||
|
||||
local address = 0x007b5c18 -- at CMap__PreUpdateAreas
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
loadStructToTable(CMapArea, ESI)
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
48
cheatengine/cmapbaseobj.lua
Normal file
48
cheatengine/cmapbaseobj.lua
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
-- #include "StructDef.lua"
|
||||
|
||||
local CImVector = Struct("CImVector")
|
||||
:field("r", "uint8")
|
||||
:field("g", "uint8")
|
||||
:field("b", "uint8")
|
||||
:field("a", "uint8")
|
||||
|
||||
local C3Vector = Struct("C3Vector")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local C2iVector = Struct("C2iVector")
|
||||
:field("x", "int32")
|
||||
:field("y", "int32")
|
||||
|
||||
local CAaBox = Struct("CAaBox")
|
||||
:embed("top", C3Vector)
|
||||
:embed("bottom", C3Vector)
|
||||
|
||||
local CAaSphere = Struct("CAaSphere")
|
||||
:embed("center", C3Vector)
|
||||
:field("d", "float")
|
||||
|
||||
local C44Matrix = Struct("C44Matrix")
|
||||
:array('m', 'float', 16)
|
||||
|
||||
local TSGrowableArray = Struct("TSGrowableArray")
|
||||
:field('m_alloc', 'uint32')
|
||||
:field('m_count', 'uint32')
|
||||
:ptr('data')
|
||||
:field('m_chunk', 'uint32')
|
||||
|
||||
local TSExplicitList = Struct("TSExplicitList")
|
||||
:field("m_linkOffset", "uint32")
|
||||
:ptr("ptr1")
|
||||
:ptr("ptr2")
|
||||
|
||||
local CMapBaseObj = Struct("CMapBaseObj")
|
||||
:ptr("void*", "vtable")
|
||||
:field("objectIndex", "uint32")
|
||||
:field("type", "uint16")
|
||||
:field("refCount", "uint16")
|
||||
:field("unk_C", "int32")
|
||||
:ptr("prev")
|
||||
:ptr("next")
|
||||
:embed("objLink", TSExplicitList)
|
||||
60
cheatengine/cmapchunk.lua
Normal file
60
cheatengine/cmapchunk.lua
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
-- #include "cmapbaseobj.lua"
|
||||
|
||||
local CMapChunk = Struct('CMapChunk', CMapBaseObj)
|
||||
:embed('aIndex', C2iVector)
|
||||
:embed('sOffset', C2iVector)
|
||||
:embed('cOffset', C2iVector)
|
||||
:embed('center', C3Vector)
|
||||
:field('radius', 'float')
|
||||
:embed('bbox', CAaBox)
|
||||
:embed('bottomRight', C3Vector)
|
||||
:embed('topLeft', C3Vector)
|
||||
:embed('topLeftCoords', C3Vector)
|
||||
:field('distToCamera', 'float')
|
||||
:embed('bbox2', CAaBox)
|
||||
:ptr('detailDoodadInst')
|
||||
:ptr('renderChunk')
|
||||
:field('unk_AC', 'int32')
|
||||
:field('areaId', 'int32')
|
||||
:field('unk_B4', 'int32')
|
||||
:field('unk_B8', 'int32')
|
||||
:field('unk_BC', 'int32')
|
||||
:field('unk_C0', 'int32')
|
||||
:embed('doodadDefLinkList', TSExplicitList)
|
||||
:embed('mapObjDefLinkList', TSExplicitList)
|
||||
:embed('unkList', TSExplicitList)
|
||||
:embed('lightLinkList', TSExplicitList)
|
||||
:embed('mapSoundEmitterLinkList', TSExplicitList)
|
||||
:embed('liquidChunkLinkList', TSExplicitList)
|
||||
:ptr('chunkInfoBeginPtr')
|
||||
:ptr('header')
|
||||
:ptr('lowQualityTexMap')
|
||||
:ptr('predTexture')
|
||||
:ptr('vertices')
|
||||
:ptr('vertexShading')
|
||||
:ptr('normals')
|
||||
:ptr('shadowMap')
|
||||
:ptr('layers')
|
||||
:ptr('additionalShadowmap')
|
||||
:ptr('MCRF')
|
||||
:ptr('liquid')
|
||||
:ptr('soundEmitters')
|
||||
:field('unk_140', 'int32')
|
||||
:field('unk_144', 'int32')
|
||||
:field('unk_148', 'int32')
|
||||
:field('unk_14C', 'int32')
|
||||
:field('unk_150', 'int32')
|
||||
:field('unk_154', 'int32')
|
||||
|
||||
local address = 0x00795e25 -- at CWorldScene__LocateViewer3
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
loadStructToTable(CMapChunk, ECX)
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
17
cheatengine/cmapentity.lua
Normal file
17
cheatengine/cmapentity.lua
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
-- #include "cmapstaticentity.lua"
|
||||
|
||||
local CMapEntity = Struct("CMapEntity", CMapStaticEntity)
|
||||
:paddingTo(0x98)
|
||||
:hex("GUID", "uint64")
|
||||
:paddingTo(0xC0)
|
||||
:embed("ambientTarget", CImVector)
|
||||
:field("dirLightScaleTarget", "float")
|
||||
:paddingTo(0xD0)
|
||||
|
||||
local addr, typ = GetCGObjectAddr(readQword(0x00bd07b0)) -- target guid
|
||||
if addr then
|
||||
if typ == "unit" or typ == "player" then
|
||||
local cmapentityOffset = 0xB8
|
||||
loadStructToTable(CMapEntity, readPointer(addr + cmapentityOffset))
|
||||
end
|
||||
end
|
||||
45
cheatengine/cmapobjdef.lua
Normal file
45
cheatengine/cmapobjdef.lua
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
-- #include "cmapbaseobj.lua"
|
||||
|
||||
local CMapObjDef = Struct('CMapObjDef', CMapBaseObj)
|
||||
:ptr('unk_24')
|
||||
:ptr('unk_28')
|
||||
:ptr('unk_2C')
|
||||
:ptr('unk_30')
|
||||
:ptr('unk_34')
|
||||
:field('unk_38', 'int32')
|
||||
:embed('position', C3Vector)
|
||||
:embed('bbox', CAaBox)
|
||||
:embed('sphere', CAaSphere)
|
||||
:embed('mat', C44Matrix)
|
||||
:embed('invMat', C44Matrix)
|
||||
:field('unk_F0', 'int32')
|
||||
:ptr('owner')
|
||||
:field('unk_F8', 'int32')
|
||||
:field('unkFlags', 'uint32')
|
||||
:field('unk_100', 'int32')
|
||||
:field('unk_104', 'int32')
|
||||
:field('unk_108', 'int32')
|
||||
:field('unk_10C', 'int32')
|
||||
:field('unk_110', 'int32')
|
||||
:embed('mapObjDefGroupLinkList', TSExplicitList)
|
||||
:embed('defGroups', TSGrowableArray)
|
||||
:ptr('unk_130')
|
||||
:embed('unkGrowableArray', TSGrowableArray)
|
||||
:embed('color', CImVector)
|
||||
:field('unk_148', 'int32')
|
||||
:field('unk_14C', 'int32')
|
||||
:field('unk_150', 'int32')
|
||||
:ptr('unk_154')
|
||||
|
||||
local address = 0x0078261f -- at CMap__QueryAreaId
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
loadStructToTable(CMapObjDef, ESI)
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
42
cheatengine/cmapobjdefgroup.lua
Normal file
42
cheatengine/cmapobjdefgroup.lua
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
-- #include "cmapbaseobj.lua"
|
||||
|
||||
local CMapObjDefGroup = Struct('CMapObjDefGroup', CMapBaseObj)
|
||||
:embed('bbox', CAaBox)
|
||||
:embed('sphere', CAaSphere)
|
||||
:field('unk_4C', 'float')
|
||||
:field('groupNum', 'uint32')
|
||||
:field('unkFlags', 'uint32')
|
||||
:field('unk_58', 'int32')
|
||||
:embed('ambientColor', CImVector)
|
||||
:field('unk_60', 'int32')
|
||||
:field('unk_64', 'int32')
|
||||
:field('unk_68', 'int32')
|
||||
:embed('unkExplicitList', TSExplicitList)
|
||||
:embed('doodadDefLinkList', TSExplicitList)
|
||||
:embed('mapEntityLinkList', TSExplicitList)
|
||||
:embed('unkExplicitList', TSExplicitList)
|
||||
:embed('unkExplicitList', TSExplicitList)
|
||||
:field('unk_A8', 'int32')
|
||||
:field('unk_AC', 'int32')
|
||||
:field('unk_B0', 'int32')
|
||||
:field('unk_B4', 'int32')
|
||||
:field('unk_B8', 'int32')
|
||||
:field('unk_BC', 'int32')
|
||||
|
||||
local address = 0x0078261f -- at CMap__QueryAreaId
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
local defGroupsNum = readPointer(ESI + 0x124)
|
||||
local defGroupsDataPtr = ESI + 0x128
|
||||
local CMapObjDefGroupSize = 0xC0
|
||||
for i = 0, defGroupsNum, 1 do
|
||||
loadStructToTable(CMapObjDefGroup, readPointer(readPointer(defGroupsDataPtr)) + CMapObjDefGroupSize * i)
|
||||
end
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
17
cheatengine/cmapstaticentity.lua
Normal file
17
cheatengine/cmapstaticentity.lua
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
-- #include "cmapbaseobj.lua"
|
||||
|
||||
local CMapStaticEntity = Struct("CMapStaticEntity", CMapBaseObj)
|
||||
:paddingTo(0x28)
|
||||
:field("unkFlags", "uint32")
|
||||
:field("unkCounter", "int32")
|
||||
:field("unk_030", "float")
|
||||
:ptr("CM2Model", "model")
|
||||
:embed("sphere", CAaSphere)
|
||||
:embed("bbox", CAaBox)
|
||||
:embed("vec2", C3Vector)
|
||||
:embed("position", C3Vector)
|
||||
:field("scale", "float")
|
||||
:paddingTo(0x84)
|
||||
:embed("m2AmbietColor", CImVector)
|
||||
:embed("m2DiffuseColor", CImVector)
|
||||
:field("unk_08C", "float")
|
||||
853
profile/3.3.5a-windows-386/cheatengine/Load_CMapArea.lua
Normal file
853
profile/3.3.5a-windows-386/cheatengine/Load_CMapArea.lua
Normal file
|
|
@ -0,0 +1,853 @@
|
|||
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 CImVector = Struct("CImVector")
|
||||
:field("r", "uint8")
|
||||
:field("g", "uint8")
|
||||
:field("b", "uint8")
|
||||
:field("a", "uint8")
|
||||
|
||||
local C3Vector = Struct("C3Vector")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local C2iVector = Struct("C2iVector")
|
||||
:field("x", "int32")
|
||||
:field("y", "int32")
|
||||
|
||||
local CAaBox = Struct("CAaBox")
|
||||
:embed("top", C3Vector)
|
||||
:embed("bottom", C3Vector)
|
||||
|
||||
local CAaSphere = Struct("CAaSphere")
|
||||
:embed("center", C3Vector)
|
||||
:field("d", "float")
|
||||
|
||||
local C44Matrix = Struct("C44Matrix")
|
||||
:array('m', 'float', 16)
|
||||
|
||||
local TSGrowableArray = Struct("TSGrowableArray")
|
||||
:field('m_alloc', 'uint32')
|
||||
:field('m_count', 'uint32')
|
||||
:ptr('data')
|
||||
:field('m_chunk', 'uint32')
|
||||
|
||||
local TSExplicitList = Struct("TSExplicitList")
|
||||
:field("m_linkOffset", "uint32")
|
||||
:ptr("ptr1")
|
||||
:ptr("ptr2")
|
||||
|
||||
local CMapBaseObj = Struct("CMapBaseObj")
|
||||
:ptr("void*", "vtable")
|
||||
:field("objectIndex", "uint32")
|
||||
:field("type", "uint16")
|
||||
:field("refCount", "uint16")
|
||||
:field("unk_C", "int32")
|
||||
:ptr("prev")
|
||||
:ptr("next")
|
||||
:embed("objLink", TSExplicitList)
|
||||
|
||||
local CMapAreaTexture = Struct("CMapAreaTexture")
|
||||
:field("memoryCapacity", "uint32")
|
||||
:field("count", "uint32")
|
||||
:hex("memoryPtr", "uint32")
|
||||
|
||||
local CMapArea = Struct('CMapArea', CMapBaseObj)
|
||||
:embed('bottomRight', C3Vector)
|
||||
:embed('topLeft', C3Vector)
|
||||
:embed('topLeft2', C3Vector)
|
||||
:embed('index', C2iVector)
|
||||
:embed('tileChunkIndex', C2iVector)
|
||||
:embed("textures", TSGrowableArray)
|
||||
:ptr('SMMapHeader', 'header')
|
||||
:field('unk_6C', 'int32')
|
||||
:ptr('CAsyncObject', 'asyncObject')
|
||||
:embed('chunkLinkList', TSExplicitList)
|
||||
:ptr('filePtr')
|
||||
:field('fileSize', 'int32')
|
||||
:ptr('SMChunkInfo', 'chunkInfo')
|
||||
:field('unk_8C', 'int32')
|
||||
:ptr('SMDoodadDef', 'doodadDef')
|
||||
:ptr('SMMapObjDef', 'mapObjDef')
|
||||
:field('doodadDefCount', 'int32')
|
||||
:field('mapObjDefCount', 'int32')
|
||||
:ptr('m2FileNames')
|
||||
:ptr('wmoFileNames')
|
||||
:ptr('modelFilenamesOffsets')
|
||||
:ptr('wmoFilenamesOffsets')
|
||||
:ptr('flyingBbox')
|
||||
:ptr('textureFlags')
|
||||
:ptr('unk_B8')
|
||||
:ptrArray('mapChunk', 256)
|
||||
|
||||
local address = 0x007b5c18 -- at CMap__PreUpdateAreas
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
loadStructToTable(CMapArea, ESI)
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
866
profile/3.3.5a-windows-386/cheatengine/Load_CMapChunk.lua
Normal file
866
profile/3.3.5a-windows-386/cheatengine/Load_CMapChunk.lua
Normal file
|
|
@ -0,0 +1,866 @@
|
|||
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 CImVector = Struct("CImVector")
|
||||
:field("r", "uint8")
|
||||
:field("g", "uint8")
|
||||
:field("b", "uint8")
|
||||
:field("a", "uint8")
|
||||
|
||||
local C3Vector = Struct("C3Vector")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local C2iVector = Struct("C2iVector")
|
||||
:field("x", "int32")
|
||||
:field("y", "int32")
|
||||
|
||||
local CAaBox = Struct("CAaBox")
|
||||
:embed("top", C3Vector)
|
||||
:embed("bottom", C3Vector)
|
||||
|
||||
local CAaSphere = Struct("CAaSphere")
|
||||
:embed("center", C3Vector)
|
||||
:field("d", "float")
|
||||
|
||||
local C44Matrix = Struct("C44Matrix")
|
||||
:array('m', 'float', 16)
|
||||
|
||||
local TSGrowableArray = Struct("TSGrowableArray")
|
||||
:field('m_alloc', 'uint32')
|
||||
:field('m_count', 'uint32')
|
||||
:ptr('data')
|
||||
:field('m_chunk', 'uint32')
|
||||
|
||||
local TSExplicitList = Struct("TSExplicitList")
|
||||
:field("m_linkOffset", "uint32")
|
||||
:ptr("ptr1")
|
||||
:ptr("ptr2")
|
||||
|
||||
local CMapBaseObj = Struct("CMapBaseObj")
|
||||
:ptr("void*", "vtable")
|
||||
:field("objectIndex", "uint32")
|
||||
:field("type", "uint16")
|
||||
:field("refCount", "uint16")
|
||||
:field("unk_C", "int32")
|
||||
:ptr("prev")
|
||||
:ptr("next")
|
||||
:embed("objLink", TSExplicitList)
|
||||
|
||||
local CMapChunk = Struct('CMapChunk', CMapBaseObj)
|
||||
:embed('aIndex', C2iVector)
|
||||
:embed('sOffset', C2iVector)
|
||||
:embed('cOffset', C2iVector)
|
||||
:embed('center', C3Vector)
|
||||
:field('radius', 'float')
|
||||
:embed('bbox', CAaBox)
|
||||
:embed('bottomRight', C3Vector)
|
||||
:embed('topLeft', C3Vector)
|
||||
:embed('topLeftCoords', C3Vector)
|
||||
:field('distToCamera', 'float')
|
||||
:embed('bbox2', CAaBox)
|
||||
:ptr('detailDoodadInst')
|
||||
:ptr('renderChunk')
|
||||
:field('unk_AC', 'int32')
|
||||
:field('areaId', 'int32')
|
||||
:field('unk_B4', 'int32')
|
||||
:field('unk_B8', 'int32')
|
||||
:field('unk_BC', 'int32')
|
||||
:field('unk_C0', 'int32')
|
||||
:embed('doodadDefLinkList', TSExplicitList)
|
||||
:embed('mapObjDefLinkList', TSExplicitList)
|
||||
:embed('unkList', TSExplicitList)
|
||||
:embed('lightLinkList', TSExplicitList)
|
||||
:embed('mapSoundEmitterLinkList', TSExplicitList)
|
||||
:embed('liquidChunkLinkList', TSExplicitList)
|
||||
:ptr('chunkInfoBeginPtr')
|
||||
:ptr('header')
|
||||
:ptr('lowQualityTexMap')
|
||||
:ptr('predTexture')
|
||||
:ptr('vertices')
|
||||
:ptr('vertexShading')
|
||||
:ptr('normals')
|
||||
:ptr('shadowMap')
|
||||
:ptr('layers')
|
||||
:ptr('additionalShadowmap')
|
||||
:ptr('MCRF')
|
||||
:ptr('liquid')
|
||||
:ptr('soundEmitters')
|
||||
:field('unk_140', 'int32')
|
||||
:field('unk_144', 'int32')
|
||||
:field('unk_148', 'int32')
|
||||
:field('unk_14C', 'int32')
|
||||
:field('unk_150', 'int32')
|
||||
:field('unk_154', 'int32')
|
||||
|
||||
local address = 0x00795e25 -- at CWorldScene__LocateViewer3
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
loadStructToTable(CMapChunk, ECX)
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
839
profile/3.3.5a-windows-386/cheatengine/Load_CMapEntity.lua
Normal file
839
profile/3.3.5a-windows-386/cheatengine/Load_CMapEntity.lua
Normal file
|
|
@ -0,0 +1,839 @@
|
|||
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 CImVector = Struct("CImVector")
|
||||
:field("r", "uint8")
|
||||
:field("g", "uint8")
|
||||
:field("b", "uint8")
|
||||
:field("a", "uint8")
|
||||
|
||||
local C3Vector = Struct("C3Vector")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local C2iVector = Struct("C2iVector")
|
||||
:field("x", "int32")
|
||||
:field("y", "int32")
|
||||
|
||||
local CAaBox = Struct("CAaBox")
|
||||
:embed("top", C3Vector)
|
||||
:embed("bottom", C3Vector)
|
||||
|
||||
local CAaSphere = Struct("CAaSphere")
|
||||
:embed("center", C3Vector)
|
||||
:field("d", "float")
|
||||
|
||||
local C44Matrix = Struct("C44Matrix")
|
||||
:array('m', 'float', 16)
|
||||
|
||||
local TSGrowableArray = Struct("TSGrowableArray")
|
||||
:field('m_alloc', 'uint32')
|
||||
:field('m_count', 'uint32')
|
||||
:ptr('data')
|
||||
:field('m_chunk', 'uint32')
|
||||
|
||||
local TSExplicitList = Struct("TSExplicitList")
|
||||
:field("m_linkOffset", "uint32")
|
||||
:ptr("ptr1")
|
||||
:ptr("ptr2")
|
||||
|
||||
local CMapBaseObj = Struct("CMapBaseObj")
|
||||
:ptr("void*", "vtable")
|
||||
:field("objectIndex", "uint32")
|
||||
:field("type", "uint16")
|
||||
:field("refCount", "uint16")
|
||||
:field("unk_C", "int32")
|
||||
:ptr("prev")
|
||||
:ptr("next")
|
||||
:embed("objLink", TSExplicitList)
|
||||
|
||||
local CMapStaticEntity = Struct("CMapStaticEntity", CMapBaseObj)
|
||||
:paddingTo(0x28)
|
||||
:field("unkFlags", "uint32")
|
||||
:field("unkCounter", "int32")
|
||||
:field("unk_030", "float")
|
||||
:ptr("CM2Model", "model")
|
||||
:embed("sphere", CAaSphere)
|
||||
:embed("bbox", CAaBox)
|
||||
:embed("vec2", C3Vector)
|
||||
:embed("position", C3Vector)
|
||||
:field("scale", "float")
|
||||
:paddingTo(0x84)
|
||||
:embed("m2AmbietColor", CImVector)
|
||||
:embed("m2DiffuseColor", CImVector)
|
||||
:field("unk_08C", "float")
|
||||
|
||||
local CMapEntity = Struct("CMapEntity", CMapStaticEntity)
|
||||
:paddingTo(0x98)
|
||||
:hex("GUID", "uint64")
|
||||
:paddingTo(0xC0)
|
||||
:embed("ambientTarget", CImVector)
|
||||
:field("dirLightScaleTarget", "float")
|
||||
:paddingTo(0xD0)
|
||||
|
||||
local addr, typ = GetCGObjectAddr(readQword(0x00bd07b0)) -- target guid
|
||||
if addr then
|
||||
if typ == "unit" or typ == "player" then
|
||||
local cmapentityOffset = 0xB8
|
||||
loadStructToTable(CMapEntity, readPointer(addr + cmapentityOffset))
|
||||
end
|
||||
end
|
||||
851
profile/3.3.5a-windows-386/cheatengine/Load_CMapObjDef.lua
Normal file
851
profile/3.3.5a-windows-386/cheatengine/Load_CMapObjDef.lua
Normal file
|
|
@ -0,0 +1,851 @@
|
|||
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 CImVector = Struct("CImVector")
|
||||
:field("r", "uint8")
|
||||
:field("g", "uint8")
|
||||
:field("b", "uint8")
|
||||
:field("a", "uint8")
|
||||
|
||||
local C3Vector = Struct("C3Vector")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local C2iVector = Struct("C2iVector")
|
||||
:field("x", "int32")
|
||||
:field("y", "int32")
|
||||
|
||||
local CAaBox = Struct("CAaBox")
|
||||
:embed("top", C3Vector)
|
||||
:embed("bottom", C3Vector)
|
||||
|
||||
local CAaSphere = Struct("CAaSphere")
|
||||
:embed("center", C3Vector)
|
||||
:field("d", "float")
|
||||
|
||||
local C44Matrix = Struct("C44Matrix")
|
||||
:array('m', 'float', 16)
|
||||
|
||||
local TSGrowableArray = Struct("TSGrowableArray")
|
||||
:field('m_alloc', 'uint32')
|
||||
:field('m_count', 'uint32')
|
||||
:ptr('data')
|
||||
:field('m_chunk', 'uint32')
|
||||
|
||||
local TSExplicitList = Struct("TSExplicitList")
|
||||
:field("m_linkOffset", "uint32")
|
||||
:ptr("ptr1")
|
||||
:ptr("ptr2")
|
||||
|
||||
local CMapBaseObj = Struct("CMapBaseObj")
|
||||
:ptr("void*", "vtable")
|
||||
:field("objectIndex", "uint32")
|
||||
:field("type", "uint16")
|
||||
:field("refCount", "uint16")
|
||||
:field("unk_C", "int32")
|
||||
:ptr("prev")
|
||||
:ptr("next")
|
||||
:embed("objLink", TSExplicitList)
|
||||
|
||||
local CMapObjDef = Struct('CMapObjDef', CMapBaseObj)
|
||||
:ptr('unk_24')
|
||||
:ptr('unk_28')
|
||||
:ptr('unk_2C')
|
||||
:ptr('unk_30')
|
||||
:ptr('unk_34')
|
||||
:field('unk_38', 'int32')
|
||||
:embed('position', C3Vector)
|
||||
:embed('bbox', CAaBox)
|
||||
:embed('sphere', CAaSphere)
|
||||
:embed('mat', C44Matrix)
|
||||
:embed('invMat', C44Matrix)
|
||||
:field('unk_F0', 'int32')
|
||||
:ptr('owner')
|
||||
:field('unk_F8', 'int32')
|
||||
:field('unkFlags', 'uint32')
|
||||
:field('unk_100', 'int32')
|
||||
:field('unk_104', 'int32')
|
||||
:field('unk_108', 'int32')
|
||||
:field('unk_10C', 'int32')
|
||||
:field('unk_110', 'int32')
|
||||
:embed('mapObjDefGroupLinkList', TSExplicitList)
|
||||
:embed('defGroups', TSGrowableArray)
|
||||
:ptr('unk_130')
|
||||
:embed('unkGrowableArray', TSGrowableArray)
|
||||
:embed('color', CImVector)
|
||||
:field('unk_148', 'int32')
|
||||
:field('unk_14C', 'int32')
|
||||
:field('unk_150', 'int32')
|
||||
:ptr('unk_154')
|
||||
|
||||
local address = 0x0078261f -- at CMap__QueryAreaId
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
loadStructToTable(CMapObjDef, ESI)
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
848
profile/3.3.5a-windows-386/cheatengine/Load_CMapObjDefGroup.lua
Normal file
848
profile/3.3.5a-windows-386/cheatengine/Load_CMapObjDefGroup.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 CImVector = Struct("CImVector")
|
||||
:field("r", "uint8")
|
||||
:field("g", "uint8")
|
||||
:field("b", "uint8")
|
||||
:field("a", "uint8")
|
||||
|
||||
local C3Vector = Struct("C3Vector")
|
||||
:field("x", "float")
|
||||
:field("y", "float")
|
||||
:field("z", "float")
|
||||
|
||||
local C2iVector = Struct("C2iVector")
|
||||
:field("x", "int32")
|
||||
:field("y", "int32")
|
||||
|
||||
local CAaBox = Struct("CAaBox")
|
||||
:embed("top", C3Vector)
|
||||
:embed("bottom", C3Vector)
|
||||
|
||||
local CAaSphere = Struct("CAaSphere")
|
||||
:embed("center", C3Vector)
|
||||
:field("d", "float")
|
||||
|
||||
local C44Matrix = Struct("C44Matrix")
|
||||
:array('m', 'float', 16)
|
||||
|
||||
local TSGrowableArray = Struct("TSGrowableArray")
|
||||
:field('m_alloc', 'uint32')
|
||||
:field('m_count', 'uint32')
|
||||
:ptr('data')
|
||||
:field('m_chunk', 'uint32')
|
||||
|
||||
local TSExplicitList = Struct("TSExplicitList")
|
||||
:field("m_linkOffset", "uint32")
|
||||
:ptr("ptr1")
|
||||
:ptr("ptr2")
|
||||
|
||||
local CMapBaseObj = Struct("CMapBaseObj")
|
||||
:ptr("void*", "vtable")
|
||||
:field("objectIndex", "uint32")
|
||||
:field("type", "uint16")
|
||||
:field("refCount", "uint16")
|
||||
:field("unk_C", "int32")
|
||||
:ptr("prev")
|
||||
:ptr("next")
|
||||
:embed("objLink", TSExplicitList)
|
||||
|
||||
local CMapObjDefGroup = Struct('CMapObjDefGroup', CMapBaseObj)
|
||||
:embed('bbox', CAaBox)
|
||||
:embed('sphere', CAaSphere)
|
||||
:field('unk_4C', 'float')
|
||||
:field('groupNum', 'uint32')
|
||||
:field('unkFlags', 'uint32')
|
||||
:field('unk_58', 'int32')
|
||||
:embed('ambientColor', CImVector)
|
||||
:field('unk_60', 'int32')
|
||||
:field('unk_64', 'int32')
|
||||
:field('unk_68', 'int32')
|
||||
:embed('unkExplicitList', TSExplicitList)
|
||||
:embed('doodadDefLinkList', TSExplicitList)
|
||||
:embed('mapEntityLinkList', TSExplicitList)
|
||||
:embed('unkExplicitList', TSExplicitList)
|
||||
:embed('unkExplicitList', TSExplicitList)
|
||||
:field('unk_A8', 'int32')
|
||||
:field('unk_AC', 'int32')
|
||||
:field('unk_B0', 'int32')
|
||||
:field('unk_B4', 'int32')
|
||||
:field('unk_B8', 'int32')
|
||||
:field('unk_BC', 'int32')
|
||||
|
||||
local address = 0x0078261f -- at CMap__QueryAreaId
|
||||
debugger_onBreakpoint = nil
|
||||
function onBreakpoint()
|
||||
local defGroupsNum = readPointer(ESI + 0x124)
|
||||
local defGroupsDataPtr = ESI + 0x128
|
||||
local CMapObjDefGroupSize = 0xC0
|
||||
for i = 0, defGroupsNum, 1 do
|
||||
loadStructToTable(CMapObjDefGroup, readPointer(readPointer(defGroupsDataPtr)) + CMapObjDefGroupSize * i)
|
||||
end
|
||||
debugger_onBreakpoint = nil
|
||||
debug_removeBreakpoint(address)
|
||||
debug_continueFromBreakpoint(co_run)
|
||||
return 1
|
||||
end
|
||||
|
||||
debug_setBreakpoint(address)
|
||||
debugger_onBreakpoint = onBreakpoint
|
||||
|
|
@ -11,3 +11,8 @@ fi
|
|||
./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
|
||||
./script/build-ce-loader-script -i $1/cmapentity.lua -o $2/Load_CMapEntity.lua
|
||||
./script/build-ce-loader-script -i $1/cmaparea.lua -o $2/Load_CMapArea.lua
|
||||
./script/build-ce-loader-script -i $1/cmapchunk.lua -o $2/Load_CMapChunk.lua
|
||||
./script/build-ce-loader-script -i $1/cmapobjdef.lua -o $2/Load_CMapObjDef.lua
|
||||
./script/build-ce-loader-script -i $1/cmapobjdefgroup.lua -o $2/Load_CMapObjDefGroup.lua
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue