From fb3e658eac7bdb62c14b1ccf97c83accf8b0605a Mon Sep 17 00:00:00 2001 From: Gottfried Leibniz <37632412+gottfriedleibniz@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:49:17 -0300 Subject: [PATCH] tweak(game/five): revive removed natives Allowing reimplemented natives to be referenced via its crossmap. Fixes https://github.com/citizenfx/fivem/issues/2328 --- .../rage-scripting-five/include/scrEngine.h | 3 ++ .../rage-scripting-five/src/TableBuilder.cpp | 42 ++++++++++++++++--- .../rage-scripting-five/src/scrEngine.cpp | 34 +++++++++++---- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/code/components/rage-scripting-five/include/scrEngine.h b/code/components/rage-scripting-five/include/scrEngine.h index b01b99c1b5..5ccd8ea16e 100644 --- a/code/components/rage-scripting-five/include/scrEngine.h +++ b/code/components/rage-scripting-five/include/scrEngine.h @@ -70,6 +70,9 @@ class // queues the registration of a custom native function handler with an identifier static void RegisterNativeHandler(uint64_t nativeIdentifier, NativeHandler handler); + // queues the registration of a custom native function handler that revives a removed identifier + static void ReviveNativeHandler(uint64_t nativeIdentifier, NativeHandler handler); + public: static fwEvent<> OnScriptInit; diff --git a/code/components/rage-scripting-five/src/TableBuilder.cpp b/code/components/rage-scripting-five/src/TableBuilder.cpp index 0fb549757d..e4f9c89f42 100644 --- a/code/components/rage-scripting-five/src/TableBuilder.cpp +++ b/code/components/rage-scripting-five/src/TableBuilder.cpp @@ -13,8 +13,14 @@ #include +struct CrossMappingEntry +{ + uint64_t entries[28]; +}; + static std::unordered_map g_mappingTable; static std::shared_ptr g_mappingInitializer; +static std::unordered_map g_unmappedTable; namespace rage { @@ -32,6 +38,22 @@ namespace rage return it->second; } + + void ReviveNative(uint64_t inNative) + { + g_mappingInitializer->Wait(); + + if (auto it = g_unmappedTable.find(inNative); it != g_unmappedTable.end()) + { + for (uint64_t hash : it->second->entries) + { + if (hash != 0 && hash != inNative) + { + g_mappingTable.insert({ hash, inNative }); + } + } + } + } } extern "C" DLL_EXPORT uint64_t MapNative(uint64_t inNative) @@ -39,10 +61,10 @@ extern "C" DLL_EXPORT uint64_t MapNative(uint64_t inNative) return rage::MapNative(inNative); } -struct CrossMappingEntry +extern "C" DLL_EXPORT void ReviveNative(uint64_t inNative) { - uint64_t entries[28]; -}; + rage::ReviveNative(inNative); +} static void DoMapping() { @@ -208,11 +230,21 @@ static void DoMapping() { for (auto& nativeEntry : crossMapping_universal) { + bool hasNative = nativeEntry.entries[maxVersion] != 0; for (int i = 0; i < maxVersion; i++) { - if (nativeEntry.entries[i] != 0 && nativeEntry.entries[maxVersion] != 0) + if (uint64_t hash = nativeEntry.entries[i]) { - g_mappingTable.insert({ nativeEntry.entries[i], nativeEntry.entries[maxVersion] }); + if (hasNative) + { + g_mappingTable.insert({ hash, nativeEntry.entries[maxVersion] }); + } + else + { + // Native was removed this version: add it to a separate table + // so it can potentially be revived by custom implementations. + g_unmappedTable.insert({ hash, &nativeEntry }); + } } } } diff --git a/code/components/rage-scripting-five/src/scrEngine.cpp b/code/components/rage-scripting-five/src/scrEngine.cpp index 4d682da580..f588776d48 100644 --- a/code/components/rage-scripting-five/src/scrEngine.cpp +++ b/code/components/rage-scripting-five/src/scrEngine.cpp @@ -311,6 +311,7 @@ void scrEngine::CreateThread(GtaThread* thread) } uint64_t MapNative(uint64_t inNative); +void ReviveNative(uint64_t inNative); template bool RegisterNativeOverride(uint64_t hash, scrEngine::NativeHandler handler) @@ -406,6 +407,18 @@ void scrEngine::RegisterNativeHandler(uint64_t nativeIdentifier, NativeHandler h g_nativeHandlers.push_back(std::make_pair(nativeIdentifier, handler)); } +void scrEngine::ReviveNativeHandler(uint64_t nativeIdentifier, NativeHandler handler) +{ + RegisterNativeHandler(nativeIdentifier, handler); + if (MapNative(nativeIdentifier) == nativeIdentifier) + { + // If two builds share the same maxVersion index, e.g., 2545 and 2612, + // and a script command was removed, then the native may be found in the + // mapping table instead of the unmapped table. + ReviveNative(nativeIdentifier); + } +} + static InitFunction initFunction([] () { scrEngine::OnScriptInit.Connect([] () @@ -548,19 +561,22 @@ static InitFunction initFunction([] if (xbr::IsGameBuildOrGreater<2612>()) { // IS_BIT_SET is missing in b2612+, re-adding for compatibility - rage::scrEngine::RegisterNativeHandler(0xE2D0C323A1AE5D85 /* 2545 hash */, [](rage::scrNativeCallContext* ctx) + rage::scrEngine::OnScriptInit.Connect([]() { - bool result = false; + rage::scrEngine::ReviveNativeHandler(0xE2D0C323A1AE5D85 /* 2545 hash */, [](rage::scrNativeCallContext* ctx) + { + bool result = false; - auto value = ctx->GetArgument(0); - auto offset = ctx->GetArgument(1); + auto value = ctx->GetArgument(0); + auto offset = ctx->GetArgument(1); - if (offset < 32) - { - result = (value & (1 << offset)) != 0; - } + if (offset < 32) + { + result = (value & (1 << offset)) != 0; + } - ctx->SetResult(0, result); + ctx->SetResult(0, result); + }); }); } });