Skip to content

Commit

Permalink
Merge Fix crash in ApplyForceToEntity (mr-586)
Browse files Browse the repository at this point in the history
bb18cbf - fix(natives/five): check entity validity before calling ApplyForceToEntity
535fc6b - fix(game/five): improve fwEntity::IsOfTypeH on b2802+
  • Loading branch information
prikolium-cfx committed Nov 5, 2024
2 parents 63bbcd5 + bb18cbf commit 675069c
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 37 deletions.
5 changes: 5 additions & 0 deletions code/components/extra-natives-five/include/Local.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ class CPed : public fwEntity
{

};

class CPhysical : fwEntity
{

};
29 changes: 29 additions & 0 deletions code/components/extra-natives-five/src/NativeFixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint32_t>(0);
const auto entity = rage::fwScriptGuid::GetBaseFromGuid(handle);

if (entity && entity->IsOfType<CPhysical>())
{
handler(ctx);
}
});
}

static HookFunction hookFunction([]()
{
g_fireInstances = (std::array<FireInfoEntry, 128>*)(hook::get_address<uintptr_t>(hook::get_pattern("74 47 48 8D 0D ? ? ? ? 48 8B D3", 2), 3, 7) + 0x10);
Expand Down Expand Up @@ -460,5 +487,7 @@ static HookFunction hookFunction([]()
FixReplaceHudColour();

FixDrawMarker();

FixApplyForceToEntity();
});
});
39 changes: 2 additions & 37 deletions code/components/gta-streaming-five/include/EntitySystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,43 +488,8 @@ class STREAMING_EXPORT fwEntity : public rage::fwRefAwareBase
return (this->*(get_member<TFn>(vtbl[MapEntityMethod<offset>() / 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)
{
Expand Down
77 changes: 77 additions & 0 deletions code/components/gta-streaming-five/src/EntitySystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint32_t, void*> 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<uint32_t*>(hook::get_pattern("48 8B D3 48 89 73 08 E8", -64));

if (xbr::IsGameBuildOrGreater<2802>())
{
FindNewTypeIds();
}
});

static hook::cdecl_stub<fwArchetype*(uint32_t nameHash, rage::fwModelId& id)> getArchetype([]()
Expand Down

0 comments on commit 675069c

Please sign in to comment.