Publish source: Unlicense, public README, repo hygiene

The remote was previously a distribution-only point for pre-built DLLs.
This opens the source.

- LICENSE: Unlicense, with a GPL-3.0 carve-out for src/dpslog/WeirdDPSMate
  (a DPSMate fork that keeps its own license)
- README.md replaces the stale internal one with the user-facing docs from
  DLL_README.md, swapping the 'Why No Source Code?' section for build and
  layout notes. DLL_README.md is dropped; one README now serves both.
- RELEASING.md: drop the trim-the-README-per-release dance and the
  remote/WeirdUtils/ distribution clone, both obsolete now
- gitignore agent/editor scratch, build caches, the vendored WSBT addon,
  and the WeirdThreat/uwu-logs checkouts (separate upstream repos)
- Commit outstanding module work: superweirdo, clickthrough portal visuals,
  transform44 decompiles, worldmarkers demo presets, tools/
This commit is contained in:
MarcelineVQ
2026-07-27 21:47:55 -07:00
parent f500147fc7
commit b12fc820ef
42 changed files with 5563 additions and 727 deletions
+101
View File
@@ -0,0 +1,101 @@
# WoW 1.12.1 CPU Profiling Analysis
Source: `perf.data.perfparser` (July 2025 recording via hotspot)
Exported: `cycles.out` (stack-collapsed format)
## Full CPU Time Breakdown
| % | Category | Notes |
|---|---|---|
| 33.18% | **Hooked WoW functions** | 38 hooks in transform44 module |
| 19.19% | GPU/Driver | d3d9.dll (DXVK) + amdvlk32.so -- untouchable |
| 18.74% | WoW long tail | ~2000+ functions each <0.15% -- not worth hooking |
| 13.15% | WoW mid-tier | 50 functions at 0.15-0.39% -- hookable but diminishing returns |
| 8.43% | Lua VM | lua_vm_execute, luaS_newlstr, etc. -- interpreter overhead |
| 5.38% | Unresolved | WoW.exe code not in Ghidra symbol map |
| 1.93% | Wine/System | ntdll, kernel32, wine internals |
## Hooked Functions (38 total, by self-time %)
### Already existed (6 hooks)
| % | Address | Name | Convention |
|---|---|---|---|
| 2.80% | 0x714260 | transformMatrix4x4 | thiscall RET 0x10 |
| 2.06% | 0x713d50 | findInterpolationIndices | thiscall RET 0x10 |
| 0.73% | 0x707680 | renderFrame | thiscall RET 0x4 |
| 0.44% | 0x713ea0 | interpolateAnimationKeyframes | fastcall RET 0x8 |
| - | 0x708900 | executeSceneRenderPass | thiscall RET 0x4 |
| - | 0x76FB00 | RenderTextureQuads | fastcall RET |
| - | 0x616620 | CMovement::Process | thiscall RET 0x8 |
### New perf-identified hotspots (28 hooks)
| % | Address | Name | Convention |
|---|---|---|---|
| 3.95% | 0x6318c0 | ClipPolygonToSinglePlane | stdcall RET 0x4 |
| 3.65% | 0x5ca2d0 | GetOrCreateCharacterGlyph | stdcall RET 0x8 |
| 1.73% | 0x7b2a50 | RenderParticleSprites | thiscall RET 0x8 |
| 1.57% | 0x6abc40 | processLinkedListCollision | thiscall RET 0x8 |
| 1.18% | 0x6afad0 | UpdateEntityAndChunksPositions | thiscall RET |
| 1.15% | 0x765650 | renderAllFrameLayers | thiscall RET 0x4 |
| 1.07% | 0x5ccbe0 | RenderTextToVertexBuffer | thiscall RET 0x18 |
| 1.02% | 0x58a3d0 | RenderComplexGeometry | stdcall RET 0x24 |
| 0.93% | 0x6c1f70 | updateEntitiesInBounds | thiscall RET 0x4 |
| 0.88% | 0x5cdf40 | updateTextFrameCounter | thiscall RET |
| 0.66% | 0x6816f0 | AddToSpatialGrid | thiscall RET |
| 0.65% | 0x7c29f0 | ray_tri_intersect_idx_ushort | stdcall RET 0x10 |
| 0.64% | 0x710b90 | ManageLinkedListNode | thiscall RET 0x4 |
| 0.63% | 0x7b9b10 | calculateColorValues | thiscall RET 0x18 |
| 0.61% | 0x686640 | SetVector3 | thiscall RET |
| 0.59% | 0x6b8c60 | PerformSpatialCulling | thiscall RET 0x8 |
| 0.59% | 0x6b88e0 | performCollisionDetection | thiscall RET 0x8 |
| 0.55% | 0x7b5a10 | ProcessActiveParticles | stdcall RET 0x8 |
| 0.53% | 0x404130 | CallbackIterator | stdcall RET 0x10 |
| 0.50% | 0x464890 | FindObjectByGUID | stdcall RET 0x8 |
| 0.49% | 0x632700 | RayTriangleIntersection | thiscall RET 0x20 |
| 0.48% | 0x70cb30 | DrawBatchProj | thiscall RET |
| 0.47% | 0x702000 | FindLuaFunction | stdcall RET 0x4 |
| 0.44% | 0x5a0f50 | RenderSpriteQuads | thiscall RET 0xc |
| 0.44% | 0x718960 | renderSceneNode | thiscall RET |
| 0.44% | 0x6cffc0 | generateTerrainChunk | thiscall RET |
| 0.43% | 0x593840 | D3D_SetTexture | thiscall RET 0x8 |
| 0.42% | 0x6b8b70 | checkBoundingBoxIntersection | stdcall RET 0x8 |
### Unresolved-callee hooks (4 hooks)
| % | Address | Name | Convention |
|---|---|---|---|
| ~0.5% | 0x7bdd60 | rotateMatrixByAxisAngle | thiscall RET 0xc |
| ~0.1% | 0x632460 | BuildTrianglePlanes | thiscall RET 0xc |
| ~0.2% | 0x7b3d20 | SetupParticleRendering | thiscall RET 0x4 |
| ~0.2% | 0x5ce0c0 | renderTextLine | thiscall RET 0x10 |
## Unhooked Mid-Tier (50 functions, 13.15% total)
Not worth individual hooks -- too small or too high-frequency (hook overhead would distort):
| % | Name | Why not hook |
|---|---|---|
| 0.39% | compareRenderItemsExtended | Sort comparator, millions of calls |
| 0.39% | GetCachedData | Cache accessor, extremely hot path |
| 0.39% | raycastPickObjects | Moderate frequency |
| 0.35% | inflateDecodeLiteralsAndLengths | Decompression, bursty |
| 0.34% | UpdateParticlePhysics | Per-particle, very hot |
| 0.34% | updateAnimationSystem | Could be interesting entry point |
| 0.32% | ClntObjMgrObjectPtr | Object lookup, called everywhere |
| 0.29% | multiplyMatrix4x4 | Tiny function, massive call count |
| 0.26% | quickSortArray | Sort impl, millions of comparisons |
| ... | (40 more at 0.15-0.31%) | |
## Key Insights
1. **GPU/Driver is 19%** -- nothing we can do about d3d9.dll/amdvlk overhead
2. **Lua VM is 8.4%** -- addon code execution, not optimizable from DLL side
3. **Frustum clipping (ClipPolygonToSinglePlane) is the #1 WoW hotspot at 3.95%** -- pure math, SSE candidate via binary patch (no hook overhead)
4. **Font rendering (GetOrCreateCharacterGlyph) is #2 at 3.65%** -- potential cache optimization
5. **Bone pipeline (t44 + findInterp + interpKf) totals ~5.3%** -- SSE interp hook overhead negates savings; binary patch or full t44 rewrite needed
6. **Hook overhead matters** -- for functions called >10k/frame (lerp, matrix multiply, sort comparators), detour trampoline cost (~30 cycles) exceeds any savings
## Files
- `cycles.out` -- raw stack-collapsed perf data
- `perf.data.perfparser` -- hotspot binary cache (5.3GB)
- Ghidra symbols: `/media/faststore/tmp/Dis/symbols.nm`
+1 -1
View File
@@ -1007,7 +1007,7 @@ fn calcScaledInverse(this_mat: u32, out: u32, scale: f32) void {
// mat3(offset_vec3*), mat4(scale_float_bits)
// =============================================================================
export fn transformImpl_BASELINE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.c) void {
export fn transformImpl_BASELINE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.{ .x86_thiscall = .{} }) void {
@setEvalBranchQuota(50000);
// =========================================================================
@@ -0,0 +1,167 @@
// RTQ (RenderTextureQuads) full rendering pipeline
// All functions in the per-item draw call chain
//
// Call chain per item:
// RenderTextureQuads (0x76FB00) -- the outer loop
// -> InitializeRenderingPipeline (0x58A2A0) -- stores vertCount, calls RenderComplexGeometry
// -> RenderComplexGeometry (0x58A3D0) -- format detect, VB create/fill, DrawPrimitive
// -> CreateVertexBuffer (0x58A140 -> 0x594500) -- pool VB allocation
// -> LockVertexBuffer (0x58A080) -- vtable call to CGxDevice+0xA8
// -> [per-vertex interleave loop]
// -> UnlockVertexBuffer (0x58A0A0) -- vtable call to CGxDevice+0xAC
// -> DrawPrimitive (0x58A7C0) -- state update + SetRenderingCommand
// -> RenderVertexBuffer (0x58A2E0) -- creates INDEX VB, issues DrawIndexedPrimitive
// -> CreateAndBindVertexBuffer (0x58A750) -- index VB (bufferType=1)
// -> CallGfxDeviceMethod_Wrapper (0x58A830) -- actual D3D9 DrawIndexedPrimitive
// -> EmptyRenderFunction (0x58A340) -- RET (no-op)
// === InitializeRenderingPipeline (0x58A2A0) -- 54 bytes ===
// __fastcall(ECX=vertCount, EDX=xyzPtr, 11 stack params)
// Stores vertCount to global g_currentPrimitiveType and tail-calls RenderComplexGeometry.
void __fastcall InitializeRenderingPipeline(int vertCount, void** xyzPtr, /* ...11 stack params */)
{
g_currentPrimitiveType = (void*)vertCount; // stored at some global
RenderComplexGeometry(vertCount, xyzPtr, /* forward all params */);
}
// === CreateVertexBuffer wrapper (0x58A140) -- 24 bytes ===
// __fastcall(ECX=bufferType, EDX=vertexSize, stack: vertexCount)
// Thin wrapper: loads CGxDevice from global, calls D3D_CreateVertexBuffer.
void __fastcall CreateVertexBuffer(int bufferType, int vertexSize, int vertexCount)
{
D3D_CreateVertexBuffer(CGxDeviceD3d__device, bufferType, vertexSize, vertexCount);
}
// === D3D_CreateVertexBuffer (0x594500) -- 75 bytes ===
// __thiscall(ECX=CGxDevice, stack: bufferType, vertexSize, vertexCount)
// Returns a pool handle. The pool is indexed by bufferType at CGxDevice+0x26CC.
// Reuses existing D3D9 VB unless the requested size exceeds current allocation.
int __thiscall D3D_CreateVertexBuffer(void* this, int bufferType, int vertexSize, int vertexCount)
{
int poolHandle = *(int*)((int)this + bufferType * 4 + 0x26CC);
int d3dVB = *(int*)(poolHandle + 8);
if (d3dVB != 0 && *(uint*)(d3dVB + 0x10) < (uint)(vertexSize * vertexCount)) {
// Existing VB too small -- resize via vtable call
(*(code**)(*this + 0xA0))(d3dVB, vertexSize * vertexCount);
}
SetDimensionsAndSize(poolHandle, vertexSize, vertexCount);
return poolHandle;
}
// === SetDimensionsAndSize (0x5946F0) -- 32 bytes ===
void SetDimensionsAndSize(int handle, int vertexSize, int vertexCount)
{
*(int*)(handle + 0x0C) = vertexSize;
*(int*)(handle + 0x10) = vertexCount;
*(int*)(handle + 0x14) = vertexSize * vertexCount;
*(char*)(handle + 0x1C) = 0; // clear dirty flag
}
// === LockVertexBuffer (0x58A080) -- 18 bytes ===
// __fastcall(ECX=poolHandle)
// Calls CGxDevice vtable[0xA8/4 = 42] to lock the D3D9 VB.
// Returns pointer to locked VB memory.
void* __fastcall LockVertexBuffer(void* poolHandle)
{
return (*(code**)(*(int*)CGxDeviceD3d__device + 0xA8))(poolHandle);
}
// === UnlockVertexBuffer (0x58A0A0) -- 27 bytes ===
// __fastcall(ECX=poolHandle, EDX=byteCount)
// Calls CGxDevice vtable[0xAC/4 = 43] to unlock, then marks pool handle active.
void __fastcall UnlockVertexBuffer(int poolHandle, int byteCount)
{
(*(code**)(*(int*)CGxDeviceD3d__device + 0xAC))(poolHandle, byteCount);
SetObjectActiveFlag(poolHandle);
}
// === UpdateBufferData (0x58A0C0) -- 57 bytes ===
// __fastcall(ECX=poolHandle, EDX=srcData, stack: byteCount, unused)
// Used by CreateAndBindVertexBuffer for INDEX buffer filling.
// If byteCount==0, auto-computes from handle's vertexSize*vertexCount.
void __fastcall UpdateBufferData(int poolHandle, void* srcData, int byteCount, void* unused)
{
if (byteCount == 0) {
byteCount = *(int*)(poolHandle + 0x10) * *(int*)(poolHandle + 0x0C);
}
(*(code**)(*(int*)CGxDeviceD3d__device + 0xB0))(poolHandle, srcData, byteCount, unused);
SetObjectActiveFlag(poolHandle);
}
// === DrawPrimitive / GxDevice dispatch (0x58A7C0) -- 54 bytes ===
// __fastcall(ECX=poolHandle, EDX=formatCode)
// Reads per-format state from table at 0x809C00 (16 bytes per entry):
// +0x00: ptr to state array (GxDevice render state descriptors)
// +0x04: state array element count
// +0x08: primitive type mapping
// +0x0C: dirty flags bitmask
// Applies state, marks dirty, stores draw command in CGxDevice for D3D9 submission.
void __fastcall DrawPrimitive(int poolHandle, int formatCode)
{
int* stateTable = (int*)(0x809C00 + formatCode * 0x10);
UpdateGfxStateArray(poolHandle, stateTable[0], stateTable[1]);
MarkStateDirty(stateTable[3]);
SetRenderingCommand(CGxDeviceD3d__device, poolHandle, formatCode);
}
// === SetRenderingCommand (0x592AA0) -- 40 bytes ===
// __thiscall(ECX=CGxDevice, stack: poolHandle, formatCode)
// Stores the draw command into CGxDevice for later D3D9 submission.
void __thiscall SetRenderingCommand(void* this, void* poolHandle, int formatCode)
{
*(int*)((int)this + 0x27E0) = formatCode;
*(void**)((int)this + 0x27E4) = poolHandle;
*(int*)((int)this + 0x27E8) = *(int*)(0x809C08 + formatCode * 0x10);
}
// === RenderVertexBuffer (0x58A2E0) -- 82 bytes ===
// __fastcall(ECX=primType, EDX=vertCount, stack: indexPtr)
// Creates index VB (bufferType=1), binds it, issues DrawIndexedPrimitive.
void __fastcall RenderVertexBuffer(int primType, int vertCount, void* indexPtr)
{
if (g_currentPrimitiveType != NULL) {
CreateAndBindVertexBuffer(vertCount, indexPtr); // index VB
short adjustedPrimType = (short)g_currentPrimitiveType - 1;
// Build draw call struct on stack
struct { void* primPtr; void* unused; short vertCount; short flags; } call;
call.primPtr = primType;
call.unused = NULL;
call.vertCount = vertCount;
call.flags = 0;
CallGfxDeviceMethod_Wrapper(&call, 1); // -> D3D9 DrawIndexedPrimitive
}
}
// === CreateAndBindVertexBuffer (0x58A750) -- 44 bytes ===
// __fastcall(ECX=vertCount, EDX=dataPtr)
// Used for INDEX buffer (bufferType=1, vertexSize=2 = sizeof(u16)).
void __fastcall CreateAndBindVertexBuffer(int vertCount, void* dataPtr)
{
int handle = CreateVertexBuffer(1, 2, vertCount); // pool slot 1, 2 bytes/index
UpdateBufferData(handle, dataPtr, 0, NULL);
SetStreamSource(handle); // bind as index stream
}
// === Vertex format tables (from game memory) ===
//
// Stride table at 0x85A7A8 (indexed by format code):
// fmt 0: stride=12 xyz only
// fmt 1: stride=24 xyz + defaultTC
// fmt 2: stride=28 xyz + defaultTC + additional(4)
// fmt 3: stride=32 xyz + defaultTC + texcoord1(8)
// fmt 4: stride=36 xyz + defaultTC + additional(4) + texcoord1(8)
// fmt 5: stride=40 xyz + defaultTC + texcoord1(8) + texcoord2(8)
// fmt 6: stride=44 xyz + defaultTC + additional(4) + texcoord1(8) + texcoord2(8)
// fmt 7: stride=16 xyz + additional(4)
// fmt 8: stride=24 xyz + additional(4) + texcoord1(8)
// fmt 9: stride=32 xyz + additional(4) + texcoord1(8) + texcoord2(8)
// fmt 10: stride=20 xyz + texcoord1(8)
// fmt 11: stride=28 xyz + texcoord1(8) + texcoord2(8)
//
// Element offset table at 0x8097A8 (indexed by format*13 + element):
// Element 0 = xyz position (always at offset 0)
// Element 3 = defaultTexCoord
// Element 4 = additionalData (color)
// Element 5 = texcoord1
// Element 6 = texcoord2
// Value -1 = element not present in this format
@@ -0,0 +1,166 @@
// RenderComplexGeometry (0x58A3D0) -- 886 bytes
// __fastcall(ECX=vertCount, EDX=xyzPtr, 11 stack params)
//
// Called from InitializeRenderingPipeline (0x58A2A0) which just stores
// vertCount to global [0xC0ED2C] then tail-calls this function.
//
// Purpose: Determine vertex format from which input pointers are non-null,
// create/reuse a pool VB, lock it, interleave all input arrays into the VB
// at the computed stride, unlock, then issue a DrawPrimitive.
//
// Parameters (after fastcall mapping):
// param_1 = vertCount (ECX)
// param_2 = xyzPtr (EDX) -- 3 floats per vert, stride in param_3
// param_3 = xyzStride (stack) -- typically 0x0C (12 bytes)
// param_4 = defaultTexCoordPtr (stack) -- game constant at 0xCF4CF4, 3 floats/vert
// param_5 = defaultTexCoordStride (stack) -- 0 means use param_4 as single value
// param_6 = additionalDataPtr (stack) -- per-vert color (DWORD), or NULL
// param_7 = additionalDataStride (stack)
// param_8 = texCoord1Ptr (stack) -- unused in RTQ path
// param_9 = texCoord1Stride (stack)
// param_10 = texCoord2Ptr (stack) -- UV coords, 2 floats/vert
// param_11 = texCoord2Stride (stack) -- typically 8
//
// Vertex format table:
// Format codes 0-11, determined by which of param_4/6/8/10 are non-null.
// Stride table at 0x85A7A8: [12,24,28,32,36,40,44,16,24,32,20,28]
// Element offset table at 0x8097A8: indexed by (format*13 + element)*4
// Elements: 0=xyz, 3=defaultTC, 4=additional, 5=texcoord1, 6=texcoord2
//
// For RTQ with additionalData (format 4, stride 36):
// [0-11] xyz (12 bytes, 3 floats)
// [12-23] defaultTC (12 bytes, 3 floats from 0xCF4CF4)
// [24-27] additional (4 bytes, DWORD color)
// [28-35] texcoord2 (8 bytes, 2 floats UV)
//
// For RTQ without additionalData (format 1, stride 24):
// [0-11] xyz (12 bytes)
// [12-23] defaultTC (12 bytes)
//
// Per-vertex loop:
// 1. Copy xyz (3 dwords) at element offset 0
// 2. Copy defaultTC (3 dwords) at element offset 3 (12 bytes)
// 3. Check lighting flag at CGxDevice+0x258:
// - If flag == 1: byte-swap color (BGRA -> RGBA or similar)
// - Else: copy color as-is
// 4. Copy additional data (1 dword) at element offset 4
// 5. Copy texcoord1 (2 dwords) at element offset 5
// 6. Copy texcoord2 (2 dwords) at element offset 6
// Each pointer advances by its respective stride per vertex.
// VB write pointer advances by the interleaved stride per vertex.
//
// After loop: UnlockVertexBuffer, then DrawPrimitive(poolHandle, formatCode)
//
// DrawPrimitive (0x58A7C0):
// Reads from a per-format-code table at 0x809C00 (stride 0x10):
// +0x00: state array ptr
// +0x04: state array count
// +0x08: D3D primitive type table index
// +0x0C: dirty flags mask
// Calls UpdateGfxStateArray to apply GxDevice state changes,
// then MarkStateDirty, then SetRenderingCommand which stores
// the draw command into CGxDevice+0x27E0..0x27E8 for later
// submission to D3D9.
//
// Pool VB system (D3D_CreateVertexBuffer at 0x594500):
// CGxDevice has pool slots at +0x26CC indexed by bufferType.
// RenderComplexGeometry uses bufferType=0.
// Pool handle struct:
// +0x08: D3D9 IDirect3DVertexBuffer9*
// +0x0C: vertexSize (bytes per vertex for this format)
// +0x10: vertexCount
// +0x14: total bytes (vertexSize * vertexCount)
// +0x1C: dirty flag (cleared by SetDimensionsAndSize)
// The pool VB is reused across calls. Only recreated if the
// requested size (vertexSize * vertexCount) exceeds the current
// allocation at +0x14. This means no D3D9 CreateVertexBuffer
// overhead on normal frames.
//
// RenderVertexBuffer (0x58A2E0):
// Called AFTER RenderComplexGeometry returns. Uses a DIFFERENT pool
// (bufferType=1) for index data. Creates/binds an index VB with
// the quad indices {0,1,2,0,2,3}, then calls DrawIndexedPrimitive
// via CallGfxDeviceMethod_Wrapper.
void __fastcall
RenderComplexGeometry(
int vertCount, // ECX
void** xyzPtr, // EDX
int xyzStride, // [ebp+0x08]
void** defaultTCPtr, // [ebp+0x0C]
int defaultTCStride, // [ebp+0x10]
void** additionalPtr, // [ebp+0x14]
int additionalStride, // [ebp+0x18]
void** texCoord1Ptr, // [ebp+0x1C]
int texCoord1Stride, // [ebp+0x20]
void** texCoord2Ptr, // [ebp+0x24]
int texCoord2Stride) // [ebp+0x28]
{
// Step 1: Format detection -- determine which of the 12 interleaved
// vertex formats to use based on which input pointers are non-null.
int formatCode = 1; // default: xyz + defaultTC
// Complex nested-if tree mapping (defaultTC, additional, tc1, tc2)
// presence to format codes 0-11. See format table above.
// Step 2: Get stride and allocate pool VB
int stride = GetDataPointerByIndex(formatCode); // stride table lookup
void* poolHandle = CreateVertexBuffer(0, stride, vertCount);
char* vbData = LockVertexBuffer(poolHandle);
// Step 3: Compute write pointers for each element within the VB
char* xyzDst = vbData + GetMatrixElementPointer(formatCode, 0);
char* tcDst = (defaultTCPtr) ? vbData + GetMatrixElementPointer(formatCode, 3) : &dummy;
char* addDst = (additionalPtr) ? vbData + GetMatrixElementPointer(formatCode, 4) : &dummy;
char* tc1Dst = (texCoord1Ptr) ? vbData + GetMatrixElementPointer(formatCode, 5) : &dummy;
char* tc2Dst = (texCoord2Ptr) ? vbData + GetMatrixElementPointer(formatCode, 6) : &dummy;
// Per-element advance: stride if present, 0 if writing to dummy
int tcAdv = defaultTCPtr ? stride : 0;
int addAdv = additionalPtr ? stride : 0;
int tc1Adv = texCoord1Ptr ? stride : 0;
int tc2Adv = texCoord2Ptr ? stride : 0;
// Step 4: Per-vertex interleave loop
for (int v = 0; v < vertCount; v++) {
// XYZ: always 12 bytes (3 floats)
*(int*)(xyzDst + 0) = *(int*)(xyzPtr + 0);
*(int*)(xyzDst + 4) = *(int*)(xyzPtr + 4);
*(int*)(xyzDst + 8) = *(int*)(xyzPtr + 8);
xyzDst += stride;
xyzPtr += xyzStride;
// DefaultTC: 12 bytes (3 floats)
*(int*)(tcDst + 0) = *(int*)(defaultTCPtr + 0);
*(int*)(tcDst + 4) = *(int*)(defaultTCPtr + 4);
*(int*)(tcDst + 8) = *(int*)(defaultTCPtr + 8);
tcDst += tcAdv;
defaultTCPtr += defaultTCStride;
// Additional (color): conditional byte-swap based on lighting flag
int lightingInfo = UpdateLightingOffset(); // returns CGxDevice + 0x23C
if (*(int*)(lightingInfo + 0x1C) == 1) {
// Byte swap: BGRA -> RGBA (swap bytes 0 and 2)
*(int*)(addDst) = CONCAT(byte3, byte0, byte1, byte2);
} else {
*(int*)(addDst) = *(int*)(additionalPtr);
}
addDst += addAdv;
additionalPtr += additionalStride;
// TexCoord1: 8 bytes (2 floats)
*(int*)(tc1Dst + 0) = *(int*)(texCoord1Ptr + 0);
*(int*)(tc1Dst + 4) = *(int*)(texCoord1Ptr + 4);
tc1Dst += tc1Adv;
texCoord1Ptr += texCoord1Stride;
// TexCoord2: 8 bytes (2 floats)
*(int*)(tc2Dst + 0) = *(int*)(texCoord2Ptr + 0);
*(int*)(tc2Dst + 4) = *(int*)(texCoord2Ptr + 4);
tc2Dst += tc2Adv;
texCoord2Ptr += texCoord2Stride;
}
// Step 5: Finalize
UnlockVertexBuffer(poolHandle, stride * vertCount);
DrawPrimitive(poolHandle, formatCode);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,255 @@
// =============================================================================
// processLinkedListCollision @ 0x006abc40
// Function range: 0x006abc40 -- 0x006abd88 (329 bytes)
// Generated by Ghidra headless analysis
// =============================================================================
// =============================================================================
// SECTION 1: DECOMPILATION
// =============================================================================
undefined * __fastcall
processLinkedListCollision(int *param_1,float *param_2,uint *param_3,uint param_4)
{
int iVar1;
uint uVar2;
int iVar3;
undefined *puVar4;
undefined4 *puVar5;
undefined **ppuVar6;
undefined *local_28;
undefined *local_24;
undefined *local_20;
undefined *local_1c;
undefined *local_18;
undefined *local_14;
undefined *local_10;
undefined *local_c;
undefined *local_8;
if ((param_4 & 0xf0000f) != 0) {
local_8 = (undefined *)param_1[2];
local_10 = (undefined *)param_1;
local_c = (undefined *)param_2;
if ((((uint)local_8 & 1) != 0) || (local_8 == (undefined *)0x0)) {
local_8 = (undefined *)0x0;
}
while ((puVar4 = local_8, ((uint)local_8 & 1) == 0 && (local_8 != (undefined *)0x0))) {
iVar1 = *(int *)(local_8 + 4);
if (((*(ushort *)(iVar1 + 0xc) & 0x100) == 0) &&
((*(undefined **)(iVar1 + 0x8c) != PTR_00c89f20 && (*(int *)(iVar1 + 0x88) != 0)))) {
if (*(int *)(iVar1 + 0x180) == 0 && *(int *)(iVar1 + 0x184) == 0) {
uVar2 = param_4 & 0xf;
}
else {
uVar2 = param_4 & 0xf00000;
}
if (uVar2 != 0) {
if (-1 < (char)*(ushort *)(iVar1 + 0xc)) {
return (undefined *)0x0;
}
puVar5 = (undefined4 *)(iVar1 + 0x14c);
ppuVar6 = &local_28;
for (iVar3 = 6; iVar3 != 0; iVar3 = iVar3 + -1) {
*ppuVar6 = (undefined *)*puVar5;
puVar5 = puVar5 + 1;
ppuVar6 = ppuVar6 + 1;
}
if (((((float)local_28 < *(float *)(local_c + 0xc) !=
((float)local_28 == *(float *)(local_c + 0xc))) &&
((float)local_24 < *(float *)(local_c + 0x10) !=
((float)local_24 == *(float *)(local_c + 0x10)))) &&
((float)local_20 < *(float *)(local_c + 0x14) !=
((float)local_20 == *(float *)(local_c + 0x14)))) &&
(((*(float *)local_c <= (float)local_1c && (*(float *)(local_c + 4) <= (float)local_18)
) && (*(float *)(local_c + 8) <= (float)local_14)))) {
addGeometryToBuffer((float *)local_c,iVar1,param_3);
puVar4 = local_8;
}
*(undefined **)(iVar1 + 0x8c) = PTR_00c89f20;
}
}
local_8 = *(undefined **)(puVar4 + *(int *)local_10 + 4);
}
}
return (undefined *)0x1;
}
// =============================================================================
// SECTION 2: FULL DISASSEMBLY
// =============================================================================
//
// 0x006abc40 55 PUSH EBP
// 0x006abc41 8b ec MOV EBP,ESP
// 0x006abc43 83 ec 24 SUB ESP,0x24
// 0x006abc46 53 PUSH EBX
// 0x006abc47 8b c1 MOV EAX,ECX
// 0x006abc49 8b 4d 0c MOV ECX,dword ptr [EBP + 0xc]
// 0x006abc4c f7 c1 0f 00 f0 00 TEST ECX,0xf0000f
// 0x006abc52 56 PUSH ESI
// 0x006abc53 57 PUSH EDI
// 0x006abc54 89 55 f8 MOV dword ptr [EBP + -0x8],EDX
// 0x006abc57 89 45 f4 MOV dword ptr [EBP + -0xc],EAX
// 0x006abc5a 0f 84 1b 01 00 00 JZ 0x006abd7b
// 0x006abc60 8b 40 08 MOV EAX,dword ptr [EAX + 0x8]
// 0x006abc63 a8 01 TEST AL,0x1
// 0x006abc65 75 04 JNZ 0x006abc6b
// 0x006abc67 85 c0 TEST EAX,EAX
// 0x006abc69 75 07 JNZ 0x006abc72
// 0x006abc6b 33 d2 XOR EDX,EDX
// 0x006abc6d 89 55 fc MOV dword ptr [EBP + -0x4],EDX
// 0x006abc70 eb 06 JMP 0x006abc78
// 0x006abc72 89 45 fc MOV dword ptr [EBP + -0x4],EAX
// 0x006abc75 8b 55 fc MOV EDX,dword ptr [EBP + -0x4]
// 0x006abc78 f6 c2 01 TEST DL,0x1
// 0x006abc7b 0f 85 fa 00 00 00 JNZ 0x006abd7b
// 0x006abc81 85 d2 TEST EDX,EDX
// 0x006abc83 0f 84 f2 00 00 00 JZ 0x006abd7b
// 0x006abc89 8b 5a 04 MOV EBX,dword ptr [EDX + 0x4]
// 0x006abc8c 66 8b 43 0c MOV AX,word ptr [EBX + 0xc]
// 0x006abc90 f6 c4 01 TEST AH,0x1
// 0x006abc93 0f 85 c1 00 00 00 JNZ 0x006abd5a
// 0x006abc99 8b 35 20 9f c8 00 MOV ESI,dword ptr [0x00c89f20]
// 0x006abc9f 39 b3 8c 00 00 00 CMP dword ptr [EBX + 0x8c],ESI
// 0x006abca5 0f 84 af 00 00 00 JZ 0x006abd5a
// 0x006abcab 8b b3 88 00 00 00 MOV ESI,dword ptr [EBX + 0x88]
// 0x006abcb1 85 f6 TEST ESI,ESI
// 0x006abcb3 0f 84 a1 00 00 00 JZ 0x006abd5a
// 0x006abcb9 8b b3 80 01 00 00 MOV ESI,dword ptr [EBX + 0x180]
// 0x006abcbf 0b b3 84 01 00 00 OR ESI,dword ptr [EBX + 0x184]
// 0x006abcc5 74 08 JZ 0x006abccf
// 0x006abcc7 f7 c1 00 00 f0 00 TEST ECX,0xf00000
// 0x006abccd eb 03 JMP 0x006abcd2
// 0x006abccf f6 c1 0f TEST CL,0xf
// 0x006abcd2 0f 84 82 00 00 00 JZ 0x006abd5a
// 0x006abcd8 84 c0 TEST AL,AL
// 0x006abcda 0f 89 90 00 00 00 JNS 0x006abd70
// 0x006abce0 8d b3 4c 01 00 00 LEA ESI,[EBX + 0x14c]
// 0x006abce6 b9 06 00 00 00 MOV ECX,0x6
// 0x006abceb 8d 7d dc LEA EDI,[EBP + -0x24]
// 0x006abcee f3 a5 MOVSD.REP ES:EDI,ESI
// 0x006abcf0 d9 45 dc FLD float ptr [EBP + -0x24]
// 0x006abcf3 8b 4d f8 MOV ECX,dword ptr [EBP + -0x8]
// 0x006abcf6 d8 59 0c FCOMP float ptr [ECX + 0xc]
// 0x006abcf9 df e0 FNSTSW AX
// 0x006abcfb f6 c4 41 TEST AH,0x41
// 0x006abcfe 7a 4e JP 0x006abd4e
// 0x006abd00 d9 45 e0 FLD float ptr [EBP + -0x20]
// 0x006abd03 d8 59 10 FCOMP float ptr [ECX + 0x10]
// 0x006abd06 df e0 FNSTSW AX
// 0x006abd08 f6 c4 41 TEST AH,0x41
// 0x006abd0b 7a 41 JP 0x006abd4e
// 0x006abd0d d9 45 e4 FLD float ptr [EBP + -0x1c]
// 0x006abd10 d8 59 14 FCOMP float ptr [ECX + 0x14]
// 0x006abd13 df e0 FNSTSW AX
// 0x006abd15 f6 c4 41 TEST AH,0x41
// 0x006abd18 7a 34 JP 0x006abd4e
// 0x006abd1a d9 45 e8 FLD float ptr [EBP + -0x18]
// 0x006abd1d d8 19 FCOMP float ptr [ECX]
// 0x006abd1f df e0 FNSTSW AX
// 0x006abd21 f6 c4 01 TEST AH,0x1
// 0x006abd24 75 28 JNZ 0x006abd4e
// 0x006abd26 d9 45 ec FLD float ptr [EBP + -0x14]
// 0x006abd29 d8 59 04 FCOMP float ptr [ECX + 0x4]
// 0x006abd2c df e0 FNSTSW AX
// 0x006abd2e f6 c4 01 TEST AH,0x1
// 0x006abd31 75 1b JNZ 0x006abd4e
// 0x006abd33 d9 45 f0 FLD float ptr [EBP + -0x10]
// 0x006abd36 d8 59 08 FCOMP float ptr [ECX + 0x8]
// 0x006abd39 df e0 FNSTSW AX
// 0x006abd3b f6 c4 01 TEST AH,0x1
// 0x006abd3e 75 0e JNZ 0x006abd4e
// 0x006abd40 8b 45 08 MOV EAX,dword ptr [EBP + 0x8]
// 0x006abd43 50 PUSH EAX
// 0x006abd44 8b d3 MOV EDX,EBX
// 0x006abd46 e8 45 00 00 00 CALL 0x006abd90
// 0x006abd4b 8b 55 fc MOV EDX,dword ptr [EBP + -0x4]
// 0x006abd4e 8b 0d 20 9f c8 00 MOV ECX,dword ptr [0x00c89f20]
// 0x006abd54 89 8b 8c 00 00 00 MOV dword ptr [EBX + 0x8c],ECX
// 0x006abd5a 8b 45 f4 MOV EAX,dword ptr [EBP + -0xc]
// 0x006abd5d 8b 00 MOV EAX,dword ptr [EAX]
// 0x006abd5f 8b 4c 10 04 MOV ECX,dword ptr [EAX + EDX*0x1 + 0x4]
// 0x006abd63 03 c2 ADD EAX,EDX
// 0x006abd65 89 4d fc MOV dword ptr [EBP + -0x4],ECX
// 0x006abd68 8b 4d 0c MOV ECX,dword ptr [EBP + 0xc]
// 0x006abd6b e9 05 ff ff ff JMP 0x006abc75
// 0x006abd70 5f POP EDI
// 0x006abd71 5e POP ESI
// 0x006abd72 33 c0 XOR EAX,EAX
// 0x006abd74 5b POP EBX
// 0x006abd75 8b e5 MOV ESP,EBP
// 0x006abd77 5d POP EBP
// 0x006abd78 c2 08 00 RET 0x8
// 0x006abd7b 5f POP EDI
// 0x006abd7c 5e POP ESI
// 0x006abd7d b8 01 00 00 00 MOV EAX,0x1
// 0x006abd82 5b POP EBX
// 0x006abd83 8b e5 MOV ESP,EBP
// 0x006abd85 5d POP EBP
// 0x006abd86 c2 08 00 RET 0x8
//
// Total instructions: 108
// =============================================================================
// SECTION 3: PROLOGUE (first 30 instructions)
// =============================================================================
//
// 0x006abc40 55 PUSH EBP
// 0x006abc41 8b ec MOV EBP,ESP
// 0x006abc43 83 ec 24 SUB ESP,0x24
// 0x006abc46 53 PUSH EBX
// 0x006abc47 8b c1 MOV EAX,ECX
// 0x006abc49 8b 4d 0c MOV ECX,dword ptr [EBP + 0xc]
// 0x006abc4c f7 c1 0f 00 f0 00 TEST ECX,0xf0000f
// 0x006abc52 56 PUSH ESI
// 0x006abc53 57 PUSH EDI
// 0x006abc54 89 55 f8 MOV dword ptr [EBP + -0x8],EDX
// 0x006abc57 89 45 f4 MOV dword ptr [EBP + -0xc],EAX
// 0x006abc5a 0f 84 1b 01 00 00 JZ 0x006abd7b
// 0x006abc60 8b 40 08 MOV EAX,dword ptr [EAX + 0x8]
// 0x006abc63 a8 01 TEST AL,0x1
// 0x006abc65 75 04 JNZ 0x006abc6b
// 0x006abc67 85 c0 TEST EAX,EAX
// 0x006abc69 75 07 JNZ 0x006abc72
// 0x006abc6b 33 d2 XOR EDX,EDX
// 0x006abc6d 89 55 fc MOV dword ptr [EBP + -0x4],EDX
// 0x006abc70 eb 06 JMP 0x006abc78
// 0x006abc72 89 45 fc MOV dword ptr [EBP + -0x4],EAX
// 0x006abc75 8b 55 fc MOV EDX,dword ptr [EBP + -0x4]
// 0x006abc78 f6 c2 01 TEST DL,0x1
// 0x006abc7b 0f 85 fa 00 00 00 JNZ 0x006abd7b
// 0x006abc81 85 d2 TEST EDX,EDX
// 0x006abc83 0f 84 f2 00 00 00 JZ 0x006abd7b
// 0x006abc89 8b 5a 04 MOV EBX,dword ptr [EDX + 0x4]
// 0x006abc8c 66 8b 43 0c MOV AX,word ptr [EBX + 0xc]
// 0x006abc90 f6 c4 01 TEST AH,0x1
// 0x006abc93 0f 85 c1 00 00 00 JNZ 0x006abd5a
// =============================================================================
// SECTION 4: ALL RET INSTRUCTIONS (stack cleanup indicator)
// =============================================================================
//
// 0x006abd78 c2 08 00 RET 0x8
// 0x006abd86 c2 08 00 RET 0x8
// =============================================================================
// SECTION 5: XREFS TO 0x006abc40 (callers)
// =============================================================================
//
// 0x006aad4c [UNCONDITIONAL_CALL] in processGeometryBoundsCheck (0x006aaab0)
// 0x006ab4a1 [UNCONDITIONAL_CALL] in processTerrainChunkMeshGeneration (0x006aadc0)
//
// Total callers: 2
// =============================================================================
// SECTION 6: XREFS FROM 0x006abc40 (callees)
// =============================================================================
//
// 0x006abd90 addGeometryToBuffer
//
// Total callees: 1
@@ -0,0 +1,659 @@
// =============================================================================
// renderTextToBuffer @ 0x005CDC20
// Decompiled from WoW.exe 1.12.1 (build 5875) via Ghidra 11.4.2
// Single caller of RenderTextToVertexBuffer (0x5CCBE0)
// =============================================================================
// -----------------------------------------------------------------------------
// CALLING CONVENTION (verified from assembly)
// -----------------------------------------------------------------------------
// __thiscall: ECX = this (text object ptr), saved to EBX immediately
// 0x005cdc20 PUSH EBP
// 0x005cdc21 MOV EBP,ESP
// 0x005cdc23 SUB ESP,0x74
// 0x005cdc26 PUSH EBX
// 0x005cdc27 PUSH ESI
// 0x005cdc28 MOV EBX,ECX <-- this ptr saved to EBX
// ...
// 0x005cdee8 RET <-- single RET, no stack cleanup = __thiscall (0 stack params)
//
// Signature: void __thiscall renderTextToBuffer(TextObject* this)
// No stack parameters. Single RET (no RET N).
// -----------------------------------------------------------------------------
// XREFS TO (callers)
// -----------------------------------------------------------------------------
// 0x005cd6aa from validateAndPrepareText (UNCONDITIONAL_CALL)
// 0x005cd426 from GetVertexBufferData (UNCONDITIONAL_CALL)
//
// Two callers:
// 1. validateAndPrepareText (0x5cd6aa) -- validation/preparation path
// 2. GetVertexBufferData (0x5cd426) -- vertex buffer retrieval path
// -----------------------------------------------------------------------------
// XREFS FROM (callees)
// -----------------------------------------------------------------------------
// 0x005c6fa0 ConvertPixelsToScreen
// 0x0040a2b0 __ftol (float-to-long)
// 0x005c2810 ParseTextFormatCodes
// 0x005c7260 WrapTextToWidth
// 0x005c7010 ConvertPixelsToScreenAlt
// 0x005ccbe0 RenderTextToVertexBuffer <<<< the target
// 0x005cd310 AddRectangleToBuffer
// 0x005cdf70 finalizeTextLayout
// 0x005cd4d0 renderFadeEffect
// -----------------------------------------------------------------------------
// TEXT OBJECT FIELD MAP (offsets from this/EBX)
// -----------------------------------------------------------------------------
// +0x1c float lineHeight (or spacing-related metric)
// +0x28 float xOffset (used when bit7 of flags is clear)
// +0x2c float color/style data (passed to RenderTextToVertexBuffer)
// +0x34 float indentOrShadow (used when flags bit0 is set)
// +0x3c float maxWidth (passed to WrapTextToWidth)
// +0x40 float maxHeight (vertical overflow check)
// +0x44 void* fontObject (passed to WrapTextToWidth as ECX)
// +0x48 char* textString (the actual text to render)
// +0x54 int alignment (0=left, 1=center, 2=right)
// +0x58 float some metric (initial value for local_c / line step)
// +0x5c uint flags bitfield:
// bit0: has indent/shadow
// bit1 (0x02): single-line mode (breaks loop after first line)
// bit5 (0x20): has fade effect
// bit7 (0x80): pixel mode vs screen mode
// +0x60 uint resultFlags (OR'd with per-line flags from RenderTextToVertexBuffer)
// +0x68 int fadeStart (passed to renderFadeEffect)
// +0x6c int fadeEnd (passed to renderFadeEffect)
// +0x90 uint lineCountOutput (zeroed at start, not the dirty flag)
// +0x9c int LINE COUNTER / DIRTY FLAG -- THE KEY FIELD
// - Checked FIRST: if (this+0x9c != 0) return immediately
// - Incremented after each line rendered
// - Also incremented for format-code-only lines (ParseTextFormatCodes returns 2)
// - Acts as both "already rendered" guard AND line counter
// -----------------------------------------------------------------------------
// DIRTY/VALID FLAG ANALYSIS
// -----------------------------------------------------------------------------
//
// The field at +0x9c is the critical gate. The function's VERY FIRST check is:
//
// if (this->field_0x9c != 0) return; // already rendered, skip
// if (this->textString == NULL) return; // no text
// if (*this->textString == '\0') return; // empty text
//
// Assembly proof:
// 0x005cdc2a MOV EAX,dword ptr [EBX + 0x9c]
// 0x005cdc33 CMP EAX,EDI ; EDI = 0
// 0x005cdc35 JNZ 0x005cdee2 ; bail if non-zero
// 0x005cdc3b MOV EAX,dword ptr [EBX + 0x48]
// 0x005cdc3e CMP EAX,EDI
// 0x005cdc40 JZ 0x005cdee2 ; bail if text ptr is NULL
// 0x005cdc46 CMP byte ptr [EAX],0x0
// 0x005cdc49 JZ 0x005cdee2 ; bail if text is empty
//
// Then +0x9c is INCREMENTED after each rendered line:
// 0x005cde55 MOV ECX,dword ptr [EBX + 0x9c]
// 0x005cde5e INC ECX
// 0x005cde62 MOV dword ptr [EBX + 0x9c],ECX
//
// And also incremented for format-code lines (ParseTextFormatCodes == 2):
// 0x005cdd81 MOV EAX,dword ptr [EBX + 0x9c]
// 0x005cdd8c INC EAX
// 0x005cdd90 MOV dword ptr [EBX + 0x9c],EAX
//
// CONCLUSION: +0x9c serves as BOTH:
// 1. A "dirty/needs-render" flag (0 = needs render, non-zero = already done)
// 2. A line counter (counts lines processed during rendering)
//
// To force re-render: set this->field_0x9c = 0
// To prevent render: set this->field_0x9c = non-zero
//
// The callers (validateAndPrepareText, GetVertexBufferData) presumably
// reset +0x9c to 0 when text changes, triggering re-render on next call.
// +0x90 is zeroed at entry:
// 0x005cdc4f MOV dword ptr [EBX + 0x90],EDI ; = 0
// This appears to be a separate output counter, not the dirty flag.
// -----------------------------------------------------------------------------
// FLOW SUMMARY
// -----------------------------------------------------------------------------
//
// 1. Guard: if +0x9c != 0 || textString is null/empty -> return
// 2. Zero +0x90 (line output counter?)
// 3. Compute vertical layout params from +0x1c, +0x58, flags
// 4. Loop over text lines:
// a. Check vertical overflow (accumulated height vs +0x40 maxHeight)
// b. ParseTextFormatCodes -- handle color/format escapes (returns 2 = consumed)
// c. WrapTextToWidth -- break text at word boundaries for +0x3c width
// d. Handle alignment (+0x54): left(0), center(1), right(2) via ConvertPixelsToScreenAlt
// e. CALL RenderTextToVertexBuffer(this, textPtr, charCount, &color, &offset, &flags, &state)
// f. Increment +0x9c (line counter)
// g. If state==2, call AddRectangleToBuffer (highlight/selection rect)
// h. OR per-line flags into +0x60
// i. If flags bit1 (single-line mode), break
// j. Advance to next line
// 5. Call finalizeTextLayout(this)
// 6. If flags bit5 (fade), call renderFadeEffect(this, fadeStart, fadeEnd)
// -----------------------------------------------------------------------------
// RenderTextToVertexBuffer CALL SITE DETAIL (at 0x5CDE50)
// -----------------------------------------------------------------------------
// __thiscall: ECX = this (text object)
// Stack args (6, pushed right-to-left):
// push &state (EBP-0x74, local_74 area -- tracks render state, value 2 = highlight)
// push &flags (EBP-0x24, output flags OR'd into +0x60)
// push charCount (EBP-0x18, from WrapTextToWidth output)
// push &offset (EBP-0x48, vertical/horizontal position floats)
// push &color (EBP-0x30, color/style data)
// push textPtr (ESI, current position in text string)
//
// Assembly at call site:
// 0x005cde39 LEA EAX,[EBP + -0x74]
// 0x005cde3c PUSH EAX ; &state
// 0x005cde3d LEA ECX,[EBP + -0x24]
// 0x005cde40 PUSH ECX ; &flags
// 0x005cde41 MOV ECX,dword ptr [EBP + -0x18]
// 0x005cde44 LEA EDX,[EBP + -0x48]
// 0x005cde47 PUSH EDX ; &offset
// 0x005cde48 LEA EAX,[EBP + -0x30]
// 0x005cde4b PUSH EAX ; &color
// 0x005cde4c PUSH ECX ; charCount
// 0x005cde4d PUSH ESI ; textPtr
// 0x005cde4e MOV ECX,EBX ; this
// 0x005cde50 CALL 0x005ccbe0 ; RenderTextToVertexBuffer
// =============================================================================
// DECOMPILED C (Ghidra raw output, lightly annotated)
// =============================================================================
/* WARNING: Variable defined which should be unmapped: local_88 */
void __fastcall renderTextToBuffer(void *param_1) // ECX = this
{
byte bVar1;
uint uVar2;
undefined *puVar3;
int iVar4;
undefined **ppuVar5;
byte *pbVar6;
float unaff_EDI;
undefined4 *puVar7;
float10 extraout_ST0;
float10 fVar8;
float10 extraout_ST0_00;
float10 extraout_ST0_01;
float10 extraout_ST0_02;
ulonglong uVar9;
float10 *pfVar10;
float fVar11;
undefined *local_88;
undefined *local_78;
undefined *local_74;
undefined *local_70;
undefined *local_6c;
undefined *local_68;
undefined *local_4c;
undefined *local_48;
undefined *local_44;
undefined *local_40;
uint uStack_3c;
undefined *local_38;
undefined *local_34;
undefined *local_30;
undefined *local_2c;
undefined *local_28;
undefined *local_24;
undefined *local_20;
undefined *local_1c;
undefined *local_18;
undefined *local_14;
undefined *local_10;
undefined *local_c;
byte local_5;
// --- GUARD: dirty/valid check ---
if (((*(int *)((int)param_1 + 0x9c) != 0) || // already rendered?
(*(char **)((int)param_1 + 0x48) == (char *)0x0)) || // no text ptr?
(**(char **)((int)param_1 + 0x48) == '\0')) { // empty text?
return;
}
// --- Reset line output counter ---
*(undefined4 *)((int)param_1 + 0x90) = 0;
// --- Read fade params ---
local_2c = *(undefined **)((int)param_1 + 0x6c); // fadeEnd
local_38 = *(undefined **)((int)param_1 + 0x68); // fadeStart
// --- Convert line height to screen coords ---
ConvertPixelsToScreen(
(void *)(*(uint *)((int)param_1 + 0x5c) >> 7 & 1),
(float10 *)-*(float *)((int)param_1 + 0x1c),
unaff_EDI);
uVar2 = *(uint *)((int)param_1 + 0x5c) & 0x80; // pixel mode flag
local_4c = (undefined *)0x0;
if (uVar2 == 0) {
local_44 = *(undefined **)((int)param_1 + 0x28); // xOffset
local_48 = (undefined *)(float)extraout_ST0;
} else {
local_48 = (undefined *)0x0;
local_44 = (undefined *)0x0;
}
local_34 = *(undefined **)((int)param_1 + 0x2c); // color/style
local_10 = *(undefined **)((int)param_1 + 0x58); // line step metric
pbVar6 = *(byte **)((int)param_1 + 0x48); // text string ptr
// Init state vars
local_78 = (undefined *)0x0;
local_70 = (undefined *)0x0;
local_68 = (undefined *)0x0;
local_20 = (undefined *)0x0;
local_1c = (undefined *)0x0;
local_14 = (undefined *)0x0;
// Compute line height (pixel mode vs screen mode)
if (uVar2 == 0) {
// Screen mode: round to integer pixels, then back to screen coords
local_40 = PTR_00c2b9a0;
uStack_3c = 0;
uVar9 = __ftol();
local_40 = (undefined *)uVar9;
uStack_3c = 0;
local_18 = (undefined *)(float)(uVar9 & 0xffffffff);
local_10 = (undefined *)(float)((float10)(uVar9 & 0xffffffff) / extraout_ST0_00);
ConvertPixelsToScreen((void *)0x0, *(float10 **)((int)param_1 + 0x1c), unaff_EDI);
fVar8 = extraout_ST0_01 + (float10)(float)local_18;
} else {
// Pixel mode: just add
fVar8 = (float10)(float)local_10 + (float10)*(float *)((int)param_1 + 0x1c);
}
local_c = (undefined *)(float)fVar8; // total line step (height + spacing)
local_5 = 1; // first line flag
local_74 = (undefined *)((float)local_c + (float)local_48); // current Y position
bVar1 = *pbVar6;
local_18 = (undefined *)0x0; // accumulated vertical height
local_6c = local_48;
// --- MAIN RENDERING LOOP: iterate over lines ---
do {
// Check: end of text OR vertical overflow
if (((bVar1 == 0) ||
(*(float *)((int)param_1 + 0x40) <= (float)local_18)) ||
(local_18 = (undefined *)((float)local_18 + *(float *)((int)param_1 + 0x1c) + (float)local_10),
bVar1 == 0))
goto LAB_005cdebc; // done
// Parse format/color codes
puVar3 = ParseTextFormatCodes(
pbVar6, &local_30, (uint *)0x0,
*(uint *)((int)param_1 + 0x5c), &uStack_3c);
local_14 = (undefined *)0x0;
if (puVar3 == (undefined *)0x2) {
// Format code consumed entire segment -- adjust position, skip render
local_48 = (undefined *)((float)local_48 - (float)local_c);
*(int *)((int)param_1 + 0x9c) = *(int *)((int)param_1 + 0x9c) + 1; // increment line counter
pbVar6 = pbVar6 + (int)local_30;
} else {
// Check word wrap flag
if (((uint)*(float10 **)((int)param_1 + 0x5c) & 1) == 0) {
local_24 = (undefined *)0x0;
} else {
local_24 = *(undefined **)((int)param_1 + 0x34); // indent
}
pfVar10 = *(float10 **)((int)param_1 + 0x1c);
// Word-wrap the text to fit maxWidth
WrapTextToWidth(
*(float10 **)((int)param_1 + 0x44), // font
pbVar6, // text
pfVar10, // line height
*(float *)((int)param_1 + 0x3c), // maxWidth
(int *)&local_1c, // out: charCount
(float *)&local_14, // out: line width
&local_20, // out: next line ptr
(float10 *)local_24, // indent
*(float10 **)((int)param_1 + 0x5c), // flags
&local_5); // first line flag
// Check if wrapping produced valid output
if (((local_20 == pbVar6) || (local_20 == (undefined *)0x0)) ||
((local_1c == (undefined *)0x0 && (*local_20 == '\0')))) {
LAB_005cdebc:
// --- FINALIZE ---
finalizeTextLayout((int)param_1);
if ((*(byte *)((int)param_1 + 0x5c) & 0x20) == 0) {
return;
}
// Fade effect
if ((local_38 == (undefined *)0xffffffff) &&
(local_2c == (undefined *)0xffffffff)) {
return;
}
renderFadeEffect(param_1, (int)local_38, (int)local_2c);
return;
}
// --- ALIGNMENT ---
puVar3 = local_14;
if (*(int *)((int)param_1 + 0x54) == 2) {
// Right-aligned
LAB_005cde14:
ConvertPixelsToScreenAlt(
(void *)(*(uint *)((int)param_1 + 0x5c) >> 7 & 1),
(float10 *)-(float)puVar3, unaff_EDI);
local_4c = (undefined *)(float)extraout_ST0_02;
} else if (*(int *)((int)param_1 + 0x54) == 1) {
// Center-aligned
puVar3 = (undefined *)((float)local_14 * StaticFloat0_5);
goto LAB_005cde14;
}
// else: left-aligned, local_4c stays 0
local_28 = (undefined *)0x0;
if (local_78 != (undefined *)0x0) {
local_70 = local_4c;
}
fVar11 = 8.528623e-39; // junk / uninitialized
puVar3 = local_1c;
// --- THE CALL: render this line's glyphs to vertex buffer ---
RenderTextToVertexBuffer(
param_1, // this (ECX)
pbVar6, // textPtr (current line start)
(int)local_1c, // charCount
(uint *)&local_34, // &color/style
(float *)&local_4c, // &position offset
(uint *)&local_28, // &output flags
(int *)&local_78); // &render state
// Increment line counter (+0x9c)
*(int *)((int)param_1 + 0x9c) = *(int *)((int)param_1 + 0x9c) + 1;
// If state == 2, add highlight/selection rectangle
if (local_78 == (undefined *)0x2) {
ppuVar5 = &local_74;
puVar7 = (undefined4 *)&stack0xffffff5c;
for (iVar4 = 8; iVar4 != 0; iVar4 = iVar4 + -1) {
*puVar7 = *ppuVar5;
ppuVar5 = ppuVar5 + 1;
puVar7 = puVar7 + 1;
}
AddRectangleToBuffer(param_1, (float)pfVar10, fVar11, (float)pbVar6, (float)puVar3);
}
// Advance vertical position
local_48 = (undefined *)((float)local_48 - (float)local_c);
// Accumulate result flags
*(uint *)((int)param_1 + 0x60) = *(uint *)((int)param_1 + 0x60) | (uint)local_28;
// Move to next line
pbVar6 = local_20;
// Single-line mode check: if bit1 set, done after first line
if ((*(byte *)((int)param_1 + 0x5c) & 2) != 0)
goto LAB_005cdebc;
}
bVar1 = *pbVar6;
local_6c = (undefined *)((float)local_6c - (float)local_c);
local_74 = (undefined *)((float)local_74 - (float)local_c);
} while (true);
}
// =============================================================================
// FULL DISASSEMBLY LISTING
// =============================================================================
//
// 0x005cdc20 PUSH EBP
// 0x005cdc21 MOV EBP,ESP
// 0x005cdc23 SUB ESP,0x74
// 0x005cdc26 PUSH EBX
// 0x005cdc27 PUSH ESI
// 0x005cdc28 MOV EBX,ECX
// 0x005cdc2a MOV EAX,dword ptr [EBX + 0x9c]
// 0x005cdc30 PUSH EDI
// 0x005cdc31 XOR EDI,EDI
// 0x005cdc33 CMP EAX,EDI
// 0x005cdc35 JNZ 0x005cdee2
// 0x005cdc3b MOV EAX,dword ptr [EBX + 0x48]
// 0x005cdc3e CMP EAX,EDI
// 0x005cdc40 JZ 0x005cdee2
// 0x005cdc46 CMP byte ptr [EAX],0x0
// 0x005cdc49 JZ 0x005cdee2
// 0x005cdc4f MOV dword ptr [EBX + 0x90],EDI
// 0x005cdc55 FLD float ptr [EBX + 0x1c]
// 0x005cdc58 MOV ECX,dword ptr [EBX + 0x6c]
// 0x005cdc5b FCHS
// 0x005cdc5d MOV EAX,dword ptr [EBX + 0x68]
// 0x005cdc60 PUSH ECX
// 0x005cdc61 MOV dword ptr [EBP + -0x28],ECX
// 0x005cdc64 FSTP float ptr [ESP]
// 0x005cdc67 MOV ECX,dword ptr [EBX + 0x5c]
// 0x005cdc6a SHR ECX,0x7
// 0x005cdc6d AND ECX,0x1
// 0x005cdc70 MOV dword ptr [EBP + -0x34],EAX
// 0x005cdc73 CALL 0x005c6fa0
// 0x005cdc78 MOV EAX,dword ptr [EBX + 0x5c]
// 0x005cdc7b AND EAX,0x80
// 0x005cdc80 MOV dword ptr [EBP + -0x48],EDI
// 0x005cdc83 JZ 0x005cdc8f
// 0x005cdc85 FSTP ST0
// 0x005cdc87 MOV dword ptr [EBP + -0x44],EDI
// 0x005cdc8a MOV dword ptr [EBP + -0x40],EDI
// 0x005cdc8d JMP 0x005cdc98
// 0x005cdc8f MOV EDX,dword ptr [EBX + 0x28]
// 0x005cdc92 FSTP float ptr [EBP + -0x44]
// 0x005cdc95 MOV dword ptr [EBP + -0x40],EDX
// 0x005cdc98 CMP EAX,EDI
// 0x005cdc9a MOV ECX,dword ptr [EBX + 0x2c]
// 0x005cdc9d MOV EDX,dword ptr [EBX + 0x58]
// 0x005cdca0 MOV ESI,dword ptr [EBX + 0x48]
// 0x005cdca3 MOV dword ptr [EBP + -0x30],ECX
// 0x005cdca6 MOV dword ptr [EBP + -0x74],EDI
// 0x005cdca9 MOV dword ptr [EBP + -0x6c],0x0
// 0x005cdcb0 MOV dword ptr [EBP + -0x64],0x0
// 0x005cdcb7 MOV dword ptr [EBP + -0x1c],EDI
// 0x005cdcba MOV dword ptr [EBP + -0x18],EDI
// 0x005cdcbd MOV dword ptr [EBP + -0x10],0x0
// 0x005cdcc4 MOV dword ptr [EBP + -0xc],EDX
// 0x005cdcc7 JZ 0x005cdcd1
// 0x005cdcc9 FLD float ptr [EBP + -0xc]
// 0x005cdccc FADD float ptr [EBX + 0x1c]
// 0x005cdccf JMP 0x005cdd10
// 0x005cdcd1 MOV EAX,[0x00c2b9a0]
// 0x005cdcd6 MOV dword ptr [EBP + -0x3c],EAX
// 0x005cdcd9 MOV dword ptr [EBP + -0x38],EDI
// 0x005cdcdc FILD qword ptr [EBP + -0x3c]
// 0x005cdcdf FLD float ptr [EBP + -0xc]
// 0x005cdce2 FMUL ST1
// 0x005cdce4 FADD float ptr [0x00808120]
// 0x005cdcea CALL 0x0040a2b0
// 0x005cdcef MOV dword ptr [EBP + -0x3c],EAX
// 0x005cdcf2 MOV dword ptr [EBP + -0x38],EDI
// 0x005cdcf5 FILD qword ptr [EBP + -0x3c]
// 0x005cdcf8 MOV ECX,dword ptr [EBX + 0x1c]
// 0x005cdcfb PUSH ECX
// 0x005cdcfc FST float ptr [EBP + -0x14]
// 0x005cdcff XOR ECX,ECX
// 0x005cdd01 FDIV ST0,ST1
// 0x005cdd03 FSTP float ptr [EBP + -0xc]
// 0x005cdd06 FSTP ST0
// 0x005cdd08 CALL 0x005c6fa0
// 0x005cdd0d FADD float ptr [EBP + -0x14]
// 0x005cdd10 MOV EDX,dword ptr [EBP + -0x44]
// 0x005cdd13 FSTP float ptr [EBP + -0x8]
// 0x005cdd16 FLD float ptr [EBP + -0x8]
// 0x005cdd19 MOV byte ptr [EBP + -0x1],0x1
// 0x005cdd1d FADD float ptr [EBP + -0x44]
// 0x005cdd20 MOV CL,byte ptr [ESI]
// 0x005cdd22 TEST CL,CL
// 0x005cdd24 MOV dword ptr [EBP + -0x68],EDX
// 0x005cdd27 FSTP float ptr [EBP + -0x70]
// 0x005cdd2a MOV dword ptr [EBP + -0x14],0x0
// 0x005cdd31 JZ 0x005cdebc
// 0x005cdd37 FLD float ptr [EBP + -0x14]
// 0x005cdd3a FCOMP float ptr [EBX + 0x40]
// 0x005cdd3d FNSTSW AX
// 0x005cdd3f TEST AH,0x5
// 0x005cdd42 JP 0x005cdebc
// 0x005cdd48 TEST CL,CL
// 0x005cdd4a FLD float ptr [EBP + -0x14]
// 0x005cdd4d FADD float ptr [EBX + 0x1c]
// 0x005cdd50 FADD float ptr [EBP + -0xc]
// 0x005cdd53 FSTP float ptr [EBP + -0x14]
// 0x005cdd56 JZ 0x005cdebc
// 0x005cdd5c MOV ECX,dword ptr [EBX + 0x5c]
// 0x005cdd5f LEA EAX,[EBP + -0x38]
// 0x005cdd62 PUSH EAX
// 0x005cdd63 PUSH ECX
// 0x005cdd64 PUSH EDI
// 0x005cdd65 LEA EDX,[EBP + -0x2c]
// 0x005cdd68 MOV ECX,ESI
// 0x005cdd6a CALL 0x005c2810
// 0x005cdd6f CMP EAX,0x2
// 0x005cdd72 MOV dword ptr [EBP + -0x10],0x0
// 0x005cdd79 JNZ 0x005cdd9b
// 0x005cdd7b FLD float ptr [EBP + -0x44]
// 0x005cdd7e MOV ECX,dword ptr [EBP + -0x2c]
// 0x005cdd81 MOV EAX,dword ptr [EBX + 0x9c]
// 0x005cdd87 FSUB float ptr [EBP + -0x8]
// 0x005cdd8a ADD ESI,ECX
// 0x005cdd8c INC EAX
// 0x005cdd8d FSTP float ptr [EBP + -0x44]
// 0x005cdd90 MOV dword ptr [EBX + 0x9c],EAX
// 0x005cdd96 JMP 0x005cdea0
// 0x005cdd9b MOV EAX,dword ptr [EBX + 0x5c]
// 0x005cdd9e TEST AL,0x1
// 0x005cdda0 JZ 0x005cddaa
// 0x005cdda2 MOV EDX,dword ptr [EBX + 0x34]
// 0x005cdda5 MOV dword ptr [EBP + -0x20],EDX
// 0x005cdda8 JMP 0x005cddb1
// 0x005cddaa MOV dword ptr [EBP + -0x20],0x0
// 0x005cddb1 MOV EDX,dword ptr [EBP + -0x20]
// 0x005cddb4 LEA ECX,[EBP + -0x1]
// 0x005cddb7 PUSH ECX
// 0x005cddb8 PUSH EAX
// 0x005cddb9 PUSH EDX
// 0x005cddba LEA EAX,[EBP + -0x1c]
// 0x005cddbd PUSH EAX
// 0x005cddbe MOV EAX,dword ptr [EBX + 0x3c]
// 0x005cddc1 LEA ECX,[EBP + -0x10]
// 0x005cddc4 PUSH ECX
// 0x005cddc5 MOV ECX,dword ptr [EBX + 0x1c]
// 0x005cddc8 LEA EDX,[EBP + -0x18]
// 0x005cddcb PUSH EDX
// 0x005cddcc PUSH EAX
// 0x005cddcd PUSH ECX
// 0x005cddce MOV ECX,dword ptr [EBX + 0x44]
// 0x005cddd1 MOV EDX,ESI
// 0x005cddd3 CALL 0x005c7260
// 0x005cddd8 MOV EAX,dword ptr [EBP + -0x1c]
// 0x005cdddb CMP EAX,ESI
// 0x005cdddd JZ 0x005cdebc
// 0x005cdde3 CMP EAX,EDI
// 0x005cdde5 JZ 0x005cdebc
// 0x005cddeb CMP dword ptr [EBP + -0x18],EDI
// 0x005cddee JNZ 0x005cddf9
// 0x005cddf0 CMP byte ptr [EAX],0x0
// 0x005cddf3 JZ 0x005cdebc
// 0x005cddf9 MOV EAX,dword ptr [EBX + 0x54]
// 0x005cddfc CMP EAX,0x2
// 0x005cddff JNZ 0x005cde06
// 0x005cde01 FLD float ptr [EBP + -0x10]
// 0x005cde04 JMP 0x005cde14
// 0x005cde06 CMP EAX,0x1
// 0x005cde09 JNZ 0x005cde2b
// 0x005cde0b FLD float ptr [EBP + -0x10]
// 0x005cde0e FMUL float ptr [0x007ffa24]
// 0x005cde14 PUSH ECX
// 0x005cde15 FCHS
// 0x005cde17 MOV ECX,dword ptr [EBX + 0x5c]
// 0x005cde1a FSTP float ptr [ESP]
// 0x005cde1d SHR ECX,0x7
// 0x005cde20 AND ECX,0x1
// 0x005cde23 CALL 0x005c7010
// 0x005cde28 FSTP float ptr [EBP + -0x48]
// 0x005cde2b CMP dword ptr [EBP + -0x74],EDI
// 0x005cde2e MOV dword ptr [EBP + -0x24],EDI
// 0x005cde31 JZ 0x005cde39
// 0x005cde33 MOV EDX,dword ptr [EBP + -0x48]
// 0x005cde36 MOV dword ptr [EBP + -0x6c],EDX
// 0x005cde39 LEA EAX,[EBP + -0x74]
// 0x005cde3c PUSH EAX
// 0x005cde3d LEA ECX,[EBP + -0x24]
// 0x005cde40 PUSH ECX
// 0x005cde41 MOV ECX,dword ptr [EBP + -0x18]
// 0x005cde44 LEA EDX,[EBP + -0x48]
// 0x005cde47 PUSH EDX
// 0x005cde48 LEA EAX,[EBP + -0x30]
// 0x005cde4b PUSH EAX
// 0x005cde4c PUSH ECX
// 0x005cde4d PUSH ESI
// 0x005cde4e MOV ECX,EBX
// 0x005cde50 CALL 0x005ccbe0
// 0x005cde55 MOV ECX,dword ptr [EBX + 0x9c]
// 0x005cde5b MOV EAX,dword ptr [EBP + -0x74]
// 0x005cde5e INC ECX
// 0x005cde5f CMP EAX,0x2
// 0x005cde62 MOV dword ptr [EBX + 0x9c],ECX
// 0x005cde68 JNZ 0x005cde82
// 0x005cde6a SUB ESP,0x20
// 0x005cde6d MOV EDI,ESP
// 0x005cde6f MOV ECX,0x8
// 0x005cde74 LEA ESI,[EBP + -0x70]
// 0x005cde77 MOVSD.REP ES:EDI,ESI
// 0x005cde79 MOV ECX,EBX
// 0x005cde7b CALL 0x005cd310
// 0x005cde80 XOR EDI,EDI
// 0x005cde82 FLD float ptr [EBP + -0x44]
// 0x005cde85 MOV ECX,dword ptr [EBX + 0x60]
// 0x005cde88 MOV EDX,dword ptr [EBP + -0x24]
// 0x005cde8b FSUB float ptr [EBP + -0x8]
// 0x005cde8e MOV AL,byte ptr [EBX + 0x5c]
// 0x005cde91 MOV ESI,dword ptr [EBP + -0x1c]
// 0x005cde94 OR ECX,EDX
// 0x005cde96 FSTP float ptr [EBP + -0x44]
// 0x005cde99 TEST AL,0x2
// 0x005cde9b MOV dword ptr [EBX + 0x60],ECX
// 0x005cde9e JNZ 0x005cdebc
// 0x005cdea0 FLD float ptr [EBP + -0x68]
// 0x005cdea3 MOV CL,byte ptr [ESI]
// 0x005cdea5 TEST CL,CL
// 0x005cdea7 FSUB float ptr [EBP + -0x8]
// 0x005cdeaa FSTP float ptr [EBP + -0x68]
// 0x005cdead FLD float ptr [EBP + -0x70]
// 0x005cdeb0 FSUB float ptr [EBP + -0x8]
// 0x005cdeb3 FSTP float ptr [EBP + -0x70]
// 0x005cdeb6 JNZ 0x005cdd37
// 0x005cdebc MOV ECX,EBX
// 0x005cdebe CALL 0x005cdf70
// 0x005cdec3 TEST byte ptr [EBX + 0x5c],0x20
// 0x005cdec7 JZ 0x005cdee2
// 0x005cdec9 MOV EAX,dword ptr [EBP + -0x34]
// 0x005cdecc CMP EAX,-0x1
// 0x005cdecf JNZ 0x005cded6
// 0x005cded1 CMP dword ptr [EBP + -0x28],EAX
// 0x005cded4 JZ 0x005cdee2
// 0x005cded6 MOV ECX,dword ptr [EBP + -0x28]
// 0x005cded9 PUSH ECX
// 0x005cdeda PUSH EAX
// 0x005cdedb MOV ECX,EBX
// 0x005cdedd CALL 0x005cd4d0
// 0x005cdee2 POP EDI
// 0x005cdee3 POP ESI
// 0x005cdee4 POP EBX
// 0x005cdee5 MOV ESP,EBP
// 0x005cdee7 POP EBP
// 0x005cdee8 RET