From cfc252f161690dd922522afc73026e8d8ff716a5 Mon Sep 17 00:00:00 2001 From: MarcelineVQ Date: Thu, 12 Mar 2026 11:03:23 -0700 Subject: [PATCH] Add render pipeline profiling: 5 hooks across render/movement path Hooks executeSceneRenderPass (0x708900), renderFrame (0x707680), transformMatrix4x4 (0x714260), RenderTextureQuads (0x76FB00), and CMovement::ProcessUnitMovementUpdate (0x616620). Unified stats dump every 180 render passes shows per-frame call counts, cycle costs, bone counts, recursion depth, and quad item counts. --- ...comp_CMovement_ProcessUnitMovementUpdate.c | 100 ++ .../decompiled/decomp_RenderTextureQuads.c | 66 ++ .../decomp_executeSceneRenderPass.c | 36 + .../decompiled/decomp_renderFrame.c | 880 ++++++++++++++++++ src/transform44/transform44.zig | 243 +++-- 5 files changed, 1266 insertions(+), 59 deletions(-) create mode 100644 src/transform44/decompiled/decomp_CMovement_ProcessUnitMovementUpdate.c create mode 100644 src/transform44/decompiled/decomp_RenderTextureQuads.c create mode 100644 src/transform44/decompiled/decomp_executeSceneRenderPass.c create mode 100644 src/transform44/decompiled/decomp_renderFrame.c diff --git a/src/transform44/decompiled/decomp_CMovement_ProcessUnitMovementUpdate.c b/src/transform44/decompiled/decomp_CMovement_ProcessUnitMovementUpdate.c new file mode 100644 index 0000000..c996a20 --- /dev/null +++ b/src/transform44/decompiled/decomp_CMovement_ProcessUnitMovementUpdate.c @@ -0,0 +1,100 @@ + +/* Setting prototype: void CMovement_ProcessUnitMovementUpdate(CMovement *this, ulong timeNow, ulong + lastUpdate, CGUnit *unitObj) */ + +void __thiscall +CMovement__ProcessMovementWithCollisionAndServerValidation + (void *this,ulong timeNow,ulong lastUpdate,CGUnit *unitObj) + +{ + int *piVar1; + undefined *objectManager; + undefined *puVar2; + uint uVar3; + uint stepTime; + int local_14; + uint uStack_10; + undefined *processedTime; + undefined *deltaTime; + + /* Main movement processing function - handles time-based movement updates, + collision detection, and position synchronization. Key offsets: +0x10-0x18 + (position xyz), +0x1c (facing), +0x40 (movement flags), +0x15c (unit object + reference) */ + deltaTime = (undefined *)(timeNow - lastUpdate); + processedTime = (undefined *)0x0; + objectManager = GetObjectManagerField(); + if (deltaTime < (undefined *)0xfb) { + if (deltaTime == (undefined *)0x0) goto LAB_00616789; + } + else { + lastUpdate = (ulong)(deltaTime + -0xfa + lastUpdate); + UpdatePlayerActionCounter(this,(int)(deltaTime + -0xfa)); + deltaTime = (undefined *)0xfa; + } + do { + stepTime = (int)deltaTime - (int)processedTime; + puVar2 = CheckNodeExistence((int)this + 0x150); + if ((char)puVar2 != '\0') { + uVar3 = ExtractNodeValue((int)this + 0x150); + uVar3 = *(int *)(uVar3 + 8) - lastUpdate; + if ((int)uVar3 < 0) { + uVar3 = 0; + } + if (uVar3 < stepTime) { + stepTime = uVar3; + } + } + piVar1 = *(int **)(*(int *)((int)this + 0x15c) + 8); + if (((((undefined *)*piVar1 == PTR_00c4da98) && ((undefined *)piVar1[1] == PTR_00c4da9c)) && + ((*(int *)((int)this + 0xa4) == 0 || + ((*(byte *)(*(int *)((int)this + 0xa4) + 0x18) & 4) != 0)))) && + (((*(uint *)((int)this + 0x40) & 0x200f) != 0 && + (*(int *)(objectManager + 0x130) - lastUpdate < stepTime)))) { + stepTime = *(int *)(objectManager + 0x130) - lastUpdate; + } + lastUpdate = lastUpdate + stepTime; + *(ulong *)(objectManager + 300) = lastUpdate; + if (stepTime != 0) { + if ((*(uint *)((int)this + 0x40) & 0x20ff) != 0) { + ProcessPlayerMovementWithServerValidationAndInterpolation(this,(float)lastUpdate,stepTime); + piVar1 = *(int **)((int)*(void **)((int)this + 0x15c) + 8); + if (((((undefined *)*piVar1 == PTR_00c4da98) && ((undefined *)piVar1[1] == PTR_00c4da9c)) && + ((*(uint *)((int)this + 0x40) & 0x200f) != 0)) && + (((*(int *)((int)this + 0xa4) == 0 || + ((*(byte *)(*(int *)((int)this + 0xa4) + 0x18) & 4) != 0)) && + (-1 < (int)(lastUpdate - *(int *)(objectManager + 0x130)))))) { + InitiatePlayerSpellCast + (*(void **)((int)this + 0x15c),lastUpdate,0xee,(void *)0x0,(void *)0x0); + } + } + processedTime = processedTime + stepTime; + } + puVar2 = ProcessPlayerActionQueue(this,(void *)lastUpdate); + if (puVar2 == (undefined *)0x0) { + SetAudioChannelSimple30(this,1); + break; + } + } while (processedTime < deltaTime); +LAB_00616789: + ProcessUnitPositionAndProximity(*(void **)((int)this + 0x15c),(void *)timeNow,0); + uStack_10 = (uint)((ulonglong)(double)*(float *)((int)this + 0x10) >> 0x20); + local_14 = SUB84((double)*(float *)((int)this + 0x10),0); + objectManager = IsInfiniteOrNaN(local_14,uStack_10); + if (objectManager == (undefined *)0x0) { + uStack_10 = (uint)((ulonglong)(double)*(float *)((int)this + 0x14) >> 0x20); + local_14 = SUB84((double)*(float *)((int)this + 0x14),0); + objectManager = IsInfiniteOrNaN(local_14,uStack_10); + if (objectManager == (undefined *)0x0) { + uStack_10 = (uint)((ulonglong)(double)*(float *)((int)this + 0x18) >> 0x20); + local_14 = SUB84((double)*(float *)((int)this + 0x18),0); + objectManager = IsInfiniteOrNaN(local_14,uStack_10); + if (objectManager == (undefined *)0x0) { + return; + } + } + } + RotateLogFile((byte *)s_Mover_at_invalid_position_008613fc); + return; +} + diff --git a/src/transform44/decompiled/decomp_RenderTextureQuads.c b/src/transform44/decompiled/decomp_RenderTextureQuads.c new file mode 100644 index 0000000..77cd2bc --- /dev/null +++ b/src/transform44/decompiled/decomp_RenderTextureQuads.c @@ -0,0 +1,66 @@ + +void __fastcall RenderTextureQuads(RenderBatch *param_1) + +{ + int stateValue; + int itemCount; + void *pvVar1; + int itemOffset; + undefined *local_c; + undefined *lastTexture; + char *currentTexture; + RenderItem *renderItems; + + itemCount = param_1->item_count; + if (itemCount != 0) { + lastTexture = (undefined *)0xffffffff; + BeginRender(); + SetRenderState(0xe,0); + SetRenderState(0xf,0); + SetRenderState(0x10,0); + SetRenderState(0x12,0); + if (itemCount != 0) { + itemOffset = 0; + do { + renderItems = param_1->items_ptr; + currentTexture = *(char **)((int)&renderItems->texture + itemOffset); + if (currentTexture != lastTexture) { + SetTexture(0x17,currentTexture); + lastTexture = currentTexture; + } + if (((*(int *)((int)&renderItems->additionalData + itemOffset) != 0) && + (stateValue = 2, *(int *)((int)&renderItems->renderState + itemOffset) < 2)) || + (stateValue = *(int *)((int)&renderItems->renderState + itemOffset), stateValue != 0xb)) + { + SetRenderState(7,stateValue); + } + SetTexture(0x3f,*(char **)((int)&renderItems->secondaryTexture + itemOffset)); + InitializeRenderingPipeline + (4,*(undefined ***)((int)&renderItems->vertices + itemOffset),0xc, + (undefined **)&g_defaultTexCoord_U1,0, + *(undefined ***)((int)&renderItems->additionalData + itemOffset), + *(int *)((int)&renderItems->dataStride + itemOffset),(undefined *)0x0, + (undefined *)0x0,*(undefined ***)((int)&renderItems->textureCoords + itemOffset), + 8,(undefined **)0x0,0); + RenderVertexBuffer((undefined *)0x4,4,&g_quadVertexIndices); + EmptyRenderFunction(); + itemOffset = itemOffset + 0x1c; + itemCount = itemCount + -1; + } while (itemCount != 0); + } + EndRender(); + } + if (param_1->text_data != (void *)0x0) { + DrawString((int)param_1->text_data); + } + pvVar1 = param_1->callback_list; + if ((((uint)pvVar1 & 1) != 0) || (pvVar1 == (void *)0x0)) { + pvVar1 = (void *)0x0; + } + for (; (((uint)pvVar1 & 1) == 0 && (pvVar1 != (void *)0x0)); pvVar1 = *(void **)((int)pvVar1 + 4)) + { + (**(code **)((int)pvVar1 + 8))(); + } + return; +} + diff --git a/src/transform44/decompiled/decomp_executeSceneRenderPass.c b/src/transform44/decompiled/decomp_executeSceneRenderPass.c new file mode 100644 index 0000000..98e807b --- /dev/null +++ b/src/transform44/decompiled/decomp_executeSceneRenderPass.c @@ -0,0 +1,36 @@ + +undefined * CM2Scene_ExecuteRenderPass(int renderPassIndex) + +{ + int renderer; + int iVar1; + undefined1 unaff_BP; + undefined4 *puVar2; + undefined1 renderContext [352]; + undefined callbackBuffer [12768]; + undefined4 uStackY_20; + + StackProbe(unaff_BP); + if (*(int *)(renderer + 0x148) != *(int *)(*(int *)(renderer + 4) + 8)) { + puVar2 = (undefined4 *)(renderer + 0x14c); + for (iVar1 = 0x708; iVar1 != 0; iVar1 = iVar1 + -1) { + *puVar2 = 0xffffffff; + puVar2 = puVar2 + 1; + } + *(undefined4 *)(renderer + 0x148) = *(undefined4 *)(*(int *)(renderer + 4) + 8); + } + initializeRenderContext(renderContext,renderer); + uStackY_20 = 0x70896e; + CM2SceneRenderDraw(renderContext,(undefined *)renderPassIndex,*(int *)(renderer + 0x34), + *(int *)(renderPassIndex * 0x10 + 0x54 + renderer), + *(uint *)((renderPassIndex + 5) * 0x10 + renderer)); + if (renderPassIndex == 0) { + uStackY_20 = 0x70898a; + CM2SceneRenderDraw(renderContext,(undefined *)0x0,*(int *)(renderer + 0x34), + *(int *)(renderer + 0x44),*(uint *)(renderer + 0x40)); + } + uStackY_20 = 0x70899f; + CallbackIteratorDuplicate(callbackBuffer,&DAT_00000010,4,&DAT_0070dbf0); + return (undefined *)0x1; +} + diff --git a/src/transform44/decompiled/decomp_renderFrame.c b/src/transform44/decompiled/decomp_renderFrame.c new file mode 100644 index 0000000..b16daed --- /dev/null +++ b/src/transform44/decompiled/decomp_renderFrame.c @@ -0,0 +1,880 @@ + +/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */ + +undefined * __thiscall renderFrame(void *this,float *cameraPosition) + +{ + Matrix4x4 *pMVar1; + float fVar2; + short sVar3; + ushort uVar4; + SceneObject *pSVar5; + void *pvVar6; + uint uVar7; + undefined *puVar8; + float *pfVar9; + uint uVar10; + undefined4 *puVar11; + undefined *puVar12; + uint uVar13; + int iVar14; + undefined *puVar15; + uint uVar16; + int *piVar17; + undefined4 *puVar18; + uint *puVar19; + byte *pbVar20; + int iVar21; + undefined4 *puVar22; + undefined **ppuVar23; + float transformedPosition [3]; + float worldPosition [3]; + undefined *local_74; + undefined *local_70; + undefined *local_6c; + undefined *local_64; + undefined *local_60; + undefined *local_5c; + undefined *local_58; + undefined *local_54; + undefined *local_50; + undefined *local_4c; + undefined *local_48; + undefined *local_44; + undefined *local_40; + undefined *local_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; + undefined *local_8; + + *(int *)((int)this + 0x10) = *(int *)((int)this + 0x10) + 1; + if (*(int *)((int)this + 0x148) != *(int *)(*(int *)((int)this + 4) + 8)) { + puVar11 = (undefined4 *)((int)this + 0x14c); + for (iVar14 = 0x708; iVar14 != 0; iVar14 = iVar14 + -1) { + *puVar11 = 0xffffffff; + puVar11 = puVar11 + 1; + } + *(undefined4 *)((int)this + 0x148) = *(undefined4 *)(*(int *)((int)this + 4) + 8); + } + pMVar1 = (Matrix4x4 *)((int)this + 0x9c); + local_8 = (undefined *)this; + SetTransformMatrix((undefined **)pMVar1); + *(float *)((int)this + 0xcc) = + *(float *)((int)this + 0xcc) - + (pMVar1->m00 * *cameraPosition + + *(float *)((int)this + 0xbc) * cameraPosition[2] + + *(float *)((int)this + 0xac) * cameraPosition[1]); + *(float *)((int)this + 0xd0) = + *(float *)((int)this + 0xd0) - + (*(float *)((int)this + 0xc0) * cameraPosition[2] + + *(float *)((int)this + 0xa0) * *cameraPosition + + *(float *)((int)this + 0xb0) * cameraPosition[1]); + *(float *)((int)this + 0xd4) = + *(float *)((int)this + 0xd4) - + (*(float *)((int)this + 0xc4) * cameraPosition[2] + + *(float *)((int)this + 0xa4) * *cameraPosition + + *(float *)((int)this + 0xb4) * cameraPosition[1]); + initPixelShaderDispatcher4(); + if ((*(byte *)((int)*(void **)((int)this + 4) + 4) & 4) == 0) { + for (pSVar5 = *(SceneObject **)((int)this + 0x20); pSVar5 != (SceneObject *)0x0; + pSVar5 = (SceneObject *)pSVar5->data_offset_base) { + if (*(int *)&pSVar5->field_0x1cc == 0) { + local_4c = (undefined *)0x0; + local_48 = (undefined *)0x0; + local_44 = (undefined *)0x0; + local_58 = (undefined *)0x3f800000; + local_54 = (undefined *)0x3f800000; + local_50 = (undefined *)0x3f800000; + transformMatrix4x4(pSVar5,pMVar1,(Matrix4x4 *)&local_58,(Matrix4x4 *)&local_4c, + (Matrix4x4 *)0x3f800000); + } + } + } + else { + setSessionCallbacks(*(void **)((int)this + 4),processLinkedObjectList,(undefined *)this); + for (pSVar5 = *(SceneObject **)((int)this + 0x20); pSVar5 != (SceneObject *)0x0; + pSVar5 = *(SceneObject **)(pSVar5->data_offset_base + 0x48)) { + if (*(int *)&pSVar5->field_0x1cc == 0) { + local_58 = (undefined *)0x0; + local_54 = (undefined *)0x0; + local_50 = (undefined *)0x0; + local_4c = (undefined *)0x3f800000; + local_48 = (undefined *)0x3f800000; + local_44 = (undefined *)0x3f800000; + transformMatrix4x4(pSVar5,pMVar1,(Matrix4x4 *)&local_4c,(Matrix4x4 *)&local_58, + (Matrix4x4 *)0x3f800000); + } + if (pSVar5->data_offset_base == 0) break; + } + loadSessionKey(*(int *)((int)this + 4)); + } + for (pSVar5 = *(SceneObject **)((int)this + 0x20); pSVar5 != (SceneObject *)0x0; + pSVar5 = (SceneObject *)pSVar5->data_offset_base) { + renderSceneNode(pSVar5); + } + pvVar6 = *(void **)((int)this + 0x20); + while (pvVar6 != (void *)0x0) { + *(undefined4 *)((int)this + 0x20) = *(undefined4 *)((int)pvVar6 + 0x48); + *(undefined4 *)((int)pvVar6 + 0x44) = 0; + *(undefined4 *)((int)pvVar6 + 0x48) = 0; + setupSceneRenderState(pvVar6); + pvVar6 = *(void **)((int)this + 0x20); + } + *(undefined4 *)((int)this + 0x40) = 0; + puVar11 = (undefined4 *)((int)this + 0x50); + iVar14 = 3; + do { + *puVar11 = 0; + puVar11 = puVar11 + 4; + iVar14 = iVar14 + -1; + } while (iVar14 != 0); + *(undefined4 *)((int)this + 0x30) = 0; + puVar12 = *(undefined **)((int)this + 0x24); + cameraPosition = (float *)0x0; + local_64 = (undefined *)0x0; + local_18 = puVar12; + if (puVar12 != (undefined *)0x0) { + do { + *(undefined4 *)(local_8 + 0x24) = *(undefined4 *)(puVar12 + 0x58); + *(undefined4 *)(puVar12 + 0x54) = 0; + *(undefined4 *)(puVar12 + 0x58) = 0; + *(undefined4 *)(puVar12 + 0x50) = 0; + local_18 = puVar12; + puVar8 = PrepareModelForRender(puVar12,0,0); + if (puVar8 != (undefined *)0x0) { + puVar8 = *(undefined **)(*(int *)(puVar12 + 0x30) + 0x130); + local_5c = *(undefined **)(puVar12 + 0x3b8); + local_64 = local_64 + 1; + local_10 = puVar8; + if (*(int *)(local_5c + 0x19c) == 0) { + local_28 = (undefined *)0x1; +LAB_00707a17: + local_24 = (undefined *)0x0; + } + else { + fVar2 = *(float *)(puVar12 + 0xfc); + local_38 = (undefined *) + (*(float *)(puVar12 + 0x104) * *(float *)(puVar12 + 0x104) + + *(float *)(puVar12 + 0x100) * *(float *)(puVar12 + 0x100) + fVar2 * fVar2); + SetVector3((Vector3 *)&local_4c,*(float *)(puVar8 + 0xc0) + *(float *)(puVar8 + 0xb4), + *(float *)(puVar8 + 0xc4) + *(float *)(puVar8 + 0xb8), + *(float *)(puVar8 + 200) + *(float *)(puVar8 + 0xbc)); + ScaleVector3D((float *)&local_58,(float *)&local_4c,0.5); + local_c = (undefined *)(SQRT((float)local_38) * *(float *)(puVar8 + 0xcc)); + pfVar9 = (float *)transformVector3ByMatrix4x4 + (transformedPosition,(float *)&local_58, + (float *)(puVar12 + 0xfc)); + local_74 = (undefined *)*pfVar9; + local_70 = (undefined *)pfVar9[1]; + local_6c = (undefined *)pfVar9[2]; + fVar2 = (float)local_74 * *(float *)(local_5c + 0x1a0) + + (float)local_70 * *(float *)(local_5c + 0x1a4) + + (float)local_6c * *(float *)(local_5c + 0x1a8) + *(float *)(local_5c + 0x1ac); + if ((*(byte *)(*(int *)(local_8 + 4) + 4) & 2) == 0) { + local_28 = (undefined *)(uint)((float)COLLISION_PLANE_ZERO_THRESHOLD <= fVar2); + local_24 = (undefined *)(uint)(local_28 == (undefined *)0x0); + } + else { + local_28 = (undefined *)(uint)(-(float)local_c <= fVar2); + if (fVar2 < (float)local_c == (fVar2 == (float)local_c)) goto LAB_00707a17; + local_24 = (undefined *)0x1; + } + } + local_1c = (undefined *)(uint)(*(int *)(puVar12 + 0x404) != 0); + local_34 = *(undefined **)(*(int *)(puVar12 + 0x30) + 0x138); + if ((((*(byte *)(*(int *)(local_8 + 4) + 4) & 1) == 0) || ((puVar12[4] & 1) != 0)) || + (local_c = (undefined *)0x1, *(int *)(puVar12 + 0x1c4) == 0)) { + local_c = (undefined *)0x0; + } + if (local_1c == (undefined *)0x0) { + local_38 = *(undefined **)(local_34 + 0x20); + } + else { + local_38 = *(undefined **)(puVar12 + 0x3f0); + } + local_2c = (undefined *)0x0; + if (local_38 != (undefined *)0x0) { + do { + puVar15 = local_8; + pfVar9 = cameraPosition; + if (local_1c == (undefined *)0x0) { + uVar16 = (uint)*(ushort *)(*(int *)(local_34 + 0x24) + 4 + (int)local_2c * 0x18); + pbVar20 = (byte *)(*(int *)(local_34 + 0x24) + (int)local_2c * 0x18); + local_20 = (undefined *)(uVar16 * 0x20 + *(int *)(*(int *)(puVar12 + 0x30) + 0x158)); + if (*(int *)(*(int *)(puVar12 + 0x98) + uVar16 * 4) != 0) goto LAB_00707aea; + } + else { + pbVar20 = (byte *)(*(int *)(puVar12 + 0x3ec) + (int)local_2c * 0x18); + local_20 = (undefined *) + ((uint)*(ushort *)(pbVar20 + 4) * 0x20 + *(int *)(puVar12 + 0x3f4)); +LAB_00707aea: + local_30 = *(undefined **)(puVar12 + 0x19c); + if ((uint)*(ushort *)(pbVar20 + 8) < *(uint *)(puVar8 + 0x54)) { + local_30 = (undefined *) + ((float)local_30 * + *(float *)((uint)*(ushort *)(pbVar20 + 8) * 0x50 + 0x3c + + *(int *)(puVar12 + 0xa0))); + } + if (*(short *)(pbVar20 + 0xe) != 0) { + local_30 = (undefined *) + ((float)local_30 * + *(float *)((uint)*(ushort *) + (*(int *)(puVar8 + 0xa8) + + (uint)*(ushort *)(pbVar20 + 0x14) * 2) * 0x20 + 0xc + + *(int *)(puVar12 + 0xa8))); + } + if (((float)COLLISION_PLANE_ZERO_THRESHOLD <= (float)local_30) && + ((float)local_30 != (float)COLLISION_PLANE_ZERO_THRESHOLD)) { + local_60 = (undefined *) + (*(int *)(puVar8 + 0x88) + (uint)*(ushort *)(pbVar20 + 10) * 4); + if (((*pbVar20 & 4) == 0) || + (local_40 = (undefined *)0x1, *(int *)(local_8 + 0x11c) == 0)) { + local_40 = (undefined *)0x0; + } + if ((1 < *(ushort *)(local_60 + 2)) || + (local_3c = (undefined *)0x0, (float)local_30 < _DAT_00808120)) { + local_3c = (undefined *)0x1; + } + puVar12 = local_8 + 0x2c; + uVar16 = *(int *)(local_8 + 0x30) + 1; + if (*(uint *)(local_8 + 0x2c) < uVar16) { + uVar10 = *(uint *)(local_8 + 0x38); + if (uVar10 == 0) { + uVar10 = CalculateOptimalBlockSize(puVar12,uVar16); + } + uVar16 = AlignToBlockSize(uVar16,uVar10); + SetArrayCapacity(puVar12,uVar16); + } + puVar12 = local_8; + piVar17 = (int *)(*(int *)(puVar15 + 0x30) * 0x40 + *(int *)(puVar15 + 0x34)); + *(int *)(puVar15 + 0x30) = *(int *)(puVar15 + 0x30) + 1; + if (piVar17 == (int *)0x0) { + return (undefined *)0x0; + } + iVar14 = 0; + if (local_40 == (undefined *)0x0) { + if ((((local_3c == (undefined *)0x0) && (*(int *)(local_18 + 0xb8) != 0)) && + ((*pbVar20 & 0x10) != 0)) && + ((*(int *)(*(int *)(local_18 + 0x3b8) + 0x180) == 0 && + ((*(byte *)(*(int *)(local_8 + 4) + 4) & 0x20) != 0)))) { + *piVar17 = 2; + } + else { + *piVar17 = 0; + } + } + else { + *piVar17 = 1; + } + piVar17[1] = (int)local_18; + piVar17[2] = 0; + if (((local_3c == (undefined *)0x1) && (local_28 != (undefined *)0x0)) && + ((local_24 != (undefined *)0x0 && (local_40 == (undefined *)0x0)))) { + iVar14 = 1; + } + piVar17[3] = iVar14; + piVar17[4] = (int)local_30; + piVar17[7] = (int)local_2c; + sVar3 = *(short *)(pbVar20 + 2); + piVar17[0xc] = (int)local_20; + piVar17[10] = (int)sVar3; + piVar17[0xb] = (int)pbVar20; + piVar17[0xd] = (int)local_1c; + piVar17[0xe] = -1; + piVar17[0xf] = -1; + if (((*(byte *)(*(int *)(local_8 + 4) + 4) & 8) != 0) && + (local_40 == (undefined *)0x0)) { + if (local_1c == (undefined *)0x0) { + iVar14 = *(int *)((int)local_2c * 4 + + *(int *)(*(int *)(local_18 + 0x30) + 0x150)); + } + else { + iVar14 = *(int *)((int)local_2c * 4 + *(int *)(local_18 + 0x408)); + } + iVar21 = (int)local_2c * 4; + piVar17[0xe] = iVar14; + if ((*local_60 & 1) == 0) { + piVar17[0xe] = *(int *)(*(int *)(local_18 + 0x3b8) + 0x180) + iVar14; + } + uVar16 = calculateDepthValue(local_8,piVar17[0xe]); + piVar17[0xe] = uVar16; + if ((*(byte *)(*(int *)(puVar12 + 4) + 4) & 0x10) != 0) { + if (*(int *)(*(int *)(local_18 + 0x3b8) + 0x1b0) == 0) { + piVar17[0xe] = uVar16 - 900; + } + else if (local_1c == (undefined *)0x0) { + piVar17[0xf] = *(int *)(iVar21 + *(int *)(*(int *)(local_18 + 0x30) + 0x154)); + } + else { + piVar17[0xf] = *(int *)(iVar21 + *(int *)(local_18 + 0x40c)); + } + } + } + puVar8 = local_18; + puVar12 = local_3c; + if ((int)local_3c < 1) { + piVar17[6] = *(int *)(local_18 + 0x84); + } + else { + pfVar9 = (float *)transformVector3ByMatrix4x4 + (worldPosition,(float *)(local_20 + 0x14), + (float *)((uint)*(ushort *)(local_20 + 0x12) * 0x40 + + *(int *)(local_18 + 0x94))); + piVar17[6] = (int)(pfVar9[2] * pfVar9[2] + + pfVar9[1] * pfVar9[1] + *pfVar9 * *pfVar9); + } + puVar15 = local_8; + if ((local_c == (undefined *)0x0) || (local_40 != (undefined *)0x0)) { + piVar17[5] = piVar17[6]; + } + else { + piVar17[5] = *(int *)(puVar8 + 0x84); + } + if (*piVar17 == 2) { + puVar19 = (uint *)(local_8 + 0x3c); + uVar16 = *(int *)(local_8 + 0x40) + 1; + if (*(uint *)(local_8 + 0x3c) < uVar16) { + uVar10 = *(uint *)(local_8 + 0x48); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *)(*(int *)(puVar15 + 0x44) + *(int *)(puVar15 + 0x40) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + goto LAB_00707f7a; + } + if (puVar12 == (undefined *)0x1) { + if (local_40 != (undefined *)0x0) { + if (*(int *)(local_5c + 0x19c) == 0) { + puVar19 = (uint *)(local_8 + 0x5c); + goto LAB_00707f3e; + } + puVar19 = (uint *)(local_8 + 0x6c); + uVar16 = *(int *)(local_8 + 0x70) + 1; + if (*(uint *)(local_8 + 0x6c) < uVar16) { + uVar10 = *(uint *)(local_8 + 0x78); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *) + (*(int *)(puVar15 + 0x74) + *(int *)(puVar15 + 0x70) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + goto LAB_00707f7a; + } + if (local_28 != (undefined *)0x0) { + puVar19 = (uint *)(local_8 + 0x5c); + uVar16 = *(int *)(local_8 + 0x60) + 1; + if (*puVar19 < uVar16) { + uVar10 = *(uint *)(local_8 + 0x68); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *)(*(int *)(puVar15 + 100) + *(int *)(puVar15 + 0x60) * 4) + ; + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + *(int *)(puVar15 + 0x60) = *(int *)(puVar15 + 0x60) + 1; + } + puVar12 = local_8; + if (local_24 != (undefined *)0x0) { + puVar19 = (uint *)(local_8 + 0x6c); + uVar16 = *(int *)(local_8 + 0x70) + 1; + if (*(uint *)(local_8 + 0x6c) < uVar16) { + uVar10 = *(uint *)(local_8 + 0x78); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *) + (*(int *)(puVar12 + 0x74) + *(int *)(puVar12 + 0x70) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + goto LAB_00707f7a; + } + } + else { + puVar19 = (uint *)(local_8 + (int)local_3c * 0x10 + 0x4c); +LAB_00707f3e: + uVar16 = puVar19[1] + 1; + if (*puVar19 < uVar16) { + uVar10 = puVar19[3]; + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *)(puVar19[2] + puVar19[1] * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } +LAB_00707f7a: + puVar19[1] = puVar19[1] + 1; + } + puVar12 = local_8; + pfVar9 = (float *)((int)cameraPosition + 1); + if ((((local_c != (undefined *)0x0) && (local_40 == (undefined *)0x0)) && + (0 < (int)local_3c)) && ((*local_60 & 0x10) == 0)) { + piVar17[6] = 0x7f7fffff; + ResizeDynamicArray(local_8 + 0x2c,1,1); + puVar11 = (undefined4 *) + (*(int *)(puVar12 + 0x30) * 0x40 + *(int *)(puVar12 + 0x34)); + *(int *)(puVar12 + 0x30) = *(int *)(puVar12 + 0x30) + 1; + if (puVar11 == (undefined4 *)0x0) { + return (undefined *)0x0; + } + puVar18 = (undefined4 *)((int)pfVar9 * 0x40 + -0x40 + *(int *)(puVar12 + 0x34)); + puVar22 = puVar11; + for (iVar14 = 0x10; iVar14 != 0; iVar14 = iVar14 + -1) { + *puVar22 = *puVar18; + puVar18 = puVar18 + 1; + puVar22 = puVar22 + 1; + } + puVar11[2] = 1; + if (local_28 != (undefined *)0x0) { + uVar16 = *(int *)(puVar12 + 0x60) + 1; + if (*(uint *)(puVar12 + 0x5c) < uVar16) { + uVar10 = *(uint *)(puVar12 + 0x68); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar12 + 0x5c,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar12 + 0x5c,uVar16); + } + piVar17 = (int *)(*(int *)(puVar12 + 100) + *(int *)(puVar12 + 0x60) * 4); + if (piVar17 != (int *)0x0) { + *piVar17 = (int)pfVar9; + } + *(int *)(puVar12 + 0x60) = *(int *)(puVar12 + 0x60) + 1; + } + if (local_24 != (undefined *)0x0) { + expandTimerArray(puVar12 + 0x6c,1,1); + piVar17 = (int *)(*(int *)(puVar12 + 0x74) + *(int *)(puVar12 + 0x70) * 4); + if (piVar17 != (int *)0x0) { + *piVar17 = (int)pfVar9; + } + *(int *)(puVar12 + 0x70) = *(int *)(puVar12 + 0x70) + 1; + } + pfVar9 = (float *)((int)cameraPosition + 2); + } + } + } + cameraPosition = pfVar9; + local_2c = local_2c + 1; + puVar8 = local_10; + puVar12 = local_18; + } while (local_2c < local_38); + } + local_c = (undefined *)0x0; + if (*(int *)(puVar8 + 0x134) != 0) { + local_1c = (undefined *)0x0; + local_20 = (undefined *)0x0; + do { + puVar15 = local_8; + if (local_1c[*(int *)(puVar12 + 0x3c8) + 0xbc] != '\0') { + local_14 = *(undefined **)(puVar12 + 0x19c); + if (*(int *)(local_20 + *(int *)(puVar8 + 0x138) + 0x4c) != 0) { + local_14 = (undefined *) + ((float)local_14 * + *(float *)(local_1c + *(int *)(puVar12 + 0x3c8) + 0x3c)); + } + if ((float)local_14 < (float)COLLISION_PLANE_ZERO_THRESHOLD) { + local_14 = (undefined *)0x0; + } + uVar4 = **(ushort **)(local_20 + *(int *)(puVar8 + 0x138) + 0x20); + iVar14 = *(int *)(local_10 + 0x88); + puVar12 = local_8 + 0x2c; + uVar16 = *(int *)(local_8 + 0x30) + 1; + if (*(uint *)(local_8 + 0x2c) < uVar16) { + uVar10 = *(uint *)(local_8 + 0x38); + if (uVar10 == 0) { + uVar10 = CalculateOptimalBlockSize(puVar12,uVar16); + } + uVar16 = AlignToBlockSize(uVar16,uVar10); + SetArrayCapacity(puVar12,uVar16); + } + puVar12 = local_8; + puVar11 = (undefined4 *)(*(int *)(puVar15 + 0x30) * 0x40 + *(int *)(puVar15 + 0x34)); + *(int *)(puVar15 + 0x30) = *(int *)(puVar15 + 0x30) + 1; + if (puVar11 != (undefined4 *)0x0) { + puVar11[4] = local_14; + *puVar11 = 3; + puVar11[1] = local_18; + puVar11[2] = 0; + puVar11[3] = 0; + puVar11[7] = local_c; + puVar11[10] = 0; + puVar11[5] = *(undefined4 *)(local_18 + 0x84); + puVar11[6] = *(undefined4 *)(local_18 + 0x84); + if ((1 < *(ushort *)(iVar14 + (uint)uVar4 * 4 + 2)) || + ((float)local_14 < _DAT_00808120)) { + if (local_28 == (undefined *)0x0) { + puVar19 = (uint *)(local_8 + 0x6c); + expandTimerArray(puVar19,1,1); + puVar11 = (undefined4 *) + (*(int *)(puVar12 + 0x74) + *(int *)(puVar12 + 0x70) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + } + else { + puVar19 = (uint *)(local_8 + 0x5c); + uVar16 = *(int *)(local_8 + 0x60) + 1; + if (*puVar19 < uVar16) { + uVar10 = *(uint *)(local_8 + 0x68); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *)(*(int *)(puVar12 + 100) + *(int *)(puVar12 + 0x60) * 4) + ; + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + } + } + else { + puVar19 = (uint *)(local_8 + 0x4c); + uVar16 = *(int *)(local_8 + 0x50) + 1; + if (*puVar19 < uVar16) { + uVar10 = *(uint *)(local_8 + 0x58); + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *)(*(int *)(puVar12 + 0x54) + *(int *)(puVar12 + 0x50) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + } + puVar19[1] = puVar19[1] + 1; + cameraPosition = (float *)((int)cameraPosition + 1); + } + } + local_20 = local_20 + 0xdc; + local_c = local_c + 1; + local_1c = local_1c + 0xd0; + puVar8 = local_10; + puVar12 = local_18; + } while (local_c < *(undefined **)(local_10 + 0x134)); + } + puVar8 = local_8; + if (*(int *)(puVar12 + 0x1b8) != 0) { + puVar19 = (uint *)(local_8 + 0x2c); + uVar16 = *(int *)(local_8 + 0x30) + 1; + if (*puVar19 < uVar16) { + uVar10 = *(uint *)(local_8 + 0x38); + if (uVar10 == 0) { + uVar10 = CalculateOptimalBlockSize(puVar19,uVar16); + } + uVar16 = AlignToBlockSize(uVar16,uVar10); + SetArrayCapacity(puVar19,uVar16); + } + puVar15 = local_8; + puVar11 = (undefined4 *)(*(int *)(puVar8 + 0x30) * 0x40 + *(int *)(puVar8 + 0x34)); + *(int *)(puVar8 + 0x30) = *(int *)(puVar8 + 0x30) + 1; + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = 5; + puVar11[1] = puVar12; + puVar11[2] = 0; + puVar11[3] = 0; + puVar11[4] = 0x3f800000; + puVar11[7] = 0; + puVar11[10] = 0; + puVar11[5] = *(undefined4 *)(puVar12 + 0x84); + puVar11[6] = *(undefined4 *)(puVar12 + 0x84); + if (*(int *)(puVar12 + 0x1c0) == 0) { + if (local_28 == (undefined *)0x0) { + puVar12 = local_8 + 0x6c; + goto LAB_0070835d; + } + puVar12 = local_8 + 0x5c; + expandTimerArray(puVar12,1,1); + puVar11 = (undefined4 *)(*(int *)(puVar15 + 100) + *(int *)(puVar15 + 0x60) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + } + else { + puVar12 = local_8 + 0x4c; +LAB_0070835d: + expandTimerArray(puVar12,1,1); + puVar11 = (undefined4 *)(*(int *)(puVar12 + 8) + *(int *)(puVar12 + 4) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + } + *(int *)(puVar12 + 4) = *(int *)(puVar12 + 4) + 1; + cameraPosition = (float *)((int)cameraPosition + 1); + } + } + } + puVar12 = *(undefined **)(local_8 + 0x24); + } while (puVar12 != (undefined *)0x0); + local_18 = (undefined *)0x0; + this = local_8; + } + local_14 = *(undefined **)((int)this + 0x28); + puVar12 = local_8; + do { + local_8 = puVar12; + if (local_14 == (undefined *)0x0) { + puVar8 = *(undefined **)(puVar12 + 0x40); + local_14 = (undefined *)0x0; + if ((undefined *)0x1 < puVar8) { + ppuVar23 = &PTR_00cefff8; + for (iVar14 = 0xfb; iVar14 != 0; iVar14 = iVar14 + -1) { + *ppuVar23 = (undefined *)0xffffffff; + ppuVar23 = ppuVar23 + 1; + } + local_c = (undefined *)0x0; + if (puVar8 != (undefined *)0x0) { + do { + local_38 = *(undefined **)(*(int *)(puVar12 + 0x44) + (int)local_c * 4); + iVar14 = (int)local_38 * 0x40; + uVar16 = calculateRenderStateHash(*(int *)(puVar12 + 0x34) + iVar14); + puVar15 = (undefined *)(uVar16 % 0xfb); + local_34 = puVar15; + do { + puVar15 = puVar15 + 1; + if ((undefined *)0xfa < puVar15) { + puVar15 = (undefined *)0x0; + } + if (((&PTR_00cefff8)[(int)puVar15] == (undefined *)0xffffffff) || + (puVar15 == local_34)) { + (&PTR_00cefff8)[(int)puVar15] = local_38; + break; + } + uVar16 = compareRenderStates((int)local_38,(int)(&PTR_00cefff8)[(int)puVar15], + (int)puVar12); + } while (uVar16 != 0); + *(undefined **)(*(int *)(puVar12 + 0x34) + 0x24 + iVar14) = + (&PTR_00cefff8)[(int)puVar15]; + local_c = local_c + 1; + } while (local_c < puVar8); + } + } + heapSort(compareRenderPriority,*(void ***)(puVar12 + 0x44),(uint)puVar8,puVar12); + uVar16 = 0; + local_10 = (undefined *)0x0; + if (puVar8 != (undefined *)0x0) { + do { + local_c = *(undefined **)(*(int *)(puVar12 + 0x44) + (int)local_10 * 4); + *(undefined **)(*(int *)(puVar12 + 0x44) + uVar16 * 4) = local_c; + puVar15 = local_10; + while( true ) { + puVar15 = puVar15 + 1; + uVar10 = uVar16 + 1; + if ((puVar8 <= puVar15) || + (uVar13 = compareRenderPriority + ((int)local_c, + *(int *)(*(int *)(puVar12 + 0x44) + (int)puVar15 * 4), + (int)puVar12), uVar13 != 0)) break; + *(undefined4 *)(uVar10 * 4 + *(int *)(puVar12 + 0x44)) = + *(undefined4 *)(*(int *)(puVar12 + 0x44) + (int)puVar15 * 4); + uVar16 = uVar10; + } + if ((uint)((int)puVar15 - (int)local_10) < 2) { + *(undefined4 *)((int)local_c * 0x40 + *(int *)(puVar12 + 0x34)) = 0; + uVar10 = *(uint *)(puVar12 + 0x50) + 1; + if (*(uint *)(puVar12 + 0x4c) < uVar10) { + uVar13 = *(uint *)(puVar12 + 0x58); + if (uVar13 == 0) { + if (uVar10 < 0x40) { + uVar13 = uVar10; + for (uVar7 = *(uint *)(puVar12 + 0x50) & uVar10; uVar7 != 0; + uVar7 = uVar7 - 1 & uVar7) { + uVar13 = uVar7; + } + if (uVar13 == 0) { + uVar13 = 1; + } + } + else { + *(undefined4 *)(puVar12 + 0x58) = 0x40; + uVar13 = 0x40; + } + } + uVar10 = alignToMultiple(uVar10,uVar13); + resizePointerArray(puVar12 + 0x4c,uVar10); + } + puVar11 = (undefined4 *)(*(int *)(puVar12 + 0x54) + *(int *)(puVar12 + 0x50) * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = local_c; + } + *(int *)(puVar12 + 0x50) = *(int *)(puVar12 + 0x50) + 1; + } + else { + *(int *)((int)local_c * 0x40 + 0x20 + *(int *)(puVar12 + 0x34)) = + (int)puVar15 - (int)local_10; + local_10 = puVar15 + -1; + uVar16 = uVar10; + } + local_10 = local_10 + 1; + } while (local_10 < puVar8); + } + if ((*(uint *)(puVar12 + 0x40) < uVar16) && (*(uint *)(puVar12 + 0x3c) < uVar16)) { + uVar10 = *(uint *)(puVar12 + 0x48); + if (uVar10 == 0) { + if (uVar16 < 0x40) { + uVar13 = uVar16 - 1 & uVar16; + uVar10 = uVar16; + while (uVar7 = uVar13, uVar7 != 0) { + uVar10 = uVar7; + uVar13 = uVar7 - 1 & uVar7; + } + if (uVar10 == 0) { + uVar10 = 1; + } + } + else { + *(undefined4 *)(puVar12 + 0x48) = 0x40; + uVar10 = 0x40; + } + } + uVar10 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar12 + 0x3c,uVar10); + } + *(uint *)(puVar12 + 0x40) = uVar16; + heapSort(compareRenderItems,*(void ***)(puVar12 + 0x54),*(uint *)(puVar12 + 0x50),puVar12); + heapSort(compareRenderItemsExtended,*(void ***)(puVar12 + 100),*(uint *)(puVar12 + 0x60), + puVar12); + heapSort(compareRenderItemsExtended,*(void ***)(puVar12 + 0x74),*(uint *)(puVar12 + 0x70), + puVar12); + return (undefined *)0x1; + } + *(undefined4 *)(puVar12 + 0x28) = *(undefined4 *)(local_14 + 0x3e0); + *(undefined4 *)(local_14 + 0x3dc) = 0; + *(undefined4 *)(local_14 + 0x3e0) = 0; + puVar12 = PrepareModelForRender(local_14,0,0); + if (puVar12 != (undefined *)0x0) { + iVar14 = *(int *)(local_14 + 0x3b8); + puVar12 = *(undefined **)(*(int *)(local_14 + 0x30) + 0x130); + local_24 = puVar12; + if (*(int *)(iVar14 + 0x19c) == 0) { +LAB_007084dd: + local_c = (undefined *)0x1; + } + else { + pfVar9 = (float *)(local_14 + 0xfc); + fVar2 = *pfVar9; + local_34 = (undefined *) + (*(float *)(local_14 + 0x104) * *(float *)(local_14 + 0x104) + + *(float *)(local_14 + 0x100) * *(float *)(local_14 + 0x100) + fVar2 * fVar2); + SetVector3((Vector3 *)&local_4c,*(float *)(puVar12 + 0xc0) + *(float *)(puVar12 + 0xb4), + *(float *)(puVar12 + 0xc4) + *(float *)(puVar12 + 0xb8), + *(float *)(puVar12 + 200) + *(float *)(puVar12 + 0xbc)); + ScaleVector3D((float *)&local_58,(float *)&local_4c,0.5); + local_34 = (undefined *)(SQRT((float)local_34) * *(float *)(puVar12 + 0xcc)); + pfVar9 = (float *)transformVector3ByMatrix4x4(worldPosition,(float *)&local_58,pfVar9); + local_74 = (undefined *)*pfVar9; + local_70 = (undefined *)pfVar9[1]; + local_6c = (undefined *)pfVar9[2]; + local_c = (undefined *)0x0; + fVar2 = (float)local_74 * *(float *)(iVar14 + 0x1a0) + + (float)local_70 * *(float *)(iVar14 + 0x1a4) + + (float)local_6c * *(float *)(iVar14 + 0x1a8) + *(float *)(iVar14 + 0x1ac); + if (-(float)local_34 < fVar2 != (-(float)local_34 == fVar2)) goto LAB_007084dd; + } + local_20 = (undefined *)0x0; + if (*(int *)(puVar12 + 0x13c) != 0) { + local_2c = (undefined *)0x0; + local_1c = (undefined *)0x0; + do { + puVar15 = local_8; + puVar8 = local_1c; + iVar14 = *(int *)(puVar12 + 0x140); + if (((*(int *)(local_2c + *(int *)(local_14 + 0x3d0) + 0x164) != 0) && + (local_10 = *(undefined **)(local_14 + 0x19c), + (float)COLLISION_PLANE_ZERO_THRESHOLD <= (float)local_10)) && + ((float)local_10 != (float)COLLISION_PLANE_ZERO_THRESHOLD)) { + puVar12 = local_8 + 0x2c; + uVar16 = *(int *)(local_8 + 0x30) + 1; + if (*(uint *)(local_8 + 0x2c) < uVar16) { + uVar10 = *(uint *)(local_8 + 0x38); + if (uVar10 == 0) { + uVar10 = CalculateOptimalBlockSize(puVar12,uVar16); + } + uVar16 = AlignToBlockSize(uVar16,uVar10); + SetArrayCapacity(puVar12,uVar16); + } + puVar11 = (undefined4 *)(*(int *)(puVar15 + 0x30) * 0x40 + *(int *)(puVar15 + 0x34)); + *(int *)(puVar15 + 0x30) = *(int *)(puVar15 + 0x30) + 1; + if (puVar11 != (undefined4 *)0x0) { + puVar11[4] = local_10; + *puVar11 = 4; + puVar11[1] = local_14; + puVar11[2] = 0; + puVar11[3] = 0; + puVar11[7] = local_20; + puVar11[10] = (uint)*(ushort *)(puVar8 + iVar14 + 0x2e); + puVar11[5] = *(undefined4 *)(local_14 + 0x84); + puVar11[6] = *(undefined4 *)(local_14 + 0x84); + if ((1 < *(ushort *)(puVar8 + iVar14 + 0x28)) || ((float)local_10 < _DAT_00808120)) { + puVar19 = (uint *)(local_8 + 0x5c); + if (local_c == (undefined *)0x0) { + puVar19 = (uint *)(local_8 + 0x6c); + } + } + else { + puVar19 = (uint *)(local_8 + 0x4c); + } + uVar16 = puVar19[1] + 1; + if (*puVar19 < uVar16) { + uVar10 = puVar19[3]; + if (uVar10 == 0) { + uVar10 = calculateArrayCapacity(puVar19,uVar16); + } + uVar16 = alignToMultiple(uVar16,uVar10); + resizePointerArray(puVar19,uVar16); + } + puVar11 = (undefined4 *)(puVar19[2] + puVar19[1] * 4); + if (puVar11 != (undefined4 *)0x0) { + *puVar11 = cameraPosition; + } + puVar19[1] = puVar19[1] + 1; + cameraPosition = (float *)((int)cameraPosition + 1); + } + } + local_20 = local_20 + 1; + local_1c = local_1c + 0x1f8; + local_2c = local_2c + 0x16c; + puVar12 = local_24; + } while (local_20 < *(undefined **)(local_24 + 0x13c)); + } + } + local_14 = *(undefined **)(local_8 + 0x28); + puVar12 = local_8; + } while( true ); +} + diff --git a/src/transform44/transform44.zig b/src/transform44/transform44.zig index 2c46d83..821e0a8 100644 --- a/src/transform44/transform44.zig +++ b/src/transform44/transform44.zig @@ -1,21 +1,14 @@ -//! transform44 — profiling & optimization of transformMatrix4x4 (0x714260) +//! transform44 — render pipeline profiling & M2 bone transform optimization //! -//! transformMatrix4x4 is the main per-frame bone transform engine for ALL visible -//! M2 models. 17703 bytes. Called from renderFrame, renderSceneNode, -//! updateAnimationTransform. Recursive for attached child objects (weapons, etc). +//! Hooks 5 functions in the render/update pipeline to measure per-frame costs: //! -//! Phase 1: Profiling instrumentation — measures call frequency, early-exit rate, -//! cycle cost, and bone counts to identify optimization targets. +//! executeSceneRenderPass (0x708900) — top-level render pass dispatch +//! renderFrame (0x707680) — per-model render (calls transformMatrix4x4) +//! transformMatrix4x4 (0x714260) — per-model bone transform engine (17703 bytes) +//! RenderTextureQuads (0x76FB00) — batched quad rendering +//! CMovement::Process (0x616620) — per-unit movement update //! -//! Key inner functions (call counts within transformMatrix4x4): -//! findInterpolationIndices (0x713d50) — 58 calls, binary/linear keyframe search -//! interpolateAnimationKeyframes (0x713ea0) — 2 calls, vec4 keyframe lerp -//! getInterpolatedFloat (0x71af20) — 4 calls, scalar keyframe lerp -//! getIndexOffset (0x71aff0) — 12 calls, trivial (ptr + idx*2) -//! setShortValue (0x71b010) — 12 calls, trivial (write u16) -//! scaleMatrix3x3ByVector (0x7bdca0) — 2 calls, x87 FPU (SSE candidate) -//! ApplyTranslationMatrix (0x7bdc40) — 5 calls, x87 FPU (SSE candidate) -//! rotateMatrixByQuaternion (0x7bddb0) — 1 call, builds rot matrix then SSE multiply +//! Stats are dumped every DUMP_FRAMES render passes (~2s at 60fps). const std = @import("std"); const hook = @import("zhook"); @@ -33,21 +26,42 @@ pub fn isActive() bool { } // ============================================================================= -// Profiling state +// Profiling state — unified dump every DUMP_FRAMES render passes // ============================================================================= -const DUMP_INTERVAL: u32 = 500; +const DUMP_FRAMES: u32 = 180; var prof = ProfState{}; +var t44_depth: u32 = 0; // recursion depth — survives resets const ProfState = struct { - calls: u32 = 0, - early_exits: u32 = 0, - cycles: u64 = 0, - total_bones: u64 = 0, - max_bones: u32 = 0, - max_depth: u32 = 0, - depth: u32 = 0, + // Frame counter (incremented by executeSceneRenderPass) + frames: u32 = 0, + + // transformMatrix4x4 (0x714260) + t44_calls: u32 = 0, + t44_early: u32 = 0, + t44_cycles: u64 = 0, + t44_bones: u64 = 0, + t44_max_bones: u32 = 0, + t44_max_depth: u32 = 0, + + // renderFrame (0x707680) + rf_calls: u32 = 0, + rf_cycles: u64 = 0, + + // executeSceneRenderPass (0x708900) + erp_calls: u32 = 0, + erp_cycles: u64 = 0, + + // RenderTextureQuads (0x76FB00) + rtq_calls: u32 = 0, + rtq_cycles: u64 = 0, + rtq_items: u64 = 0, + + // CMovement::ProcessUnitMovementUpdate (0x616620) + mov_calls: u32 = 0, + mov_cycles: u64 = 0, }; inline fn rdtsc() u64 { @@ -62,17 +76,12 @@ inline fn rdtsc() u64 { // ============================================================================= // Hook: transformMatrix4x4 (0x714260) -// __thiscall(ECX=SceneObject*, stack: Matrix4x4*, Matrix4x4*, Matrix4x4*, Matrix4x4*) -// RET 0x10 (4 stack params × 4 bytes) -// -// Mapped to fastcall: ECX=this, EDX=unused, stack: mat1, mat2, mat3, mat4 -// Stack cleanup is identical (4 stack params = RET 0x10 in both conventions). +// __thiscall(ECX=SceneObject*, stack: Matrix4x4* ×4) +// Fastcall mapping: ECX=this, EDX=unused, stack: mat1, mat2, mat3, mat4 +// RET 0x10 // ============================================================================= -const TRANSFORM_ADDR: u32 = 0x714260; - const TransformFn = fn (u32, u32, u32, u32, u32, u32) callconv(hook.cc.fastcall) void; - var transform_hook: hook.Detour(TransformFn) = .{}; fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(hook.cc.fastcall) void { @@ -80,8 +89,7 @@ fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u const start = rdtsc(); - // Check sync gate — predict whether original will early-exit. - // Original code: if (this+0x10 == NULL || this+0x40 == *(*(this+0x30)+0x10)) return; + // Check sync gate — predict early exit const model_data = hook.readMem(u32, this + 0x10); var is_early = false; var bone_count: u32 = 0; @@ -94,43 +102,152 @@ fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u const anim_sync = hook.readMem(u32, anim_ctx + 0x10); if (sync_val == anim_sync) is_early = true; } - // Read bone count from model header at this+0x2C -> +0x34 const model_hdr = hook.readMem(u32, this + 0x2C); if (model_hdr != 0) { bone_count = hook.readMem(u32, model_hdr + 0x34); } } - prof.depth += 1; - if (prof.depth > prof.max_depth) prof.max_depth = prof.depth; + t44_depth += 1; + if (t44_depth > prof.t44_max_depth) prof.t44_max_depth = t44_depth; transform_hook.callOriginal(.{ this, edx, mat1, mat2, mat3, mat4 }); - prof.depth -= 1; + t44_depth -= 1; const elapsed = rdtsc() - start; - prof.cycles +|= elapsed; - prof.calls += 1; - if (is_early) prof.early_exits += 1; - prof.total_bones +|= bone_count; - if (bone_count > prof.max_bones) prof.max_bones = bone_count; - - // Dump stats periodically (only at depth 0 to avoid spam during recursion) - if (prof.depth == 0 and prof.calls >= DUMP_INTERVAL) { - dumpStats(); - } + prof.t44_cycles +|= elapsed; + prof.t44_calls += 1; + if (is_early) prof.t44_early += 1; + prof.t44_bones +|= bone_count; + if (bone_count > prof.t44_max_bones) prof.t44_max_bones = bone_count; } +// ============================================================================= +// Hook: renderFrame (0x707680) +// __thiscall(ECX=this, stack: float* cameraPosition) +// Fastcall mapping: ECX=this, EDX=unused, stack: cameraPos +// RET 0x4 +// ============================================================================= + +const RenderFrameFn = fn (u32, u32, u32) callconv(hook.cc.fastcall) ?*anyopaque; +var render_frame_hook: hook.Detour(RenderFrameFn) = .{}; + +fn renderFrameDetour(this: u32, edx: u32, camera_pos: u32) callconv(hook.cc.fastcall) ?*anyopaque { + asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true }); + + const start = rdtsc(); + const ret = render_frame_hook.callOriginal(.{ this, edx, camera_pos }); + prof.rf_cycles +|= rdtsc() - start; + prof.rf_calls += 1; + return ret; +} + +// ============================================================================= +// Hook: executeSceneRenderPass (0x708900) +// __thiscall(ECX=scene, stack: renderPassIndex) — Ghidra labels __stdcall but +// prologue does MOV ESI,ECX (saves this). RET 0x4. +// Fastcall mapping: ECX=this, EDX=unused, stack: passIndex +// ============================================================================= + +const ExecRenderPassFn = fn (u32, u32, u32) callconv(hook.cc.fastcall) ?*anyopaque; +var exec_render_pass_hook: hook.Detour(ExecRenderPassFn) = .{}; + +fn execRenderPassDetour(this: u32, edx: u32, pass_index: u32) callconv(hook.cc.fastcall) ?*anyopaque { + asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true }); + + const start = rdtsc(); + const ret = exec_render_pass_hook.callOriginal(.{ this, edx, pass_index }); + prof.erp_cycles +|= rdtsc() - start; + prof.erp_calls += 1; + + // Use render pass as frame boundary for dump trigger + prof.frames += 1; + if (prof.frames >= DUMP_FRAMES) { + dumpStats(); + } + return ret; +} + +// ============================================================================= +// Hook: RenderTextureQuads (0x76FB00) +// __fastcall(ECX=RenderBatch*) — no stack params, RET +// RenderBatch+0xC = item_count +// ============================================================================= + +const RenderQuadsFn = fn (u32, u32) callconv(hook.cc.fastcall) void; +var render_quads_hook: hook.Detour(RenderQuadsFn) = .{}; + +fn renderQuadsDetour(batch: u32, edx: u32) callconv(hook.cc.fastcall) void { + asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true }); + + // Read item count before call (batch+0xC) + const items: u32 = if (batch != 0) hook.readMem(u32, batch + 0xC) else 0; + const start = rdtsc(); + render_quads_hook.callOriginal(.{ batch, edx }); + prof.rtq_cycles +|= rdtsc() - start; + prof.rtq_calls += 1; + prof.rtq_items +|= items; +} + +// ============================================================================= +// Hook: CMovement::ProcessUnitMovementUpdate (0x616620) +// __thiscall(ECX=this, stack: timeNow(u32), lastUpdate(u32)) +// RET 0x8 (2 stack params) +// Fastcall mapping: ECX=this, EDX=unused, stack: timeNow, lastUpdate +// ============================================================================= + +const MovementFn = fn (u32, u32, u32, u32) callconv(hook.cc.fastcall) void; +var movement_hook: hook.Detour(MovementFn) = .{}; + +fn movementDetour(this: u32, edx: u32, time_now: u32, last_update: u32) callconv(hook.cc.fastcall) void { + asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true }); + + const start = rdtsc(); + movement_hook.callOriginal(.{ this, edx, time_now, last_update }); + prof.mov_cycles +|= rdtsc() - start; + prof.mov_calls += 1; +} + +// ============================================================================= +// Unified stats dump +// ============================================================================= + fn dumpStats() void { - const real = prof.calls - prof.early_exits; - const avg_all = if (prof.calls > 0) prof.cycles / prof.calls else 0; - const avg_real = if (real > 0) prof.cycles / real else 0; - const avg_bones = if (real > 0) prof.total_bones / real else 0; - log.fmt("[prof] {d} calls ({d} work, {d} skip), {d}/{d} cyc avg/work, bones avg={d} max={d}, depth={d}\n", .{ - prof.calls, real, prof.early_exits, - avg_all, avg_real, avg_bones, - prof.max_bones, prof.max_depth, + const f = prof.frames; + if (f == 0) return; + + // transformMatrix4x4 + const t44_real = prof.t44_calls - prof.t44_early; + const t44_avg = if (t44_real > 0) prof.t44_cycles / t44_real else 0; + const t44_avg_bones = if (t44_real > 0) prof.t44_bones / t44_real else 0; + + // Per-frame averages + const rf_per_f = prof.rf_calls / f; + const t44_per_f = prof.t44_calls / f; + const rtq_per_f = prof.rtq_calls / f; + const mov_per_f = prof.mov_calls / f; + const erp_per_f = prof.erp_calls / f; + + // Cycle totals per frame + const rf_cyc_f = prof.rf_cycles / f; + const t44_cyc_f = prof.t44_cycles / f; + const rtq_cyc_f = prof.rtq_cycles / f; + const mov_cyc_f = prof.mov_cycles / f; + const erp_cyc_f = prof.erp_cycles / f; + + log.fmt( + \\[prof] {d} frames | per-frame: erp={d} rf={d} t44={d}({d}skip) rtq={d} mov={d} + \\ cycles/frame: erp={d}K rf={d}K t44={d}K rtq={d}K mov={d}K + \\ t44: {d}cyc/work bones_avg={d} max={d} depth={d} | rtq_items/frame={d} + \\ + , .{ + f, + erp_per_f, rf_per_f, t44_per_f, prof.t44_early / f, rtq_per_f, mov_per_f, + erp_cyc_f / 1000, rf_cyc_f / 1000, t44_cyc_f / 1000, rtq_cyc_f / 1000, mov_cyc_f / 1000, + t44_avg, t44_avg_bones, prof.t44_max_bones, prof.t44_max_depth, + prof.rtq_items / f, }); - // Reset but preserve depth (we're always at depth 0 here) + prof = ProfState{}; } @@ -145,13 +262,21 @@ pub fn installHooks() void { if (!g_is_hook_owner) return; log = logging.Logger.open(module_name, .both); - _ = transform_hook.attach(TRANSFORM_ADDR, &transformDetour); - log.print("transform44: profiling hook installed at 0x714260\n"); + _ = transform_hook.attach(0x714260, &transformDetour); + _ = render_frame_hook.attach(0x707680, &renderFrameDetour); + _ = exec_render_pass_hook.attach(0x708900, &execRenderPassDetour); + _ = render_quads_hook.attach(0x76FB00, &renderQuadsDetour); + _ = movement_hook.attach(0x616620, &movementDetour); + log.print("transform44: 5 profiling hooks installed\n"); } pub fn removeHooks() void { if (g_is_hook_owner) { transform_hook.detach(); + render_frame_hook.detach(); + exec_render_pass_hook.detach(); + render_quads_hook.detach(); + movement_hook.detach(); log.close(); mod_mutex.release(&g_mutex); }