Files
90CNServer/client-patch/90CN_ui_iterator_guard.inl
T

96 lines
4.3 KiB
C++

// Included inside 90CN.cpp's namespace after its existing patch helpers.
// Only the evidenced UI update iterator call-site is patched, never the CRT.
uintptr_t g_uiIteratorNativeAdvance = 0;
uintptr_t g_uiIteratorAdvanceResume = 0;
uintptr_t g_uiIteratorNextBucket = 0;
volatile LONG g_uiIteratorInvalidations = 0;
int __stdcall CanAdvanceUiUpdateIterator(unsigned int bucket,
const uintptr_t* owner, const uintptr_t* iterator)
{
dnf90::UiIteratorSnapshot s = {};
__try {
s.owner = reinterpret_cast<uintptr_t>(owner);
if (owner && iterator) {
s.proxy = owner[0]; s.iteratorProxy = iterator[0];
s.begin = owner[1]; s.end = owner[2]; s.current = iterator[2];
if (s.proxy && s.iteratorProxy == s.proxy)
s.proxyOwner = *reinterpret_cast<const uintptr_t*>(s.proxy);
}
} __except (EXCEPTION_EXECUTE_HANDLER) { s.proxyOwner = 0; }
if (dnf90::CanAdvanceUiIterator(s)) return 1;
LONG n = InterlockedIncrement(&g_uiIteratorInvalidations);
if (n <= 16) LogLine("ui-update iterator invalidated bucket=%u owner=%08X "
"proxy=%08X iter_proxy=%08X begin=%08X end=%08X current=%08X "
"action=defer_remaining_bucket_until_next_frame hit=%ld",
bucket, s.owner, s.proxy, s.iteratorProxy, s.begin, s.end, s.current, n);
return 0;
}
__declspec(naked) void ProxyUiUpdateIteratorAdvance()
{
__asm {
// Entry is a JMP replacing CALL; result pointer and dummy are on stack.
push eax
push ecx
push edx
push ecx
push dword ptr [ebp - 14h]
push dword ptr [ebp - 1Ch]
call CanAdvanceUiUpdateIterator
test eax, eax
pop edx
pop ecx
pop eax
jz invalidated
call dword ptr [g_uiIteratorNativeAdvance]
jmp dword ptr [g_uiIteratorAdvanceResume]
invalidated:
// Native ++ would have consumed two arguments (ret 8). Do not compare
// an invalid iterator again; the next bucket constructs a fresh one.
add esp, 8
jmp dword ptr [g_uiIteratorNextBucket]
}
}
bool InstallUiIteratorAdvanceCompatibility()
{
const uintptr_t site = g_dnfBase + 0x02BF2732;
static const unsigned char kLoopContract[] = {
0x8B,0x0F,0x8B,0x11,0x8B,0x42,0x10,0xFF,0xD0,0x6A,0x00,
0x8D,0x4D,0xB4,0x51,0x8D,0x4D,0xD4,0xE8,0x19,0x02,0x7F,0xFD,
0xE9,0x77,0xFF,0xFF,0xFF,0x8B,0x45,0xE4,0x83,0x45,0xEC,0x14,
0x40,0x89,0x45,0xE4,0x3D,0x87,0x04,0x00,0x00,0x0F,0x8C,0x55,0xFF,0xFF,0xFF
};
static const unsigned char kBeginContract[] = {
0x55,0x8B,0xEC,0x8B,0x45,0x08,0x8B,0x51,0x04,0x8B,0x09,
0xC7,0x40,0x04,0,0,0,0,0x89,0x50,0x08,0x89,0x08,0x5D,0xC2,0x04,0
};
static const unsigned char kAdvanceContract[] = {
0x55,0x8B,0xEC,0x8B,0x45,0x08,0x8B,0x11,0x56,0xC7,0x00,0,0,0,0
};
if (!BytesMatch(reinterpret_cast<unsigned char*>(g_dnfBase+0x02BF2720),
kLoopContract,sizeof(kLoopContract)) ||
!BytesMatch(reinterpret_cast<unsigned char*>(g_dnfBase+0x00A44ED0),
kBeginContract,sizeof(kBeginContract)) ||
!BytesMatch(reinterpret_cast<unsigned char*>(g_dnfBase+0x003E2950),
kAdvanceContract,sizeof(kAdvanceContract))) {
LogLine("ui-update iterator compatibility disabled: native signature mismatch");
return false;
}
g_uiIteratorNativeAdvance = g_dnfBase + 0x003E2950;
g_uiIteratorAdvanceResume = g_dnfBase + 0x02BF2737;
g_uiIteratorNextBucket = g_dnfBase + 0x02BF273C;
DWORD oldProtection = 0;
if (!VirtualProtect(reinterpret_cast<void*>(site),5,PAGE_EXECUTE_READWRITE,&oldProtection)) {
LogLine("ui-update iterator compatibility protection failed error=%lu",GetLastError());
return false;
}
unsigned char jump[5] = {0xE9};
*reinterpret_cast<int32_t*>(jump+1) = static_cast<int32_t>(
reinterpret_cast<intptr_t>(&ProxyUiUpdateIteratorAdvance)-static_cast<intptr_t>(site+5));
memcpy(reinterpret_cast<void*>(site),jump,sizeof(jump));
DWORD ignored = 0;
VirtualProtect(reinterpret_cast<void*>(site),5,oldProtection,&ignored);
FlushInstructionCache(GetCurrentProcess(),reinterpret_cast<void*>(site),5);
LogLine("ui-update mutation-safe iterator installed site=%08X next_bucket=%08X",site,g_uiIteratorNextBucket);
return true;
}