From d74e8b50352c740ba6b87e223c134d45be798a49 Mon Sep 17 00:00:00 2001 From: TheRedDaemon <66257843+TheRedDaemon@users.noreply.github.com> Date: Sun, 24 May 2026 18:43:13 +0200 Subject: [PATCH 1/2] reimplement: SHC_3BB0A8C1_0x00471830 and support functions 100% --- src/OpenSHC/IO/LowLevelMemory.hpp | 7 +- src/OpenSHC/IO/LowLevelMemory/copyData+.cpp | 264 ++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 8 +- 3 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 src/OpenSHC/IO/LowLevelMemory/copyData+.cpp diff --git a/src/OpenSHC/IO/LowLevelMemory.hpp b/src/OpenSHC/IO/LowLevelMemory.hpp index 3c2f95d9..e1d8cc3f 100644 --- a/src/OpenSHC/IO/LowLevelMemory.hpp +++ b/src/OpenSHC/IO/LowLevelMemory.hpp @@ -9,6 +9,7 @@ #pragma once #include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + #include "crtdefs.h" namespace OpenSHC { @@ -21,9 +22,9 @@ namespace IO { // SIZE: 0x00000014 class LowLevelMemory { public: - int src; // 0x00000000 length: 4 - pointer* destination; // 0x00000004 length: 4 - pointer* size; // 0x00000008 length: 4 + pointer src; // 0x00000000 length: 4 + pointer destination; // 0x00000004 length: 4 + int size; // 0x00000008 length: 4 int value; // 0x0000000C length: 4 int field4_0x10; // 0x00000010 length: 4 diff --git a/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp b/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp new file mode 100644 index 00000000..b570bac0 --- /dev/null +++ b/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp @@ -0,0 +1,264 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +namespace OpenSHC { +namespace IO { + + // NOTE: The calls to the copyData... functions are optimized, which means the compiler knew the content of these + // functions. The most likely thing is, that these functions shared a file. It does therefore only work if the + // resolvers are active. + + // FUNCTION: STRONGHOLDCRUSADER 0x00471830 + void LowLevelMemory::copyData(size_t size, void* src, void* destination) + { + this->size = size; + this->src = src; + this->destination = destination; + MACRO_CALL_MEMBER(LowLevelMemory_Func::copyData0x100, this)(); + MACRO_CALL_MEMBER(LowLevelMemory_Func::copyData0x010, this)(); + MACRO_CALL_MEMBER(LowLevelMemory_Func::copyData0x001, this)(); + } + + // NOTE: Heavy use of assembly. + // In the original code, these functions have a strange structure, using a simple loop jmp and writing the values + // to the stack before moving them into a register again. /O2 would normally optimize the prolog and epilog code + // to not use the stack and use a more optimized loop. Regardless what the original reason was, no other way was + // found so far aside of using assembly, which provokes the strange "store in stack then load" pattern. + // + // Since these are low level support functions anyway, I would consider this good enough. They can be visited if + // anything else is done to be rejudged. + + // FUNCTION: STRONGHOLDCRUSADER 0x0046ABA0 + void LowLevelMemory::copyData0x100() + { + unsigned int* localDest = (unsigned int*)this->destination; + unsigned int* localSrc = (unsigned int*)this->src; + size_t localSize = this->size; + + __asm + { + mov edi, localDest + mov esi, localSrc + mov ebx, localSize + + loop_start: + cmp ebx, 0x100 + jl loop_end + + mov eax, dword ptr [esi + 0x00] + mov dword ptr [edi + 0x00], eax + mov eax, dword ptr [esi + 0x04] + mov dword ptr [edi + 0x04], eax + mov eax, dword ptr [esi + 0x08] + mov dword ptr [edi + 0x08], eax + mov eax, dword ptr [esi + 0x0c] + mov dword ptr [edi + 0x0c], eax + mov eax, dword ptr [esi + 0x10] + mov dword ptr [edi + 0x10], eax + mov eax, dword ptr [esi + 0x14] + mov dword ptr [edi + 0x14], eax + mov eax, dword ptr [esi + 0x18] + mov dword ptr [edi + 0x18], eax + mov eax, dword ptr [esi + 0x1c] + mov dword ptr [edi + 0x1c], eax + mov eax, dword ptr [esi + 0x20] + mov dword ptr [edi + 0x20], eax + mov eax, dword ptr [esi + 0x24] + mov dword ptr [edi + 0x24], eax + mov eax, dword ptr [esi + 0x28] + mov dword ptr [edi + 0x28], eax + mov eax, dword ptr [esi + 0x2c] + mov dword ptr [edi + 0x2c], eax + mov eax, dword ptr [esi + 0x30] + mov dword ptr [edi + 0x30], eax + mov eax, dword ptr [esi + 0x34] + mov dword ptr [edi + 0x34], eax + mov eax, dword ptr [esi + 0x38] + mov dword ptr [edi + 0x38], eax + mov eax, dword ptr [esi + 0x3c] + mov dword ptr [edi + 0x3c], eax + mov eax, dword ptr [esi + 0x40] + mov dword ptr [edi + 0x40], eax + mov eax, dword ptr [esi + 0x44] + mov dword ptr [edi + 0x44], eax + mov eax, dword ptr [esi + 0x48] + mov dword ptr [edi + 0x48], eax + mov eax, dword ptr [esi + 0x4c] + mov dword ptr [edi + 0x4c], eax + mov eax, dword ptr [esi + 0x50] + mov dword ptr [edi + 0x50], eax + mov eax, dword ptr [esi + 0x54] + mov dword ptr [edi + 0x54], eax + mov eax, dword ptr [esi + 0x58] + mov dword ptr [edi + 0x58], eax + mov eax, dword ptr [esi + 0x5c] + mov dword ptr [edi + 0x5c], eax + mov eax, dword ptr [esi + 0x60] + mov dword ptr [edi + 0x60], eax + mov eax, dword ptr [esi + 0x64] + mov dword ptr [edi + 0x64], eax + mov eax, dword ptr [esi + 0x68] + mov dword ptr [edi + 0x68], eax + mov eax, dword ptr [esi + 0x6c] + mov dword ptr [edi + 0x6c], eax + mov eax, dword ptr [esi + 0x70] + mov dword ptr [edi + 0x70], eax + mov eax, dword ptr [esi + 0x74] + mov dword ptr [edi + 0x74], eax + mov eax, dword ptr [esi + 0x78] + mov dword ptr [edi + 0x78], eax + mov eax, dword ptr [esi + 0x7c] + mov dword ptr [edi + 0x7c], eax + mov eax, dword ptr [esi + 0x80] + mov dword ptr [edi + 0x80], eax + mov eax, dword ptr [esi + 0x84] + mov dword ptr [edi + 0x84], eax + mov eax, dword ptr [esi + 0x88] + mov dword ptr [edi + 0x88], eax + mov eax, dword ptr [esi + 0x8c] + mov dword ptr [edi + 0x8c], eax + mov eax, dword ptr [esi + 0x90] + mov dword ptr [edi + 0x90], eax + mov eax, dword ptr [esi + 0x94] + mov dword ptr [edi + 0x94], eax + mov eax, dword ptr [esi + 0x98] + mov dword ptr [edi + 0x98], eax + mov eax, dword ptr [esi + 0x9c] + mov dword ptr [edi + 0x9c], eax + mov eax, dword ptr [esi + 0xa0] + mov dword ptr [edi + 0xa0], eax + mov eax, dword ptr [esi + 0xa4] + mov dword ptr [edi + 0xa4], eax + mov eax, dword ptr [esi + 0xa8] + mov dword ptr [edi + 0xa8], eax + mov eax, dword ptr [esi + 0xac] + mov dword ptr [edi + 0xac], eax + mov eax, dword ptr [esi + 0xb0] + mov dword ptr [edi + 0xb0], eax + mov eax, dword ptr [esi + 0xb4] + mov dword ptr [edi + 0xb4], eax + mov eax, dword ptr [esi + 0xb8] + mov dword ptr [edi + 0xb8], eax + mov eax, dword ptr [esi + 0xbc] + mov dword ptr [edi + 0xbc], eax + mov eax, dword ptr [esi + 0xc0] + mov dword ptr [edi + 0xc0], eax + mov eax, dword ptr [esi + 0xc4] + mov dword ptr [edi + 0xc4], eax + mov eax, dword ptr [esi + 0xc8] + mov dword ptr [edi + 0xc8], eax + mov eax, dword ptr [esi + 0xcc] + mov dword ptr [edi + 0xcc], eax + mov eax, dword ptr [esi + 0xd0] + mov dword ptr [edi + 0xd0], eax + mov eax, dword ptr [esi + 0xd4] + mov dword ptr [edi + 0xd4], eax + mov eax, dword ptr [esi + 0xd8] + mov dword ptr [edi + 0xd8], eax + mov eax, dword ptr [esi + 0xdc] + mov dword ptr [edi + 0xdc], eax + mov eax, dword ptr [esi + 0xe0] + mov dword ptr [edi + 0xe0], eax + mov eax, dword ptr [esi + 0xe4] + mov dword ptr [edi + 0xe4], eax + mov eax, dword ptr [esi + 0xe8] + mov dword ptr [edi + 0xe8], eax + mov eax, dword ptr [esi + 0xec] + mov dword ptr [edi + 0xec], eax + mov eax, dword ptr [esi + 0xf0] + mov dword ptr [edi + 0xf0], eax + mov eax, dword ptr [esi + 0xf4] + mov dword ptr [edi + 0xf4], eax + mov eax, dword ptr [esi + 0xf8] + mov dword ptr [edi + 0xf8], eax + mov eax, dword ptr [esi + 0xfc] + mov dword ptr [edi + 0xfc], eax + + sub ebx, 0x100 + add esi, 0x100 + add edi, 0x100 + + jmp loop_start + loop_end: + mov localDest, edi + mov localSrc, esi + mov localSize, ebx + } + + this->destination = (void*)localDest; + this->src = (void*)localSrc; + this->size = localSize; + } + + // FUNCTION: STRONGHOLDCRUSADER 0x0046AB30 + void LowLevelMemory::copyData0x010() + { + unsigned int* localDest = (unsigned int*)this->destination; + unsigned int* localSrc = (unsigned int*)this->src; + size_t localSize = this->size; + + __asm + { + mov edi, localDest + mov esi, localSrc + mov ebx, localSize + + loop_start: + cmp ebx, 0x10 + jl loop_end + + mov eax, dword ptr [esi + 0x00] + mov dword ptr [edi + 0x00], eax + mov eax, dword ptr [esi + 0x04] + mov dword ptr [edi + 0x04], eax + mov eax, dword ptr [esi + 0x08] + mov dword ptr [edi + 0x08], eax + mov eax, dword ptr [esi + 0x0c] + mov dword ptr [edi + 0x0c], eax + + sub ebx, 0x10 + add esi, 0x10 + add edi, 0x10 + + jmp loop_start + loop_end: + mov localDest, edi + mov localSrc, esi + mov localSize, ebx + } + + this->destination = (void*)localDest; + this->src = (void*)localSrc; + this->size = localSize; + } + + // FUNCTION: STRONGHOLDCRUSADER 0x0046AAF0 + void LowLevelMemory::copyData0x001() + { + unsigned char* localDest = (unsigned char*)this->destination; + unsigned char* localSrc = (unsigned char*)this->src; + size_t localSize = this->size; + + __asm + { + mov edi, localDest + mov esi, localSrc + mov ebx, localSize + + loop_start: + cmp ebx, 0 + jle loop_end + + mov al, [esi] + mov [edi], al + + sub ebx, 1 + add esi, 1 + add edi, 1 + + jmp loop_start + loop_end: + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 05220053..5191f9dd 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -10178,9 +10178,9 @@ SHC_3BB0A8C1_0x0046A890 | 0.0% | Pending SHC_3BB0A8C1_0x0046A8D0 | 0.0% | Pending SHC_3BB0A8C1_0x0046A910 | 0.0% | Pending SHC_3BB0A8C1_0x0046A970 | 0.0% | Pending -SHC_3BB0A8C1_0x0046AAF0 | 0.0% | Pending -SHC_3BB0A8C1_0x0046AB30 | 0.0% | Pending -SHC_3BB0A8C1_0x0046ABA0 | 0.0% | Pending +SHC_3BB0A8C1_0x0046AAF0 | 100.0% | Reimplemented, although heavy assembly usage +SHC_3BB0A8C1_0x0046AB30 | 100.0% | Reimplemented, although heavy assembly usage +SHC_3BB0A8C1_0x0046ABA0 | 100.0% | Reimplemented, although heavy assembly usage SHC_3BB0A8C1_0x0046AE50 | 0.0% | Pending SHC_3BB0A8C1_0x0046AE80 | 0.0% | Pending SHC_3BB0A8C1_0x0046AEC0 | 0.0% | Pending @@ -10476,7 +10476,7 @@ SHC_3BB0A8C1_0x004717FC | 0.0% | Pending SHC_3BB0A8C1_0x00471800 | 0.0% | Pending SHC_3BB0A8C1_0x00471804 | 0.0% | Pending SHC_3BB0A8C1_0x00471810 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00471830 | 0.0% | Pending +SHC_3BB0A8C1_0x00471830 | 100.0% | Reimplemented, broken opcodes unless support functions active SHC_3BB0A8C1_0x00471860 | 0.0% | Pending SHC_3BB0A8C1_0x00471890 | 0.0% | Pending SHC_3BB0A8C1_0x004718C0 | 0.0% | Pending From 1df30f92fa91804501dcb429af43421e8ae04303 Mon Sep 17 00:00:00 2001 From: TheRedDaemon <66257843+TheRedDaemon@users.noreply.github.com> Date: Sun, 31 May 2026 22:09:03 +0200 Subject: [PATCH 2/2] reimplement: SHC_3BB0A8C1_0x00471830 and more; add code logic comments --- src/OpenSHC/IO/LowLevelMemory/copyData+.cpp | 41 ++++++++++++++++++--- status/addresses-SHC-3BB0A8C1.txt | 6 +-- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp b/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp index b570bac0..4d7f37c3 100644 --- a/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp +++ b/src/OpenSHC/IO/LowLevelMemory/copyData+.cpp @@ -3,9 +3,9 @@ namespace OpenSHC { namespace IO { - // NOTE: The calls to the copyData... functions are optimized, which means the compiler knew the content of these - // functions. The most likely thing is, that these functions shared a file. It does therefore only work if the - // resolvers are active. + // NOTE: The calls to the copyData... functions are optimized in "copyData", which means the compiler knew the + // content of these functions. The most likely thing is that these functions shared a file. It does therefore only + // work if the resolvers are active. // FUNCTION: STRONGHOLDCRUSADER 0x00471830 void LowLevelMemory::copyData(size_t size, void* src, void* destination) @@ -24,8 +24,8 @@ namespace IO { // to not use the stack and use a more optimized loop. Regardless what the original reason was, no other way was // found so far aside of using assembly, which provokes the strange "store in stack then load" pattern. // - // Since these are low level support functions anyway, I would consider this good enough. They can be visited if - // anything else is done to be rejudged. + // The general sentiment expressed by these functions is added as a comment before the assembly code. Note, that + // actually using these would not result in the same binary. // FUNCTION: STRONGHOLDCRUSADER 0x0046ABA0 void LowLevelMemory::copyData0x100() @@ -34,6 +34,18 @@ namespace IO { unsigned int* localSrc = (unsigned int*)this->src; size_t localSize = this->size; + /* + while (localSize >= 0x100) { + // actually manually enrolled to 64 int assigns + for (int i = 0; i < 64; i++) { + localDest[i] = localSrc[i]; + } + localSrc += 64; + localDest += 64; + localSize -= 0x100; + } + */ + __asm { mov edi, localDest @@ -196,6 +208,18 @@ namespace IO { unsigned int* localSrc = (unsigned int*)this->src; size_t localSize = this->size; + /* + while (localSize >= 0x10) { + // actually manually enrolled to 4 int assigns + for (int i = 0; i < 4; i++) { + localDest[i] = localSrc[i]; + } + localSrc += 4; + localDest += 4; + localSize -= 0x10; + } + */ + __asm { mov edi, localDest @@ -238,6 +262,13 @@ namespace IO { unsigned char* localSrc = (unsigned char*)this->src; size_t localSize = this->size; + /* + // assembly logic just uses localSize to count down and src and dest up + for(size_t i = 0; i < localSize; ++i) { + localDest[i] = localSrc[i]; + } + */ + __asm { mov edi, localDest diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 5191f9dd..6b51579f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -10178,9 +10178,9 @@ SHC_3BB0A8C1_0x0046A890 | 0.0% | Pending SHC_3BB0A8C1_0x0046A8D0 | 0.0% | Pending SHC_3BB0A8C1_0x0046A910 | 0.0% | Pending SHC_3BB0A8C1_0x0046A970 | 0.0% | Pending -SHC_3BB0A8C1_0x0046AAF0 | 100.0% | Reimplemented, although heavy assembly usage -SHC_3BB0A8C1_0x0046AB30 | 100.0% | Reimplemented, although heavy assembly usage -SHC_3BB0A8C1_0x0046ABA0 | 100.0% | Reimplemented, although heavy assembly usage +SHC_3BB0A8C1_0x0046AAF0 | 100.0% | Reimplemented +SHC_3BB0A8C1_0x0046AB30 | 100.0% | Reimplemented +SHC_3BB0A8C1_0x0046ABA0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0046AE50 | 0.0% | Pending SHC_3BB0A8C1_0x0046AE80 | 0.0% | Pending SHC_3BB0A8C1_0x0046AEC0 | 0.0% | Pending