diff --git a/code/components/extra-natives-five/include/Local.h b/code/components/extra-natives-five/include/Local.h index 6a2ed536f6..b8938e7ecf 100644 --- a/code/components/extra-natives-five/include/Local.h +++ b/code/components/extra-natives-five/include/Local.h @@ -19,3 +19,8 @@ class CPed : public fwEntity { }; + +class CPhysical : fwEntity +{ + +}; diff --git a/code/components/extra-natives-five/src/NativeFixes.cpp b/code/components/extra-natives-five/src/NativeFixes.cpp index 3e7ab6e1cd..2f52e7a557 100644 --- a/code/components/extra-natives-five/src/NativeFixes.cpp +++ b/code/components/extra-natives-five/src/NativeFixes.cpp @@ -426,6 +426,33 @@ static void FixDrawMarker() } } +static void FixApplyForceToEntity() +{ + // ApplyForceToEntity checks if the entity is valid, but then does some stuff outside of that check + + constexpr const uint64_t nativeHash = 0xC5F68BE9613E2D18; // APPLY_FORCE_TO_ENTITY + + const auto originalHandler = fx::ScriptEngine::GetNativeHandler(nativeHash); + + if (!originalHandler) + { + return; + } + + const auto handler = *originalHandler; + + fx::ScriptEngine::RegisterNativeHandler(nativeHash, [handler](fx::ScriptContext& ctx) + { + const auto handle = ctx.GetArgument(0); + const auto entity = rage::fwScriptGuid::GetBaseFromGuid(handle); + + if (entity && entity->IsOfType()) + { + handler(ctx); + } + }); +} + 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); @@ -460,5 +487,7 @@ static HookFunction hookFunction([]() FixReplaceHudColour(); FixDrawMarker(); + + FixApplyForceToEntity(); }); }); diff --git a/code/components/gta-streaming-five/include/EntitySystem.h b/code/components/gta-streaming-five/include/EntitySystem.h index 3fb83a4ff4..227ea60d53 100644 --- a/code/components/gta-streaming-five/include/EntitySystem.h +++ b/code/components/gta-streaming-five/include/EntitySystem.h @@ -488,43 +488,8 @@ class STREAMING_EXPORT fwEntity : public rage::fwRefAwareBase return (this->*(get_member(vtbl[MapEntityMethod() / 8])))(__VA_ARGS__); private: - inline bool IsOfTypeH(uint32_t hash) - { - if (xbr::IsGameBuildOrGreater<2802>()) - { - return (GetTypeHash() == hash); - } - - if (xbr::IsGameBuildOrGreater<2189>()) - { - return IsOfTypeRef(hash); - } - - FORWARD_FUNC(IsOfTypeH, 0x8, hash); - } - - inline bool IsOfTypeRef(const uint32_t& hash) - { - if (xbr::IsGameBuildOrGreater<2802>()) - { - return (GetTypeHash() == hash); - } - - FORWARD_FUNC(IsOfTypeRef, 0x8, hash); - } - -private: - inline uint32_t GetTypeHash() - { - if (!xbr::IsGameBuildOrGreater<2802>()) - { - assert(false); - } - - // #TODO2802: new RTTI method, a bit weird but works, definitely need to make it less dirty at some point... - return (*(uint32_t(__fastcall**)(char*))(*(char**)this + 0x10))((char*)this); - } - + bool IsOfTypeH(uint32_t hash); + public: inline void SetupFromEntityDef(fwEntityDef* entityDef, fwArchetype* archetype, uint32_t a3) { diff --git a/code/components/gta-streaming-five/src/EntitySystem.cpp b/code/components/gta-streaming-five/src/EntitySystem.cpp index 1e0c80fe97..e9bf94d806 100644 --- a/code/components/gta-streaming-five/src/EntitySystem.cpp +++ b/code/components/gta-streaming-five/src/EntitySystem.cpp @@ -50,9 +50,86 @@ uint32_t fwSceneUpdateExtension::GetClassId() return *fwSceneUpdateExtension_classId; } +static PIMAGE_SECTION_HEADER GetSection(std::string_view name, int off = 0) +{ + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL); + PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((char*)dosHeader + dosHeader->e_lfanew); + IMAGE_SECTION_HEADER* section = IMAGE_FIRST_SECTION(ntHeader); + + int matchIdx = -1; + + for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) + { + if (name == (char*)section->Name) + { + matchIdx++; + + if (off == matchIdx) + { + return section; + } + } + + section++; + } + + return NULL; +} + +struct NewTypeId +{ + NewTypeId* Self; + uint32_t Hash; +}; + +static std::unordered_map s_newTypesIds; + +static void FindNewTypeIds() +{ + PIMAGE_SECTION_HEADER section = GetSection(".data"); + + auto here = (uintptr_t)GetModuleHandle(NULL) + section->VirtualAddress; + auto end = here + section->Misc.VirtualSize - sizeof(NewTypeId); + + for (; here <= end; here += sizeof(void*)) + { + auto type = (NewTypeId*)here; + + if (type->Self != type) + continue; + + // This'll probably find some false positives, but that shouldn't matter + + s_newTypesIds.emplace(type->Hash, type); + } +} + +bool fwEntity::IsOfTypeH(uint32_t hash) +{ + void** vtbl = *(void***)this; + + if (xbr::IsGameBuildOrGreater<2802>()) + { + void* ptr = s_newTypesIds.at(hash); + return ((bool(*)(fwEntity*, void*)) vtbl[5])(this, ptr); + } + + if (xbr::IsGameBuildOrGreater<2189>()) + { + return ((bool(*)(fwEntity*, const uint32_t&)) vtbl[1])(this, hash); + } + + return ((bool(*)(fwEntity*, uint32_t)) vtbl[1])(this, hash); +} + static HookFunction hookFunction([] { fwSceneUpdateExtension_classId = hook::get_address(hook::get_pattern("48 8B D3 48 89 73 08 E8", -64)); + + if (xbr::IsGameBuildOrGreater<2802>()) + { + FindNewTypeIds(); + } }); static hook::cdecl_stub getArchetype([]()