Migrated several fixes to use HookEach

Gives each hook intercepting functions its own thunk function,
so if another modification intercepts only one of X functions
patched by SilentPatch, this will now be preserved.

Also makes GTA SA's stored car bomb ownership fix work
with the Hoodlum EXE.
This commit is contained in:
Silent 2024-02-03 20:26:50 +01:00
parent de34cfec03
commit ef65dd7c2d
No known key found for this signature in database
GPG key ID: AE53149BB0C45AF1
8 changed files with 314 additions and 206 deletions

View file

@ -11,4 +11,21 @@
#include "Utils/MemoryMgr.h"
#include "Utils/MemoryMgr.GTA.h"
// Move this to ModUtils when it matures a bit more
#define HOOK_EACH_FUNC_CTR(name, ctr, origFunc, hook) \
template<std::size_t Ctr, typename Tuple, std::size_t... I, typename Func> \
static void _HookEachImpl_##name(Tuple&& tuple, std::index_sequence<I...>, Func&& f) \
{ \
(f(std::get<I>(tuple), origFunc<Ctr << 16 | I>, hook<Ctr << 16 | I>), ...); \
} \
\
template<std::size_t Ctr = ctr, typename Vars, typename Func> \
static void HookEach_##name(Vars&& vars, Func&& f) \
{ \
auto tuple = std::tuple_cat(std::forward<Vars>(vars)); \
_HookEachImpl_##name<Ctr>(std::move(tuple), std::make_index_sequence<std::tuple_size_v<decltype(tuple)>>{}, std::forward<Func>(f)); \
}
#define HOOK_EACH_FUNC(name, orig, hook) HOOK_EACH_FUNC_CTR(name, 0, orig, hook)
#define DISABLE_FLA_DONATION_WINDOW 0

View file

@ -8,6 +8,7 @@
#include "VehicleIII.h"
#include "ModelInfoIII.h"
#include <array>
#include <memory>
#include <Shlwapi.h>
@ -360,11 +361,19 @@ void __declspec(naked) AutoPilotTimerFix_III()
}
}
static void (__thiscall *orgGiveWeapon)( void* ped, unsigned int weapon, unsigned int ammo );
static void __fastcall GiveWeapon_SP( void* ped, void*, unsigned int weapon, unsigned int ammo )
namespace ZeroAmmoFix
{
if ( ammo == 0 ) ammo = 1;
orgGiveWeapon( ped, weapon, ammo );
template<std::size_t Index>
static void (__fastcall *orgGiveWeapon)(void* ped, void*, unsigned int weapon, unsigned int ammo);
template<std::size_t Index>
static void __fastcall GiveWeapon_SP(void* ped, void*, unsigned int weapon, unsigned int ammo)
{
orgGiveWeapon<Index>(ped, nullptr, weapon, std::max(1u, ammo));
}
HOOK_EACH_FUNC(GiveWeapon, orgGiveWeapon, GiveWeapon_SP);
}
@ -1128,12 +1137,13 @@ void Patch_III_Common()
// Fixed ammo from SCM
{
auto give_weapon = get_pattern( "6B C0 4F 51 8B 34", 0x13 );
ReadCall( give_weapon, orgGiveWeapon );
InjectHook( give_weapon, GiveWeapon_SP );
using namespace ZeroAmmoFix;
give_weapon = get_pattern( "89 C7 A1 ? ? ? ? 55 89 F9 50", 11 );
InjectHook( give_weapon, GiveWeapon_SP );
std::array<void*, 2> give_weapon = {
get_pattern( "6B C0 4F 51 8B 34", 0x13 ),
get_pattern( "89 C7 A1 ? ? ? ? 55 89 F9 50", 11 ),
};
HookEach_GiveWeapon(give_weapon, InterceptCall);
}

View file

@ -390,12 +390,13 @@ public:
uint8_t GetWeaponSkillForRenderWeaponPedsForPC_SAMP();
static inline void (CPed::*orgSay)(uint16_t phrase, uint32_t param2, float volume, bool param4, bool param5, bool param6);
template<uint16_t blackSample>
void Say_SampleBlackList(uint16_t phrase, uint32_t param2 = 0, float volume = 1.0f, bool param4 = false, bool param5 = false, bool param6 = false)
{
if ( !(phrase == blackSample) )
{
Say( phrase, param2, volume, param4, param5, param6 );
std::invoke(orgSay, this, phrase, param2, volume, param4, param5, param6);
}
}
};

View file

@ -1,6 +1,7 @@
#include "StdAfxSA.h"
#include <limits>
#include <algorithm>
#include <array>
#include <d3d9.h>
#include <Shlwapi.h>
#include <ShlObj.h>
@ -319,7 +320,6 @@ auto RpAnimBlendClumpGetAssociation = AddressByVersion<void*(*)(RpClump*, ui
static void (__thiscall* SetVolume)(void*,float);
static BOOL (*IsAlreadyRunning)();
static void (*TheScriptsLoad)();
static void (*WipeLocalVariableMemoryForMissionScript)();
static void (*DoSunAndMoon)();
auto WorldRemove = AddressByVersion<void(*)(CEntity*)>(0x563280, 0, 0x57D370, { "8B 06 8B 50 0C 8B CE FF D2 8A 46 36 24 07 3C 01 76 0D", -7 });
@ -453,6 +453,9 @@ static CAEWaveDecoder* __stdcall CAEWaveDecoderInit(CAEDataStream* pStream)
return new CAEWaveDecoder(pStream);
}
namespace ScriptFixes
{
static void BasketballFix(unsigned char* pBuf, int nSize)
{
for ( int i = 0, hits = 0; i < nSize && hits < 7; i++, pBuf++ )
@ -568,9 +571,8 @@ void TheScriptsLoad_BasketballFix()
QuadrupleStuntBonus();
}
void StartNewMission_SCMFixes()
static void StartNewMission_SCMFixes()
{
WipeLocalVariableMemoryForMissionScript();
InitializeScriptGlobals();
const int missionID = ScriptParams[0];
@ -596,6 +598,20 @@ void StartNewMission_SCMFixes()
}
template<std::size_t Index>
static void (*orgWipeLocalVariableMemoryForMissionScript)();
template<std::size_t Index>
static void WipeLocalVariableMemoryForMissionScript_ApplyFixes()
{
orgWipeLocalVariableMemoryForMissionScript<Index>();
StartNewMission_SCMFixes();
}
HOOK_EACH_FUNC(SCMFixes, orgWipeLocalVariableMemoryForMissionScript, WipeLocalVariableMemoryForMissionScript_ApplyFixes)
}
// 1.01 kinda fixed it
bool GetCurrentZoneLockedOrUnlocked(float fPosX, float fPosY)
{
@ -792,32 +808,43 @@ void CreateMirrorBuffers()
}
}
RwUInt32 (*orgGetMaxMultiSamplingLevels)();
RwUInt32 GetMaxMultiSamplingLevels()
namespace MSAAFixes
{
static RwUInt32 GetMaxMultiSamplingLevels_BitScan(RwUInt32 maxSamples)
{
RwUInt32 maxSamples = orgGetMaxMultiSamplingLevels();
RwUInt32 option;
_BitScanForward( (DWORD*)&option, maxSamples );
return option + 1;
}
static void (*orgChangeMultiSamplingLevels)(RwUInt32);
void ChangeMultiSamplingLevels( RwUInt32 level )
{
orgChangeMultiSamplingLevels( 1 << (level - 1) );
}
template<typename std::size_t Index>
static RwUInt32 (*orgGetMaxMultiSamplingLevels)();
static void (*orgSetMultiSamplingLevels)(RwUInt32);
void SetMultiSamplingLevels( RwUInt32 level )
template<typename std::size_t Index>
static RwUInt32 GetMaxMultiSamplingLevels()
{
orgSetMultiSamplingLevels( 1 << (level - 1) );
return GetMaxMultiSamplingLevels_BitScan(orgGetMaxMultiSamplingLevels<Index>());
}
HOOK_EACH_FUNC(GetMaxMultiSamplingLevels, orgGetMaxMultiSamplingLevels, GetMaxMultiSamplingLevels);
template<typename std::size_t Index>
static void (*orgSetOrChangeMultiSamplingLevels)(RwUInt32);
template<typename std::size_t Index>
static void SetOrChangeMultiSamplingLevels(RwUInt32 level)
{
orgSetOrChangeMultiSamplingLevels<Index>( 1 << (level - 1) );
}
HOOK_EACH_FUNC(SetOrChangeMultiSamplingLevels, orgSetOrChangeMultiSamplingLevels, SetOrChangeMultiSamplingLevels);
void MSAAText( char* buffer, const char*, DWORD level )
{
sprintf_s( buffer, 100, "%ux", 1 << level );
}
}
static RwInt32 numSavedVideoModes;
static RwInt32 (*orgGetNumVideoModes)();
@ -831,9 +858,11 @@ RwInt32 GetNumVideoModes_Retrieve()
return numSavedVideoModes;
}
namespace UnitializedCollisionDataFix
{
static void* (*orgMemMgrMalloc)(RwUInt32, RwUInt32);
void* CollisionData_MallocAndInit( RwUInt32 size, RwUInt32 hint )
static void* CollisionData_MallocAndInit( RwUInt32 size, RwUInt32 hint )
{
CColData* mem = (CColData*)orgMemMgrMalloc( size, hint );
@ -844,16 +873,21 @@ void* CollisionData_MallocAndInit( RwUInt32 size, RwUInt32 hint )
return mem;
}
template<std::size_t Index>
static void* (*orgNewAlloc)(size_t);
void* CollisionData_NewAndInit( size_t size )
template<std::size_t Index>
static void* CollisionData_NewAndInit(size_t size)
{
CColData* mem = (CColData*)orgNewAlloc( size );
CColData* mem = (CColData*)orgNewAlloc<Index>(size);
mem->m_bFlags = 0;
return mem;
}
HOOK_EACH_FUNC(CollisionDataNew, orgNewAlloc, CollisionData_NewAndInit);
}
static void (*orgEscalatorsUpdate)();
void UpdateEscalators()
@ -1460,20 +1494,27 @@ namespace VariableResets
using VarVariant = std::variant< bool*, int*, TimeNextMadDriverChaseCreated_t<float>*, ResetToTrue_t* >;
std::vector<VarVariant> GameVariablesToReset;
static void (*orgReInitGameObjectVariables)();
void ReInitGameObjectVariables()
static void ReInitOurVariables()
{
// First reinit "our" variables in case stock ones rely on those during resetting
for ( const auto& var : GameVariablesToReset )
{
std::visit( []( auto&& v ) {
*v = {};
}, var );
}, var );
}
orgReInitGameObjectVariables();
}
template<std::size_t Index>
static void (*orgReInitGameObjectVariables)();
template<std::size_t Index>
void ReInitGameObjectVariables()
{
// First reinit "our" variables in case stock ones rely on those during resetting
ReInitOurVariables();
orgReInitGameObjectVariables<Index>();
}
HOOK_EACH_FUNC(ReInitGameObjectVariables, orgReInitGameObjectVariables, ReInitGameObjectVariables);
}
namespace LightbeamFix
@ -2914,6 +2955,8 @@ BOOL InjectDelayedPatches_10()
if ( GetPrivateProfileIntW(L"SilentPatch", L"EnableScriptFixes", -1, wcModulePath) == 1 )
{
using namespace ScriptFixes;
// Gym glitch fix
Patch<WORD>(0x470B03, 0xCD8B);
Patch<DWORD>(0x470B0A, 0x8B04508B);
@ -2922,12 +2965,10 @@ BOOL InjectDelayedPatches_10()
InjectHook(0x470B05, &CRunningScript::GetDay_GymGlitch, HookType::Call);
// Basketball fix
ReadCall( 0x489A70, WipeLocalVariableMemoryForMissionScript );
ReadCall( 0x5D18F0, TheScriptsLoad );
InjectHook(0x5D18F0, TheScriptsLoad_BasketballFix);
// Fixed for Hoodlum
InjectHook(0x489A70, StartNewMission_SCMFixes);
InjectHook(0x4899F0, StartNewMission_SCMFixes);
InterceptCall( 0x5D18F0, TheScriptsLoad, TheScriptsLoad_BasketballFix );
std::array<uintptr_t, 2> wipeLocalVars = { 0x489A70, 0x4899F0 };
HookEach_SCMFixes(wipeLocalVars, InterceptCall);
}
if ( GetPrivateProfileIntW(L"SilentPatch", L"SkipIntroSplashes", -1, wcModulePath) == 1 )
@ -3360,6 +3401,8 @@ BOOL InjectDelayedPatches_11()
if ( GetPrivateProfileIntW(L"SilentPatch", L"EnableScriptFixes", -1, wcModulePath) == 1 )
{
using namespace ScriptFixes;
// Gym glitch fix
Patch<WORD>(0x470B83, 0xCD8B);
Patch<DWORD>(0x470B8A, 0x8B04508B);
@ -3368,12 +3411,10 @@ BOOL InjectDelayedPatches_11()
InjectHook(0x470B85, &CRunningScript::GetDay_GymGlitch, HookType::Call);
// Basketball fix
ReadCall( 0x489AF0, WipeLocalVariableMemoryForMissionScript );
ReadCall( 0x5D20D0, TheScriptsLoad );
InjectHook(0x5D20D0, TheScriptsLoad_BasketballFix);
// Fixed for Hoodlum
InjectHook(0x489A70, StartNewMission_SCMFixes);
InjectHook(0x489AF0, StartNewMission_SCMFixes);
InterceptCall( 0x5D20D0, TheScriptsLoad, TheScriptsLoad_BasketballFix );
std::array<uintptr_t, 2> wipeLocalVars = { 0x489A70, 0x489AF0 };
HookEach_SCMFixes(wipeLocalVars, InterceptCall);
}
if ( GetPrivateProfileIntW(L"SilentPatch", L"SkipIntroSplashes", -1, wcModulePath) == 1 )
@ -3536,6 +3577,8 @@ BOOL InjectDelayedPatches_Steam()
if ( GetPrivateProfileIntW(L"SilentPatch", L"EnableScriptFixes", -1, wcModulePath) == 1 )
{
using namespace ScriptFixes;
// Gym glitch fix
Patch<WORD>(0x476C2A, 0xCD8B);
Patch<DWORD>(0x476C31, 0x408B088B);
@ -3544,12 +3587,10 @@ BOOL InjectDelayedPatches_Steam()
InjectHook(0x476C2C, &CRunningScript::GetDay_GymGlitch, HookType::Call);
// Basketball fix
ReadCall( 0x4907AE, WipeLocalVariableMemoryForMissionScript );
ReadCall( 0x5EE017, TheScriptsLoad );
InjectHook(0x5EE017, TheScriptsLoad_BasketballFix);
// Fixed for Hoodlum
InjectHook(0x4907AE, StartNewMission_SCMFixes);
InjectHook(0x49072E, StartNewMission_SCMFixes);
InterceptCall( 0x5EE017, TheScriptsLoad, TheScriptsLoad_BasketballFix );
std::array<uintptr_t, 2> wipeLocalVars = { 0x4907AE, 0x49072E };
HookEach_SCMFixes(wipeLocalVars, InterceptCall);
}
if ( GetPrivateProfileIntW(L"SilentPatch", L"SmallSteamTexts", -1, wcModulePath) == 0 )
@ -3815,10 +3856,8 @@ void Patch_SA_10()
{
using namespace LightbeamFix;
ReadCall( 0x6A2EDA, CVehicle::orgDoHeadLightBeam );
InjectHook( 0x6A2EDA, &CVehicle::DoHeadLightBeam_LightBeamFixSaveObj );
InjectHook( 0x6A2EF2, &CVehicle::DoHeadLightBeam_LightBeamFixSaveObj );
InjectHook( 0x6BDE80, &CVehicle::DoHeadLightBeam_LightBeamFixSaveObj );
std::array<uintptr_t, 3> doHeadLightBeam = { 0x6A2EDA, 0x6A2EF2, 0x6BDE80 };
CVehicle::HookEach_DoHeadLightBeam(doHeadLightBeam, InterceptCall);
Patch( 0x6E0F37 + 2, &RenderStateWrapper<rwRENDERSTATEZWRITEENABLE>::PushStatePPtr );
Patch( 0x6E0F63 + 1, &RenderStateWrapper<rwRENDERSTATEZTESTENABLE>::PushStatePPtr );
@ -4038,28 +4077,25 @@ void Patch_SA_10()
InjectHook(0x72701D, CreateMirrorBuffers);
// Fixed MSAA options
Patch<BYTE>(0x57D126, 0xEB);
Nop(0x57D0E8, 2);
{
using namespace MSAAFixes;
Patch<BYTE>(AddressByRegion_10<BYTE*>(0x7F6C9B), 0xEB);
Patch<BYTE>(AddressByRegion_10<BYTE*>(0x7F60C6), 0xEB);
Patch(AddressByRegion_10<BYTE*>(0x7F6683), { 0x90, 0xE9 });
Patch<BYTE>(0x57D126, 0xEB);
Nop(0x57D0E8, 2);
ReadCall( 0x57D136, orgGetMaxMultiSamplingLevels );
InjectHook(0x57D136, GetMaxMultiSamplingLevels);
InjectHook(0x57D0EA, GetMaxMultiSamplingLevels);
Patch<BYTE>(AddressByRegion_10<BYTE*>(0x7F6C9B), 0xEB);
Patch<BYTE>(AddressByRegion_10<BYTE*>(0x7F60C6), 0xEB);
Patch(AddressByRegion_10<BYTE*>(0x7F6683), { 0x90, 0xE9 });
ReadCall( 0x5744FD, orgChangeMultiSamplingLevels );
InjectHook(0x5744FD, ChangeMultiSamplingLevels);
InjectHook(0x57D162, ChangeMultiSamplingLevels);
InjectHook(0x57D2A6, ChangeMultiSamplingLevels);
std::array<uintptr_t, 2> getMaxMultiSamplingLevels = { 0x57D136, 0x57D0EA };
HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall);
ReadCall( 0x746350, orgSetMultiSamplingLevels );
InjectHook(0x746350, SetMultiSamplingLevels);
Nop(0x57A0FC, 1);
InjectHook(0x57A0FD, MSAAText, HookType::Call);
std::array<uintptr_t, 4> setOrChangeMultiSamplingLevels = { 0x5744FD, 0x57D162, 0x57D2A6, 0x746350 };
HookEach_SetOrChangeMultiSamplingLevels(setOrChangeMultiSamplingLevels, InterceptCall);
Nop(0x57A0FC, 1);
InjectHook(0x57A0FD, MSAAText, HookType::Call);
}
// Fixed car collisions - car you're hitting gets proper damage now
InjectHook(0x5428EA, FixedCarDamage, HookType::Call);
@ -4067,15 +4103,16 @@ void Patch_SA_10()
// Car explosion crash with multimonitor
// Unitialized collision data breaking stencil shadows
VP::InterceptCall(ModCompat::Utils::GetFunctionAddrIfRerouted(0x40F870) + 0x63, orgMemMgrMalloc, CollisionData_MallocAndInit);
{
const uintptr_t pHoodlumCompat = ModCompat::Utils::GetFunctionAddrIfRerouted(0x40F740);
const uintptr_t pHoodlumCompat2 = ModCompat::Utils::GetFunctionAddrIfRerouted(0x40F810);
using namespace UnitializedCollisionDataFix;
const uintptr_t pNewAlloc = pHoodlumCompat + 0xC;
ReadCall( pNewAlloc, orgNewAlloc );
VP::InjectHook(pHoodlumCompat + 0xC, CollisionData_NewAndInit);
VP::InjectHook(pHoodlumCompat2 + 0xD, CollisionData_NewAndInit);
VP::InterceptCall(ModCompat::Utils::GetFunctionAddrIfRerouted(0x40F870) + 0x63, orgMemMgrMalloc, CollisionData_MallocAndInit);
std::array<uintptr_t, 2> newAndInit = {
ModCompat::Utils::GetFunctionAddrIfRerouted(0x40F740) + 0xC,
ModCompat::Utils::GetFunctionAddrIfRerouted(0x40F810) + 0xD,
};
HookEach_CollisionDataNew(newAndInit, InterceptCall);
}
@ -4173,14 +4210,14 @@ void Patch_SA_10()
// Animated Phoenix hood scoop
auto* automobilePreRender = (*(decltype(CAutomobile::orgAutomobilePreRender)**)(0x6B0AD2 + 2)) + 17;
CAutomobile::orgAutomobilePreRender = *automobilePreRender;
Patch(automobilePreRender, &CAutomobile::PreRender_Stub);
InjectHook(0x6C7E7A, &CAutomobile::PreRender_Stub);
InjectHook(0x6CEAEC, &CAutomobile::PreRender_Stub);
InjectHook(0x6CFADC, &CAutomobile::PreRender_Stub);
{
auto* automobilePreRender = (*(decltype(CAutomobile::orgAutomobilePreRender<0>)**)(0x6B0AD2 + 2)) + 17;
CAutomobile::orgAutomobilePreRender<0> = *automobilePreRender;
Patch(automobilePreRender, &CAutomobile::PreRender_SilentPatch<0>);
std::array<uintptr_t, 3> preRender = { 0x6C7E7A, 0x6CEAEC, 0x6CFADC };
CAutomobile::HookEach_PreRender(preRender, InterceptCall);
}
// Extra animations for planes
auto* planePreRender = (*(decltype(CPlane::orgPlanePreRender)**)(0x6C8E5A + 2)) + 17;
@ -4236,9 +4273,11 @@ void Patch_SA_10()
// Fixed bomb ownership/bombs saving for bikes
{
ReadCall( 0x44856A, CStoredCar::orgRestoreCar );
InjectHook( 0x44856A, &CStoredCar::RestoreCar_SilentPatch );
InjectHook( 0x4485DB, &CStoredCar::RestoreCar_SilentPatch );
std::array<uintptr_t, 2> restoreCar = {
ModCompat::Utils::GetFunctionAddrIfRerouted(0x448550) + 0x1A,
ModCompat::Utils::GetFunctionAddrIfRerouted(0x4485C0) + 0x1B,
};
CStoredCar::HookEach_RestoreCar(restoreCar, VP::InterceptCall);
}
@ -4354,19 +4393,18 @@ void Patch_SA_10()
// Play passenger's voice lines when killing peds with car, not only when hitting them damages player's vehicle
ReadCall( 0x5F05CA, CEntity::orgGetColModel );
InjectHook( 0x5F05CA, &CVehicle::PlayPedHitSample_GetColModel );
InterceptCall(0x5F05CA, CEntity::orgGetColModel, &CVehicle::PlayPedHitSample_GetColModel);
// Prevent samples from playing where they used to, so passengers don't comment on gently pushing peds
InjectHook( 0x6A8298, &CPed::Say_SampleBlackList<CONTEXT_GLOBAL_CAR_HIT_PED> );
InterceptCall(0x6A8298, CPed::orgSay, &CPed::Say_SampleBlackList<CONTEXT_GLOBAL_CAR_HIT_PED>);
// Reset variables on New Game
{
using namespace VariableResets;
ReadCall( 0x53C6DB, orgReInitGameObjectVariables );
InjectHook( 0x53C6DB, ReInitGameObjectVariables );
InjectHook( 0x53C76D, ReInitGameObjectVariables );
std::array<uintptr_t, 2> reInitGameObjectVariables = { 0x53C6DB, 0x53C76D };
HookEach_ReInitGameObjectVariables(reInitGameObjectVariables, InterceptCall);
// Variables to reset
GameVariablesToReset.emplace_back( *(bool**)(0x63E8D8+1) ); // CPlayerPed::bHasDisplayedPlayerQuitEnterCarHelpText
@ -4834,27 +4872,25 @@ void Patch_SA_11()
InjectHook(0x72784D, CreateMirrorBuffers);
// Fixed MSAA options
Patch<BYTE>(0x57D906, 0xEB);
Nop(0x57D8C8, 2);
{
using namespace MSAAFixes;
Patch<BYTE>(AddressByRegion_11<BYTE*>(0x7F759B), 0xEB);
Patch<BYTE>(AddressByRegion_11<BYTE*>(0x7F69C6), 0xEB);
Patch(AddressByRegion_11<BYTE*>(0x7F6F83), { 0x90, 0xE9 });
Patch<BYTE>(0x57D906, 0xEB);
Nop(0x57D8C8, 2);
ReadCall( 0x57D916, orgGetMaxMultiSamplingLevels );
InjectHook(0x57D916, GetMaxMultiSamplingLevels);
InjectHook(0x57D8CA, GetMaxMultiSamplingLevels);
Patch<BYTE>(AddressByRegion_11<BYTE*>(0x7F759B), 0xEB);
Patch<BYTE>(AddressByRegion_11<BYTE*>(0x7F69C6), 0xEB);
Patch(AddressByRegion_11<BYTE*>(0x7F6F83), { 0x90, 0xE9 });
ReadCall( 0x574A6D, orgChangeMultiSamplingLevels );
InjectHook(0x574A6D, ChangeMultiSamplingLevels);
InjectHook(0x57D942, ChangeMultiSamplingLevels);
InjectHook(0x57DA86, ChangeMultiSamplingLevels);
std::array<uintptr_t, 2> getMaxMultiSamplingLevels = { 0x57D916, 0x57D8CA };
HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall);
ReadCall( 0x746BD0, orgSetMultiSamplingLevels );
InjectHook(0x746BD0, SetMultiSamplingLevels);
std::array<uintptr_t, 4> setOrChangeMultiSamplingLevels = { 0x574A6D, 0x57D942, 0x57DA86, 0x746BD0 };
HookEach_SetOrChangeMultiSamplingLevels(setOrChangeMultiSamplingLevels, InterceptCall);
Nop(0x57A66C, 1);
InjectHook(0x57A66D, MSAAText, HookType::Call);
Nop(0x57A66C, 1);
InjectHook(0x57A66D, MSAAText, HookType::Call);
}
// Fixed car collisions - car you're hitting gets proper damage now
InjectHook(0x542D8A, FixedCarDamage, HookType::Call);
@ -5145,27 +5181,25 @@ void Patch_SA_Steam()
InjectHook(0x758E91, CreateMirrorBuffers);
// Fixed MSAA options
Patch<BYTE>(0x592BBB, 0xEB);
Nop(0x592B7F, 2);
{
using namespace MSAAFixes;
Patch<BYTE>(0x830C5B, 0xEB);
Patch<BYTE>(0x830086, 0xEB);
Patch(0x830643, { 0x90, 0xE9 });
Patch<BYTE>(0x592BBB, 0xEB);
Nop(0x592B7F, 2);
ReadCall( 0x592BCF, orgGetMaxMultiSamplingLevels );
InjectHook(0x592BCF, GetMaxMultiSamplingLevels);
InjectHook(0x592B81, GetMaxMultiSamplingLevels);
Patch<BYTE>(0x830C5B, 0xEB);
Patch<BYTE>(0x830086, 0xEB);
Patch(0x830643, { 0x90, 0xE9 });
ReadCall( 0x5897CD, orgChangeMultiSamplingLevels );
InjectHook(0x5897CD, ChangeMultiSamplingLevels);
InjectHook(0x592BFB, ChangeMultiSamplingLevels);
InjectHook(0x592D2E, ChangeMultiSamplingLevels);
std::array<uintptr_t, 2> getMaxMultiSamplingLevels = { 0x592BCF, 0x592B81 };
HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall);
ReadCall( 0x780206, orgSetMultiSamplingLevels );
InjectHook(0x780206, SetMultiSamplingLevels);
std::array<uintptr_t, 4> setOrChangeMultiSamplingLevels = { 0x5897CD, 0x592BFB, 0x592D2E, 0x780206 };
HookEach_SetOrChangeMultiSamplingLevels(setOrChangeMultiSamplingLevels, InterceptCall);
Patch(0x58F88C, { 0x90, 0xBA });
Patch(0x58F88E, MSAAText);
Patch(0x58F88C, { 0x90, 0xBA });
Patch(0x58F88E, MSAAText);
}
// Fixed car collisions - car you're hitting gets proper damage now
Nop(0x555AB8, 2);
@ -5174,12 +5208,14 @@ void Patch_SA_Steam()
// Car explosion crash with multimonitor
// Unitialized collision data breaking stencil shadows
ReadCall( 0x41A216, orgMemMgrMalloc );
InjectHook(0x41A216, CollisionData_MallocAndInit);
{
using namespace UnitializedCollisionDataFix;
ReadCall( 0x41A07C, orgNewAlloc );
InjectHook(0x41A07C, CollisionData_NewAndInit);
InjectHook(0x41A159, CollisionData_NewAndInit);
InterceptCall(0x41A216, orgMemMgrMalloc, CollisionData_MallocAndInit);
std::array<uintptr_t, 2> newAndInit = { 0x41A07C, 0x41A159 };
HookEach_CollisionDataNew(newAndInit, InterceptCall);
}
// Crash when entering advanced display options on a dual monitor machine after:
@ -5533,6 +5569,8 @@ void Patch_SA_NewBinaries_Common()
// Fixed MSAA options
{
using namespace MSAAFixes;
// TODO: Remove wildcards in patterns once transactional patching is implemented
auto func1 = pattern( "83 BE C8 00 00 00 04 ? ? E8" ).get_one();
void* func2 = get_pattern( "05 A3 ? ? ? ? 59", -1 );
@ -5551,17 +5589,19 @@ void Patch_SA_NewBinaries_Common()
void* changeMultiSamplingLevels2 = get_pattern( "8B 96 D0 00 00 00 52", -5 );
void* setMultiSamplingLevels = get_pattern( "83 C4 04 8B C7 5F 5E 5B 8B E5 5D C3 BB", -5 );
ReadCall( getMaxMultisamplingLevels.get<void>( -5 ), orgGetMaxMultiSamplingLevels );
InjectHook( getMaxMultisamplingLevels.get<void>( -5 ), GetMaxMultiSamplingLevels );
InjectHook(func1.get<void>( 7 + 2 ), GetMaxMultiSamplingLevels);
std::array<void*, 2> getMaxMultiSamplingLevels = {
getMaxMultisamplingLevels.get<void>( -5 ),
func1.get<void>( 7 + 2 ),
};
HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall);
ReadCall( changeMultiSamplingLevels, orgChangeMultiSamplingLevels );
InjectHook( changeMultiSamplingLevels, ChangeMultiSamplingLevels );
InjectHook( getMaxMultisamplingLevels.get<void>( -5 + 0x30 ), ChangeMultiSamplingLevels );
InjectHook( changeMultiSamplingLevels2, ChangeMultiSamplingLevels );
ReadCall( setMultiSamplingLevels, orgSetMultiSamplingLevels );
InjectHook( setMultiSamplingLevels, SetMultiSamplingLevels );
std::array<void*, 4> setOrChangeMultiSamplingLevels = {
changeMultiSamplingLevels,
getMaxMultisamplingLevels.get<void>( -5 + 0x30 ),
changeMultiSamplingLevels2,
setMultiSamplingLevels
};
HookEach_SetOrChangeMultiSamplingLevels(setOrChangeMultiSamplingLevels, InterceptCall);
auto msaaText = pattern( "48 50 68 ? ? ? ? 53" ).count(1); // Only so newsteam r1 doesn't crash
if ( msaaText.size() == 1 ) // transactional patching will obsolete this
@ -5585,16 +5625,16 @@ void Patch_SA_NewBinaries_Common()
// Car explosion crash with multimonitor
// Unitialized collision data breaking stencil shadows
{
using namespace UnitializedCollisionDataFix;
void* memMgrAlloc = get_pattern( "E8 ? ? ? ? 66 8B 55 08 8B 4D 10" );
void* newAlloc1 = get_pattern( "33 C9 83 C4 04 3B C1 74 36", -5 );
void* newAlloc2 = get_pattern( "33 C9 83 C4 04 3B C1 74 37", -5 );
std::array<void*, 2> newAlloc = {
get_pattern( "33 C9 83 C4 04 3B C1 74 36", -5 ),
get_pattern( "33 C9 83 C4 04 3B C1 74 37", -5 ),
};
ReadCall( memMgrAlloc, orgMemMgrMalloc );
InjectHook( memMgrAlloc, CollisionData_MallocAndInit );
ReadCall( newAlloc1, orgNewAlloc );
InjectHook( newAlloc1, CollisionData_NewAndInit );
InjectHook( newAlloc2, CollisionData_NewAndInit );
InterceptCall(memMgrAlloc, orgMemMgrMalloc, CollisionData_MallocAndInit);
HookEach_CollisionDataNew(newAlloc, InterceptCall);
}
// Crash when entering advanced display options on a dual monitor machine after:
@ -5730,12 +5770,11 @@ void Patch_SA_NewBinaries_Common()
using namespace VariableResets;
{
auto reinit1 = get_pattern( "E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? 38 1D" );
auto reinit2 = get_pattern( "E8 ? ? ? ? 89 1D ? ? ? ? E8 ? ? ? ? 5E" );
ReadCall( reinit1, orgReInitGameObjectVariables );
InjectHook( reinit1, ReInitGameObjectVariables );
InjectHook( reinit2, ReInitGameObjectVariables );
std::array<void*, 2> reInitGameObjectVariables = {
get_pattern( "E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ? 38 1D" ),
get_pattern( "E8 ? ? ? ? 89 1D ? ? ? ? E8 ? ? ? ? 5E" )
};
HookEach_ReInitGameObjectVariables(reInitGameObjectVariables, InterceptCall);
}
// Variables to reset
@ -5828,12 +5867,11 @@ void Patch_SA_NewBinaries_Common()
// Fixed bomb ownership/bombs saving for bikes
{
auto restoreForHideout = get_pattern( "8D 4E EE E8", 3 );
auto restoreImpoundGarage = get_pattern( "8D 4F EE E8", 3 );
ReadCall( restoreForHideout, CStoredCar::orgRestoreCar );
InjectHook( restoreForHideout, &CStoredCar::RestoreCar_SilentPatch );
InjectHook( restoreImpoundGarage, &CStoredCar::RestoreCar_SilentPatch );
std::array<void*, 2> restoreCar = {
get_pattern( "8D 4E EE E8", 3 ),
get_pattern( "8D 4F EE E8", 3 )
};
CStoredCar::HookEach_RestoreCar(restoreCar, InterceptCall);
}

View file

@ -24,6 +24,23 @@
#include "TheFLAUtils.h"
// Move this to ModUtils when it matures a bit more
#define HOOK_EACH_FUNC_CTR(name, ctr, origFunc, hook) \
template<std::size_t Ctr, typename Tuple, std::size_t... I, typename Func> \
static void _HookEachImpl_##name(Tuple&& tuple, std::index_sequence<I...>, Func&& f) \
{ \
(f(std::get<I>(tuple), origFunc<Ctr << 16 | I>, hook<Ctr << 16 | I>), ...); \
} \
\
template<std::size_t Ctr = ctr, typename Vars, typename Func> \
static void HookEach_##name(Vars&& vars, Func&& f) \
{ \
auto tuple = std::tuple_cat(std::forward<Vars>(vars)); \
_HookEachImpl_##name<Ctr>(std::move(tuple), std::make_index_sequence<std::tuple_size_v<decltype(tuple)>>{}, std::forward<Func>(f)); \
}
#define HOOK_EACH_FUNC(name, orig, hook) HOOK_EACH_FUNC_CTR(name, 0, orig, hook)
// SA operator delete
extern void (*GTAdelete)(void* data);
extern const char* (*GetFrameNodeName)(RwFrame*);

View file

@ -114,9 +114,7 @@ WRAPPER bool CVehicle::IsLawEnforcementVehicle() { VARJMP(varIsLawEnforcementVeh
auto GetFrameHierarchyId = AddressByVersion<int32_t(*)(RwFrame*)>(0x732A20, 0x733250, 0x76CC30);
void (CAutomobile::*CAutomobile::orgAutomobilePreRender)();
void (CPlane::*CPlane::orgPlanePreRender)();
CVehicle* (CStoredCar::*CStoredCar::orgRestoreCar)();
static int32_t random(int32_t from, int32_t to)
{
@ -263,14 +261,6 @@ bool CVehicle::IgnoresLightbeamFix() const
return SVF::ModelHasFeature( m_nModelIndex.Get(), SVF::Feature::_INTERNAL_NO_LIGHTBEAM_BFC_FIX );
}
void CVehicle::DoHeadLightBeam_LightBeamFixSaveObj(int type, CMatrix& m, bool right)
{
LightbeamFix::SetCurrentVehicle( this );
DoHeadLightBeam( type, m, right );
LightbeamFix::SetCurrentVehicle( nullptr );
}
bool CVehicle::CustomCarPlate_TextureCreate(CVehicleModelInfo* pModelInfo)
{
char PlateText[CVehicleModelInfo::PLATE_TEXT_LEN+1];
@ -551,13 +541,14 @@ RwFrame* CAutomobile::GetTowBarFrame() const
return towBar;
}
void CAutomobile::PreRender()
void CAutomobile::BeforePreRender()
{
// For rotating engine components
ms_engineCompSpeed = m_nVehicleFlags.bEngineOn ? CTimer::m_fTimeStep : 0.0f;
}
(this->*(orgAutomobilePreRender))();
void CAutomobile::AfterPreRender()
{
const int32_t extID = m_nModelIndex.Get();
if ( SVF::ModelHasFeature( extID, SVF::Feature::PHOENIX_FLUTTER ) )
{
@ -725,21 +716,19 @@ bool CTrailer::GetTowBarPos(CVector& posnOut, bool defaultPos, CVehicle* trailer
return GetTowBarPos_GTA(posnOut, defaultPos, trailer);
}
CVehicle* CStoredCar::RestoreCar_SilentPatch()
CVehicle* CStoredCar::RestoreCar_LoadBombOwnership(CVehicle* vehicle)
{
CVehicle* vehicle = (this->*(orgRestoreCar))();
if ( vehicle == nullptr ) return nullptr;
if ( m_bombType != 0 )
if (vehicle != nullptr)
{
// Fixup bomb stuff
if ( vehicle->GetClass() == VEHICLE_AUTOMOBILE || vehicle->GetClass() == VEHICLE_BIKE )
if (m_bombType != 0)
{
vehicle->SetBombOnBoard( m_bombType );
vehicle->SetBombOwner( FindPlayerPed() );
// Fixup bomb stuff
if (vehicle->GetClass() == VEHICLE_AUTOMOBILE || vehicle->GetClass() == VEHICLE_BIKE)
{
vehicle->SetBombOnBoard(m_bombType);
vehicle->SetBombOwner(FindPlayerPed());
}
}
}
return vehicle;
}

View file

@ -273,20 +273,26 @@ public:
static void SetComponentRotation( RwFrame* component, eRotAxis axis, float angle, bool absolute = true );
static void SetComponentAtomicAlpha(RpAtomic* pAtomic, int nAlpha);
static inline void (CVehicle::*orgDoHeadLightBeam)( int type, CMatrix& m, bool right );
static inline int8_t ms_lightbeamFixOverride = 0, ms_rotorFixOverride = 0; // 0 - normal, 1 - always on, -1 - always off
bool IgnoresLightbeamFix() const;
bool IgnoresRotorFix() const;
bool IsOpenTopCarOrQuadbike() const;
void DoHeadLightBeam( int type, CMatrix& m, bool right )
private:
template<std::size_t Index>
static void (CVehicle::*orgDoHeadLightBeam)(int type, CMatrix& m, bool right);
template<std::size_t Index>
void DoHeadLightBeam_LightBeamFixSaveObj(int type, CMatrix& m, bool right)
{
std::invoke( orgDoHeadLightBeam, this, type, m, right );
LightbeamFix::SetCurrentVehicle(this);
std::invoke(orgDoHeadLightBeam<Index>, this, type, m, right);
LightbeamFix::SetCurrentVehicle(nullptr);
}
void DoHeadLightBeam_LightBeamFixSaveObj( int type, CMatrix& m, bool right );
public:
HOOK_EACH_FUNC(DoHeadLightBeam, orgDoHeadLightBeam, &DoHeadLightBeam_LightBeamFixSaveObj);
};
class NOVMT CAutomobile : public CVehicle
@ -309,18 +315,27 @@ public:
BYTE __pad3[44];
public:
inline void PreRender_Stub()
{ CAutomobile::PreRender(); }
template<std::size_t Index>
static void (CAutomobile::*orgAutomobilePreRender)();
virtual void PreRender() override;
template<std::size_t Index>
void PreRender_SilentPatch()
{
BeforePreRender();
std::invoke(orgAutomobilePreRender<Index>, this);
AfterPreRender();
}
HOOK_EACH_FUNC_CTR(PreRender, 1, orgAutomobilePreRender, &PreRender_SilentPatch);
void Fix_SilentPatch();
RwFrame* GetTowBarFrame() const;
static void (CAutomobile::*orgAutomobilePreRender)();
static float ms_engineCompSpeed;
private:
void BeforePreRender();
void AfterPreRender();
void ResetFrames();
void ProcessPhoenixBlower( int32_t modelID );
void ProcessSweeper();
@ -397,10 +412,21 @@ private:
uint8_t m_nitro;
int8_t m_angleX, m_angleY, m_angleZ;
public:
private:
template<std::size_t Index>
static CVehicle* (CStoredCar::*orgRestoreCar)();
template<std::size_t Index>
CVehicle* RestoreCar_SilentPatch()
{
return RestoreCar_LoadBombOwnership(std::invoke(orgRestoreCar<Index>, this));
}
CVehicle* RestoreCar_SilentPatch();
public:
HOOK_EACH_FUNC(RestoreCar, orgRestoreCar, &RestoreCar_SilentPatch);
private:
CVehicle* RestoreCar_LoadBombOwnership(CVehicle* vehicle);
};
void ReadRotorFixExceptions(const wchar_t* pPath);

View file

@ -9,6 +9,7 @@
#include "VehicleVC.h"
#include "SVF.h"
#include <array>
#include <memory>
#include <Shlwapi.h>
@ -255,11 +256,19 @@ void __declspec(naked) AutoPilotTimerFix_VC()
}
}
static void (__thiscall *orgGiveWeapon)( void* ped, unsigned int weapon, unsigned int ammo, bool flag );
static void __fastcall GiveWeapon_SP( void* ped, void*, unsigned int weapon, unsigned int ammo, bool flag )
namespace ZeroAmmoFix
{
if ( ammo == 0 ) ammo = 1;
orgGiveWeapon( ped, weapon, ammo, flag );
template<std::size_t Index>
static void (__fastcall *orgGiveWeapon)(void* ped, void*, unsigned int weapon, unsigned int ammo);
template<std::size_t Index>
static void __fastcall GiveWeapon_SP(void* ped, void*, unsigned int weapon, unsigned int ammo)
{
orgGiveWeapon<Index>(ped, nullptr, weapon, std::max(1u, ammo));
}
HOOK_EACH_FUNC(GiveWeapon, orgGiveWeapon, GiveWeapon_SP);
}
@ -1029,12 +1038,13 @@ void Patch_VC_Common()
// Fixed ammo from SCM
{
auto give_weapon = get_pattern( "6B C0 2E 6A 01 56 8B 3C", 0x15 );
ReadCall( give_weapon, orgGiveWeapon );
InjectHook( give_weapon, GiveWeapon_SP );
using namespace ZeroAmmoFix;
give_weapon = get_pattern( "89 F9 6A 01 55 50 E8", 6 );
InjectHook( give_weapon, GiveWeapon_SP );
std::array<void*, 2> give_weapon = {
get_pattern( "6B C0 2E 6A 01 56 8B 3C", 0x15 ),
get_pattern( "89 F9 6A 01 55 50 E8", 6 ),
};
HookEach_GiveWeapon(give_weapon, InterceptCall);
}