From e84dd0328617e0e798acf46fccf7359e83e51e2a Mon Sep 17 00:00:00 2001 From: packfile <97990837+packfile@users.noreply.github.com> Date: Sat, 11 Jan 2025 10:39:16 +0000 Subject: [PATCH 1/2] tweak(extra-natives/five): clamp rope index in ADD_ROPE --- .../extra-natives-five/src/NativeFixes.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/code/components/extra-natives-five/src/NativeFixes.cpp b/code/components/extra-natives-five/src/NativeFixes.cpp index 74d5b15bf4..026c9f88c4 100644 --- a/code/components/extra-natives-five/src/NativeFixes.cpp +++ b/code/components/extra-natives-five/src/NativeFixes.cpp @@ -19,6 +19,7 @@ #include "Resource.h" #include "ScriptWarnings.h" #include +#include "ropeManager.h" static void BlockForbiddenNatives() { @@ -540,6 +541,35 @@ static void FixMissionTrain() }); } +static void FixAddRopeNative() +{ + constexpr const uint64_t ADD_ROPE = 0xE832D760399EB220; + auto handler = fx::ScriptEngine::GetNativeHandler(ADD_ROPE); + + if (!handler) + { + return; + } + + fx::ScriptEngine::RegisterNativeHandler(ADD_ROPE, [handler](fx::ScriptContext& ctx) + { + rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance(); + if (manager) + { + auto ropeIndex = ctx.GetArgument(7); + if (ropeIndex >= 0 && ropeIndex < manager->typeData.GetCount()) + { + return handler(ctx); + } + else + { + fx::scripting::Warningf("natives", "Invalid rope type was passed to ADD_ROPE (%d), should be from 0 to %d\n", ropeIndex, manager->typeData.GetCount() - 1); + } + } + ctx.SetResult(0); + }); +} + static HookFunction hookFunction([]() { g_fireInstances = (std::array*)(hook::get_address(hook::get_pattern("74 47 48 8D 0D ? ? ? ? 48 8B D3", 2), 3, 7) + 0x10); @@ -582,6 +612,8 @@ static HookFunction hookFunction([]() FixMissionTrain(); + FixAddRopeNative(); + if (xbr::IsGameBuildOrGreater<2612>()) { // IS_BIT_SET is missing in b2612+, re-adding for compatibility From 85247d151783b0c786b31b36fa72ef448e19544d Mon Sep 17 00:00:00 2001 From: packfile <97990837+packfile@users.noreply.github.com> Date: Sat, 11 Jan 2025 10:41:00 +0000 Subject: [PATCH 2/2] feat(extra-natives/five): REGISTER_ROPE_DATA --- .../extra-natives-five/include/ropeManager.h | 12 +- .../extra-natives-five/src/RopeNatives.cpp | 136 ++++++++++++++++++ .../client/citizen/common/data/gameconfig.xml | 2 +- ext/native-decls/RegisterRopeData.md | 42 ++++++ 4 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 ext/native-decls/RegisterRopeData.md diff --git a/code/components/extra-natives-five/include/ropeManager.h b/code/components/extra-natives-five/include/ropeManager.h index 3e7a66d801..0c08b69e21 100644 --- a/code/components/extra-natives-five/include/ropeManager.h +++ b/code/components/extra-natives-five/include/ropeManager.h @@ -13,11 +13,13 @@ class grcTexture; class ropeData { public: - virtual ~ropeData() = 0; + ropeData(); - virtual const char* GetName() = 0; + virtual ~ropeData() {} - virtual parStructure* parser_GetStructure() = 0; + void* operator new(size_t size); + + void operator delete(void* pointer); uint32_t numSections; // 0x8 float radius; // 0xC @@ -43,6 +45,10 @@ class ropeDataManager public: virtual ~ropeDataManager() = 0; + void UnloadRopeTextures(); + + void Load(); + atArray typeData; // 0x8 static ropeDataManager* GetInstance(); diff --git a/code/components/extra-natives-five/src/RopeNatives.cpp b/code/components/extra-natives-five/src/RopeNatives.cpp index 24f9a336ef..ddaa1e5ba7 100644 --- a/code/components/extra-natives-five/src/RopeNatives.cpp +++ b/code/components/extra-natives-five/src/RopeNatives.cpp @@ -5,9 +5,52 @@ #include #include #include +#include +#include +#include namespace rage { +static hook::thiscall_stub ropeData_ropeData([]() +{ + return hook::get_pattern("48 89 5C 24 ? 48 89 7C 24 ? 41 56 48 83 EC 20 8B 3D ? ? ? ? C7 41"); +}); + +ropeData::ropeData() +{ + ropeData_ropeData(this); +} + +void* ropeData::operator new(size_t size) +{ + return PoolAllocate(GetPool("ropeData")); +} + +void ropeData::operator delete(void* pointer) +{ + PoolRelease(GetPool("ropeData"), pointer); +} + +static hook::thiscall_stub ropeDataManager_UnloadRopeTextures([]() +{ + return hook::get_call(hook::get_pattern("E8 ? ? ? ? 48 8B CF E8 ? ? ? ? 48 8B 9F ? ? ? ? 48 8B 8B")); +}); + +void ropeDataManager::UnloadRopeTextures() +{ + ropeDataManager_UnloadRopeTextures(this); +} + +static hook::thiscall_stub ropeDataManager_Load([]() +{ + return hook::get_pattern("48 83 EC 48 E8 ? ? ? ? 4C 8B 0D"); +}); + +void ropeDataManager::Load() +{ + ropeDataManager_Load(this); +} + static ropeDataManager* g_ropeDataManager; ropeDataManager* ropeDataManager::GetInstance() @@ -33,6 +76,9 @@ ropeManager* ropeManager::GetInstance() } } +static int* ropeDataManager__txdStatus; +static bool g_hasCustomRopeTypes = false; + static HookFunction hookFunction([]() { { @@ -143,4 +189,94 @@ static HookFunction hookFunction([]() context.SetResult(updateOrder); }); + + { + auto location = hook::get_pattern("48 83 EC 28 33 D2 8D 4A 07"); + ropeDataManager__txdStatus = (int*)(hook::get_address(location + 16) + 1); + } + + fx::ScriptEngine::RegisterNativeHandler("REGISTER_ROPE_DATA", [](fx::ScriptContext& context) + { + int numSections = context.GetArgument(0); + float radius = context.GetArgument(1); + const char* diffuseTextureName = context.CheckArgument(2); + const char* normalMapName = context.CheckArgument(3); + float distanceMappingScale = context.GetArgument(4); + float uvScaleX = context.GetArgument(5); + float uvScaleY = context.GetArgument(6); + float specularFresnel = context.GetArgument(7); + float specularFalloff = context.GetArgument(8); + float specularIntensity = context.GetArgument(9); + float bumpiness = context.GetArgument(10); + int color = context.GetArgument(11); + + context.SetResult(-1); + + if (numSections <= 0) + { + fx::scripting::Warningf("natives", "Invalid numSections was passed to REGISTER_ROPE_DATA (%d), should greater than 0\n", numSections); + return; + } + + if (radius <= 0.0) + { + fx::scripting::Warningf("natives", "Invalid radius was passed to REGISTER_ROPE_DATA (%f), should greater than 0.0\n", radius); + return; + } + + rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance(); + if (!manager) + { + return; + } + + atPool* pool = rage::GetPool("ropeData"); + if (pool->GetCount() == pool->GetSize()) + { + fx::scripting::Warningf("natives", "Unable to allocate rope data in REGISTER_ROPE_DATA, pool is full\n"); + return; + } + + rage::ropeData* ropeData = new rage::ropeData(); + + ropeData->numSections = numSections; + ropeData->radius = radius; + ropeData->diffuseTextureNameHash = HashString(diffuseTextureName); + ropeData->normalMapNameHash = HashString(normalMapName); + ropeData->distanceMappingScale = distanceMappingScale; + ropeData->UVScaleX = uvScaleX; + ropeData->UVScaleY = uvScaleY; + ropeData->specularFresnel = specularFresnel; + ropeData->specularFalloff = specularFalloff; + ropeData->specularIntensity = specularIntensity; + ropeData->bumpiness = bumpiness; + ropeData->color = color; + + int ropeType = manager->typeData.GetCount(); + manager->typeData.Set(ropeType, ropeData); + + // If rope textures are loaded then put them back into the loading state + if (*ropeDataManager__txdStatus == 2) + { + manager->UnloadRopeTextures(); + *ropeDataManager__txdStatus = 1; + } + + g_hasCustomRopeTypes = true; + + context.SetResult(ropeType); + }); + + Instance::Get()->OnShutdownSession.Connect([]() + { + if (g_hasCustomRopeTypes) + { + rage::ropeDataManager* manager = rage::ropeDataManager::GetInstance(); + if (manager) + { + manager->Load(); + } + g_hasCustomRopeTypes = false; + } + }); }); diff --git a/data/client/citizen/common/data/gameconfig.xml b/data/client/citizen/common/data/gameconfig.xml index edcd81a8b4..82c23aed4b 100644 --- a/data/client/citizen/common/data/gameconfig.xml +++ b/data/client/citizen/common/data/gameconfig.xml @@ -692,7 +692,7 @@ ropeData - + diff --git a/ext/native-decls/RegisterRopeData.md b/ext/native-decls/RegisterRopeData.md new file mode 100644 index 0000000000..77c8456b89 --- /dev/null +++ b/ext/native-decls/RegisterRopeData.md @@ -0,0 +1,42 @@ +--- +ns: CFX +apiset: client +game: gta5 +--- +## REGISTER_ROPE_DATA + +```c +int REGISTER_ROPE_DATA(int numSections, float radius, char* diffuseTextureName, char* normalMapName, float distanceMappingScale, float uvScaleX, float uvScaleY, float specularFresnel, float specularFalloff, float specularIntensity, float bumpiness, int color); +``` + +Registers a custom rope data with the game. For guidance on what these values should be use common:/data/ropedata.xml as a reference. +Returns a rope type which can be passed into [ADD_ROPE](?_0xE832D760399EB220) to use a custom rope design. +Once a rope data is registered it can be used indefinitely and you should take caution not too register too many as to exceed the games limit. + +## Examples + +```lua +-- Create a thick steel cable rope above the players head +local ropeType = RegisterRopeData(6, 0.15, "steel_cable", "steel_cable_n", 1.0, 1.0, 8.775, 0.97, 30.0, 0.25, 1.775, 0x00FFFF00) +if ropeType ~= -1 then + local coords = GetEntityCoords(PlayerPedId()) + vector3(0.0, 0.0, 5.0) + AddRope(coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 25.0, ropeType, 10.0, 0.0, 1.0, false, false, false, 1.0, false, 0) + RopeLoadTextures() +end +``` + +## Parameters +* **numSections**: +* **radius**: +* **diffuseTextureName**: +* **normalMapName**: +* **uvScaleX**: +* **uvScaleY**: +* **specularFresnel**: +* **specularFalloff**: +* **specularIntensity**: +* **bumpiness**: +* **color**: + +## Return value +Returns a non-negative value on success, or -1 if the rope data could not be registered or an invalid argument is passed. \ No newline at end of file