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.h"
#include "Utils/MemoryMgr.GTA.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 #define DISABLE_FLA_DONATION_WINDOW 0

View file

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

View file

@ -24,6 +24,23 @@
#include "TheFLAUtils.h" #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 // SA operator delete
extern void (*GTAdelete)(void* data); extern void (*GTAdelete)(void* data);
extern const char* (*GetFrameNodeName)(RwFrame*); 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); auto GetFrameHierarchyId = AddressByVersion<int32_t(*)(RwFrame*)>(0x732A20, 0x733250, 0x76CC30);
void (CAutomobile::*CAutomobile::orgAutomobilePreRender)();
void (CPlane::*CPlane::orgPlanePreRender)(); void (CPlane::*CPlane::orgPlanePreRender)();
CVehicle* (CStoredCar::*CStoredCar::orgRestoreCar)();
static int32_t random(int32_t from, int32_t to) 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 ); 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) bool CVehicle::CustomCarPlate_TextureCreate(CVehicleModelInfo* pModelInfo)
{ {
char PlateText[CVehicleModelInfo::PLATE_TEXT_LEN+1]; char PlateText[CVehicleModelInfo::PLATE_TEXT_LEN+1];
@ -551,13 +541,14 @@ RwFrame* CAutomobile::GetTowBarFrame() const
return towBar; return towBar;
} }
void CAutomobile::PreRender() void CAutomobile::BeforePreRender()
{ {
// For rotating engine components // For rotating engine components
ms_engineCompSpeed = m_nVehicleFlags.bEngineOn ? CTimer::m_fTimeStep : 0.0f; ms_engineCompSpeed = m_nVehicleFlags.bEngineOn ? CTimer::m_fTimeStep : 0.0f;
}
(this->*(orgAutomobilePreRender))(); void CAutomobile::AfterPreRender()
{
const int32_t extID = m_nModelIndex.Get(); const int32_t extID = m_nModelIndex.Get();
if ( SVF::ModelHasFeature( extID, SVF::Feature::PHOENIX_FLUTTER ) ) 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); return GetTowBarPos_GTA(posnOut, defaultPos, trailer);
} }
CVehicle* CStoredCar::RestoreCar_SilentPatch() CVehicle* CStoredCar::RestoreCar_LoadBombOwnership(CVehicle* vehicle)
{ {
CVehicle* vehicle = (this->*(orgRestoreCar))(); if (vehicle != nullptr)
if ( vehicle == nullptr ) return nullptr;
if ( m_bombType != 0 )
{ {
// Fixup bomb stuff if (m_bombType != 0)
if ( vehicle->GetClass() == VEHICLE_AUTOMOBILE || vehicle->GetClass() == VEHICLE_BIKE )
{ {
vehicle->SetBombOnBoard( m_bombType ); // Fixup bomb stuff
vehicle->SetBombOwner( FindPlayerPed() ); if (vehicle->GetClass() == VEHICLE_AUTOMOBILE || vehicle->GetClass() == VEHICLE_BIKE)
{
vehicle->SetBombOnBoard(m_bombType);
vehicle->SetBombOwner(FindPlayerPed());
}
} }
} }
return vehicle; return vehicle;
} }

View file

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

View file

@ -9,6 +9,7 @@
#include "VehicleVC.h" #include "VehicleVC.h"
#include "SVF.h" #include "SVF.h"
#include <array>
#include <memory> #include <memory>
#include <Shlwapi.h> #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 ); namespace ZeroAmmoFix
static void __fastcall GiveWeapon_SP( void* ped, void*, unsigned int weapon, unsigned int ammo, bool flag )
{ {
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 // Fixed ammo from SCM
{ {
auto give_weapon = get_pattern( "6B C0 2E 6A 01 56 8B 3C", 0x15 ); using namespace ZeroAmmoFix;
ReadCall( give_weapon, orgGiveWeapon );
InjectHook( give_weapon, GiveWeapon_SP );
give_weapon = get_pattern( "89 F9 6A 01 55 50 E8", 6 ); std::array<void*, 2> give_weapon = {
InjectHook( give_weapon, GiveWeapon_SP ); 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);
} }