diff --git a/AGENTS.md b/AGENTS.md index 71e8b9ba7..f7469c9f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -151,12 +151,17 @@ restyling anything large: `__asm` block rather than C++. Functions are never `__declspec(naked)`: write a normal function, keep the compiler-generated statements as C++, and refer to parameters, locals and `this` by name. Prettify offsets with MSVC's struct-member asm syntax (`mov esi, dword ptr [eax]TileMapState.ptr_LogicLayer`) - `::` is not parseable in an - asm operand, so a resolver global cannot be named there. -- a frame pointer plus a `this` spill - the original was built without optimisation. Add `#pragma optimize(, off)` - around the function; inline asm alone does not disable optimisation. Then match the reported **frame size** exactly, - which is usually the single biggest win, because every local displacement shifts otherwise. Merge locals that share a - slot and reproduce dead stores. At `/Od` prefer a nested `if` over an early `continue` (the original inverts the test - and jumps to the loop's continue trampoline), and declare locals together at the top of the function. + asm operand, so a resolver global cannot be named there. Where the handwritten block reaches a *global* layer rather + than a `this` field, give it a C++ pointer local and name that local in the asm; the original caches the same bases in + stack slots anyway, so the inner loop still matches. `mov eax, 0` followed by `mov ax, word ptr [..]` is the giveaway + in its own right - that is a hand-rolled `movzx`, which the compiler always emits as one instruction. + Such a block is usually a **macro**: the same instruction stream appears verbatim in several functions and never as a + call, so it belongs in a hand-written `*Asm.hpp` beside them (`NeighbourFlagsAsm.hpp`, `LinkageNeighbourAsm.hpp`). + This has been worth ~45 points per function, three times over. +- a frame pointer plus a `this` spill - the original was built without optimisation, **unless the same function also + reports `mov reg, 0`**. MSVC keeps `ebp` in any function containing an `__asm` block, so for a function with + handwritten assembly the frame pointer says nothing about the optimisation level: reimplement the handwritten part as + inline asm. - a jump table - Ghidra usually renders the dispatch as a call through an unnamed pointer array and drops the arms, so rebuild the `switch` from the byte and jump tables instead of restyling the decompiler output. @@ -253,6 +258,13 @@ of the switch leaving the value it should have set uninitialised. Fixing them wa suspicious dead store is worth decompiling for rather than deleting. Note this is the opposite of the duplicate-tail pattern below, where our shared block should have been two separate branches - check the predecessor count either way. +Duplicating a `LAB_*` is correct C++ but not automatically a better match, so **measure every `goto` removal**. A +`goto` in the decompiler output usually marks a block the original compiler emitted once and jumped to, and writing +the block out twice makes MSVC emit it twice: duplicating the single shared tail in +`setClimbBasedOnClosestClimbData` - two `if` arms that set a coordinate pair and then share the distance test - was +semantically identical and cost **35 points**. Keep the `goto` where the measurement says the original shared the +block, and record that in the commit remark rather than trading match for style. + Diff patterns that were reliable (more in the cheat sheet): - Absolute `DAT_*` addresses in the original asm where the source uses `this->` mean the original accessed the global instance. diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/Constructor_PathFindingState.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/Constructor_PathFindingState.cpp new file mode 100644 index 000000000..b4bc9d9cb --- /dev/null +++ b/src/OpenSHC/Map/Navigation/PathFindingState/Constructor_PathFindingState.cpp @@ -0,0 +1,31 @@ +#include "../PathFindingState.func.hpp" + +namespace OpenSHC { +namespace Map { + namespace Navigation { + + // FUNCTION: STRONGHOLDCRUSADER 0x004A9760 + PathFindingState* PathFindingState::Constructor_PathFindingState() + { + this->field40_0x70 = 0; + this->field41_0x74 = 0; + this->DAT_lWys = 0; + this->notAllAssassinsUnk = 0; + this->DAT_Easy = 0; + this->DAT_Hard = 0; + this->DAT_Test_likely = 0; + this->DAT_Test_gatehouse = 0; + this->searchNonmatchCount = 0; + this->searchMatchCounter = 0; + this->DAT_Ass = 0; + this->calculations = 0; + this->DAT_Mini_spreads = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::clearPathFindingTileMaps, this)(); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::clearAllClimbData, this)(); + this->searchGeneration = 1; + return this; + } + + } +} +} diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/LinkageNeighbourAsm.hpp b/src/OpenSHC/Map/Navigation/PathFindingState/LinkageNeighbourAsm.hpp new file mode 100644 index 000000000..0d64d5578 --- /dev/null +++ b/src/OpenSHC/Map/Navigation/PathFindingState/LinkageNeighbourAsm.hpp @@ -0,0 +1,217 @@ +/** + HAND WRITTEN - not generated. + + path: 'OpenSHC/Map/Navigation/PathFindingState/LinkageNeighbourAsm.hpp' + + The inner loop of the PathLinkageLayer searches - pushing the eight neighbours of the tile just + taken off the queue - is handwritten assembly in the original, and it is a MACRO: the identical + instruction stream appears verbatim inside findLinkageBasedPathOrWalkRadius (0x00497740), + findSuitableSpawnLocationUnk (0x00497B80) and updateSeparateAreaTileMap (0x004995E0), never as a + call. It is recognisable by + + - "mov eax, 0" followed by "mov ax, word ptr [..]", a zero extension no compiler writes that way + (MSVC emits movzx), + - the search generation and the distance kept in 16-bit registers and compared with "cmp ax, cx" + rather than widened to int, + - every layer base reloaded from its own stack slot before each use instead of being held in a + register across the block. + + Do NOT add "#pragma optimize("", off)" to the enclosing functions. orig_asm.py --stats reports a + frame pointer for all three, but that is caused by the inline assembly itself - MSVC keeps ebp in + any function containing an __asm block - not by a missing /O2. Their prologues are plainly + optimised (the bounds check runs out of registers, "this" stays in esi), and forcing /Od costs + about half of the match. + + PathLinkageLayer bits: 0x01 north, 0x02 north-east, 0x04 east, 0x08 south-east, + 0x10 south, 0x20 south-west, 0x40 west, 0x80 north-west. + + Cardinal neighbours are entered at distance + 1 and diagonal ones at distance + 2; the north and + south rows are reached by adding directionTranslationMatrix[y][0] and [y][4] to the tile. + + The macro expects these locals in the enclosing function, by these names: + + short* _walkLayer, _certainPath the two adjacent short layers + int* _tilesQueue this->searchQueue.tilesQueue + short* _yQueue this->searchQueue.yQueue + int* _dirMatrix directionTranslationMatrix, as a flat int array + int _tile, _cY, _cLink + short _curGen, _cCardinalDistance + + and takes the queue end as the WRITEIDX macro argument, which it advances. It clobbers eax, ebx, ecx, edx, esi and + edi. + + ID must be unique within the enclosing function; it exists only to keep the assembler labels of two + expansions apart. + + The original reaches the layers through the global instance's absolute address; we load them from + locals because MSVC inline assembly cannot name DAT_TileMapState::instance ("::" is not parseable + in an asm operand). That costs the initial "mov esi, _" instructions and nothing else - the + original reloads from a stack slot at exactly those points anyway. +*/ + +#pragma once + +#include "OpenSHC/Map/TileMapState.hpp" + +// clang-format off + +// One neighbour: OFF is the byte offset on the short layers (-2, 0 or +2), BIT the linkage bit, +// DIST the distance instruction (none for a cardinal step, "add ax, 1" for a diagonal one), +// TILE the tile-delta instruction and YADJ the row-delta instruction. +#define MACRO_LINKAGE_PUSH(ID, N, WRITEIDX, OFF, BIT, DIST, TILE, YADJ) \ + __asm mov ax, word ptr [edi + edx*2 + OFF] \ + __asm cmp ax, word ptr _curGen \ + __asm je push_done_##ID##_##N \ + __asm test bl, BIT \ + __asm je push_done_##ID##_##N \ + __asm mov esi, _certainPath \ + __asm mov ax, word ptr _cCardinalDistance \ + DIST \ + __asm mov cx, word ptr _curGen \ + __asm mov word ptr [esi + edx*2 + OFF], ax \ + __asm mov word ptr [edi + edx*2 + OFF], cx \ + __asm mov ecx, WRITEIDX \ + __asm mov esi, _tilesQueue \ + __asm mov eax, edx \ + TILE \ + __asm mov dword ptr [esi + ecx*4], eax \ + __asm mov eax, _cY \ + YADJ \ + __asm mov esi, _yQueue \ + __asm mov word ptr [esi + ecx*2], ax \ + __asm add WRITEIDX, 1 \ + __asm push_done_##ID##_##N: + +#define MACRO_ASM_SAME +#define MACRO_ASM_INC __asm add eax, 1 +#define MACRO_ASM_DEC __asm sub eax, 1 +#define MACRO_ASM_DIAGONAL __asm add ax, 1 + +// The whole eight-neighbour expansion, in the original's order: west, east, then the north row +// (north, north-west, north-east) and the south row (south, south-west, south-east). +#define MACRO_LINKAGE_EXPAND_NEIGHBOURS(ID, WRITEIDX) \ + __asm mov edx, _tile \ + __asm mov edi, _walkLayer \ + __asm mov ebx, _cLink \ + MACRO_LINKAGE_PUSH(ID, w, WRITEIDX, -2, 0x40, MACRO_ASM_SAME, MACRO_ASM_DEC, MACRO_ASM_SAME) \ + MACRO_LINKAGE_PUSH(ID, e, WRITEIDX, +2, 0x04, MACRO_ASM_SAME, MACRO_ASM_INC, MACRO_ASM_SAME) \ + __asm mov eax, _cY \ + __asm shl eax, 3 \ + __asm mov esi, _dirMatrix \ + __asm add edx, dword ptr [esi + eax*4] \ + MACRO_LINKAGE_PUSH(ID, n, WRITEIDX, 0, 0x01, MACRO_ASM_SAME, MACRO_ASM_SAME, MACRO_ASM_DEC) \ + MACRO_LINKAGE_PUSH(ID, nw, WRITEIDX, -2, 0x80, MACRO_ASM_DIAGONAL, MACRO_ASM_DEC, MACRO_ASM_DEC) \ + MACRO_LINKAGE_PUSH(ID, ne, WRITEIDX, +2, 0x02, MACRO_ASM_DIAGONAL, MACRO_ASM_INC, MACRO_ASM_DEC) \ + __asm mov edx, _tile \ + __asm mov eax, _cY \ + __asm shl eax, 3 \ + __asm add eax, 4 \ + __asm mov esi, _dirMatrix \ + __asm add edx, dword ptr [esi + eax*4] \ + MACRO_LINKAGE_PUSH(ID, s, WRITEIDX, 0, 0x10, MACRO_ASM_SAME, MACRO_ASM_SAME, MACRO_ASM_INC) \ + MACRO_LINKAGE_PUSH(ID, sw, WRITEIDX, -2, 0x20, MACRO_ASM_DIAGONAL, MACRO_ASM_DEC, MACRO_ASM_INC) \ + MACRO_LINKAGE_PUSH(ID, se, WRITEIDX, +2, 0x08, MACRO_ASM_DIAGONAL, MACRO_ASM_INC, MACRO_ASM_INC) + +// The same neighbour, but only entered when OccupancyLayer says the tile is free. TOFF is the +// neighbour's offset in tiles (the occupancy layer holds one byte per tile, so it is not scaled +// like OFF is on the short layers). Needs one more local, "uchar* _occupancy". +#define MACRO_LINKAGE_PUSH_FREE(ID, N, WRITEIDX, OFF, TOFF, BIT, DIST, TILE, YADJ) \ + __asm mov ax, word ptr [edi + edx*2 + OFF] \ + __asm cmp ax, word ptr _curGen \ + __asm je push_done_##ID##_##N \ + __asm test bl, BIT \ + __asm je push_done_##ID##_##N \ + __asm mov esi, _occupancy \ + __asm mov cl, byte ptr [esi + edx + TOFF] \ + __asm cmp cl, 0 \ + __asm jne push_done_##ID##_##N \ + __asm mov esi, _certainPath \ + __asm mov ax, word ptr _cCardinalDistance \ + DIST \ + __asm mov cx, word ptr _curGen \ + __asm mov word ptr [esi + edx*2 + OFF], ax \ + __asm mov word ptr [edi + edx*2 + OFF], cx \ + __asm mov ecx, WRITEIDX \ + __asm mov esi, _tilesQueue \ + __asm mov eax, edx \ + TILE \ + __asm mov dword ptr [esi + ecx*4], eax \ + __asm mov eax, _cY \ + YADJ \ + __asm mov esi, _yQueue \ + __asm mov word ptr [esi + ecx*2], ax \ + __asm add WRITEIDX, 1 \ + __asm push_done_##ID##_##N: + +#define MACRO_LINKAGE_EXPAND_FREE_NEIGHBOURS(ID, WRITEIDX) \ + __asm mov edx, _tile \ + __asm mov edi, _walkLayer \ + __asm mov ebx, _cLink \ + MACRO_LINKAGE_PUSH_FREE(ID, w, WRITEIDX, -2, -1, 0x40, MACRO_ASM_SAME, MACRO_ASM_DEC, MACRO_ASM_SAME) \ + MACRO_LINKAGE_PUSH_FREE(ID, e, WRITEIDX, +2, +1, 0x04, MACRO_ASM_SAME, MACRO_ASM_INC, MACRO_ASM_SAME) \ + __asm mov eax, _cY \ + __asm shl eax, 3 \ + __asm mov esi, _dirMatrix \ + __asm add edx, dword ptr [esi + eax*4] \ + MACRO_LINKAGE_PUSH_FREE(ID, n, WRITEIDX, 0, 0, 0x01, MACRO_ASM_SAME, MACRO_ASM_SAME, MACRO_ASM_DEC) \ + MACRO_LINKAGE_PUSH_FREE(ID, nw, WRITEIDX, -2, -1, 0x80, MACRO_ASM_DIAGONAL, MACRO_ASM_DEC, MACRO_ASM_DEC) \ + MACRO_LINKAGE_PUSH_FREE(ID, ne, WRITEIDX, +2, +1, 0x02, MACRO_ASM_DIAGONAL, MACRO_ASM_INC, MACRO_ASM_DEC) \ + __asm mov edx, _tile \ + __asm mov eax, _cY \ + __asm shl eax, 3 \ + __asm add eax, 4 \ + __asm mov esi, _dirMatrix \ + __asm add edx, dword ptr [esi + eax*4] \ + MACRO_LINKAGE_PUSH_FREE(ID, s, WRITEIDX, 0, 0, 0x10, MACRO_ASM_SAME, MACRO_ASM_SAME, MACRO_ASM_INC) \ + MACRO_LINKAGE_PUSH_FREE(ID, sw, WRITEIDX, -2, -1, 0x20, MACRO_ASM_DIAGONAL, MACRO_ASM_DEC, MACRO_ASM_INC) \ + MACRO_LINKAGE_PUSH_FREE(ID, se, WRITEIDX, +2, +1, 0x08, MACRO_ASM_DIAGONAL, MACRO_ASM_INC, MACRO_ASM_INC) + +// The area flood-fill variant: no distance and no search generation, just "this neighbour has no +// area yet, give it mine". It tests the linkage bit first and walks the north and south rows +// west-to-east, both the other way round from the two search variants above. Needs the locals +// "ushort* _areaLayer" (PathConnectionLayer) and "short _areaID" in place of the layer pair. +#define MACRO_LINKAGE_PUSH_AREA(ID, N, WRITEIDX, OFF, BIT, TILE, YADJ) \ + __asm test bl, BIT \ + __asm je push_done_##ID##_##N \ + __asm mov ax, word ptr [edi + edx*2 + OFF] \ + __asm cmp ax, 0 \ + __asm jne push_done_##ID##_##N \ + __asm mov ax, word ptr _areaID \ + __asm mov word ptr [edi + edx*2 + OFF], ax \ + __asm mov ecx, WRITEIDX \ + __asm mov esi, _tilesQueue \ + __asm mov eax, edx \ + TILE \ + __asm mov dword ptr [esi + ecx*4], eax \ + __asm mov eax, _cY \ + YADJ \ + __asm mov esi, _yQueue \ + __asm mov word ptr [esi + ecx*2], ax \ + __asm add WRITEIDX, 1 \ + __asm push_done_##ID##_##N: + +#define MACRO_LINKAGE_EXPAND_AREA_NEIGHBOURS(ID, WRITEIDX) \ + __asm mov edx, _tile \ + __asm mov edi, _areaLayer \ + __asm mov ebx, _cLink \ + MACRO_LINKAGE_PUSH_AREA(ID, w, WRITEIDX, -2, 0x40, MACRO_ASM_DEC, MACRO_ASM_SAME) \ + MACRO_LINKAGE_PUSH_AREA(ID, e, WRITEIDX, +2, 0x04, MACRO_ASM_INC, MACRO_ASM_SAME) \ + __asm mov edx, _tile \ + __asm mov eax, _cY \ + __asm shl eax, 3 \ + __asm mov esi, _dirMatrix \ + __asm add edx, dword ptr [esi + eax*4] \ + MACRO_LINKAGE_PUSH_AREA(ID, nw, WRITEIDX, -2, 0x80, MACRO_ASM_DEC, MACRO_ASM_DEC) \ + MACRO_LINKAGE_PUSH_AREA(ID, n, WRITEIDX, 0, 0x01, MACRO_ASM_SAME, MACRO_ASM_DEC) \ + MACRO_LINKAGE_PUSH_AREA(ID, ne, WRITEIDX, +2, 0x02, MACRO_ASM_INC, MACRO_ASM_DEC) \ + __asm mov edx, _tile \ + __asm mov eax, _cY \ + __asm shl eax, 3 \ + __asm add eax, 4 \ + __asm mov esi, _dirMatrix \ + __asm add edx, dword ptr [esi + eax*4] \ + MACRO_LINKAGE_PUSH_AREA(ID, sw, WRITEIDX, -2, 0x20, MACRO_ASM_DEC, MACRO_ASM_INC) \ + MACRO_LINKAGE_PUSH_AREA(ID, s, WRITEIDX, 0, 0x10, MACRO_ASM_SAME, MACRO_ASM_INC) \ + MACRO_LINKAGE_PUSH_AREA(ID, se, WRITEIDX, +2, 0x08, MACRO_ASM_INC, MACRO_ASM_INC) + +// clang-format on diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/bindPathPlanToAlgorithmStateAndReset.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/bindPathPlanToAlgorithmStateAndReset.cpp index def1d655f..aebd50bd6 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/bindPathPlanToAlgorithmStateAndReset.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/bindPathPlanToAlgorithmStateAndReset.cpp @@ -1,4 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "../PathFindingState.func.hpp" namespace OpenSHC { namespace Map { diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/clearActiveClimbDataOfType6And7.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/clearActiveClimbDataOfType6And7.cpp new file mode 100644 index 000000000..a14ce50dd --- /dev/null +++ b/src/OpenSHC/Map/Navigation/PathFindingState/clearActiveClimbDataOfType6And7.cpp @@ -0,0 +1,26 @@ +#include "../PathFindingState.func.hpp" + +namespace OpenSHC { +namespace Map { + namespace Navigation { + + // FUNCTION: STRONGHOLDCRUSADER 0x004A52D0 + void PathFindingState::clearActiveClimbDataOfType6And7() + { + for (int climbID = 1; climbID < 200; ++climbID) { + if (this->climbData[climbID].canBeUsed != 0) { + if (this->climbData[climbID].type == 6) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::clearClimbData, this)( + climbID); + } + if (this->climbData[climbID].type == 7) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::clearClimbData, this)( + climbID); + } + } + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/clearAllClimbData.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/clearAllClimbData.cpp index 80fbfda87..0c129f361 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/clearAllClimbData.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/clearAllClimbData.cpp @@ -1,4 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "../PathFindingState.func.hpp" namespace OpenSHC { namespace Map { diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/clearClimbData.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/clearClimbData.cpp index 693e80c9d..567520e82 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/clearClimbData.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/clearClimbData.cpp @@ -1,5 +1,6 @@ +#include "../PathFindingState.func.hpp" + #include "OpenSHC/IO/LowLevelMemory.func.hpp" -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" #include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/clearPathFindingTileMaps.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/clearPathFindingTileMaps.cpp new file mode 100644 index 000000000..a775184be --- /dev/null +++ b/src/OpenSHC/Map/Navigation/PathFindingState/clearPathFindingTileMaps.cpp @@ -0,0 +1,28 @@ +#include "../PathFindingState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Navigation { + + // FUNCTION: STRONGHOLDCRUSADER 0x00496E40 + void PathFindingState::clearPathFindingTileMaps() + { + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(DAT_TileMapState::instance.PathConnectionLayer), 0, + DAT_TileMapState::instance.PathConnectionLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(DAT_TileMapState::instance.PathLinkageLayer), 0, DAT_TileMapState::instance.PathLinkageLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(DAT_TileMapState::instance.CertainPathLayer), 0, DAT_TileMapState::instance.CertainPathLayer); + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(DAT_TileMapState::instance.WalkLayer), 0, DAT_TileMapState::instance.WalkLayer); + } + + } +} +} diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/computeAttackVectorsBasedOnXAndY.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/computeAttackVectorsBasedOnXAndY.cpp new file mode 100644 index 000000000..9bc5c4a60 --- /dev/null +++ b/src/OpenSHC/Map/Navigation/PathFindingState/computeAttackVectorsBasedOnXAndY.cpp @@ -0,0 +1,43 @@ +#include "../PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Navigation { + + // FUNCTION: STRONGHOLDCRUSADER 0x004A69F0 + void PathFindingState::computeAttackVectorsBasedOnXAndY(int playerID) + { + this->calculations += 1; + int attackVectorIndex = 0; + this->searchQueue.readIndex = 0; + while (this->searchQueue.readIndex != this->searchQueue.writeIndex) { + uint y = (uint)this->searchQueue.yQueue[this->searchQueue.readIndex]; + int tile = this->searchQueue.tilesQueue[this->searchQueue.readIndex]; + uint x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + if (DAT_TileMapState::instance.CertainPathLayer[tile] != 100) { + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::budgetFloodFillOnCertainPathLayer, this)( + x, y, 100, 100); + DAT_GameState::instance.mapAndTime.attackVectors[playerID][attackVectorIndex].x = x; + DAT_GameState::instance.mapAndTime.attackVectors[playerID][attackVectorIndex].y = y; + DAT_GameState::instance.mapAndTime.attackVectors[playerID][attackVectorIndex].tribeUIDUnk = 0; + DAT_GameState::instance.mapAndTime.attackVectors[playerID][attackVectorIndex].tribeID = 0; + attackVectorIndex += 1; + } + if (attackVectorIndex >= 50) { + return; + } + this->searchQueue.readIndex += 1; + if ((int)this->searchQueue.readIndex >= Map::MC_MAP_TILES_COUNT) { + this->searchQueue.readIndex = 0; + } + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/getComputationalTileIndex.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/getComputationalTileIndex.cpp index b5329ac1f..84f0c75fe 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/getComputationalTileIndex.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/getComputationalTileIndex.cpp @@ -1,4 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.hpp" +#include "../PathFindingState.func.hpp" namespace OpenSHC { namespace Map { @@ -7,6 +7,6 @@ namespace Map { // FUNCTION: STRONGHOLDCRUSADER 0x00496E20 undefined4 PathFindingState::getComputationalTileIndex() { return this->searchQueue.writeIndex; } - } // namespace Navigation -} // namespace Map -} // namespace OpenSHC + } +} +} diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/getTileInSearchQueue.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/getTileInSearchQueue.cpp index 1b76cae82..fc5f97064 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/getTileInSearchQueue.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/getTileInSearchQueue.cpp @@ -1,4 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "../PathFindingState.func.hpp" namespace OpenSHC { namespace Map { diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/getYSmallerThanYLimit.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/getYSmallerThanYLimit.cpp index 7e1e89c5a..98cfd4f13 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/getYSmallerThanYLimit.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/getYSmallerThanYLimit.cpp @@ -1,5 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" -#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" +#include "../PathFindingState.func.hpp" namespace OpenSHC { namespace Map { diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/retraceAndCommitPathPlan.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/retraceAndCommitPathPlan.cpp index 592967e90..b264a3e20 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/retraceAndCommitPathPlan.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/retraceAndCommitPathPlan.cpp @@ -1,4 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "../PathFindingState.func.hpp" namespace OpenSHC { namespace Map { diff --git a/src/OpenSHC/Map/Navigation/PathFindingState/updatePathLinkageLayerForEachBuildingAtEachTile.cpp b/src/OpenSHC/Map/Navigation/PathFindingState/updatePathLinkageLayerForEachBuildingAtEachTile.cpp index 4d656c28f..e5117e7a5 100644 --- a/src/OpenSHC/Map/Navigation/PathFindingState/updatePathLinkageLayerForEachBuildingAtEachTile.cpp +++ b/src/OpenSHC/Map/Navigation/PathFindingState/updatePathLinkageLayerForEachBuildingAtEachTile.cpp @@ -1,4 +1,4 @@ -#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "../PathFindingState.func.hpp" #include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" @@ -9,7 +9,7 @@ namespace Map { // FUNCTION: STRONGHOLDCRUSADER 0x004A5F60 void PathFindingState::updatePathLinkageLayerForEachBuildingAtEachTile() { - for (int tileIndex = 0; tileIndex < 80400; ++tileIndex) { + for (int tileIndex = 0; tileIndex < Map::MC_MAP_TILES_COUNT; ++tileIndex) { MACRO_CALL_MEMBER( OpenSHC::Map::Navigation::PathFindingState_Func::updatePathLinkageLayerBasedOnBuildingsUnk, this)( DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tileIndex], tileIndex); diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 238a0fe8a..08355a44b 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -24347,7 +24347,7 @@ SHC_3BB0A8C1_0x00496E20 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00496E30 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00496E40 | 0.0% | Pending +SHC_3BB0A8C1_0x00496E40 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00496EA0 | 100.0% | Reimplemented @@ -25249,7 +25249,7 @@ SHC_3BB0A8C1_0x004A50E0 | 0.0% | Pending SHC_3BB0A8C1_0x004A5230 | 0.0% | Pending -SHC_3BB0A8C1_0x004A52D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004A52D0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004A5320 | 0.0% | Pending @@ -25285,7 +25285,7 @@ SHC_3BB0A8C1_0x004A67D0 | 0.0% | Pending SHC_3BB0A8C1_0x004A6940 | 0.0% | Pending -SHC_3BB0A8C1_0x004A69F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004A69F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004A6AB0 | 0.0% | Pending @@ -25463,7 +25463,7 @@ SHC_3BB0A8C1_0x004A9680 | 0.0% | Pending SHC_3BB0A8C1_0x004A96B0 | 0.0% | Pending -SHC_3BB0A8C1_0x004A9760 | 0.0% | Pending +SHC_3BB0A8C1_0x004A9760 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004A97D0 | 0.0% | Pending diff --git a/tools/mcp/AGENTS.md b/tools/mcp/AGENTS.md index d32335882..a440b9452 100644 --- a/tools/mcp/AGENTS.md +++ b/tools/mcp/AGENTS.md @@ -27,7 +27,7 @@ We want to generate assembly that matches the original binary. Each function has 1. Use `read_cpp_code_for_function` to take in the current reimplemented code for a function, which are always specified by their full namespace name, e.g. `OpenSHC::Rendering::ViewportRenderState::xyAreValid`. 2. Use `compile_cpp_code_for_function` to write the reimplemented code to the cpp file and compile the project. If there are compilation problems, try to address them. - The project is compiled using `MSVC1400` with the /Zi and /O2 flags. Use `#pragma optimize()` variations in case of issues with byte matches. + The project is compiled using `MSVC1400` with the /Zi and /O2 flags. Don't use `#pragma optimize()` variations in case of issues with byte matches, they never lead to productive matches. The project's layout is like so: methods starting with a non-capital letter are class methods. The include file for a namespace `A::B::C` is `A/B/C.hpp`. Namespaces usually need to be referred to explicitly in the case of enum constants for example. Globals usually start with three or more capital letters, like `DAT_` and can be found in `OpenSHC/Globals/.hpp`. So if global GreatGlobal is missing, find it here `OpenSHC/Globals/GreatGlobal.hpp`. These globals need to be accessed with `::ptr`, like `DAT_Example::ptr` to get a pointer. `DAT_ExampleArray[100]` becomes `(*DAT_ExampleArray::ptr)[100]`. diff --git a/tools/reimplementation-helper/batch/README.md b/tools/reimplementation-helper/batch/README.md index 08f5ad1e0..ef8e0ab6d 100644 --- a/tools/reimplementation-helper/batch/README.md +++ b/tools/reimplementation-helper/batch/README.md @@ -15,7 +15,7 @@ rewrite sources, because the Visual Studio bundled one is too old for `.clang-fo | `build_quiet.py [--keep-going]` | `build.bat RelWithDebInfo OpenSHC.dll`, prints only errors and `BUILD_OK`/`BUILD_FAIL` (build.bat exits 0 even on errors). Retries once on a PDB server error, see the note below. | | `syntax_check.py [substr...]` | `cl /Zs` every (matching) list file serially; finds all compile errors in one pass without linking. | | `reccmp_report.py [--run] pct\|save\|cmp\|diff` | Match % and *normalized* % (call/tail-jump targets and resolver addresses ignored) per function, snapshots, before/after comparison, compact asm diffs. | -| `orig_asm.py NAME [--both\|--stats]` | The **original** instruction stream of one function (not the interleaved diff), in address order; `--stats` reports the tells that decide how to reimplement it: `mov reg, 0` (handwritten asm), frame pointer (built /Od), frame size, jump tables. | +| `orig_asm.py NAME [--both\|--stats]` | The **original** instruction stream of one function (not the interleaved diff), in address order; `--stats` reports the tells that decide how to reimplement it: `mov reg, 0` (handwritten asm), frame pointer, frame size, jump tables. | | `show_functions.py PREFIX [--skip-100]` | Print function bodies under `src/OpenSHC/PREFIX` with their percentages, in the block format `splice_functions.py` reads. | | `splice_functions.py BLOCKS.txt` | Replace many function bodies at once (keeps the `// FUNCTION:` line, can add includes/usings), then clang-format. | | `commit_progress_batch.py PREFIX... [Name=remark]` | One `reimplement:` commit per changed function with its status line, like `commit_progress`. |