diff --git a/SilentPatch/StdAfx.h b/SilentPatch/StdAfx.h index dd987aa..53e00fe 100644 --- a/SilentPatch/StdAfx.h +++ b/SilentPatch/StdAfx.h @@ -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 \ + static void _HookEachImpl_##name(Tuple&& tuple, std::index_sequence, Func&& f) \ + { \ + (f(std::get(tuple), origFunc, hook), ...); \ + } \ + \ + template \ + static void HookEach_##name(Vars&& vars, Func&& f) \ + { \ + auto tuple = std::tuple_cat(std::forward(vars)); \ + _HookEachImpl_##name(std::move(tuple), std::make_index_sequence>{}, std::forward(f)); \ + } + +#define HOOK_EACH_FUNC(name, orig, hook) HOOK_EACH_FUNC_CTR(name, 0, orig, hook) + #define DISABLE_FLA_DONATION_WINDOW 0 diff --git a/SilentPatchIII/SilentPatchIII.cpp b/SilentPatchIII/SilentPatchIII.cpp index 7469d7b..0c49bb2 100644 --- a/SilentPatchIII/SilentPatchIII.cpp +++ b/SilentPatchIII/SilentPatchIII.cpp @@ -8,6 +8,7 @@ #include "VehicleIII.h" #include "ModelInfoIII.h" +#include #include #include @@ -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 +static void (__fastcall *orgGiveWeapon)(void* ped, void*, unsigned int weapon, unsigned int ammo); + +template +static void __fastcall GiveWeapon_SP(void* ped, void*, unsigned int weapon, unsigned int ammo) +{ + orgGiveWeapon(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 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); } diff --git a/SilentPatchSA/PedSA.h b/SilentPatchSA/PedSA.h index f406ea7..64ee57e 100644 --- a/SilentPatchSA/PedSA.h +++ b/SilentPatchSA/PedSA.h @@ -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 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); } } }; diff --git a/SilentPatchSA/SilentPatchSA.cpp b/SilentPatchSA/SilentPatchSA.cpp index a9056f7..c35dd42 100644 --- a/SilentPatchSA/SilentPatchSA.cpp +++ b/SilentPatchSA/SilentPatchSA.cpp @@ -1,6 +1,7 @@ #include "StdAfxSA.h" #include #include +#include #include #include #include @@ -319,7 +320,6 @@ auto RpAnimBlendClumpGetAssociation = AddressByVersion(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 +static void (*orgWipeLocalVariableMemoryForMissionScript)(); + +template +static void WipeLocalVariableMemoryForMissionScript_ApplyFixes() +{ + orgWipeLocalVariableMemoryForMissionScript(); + 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 +static RwUInt32 (*orgGetMaxMultiSamplingLevels)(); -static void (*orgSetMultiSamplingLevels)(RwUInt32); -void SetMultiSamplingLevels( RwUInt32 level ) +template +static RwUInt32 GetMaxMultiSamplingLevels() { - orgSetMultiSamplingLevels( 1 << (level - 1) ); + return GetMaxMultiSamplingLevels_BitScan(orgGetMaxMultiSamplingLevels()); } +HOOK_EACH_FUNC(GetMaxMultiSamplingLevels, orgGetMaxMultiSamplingLevels, GetMaxMultiSamplingLevels); + +template +static void (*orgSetOrChangeMultiSamplingLevels)(RwUInt32); + +template +static void SetOrChangeMultiSamplingLevels(RwUInt32 level) +{ + orgSetOrChangeMultiSamplingLevels( 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 static void* (*orgNewAlloc)(size_t); -void* CollisionData_NewAndInit( size_t size ) + +template +static void* CollisionData_NewAndInit(size_t size) { - CColData* mem = (CColData*)orgNewAlloc( size ); + CColData* mem = (CColData*)orgNewAlloc(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*, ResetToTrue_t* >; std::vector 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 + static void (*orgReInitGameObjectVariables)(); + + template + void ReInitGameObjectVariables() + { + // First reinit "our" variables in case stock ones rely on those during resetting + ReInitOurVariables(); + orgReInitGameObjectVariables(); + } + 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(0x470B03, 0xCD8B); Patch(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 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(0x470B83, 0xCD8B); Patch(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 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(0x476C2A, 0xCD8B); Patch(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 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 doHeadLightBeam = { 0x6A2EDA, 0x6A2EF2, 0x6BDE80 }; + CVehicle::HookEach_DoHeadLightBeam(doHeadLightBeam, InterceptCall); Patch( 0x6E0F37 + 2, &RenderStateWrapper::PushStatePPtr ); Patch( 0x6E0F63 + 1, &RenderStateWrapper::PushStatePPtr ); @@ -4038,28 +4077,25 @@ void Patch_SA_10() InjectHook(0x72701D, CreateMirrorBuffers); // Fixed MSAA options - Patch(0x57D126, 0xEB); - Nop(0x57D0E8, 2); + { + using namespace MSAAFixes; - Patch(AddressByRegion_10(0x7F6C9B), 0xEB); - Patch(AddressByRegion_10(0x7F60C6), 0xEB); - Patch(AddressByRegion_10(0x7F6683), { 0x90, 0xE9 }); + Patch(0x57D126, 0xEB); + Nop(0x57D0E8, 2); - ReadCall( 0x57D136, orgGetMaxMultiSamplingLevels ); - InjectHook(0x57D136, GetMaxMultiSamplingLevels); - InjectHook(0x57D0EA, GetMaxMultiSamplingLevels); + Patch(AddressByRegion_10(0x7F6C9B), 0xEB); + Patch(AddressByRegion_10(0x7F60C6), 0xEB); + Patch(AddressByRegion_10(0x7F6683), { 0x90, 0xE9 }); - ReadCall( 0x5744FD, orgChangeMultiSamplingLevels ); - InjectHook(0x5744FD, ChangeMultiSamplingLevels); - InjectHook(0x57D162, ChangeMultiSamplingLevels); - InjectHook(0x57D2A6, ChangeMultiSamplingLevels); + std::array getMaxMultiSamplingLevels = { 0x57D136, 0x57D0EA }; + HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall); - ReadCall( 0x746350, orgSetMultiSamplingLevels ); - InjectHook(0x746350, SetMultiSamplingLevels); - - Nop(0x57A0FC, 1); - InjectHook(0x57A0FD, MSAAText, HookType::Call); + std::array 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 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 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 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 ); + InterceptCall(0x6A8298, CPed::orgSay, &CPed::Say_SampleBlackList); // Reset variables on New Game { using namespace VariableResets; - ReadCall( 0x53C6DB, orgReInitGameObjectVariables ); - InjectHook( 0x53C6DB, ReInitGameObjectVariables ); - InjectHook( 0x53C76D, ReInitGameObjectVariables ); + std::array 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(0x57D906, 0xEB); - Nop(0x57D8C8, 2); + { + using namespace MSAAFixes; - Patch(AddressByRegion_11(0x7F759B), 0xEB); - Patch(AddressByRegion_11(0x7F69C6), 0xEB); - Patch(AddressByRegion_11(0x7F6F83), { 0x90, 0xE9 }); + Patch(0x57D906, 0xEB); + Nop(0x57D8C8, 2); - ReadCall( 0x57D916, orgGetMaxMultiSamplingLevels ); - InjectHook(0x57D916, GetMaxMultiSamplingLevels); - InjectHook(0x57D8CA, GetMaxMultiSamplingLevels); + Patch(AddressByRegion_11(0x7F759B), 0xEB); + Patch(AddressByRegion_11(0x7F69C6), 0xEB); + Patch(AddressByRegion_11(0x7F6F83), { 0x90, 0xE9 }); - ReadCall( 0x574A6D, orgChangeMultiSamplingLevels ); - InjectHook(0x574A6D, ChangeMultiSamplingLevels); - InjectHook(0x57D942, ChangeMultiSamplingLevels); - InjectHook(0x57DA86, ChangeMultiSamplingLevels); + std::array getMaxMultiSamplingLevels = { 0x57D916, 0x57D8CA }; + HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall); - ReadCall( 0x746BD0, orgSetMultiSamplingLevels ); - InjectHook(0x746BD0, SetMultiSamplingLevels); + std::array 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(0x592BBB, 0xEB); - Nop(0x592B7F, 2); + { + using namespace MSAAFixes; - Patch(0x830C5B, 0xEB); - Patch(0x830086, 0xEB); - Patch(0x830643, { 0x90, 0xE9 }); + Patch(0x592BBB, 0xEB); + Nop(0x592B7F, 2); - ReadCall( 0x592BCF, orgGetMaxMultiSamplingLevels ); - InjectHook(0x592BCF, GetMaxMultiSamplingLevels); - InjectHook(0x592B81, GetMaxMultiSamplingLevels); + Patch(0x830C5B, 0xEB); + Patch(0x830086, 0xEB); + Patch(0x830643, { 0x90, 0xE9 }); - ReadCall( 0x5897CD, orgChangeMultiSamplingLevels ); - InjectHook(0x5897CD, ChangeMultiSamplingLevels); - InjectHook(0x592BFB, ChangeMultiSamplingLevels); - InjectHook(0x592D2E, ChangeMultiSamplingLevels); + std::array getMaxMultiSamplingLevels = { 0x592BCF, 0x592B81 }; + HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall); - ReadCall( 0x780206, orgSetMultiSamplingLevels ); - InjectHook(0x780206, SetMultiSamplingLevels); + std::array 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 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( -5 ), orgGetMaxMultiSamplingLevels ); - InjectHook( getMaxMultisamplingLevels.get( -5 ), GetMaxMultiSamplingLevels ); - InjectHook(func1.get( 7 + 2 ), GetMaxMultiSamplingLevels); + std::array getMaxMultiSamplingLevels = { + getMaxMultisamplingLevels.get( -5 ), + func1.get( 7 + 2 ), + }; + HookEach_GetMaxMultiSamplingLevels(getMaxMultiSamplingLevels, InterceptCall); - ReadCall( changeMultiSamplingLevels, orgChangeMultiSamplingLevels ); - InjectHook( changeMultiSamplingLevels, ChangeMultiSamplingLevels ); - InjectHook( getMaxMultisamplingLevels.get( -5 + 0x30 ), ChangeMultiSamplingLevels ); - InjectHook( changeMultiSamplingLevels2, ChangeMultiSamplingLevels ); - - ReadCall( setMultiSamplingLevels, orgSetMultiSamplingLevels ); - InjectHook( setMultiSamplingLevels, SetMultiSamplingLevels ); + std::array setOrChangeMultiSamplingLevels = { + changeMultiSamplingLevels, + getMaxMultisamplingLevels.get( -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 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 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 restoreCar = { + get_pattern( "8D 4E EE E8", 3 ), + get_pattern( "8D 4F EE E8", 3 ) + }; + CStoredCar::HookEach_RestoreCar(restoreCar, InterceptCall); } diff --git a/SilentPatchSA/StdAfxSA.h b/SilentPatchSA/StdAfxSA.h index e15ad29..da59f67 100644 --- a/SilentPatchSA/StdAfxSA.h +++ b/SilentPatchSA/StdAfxSA.h @@ -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 \ + static void _HookEachImpl_##name(Tuple&& tuple, std::index_sequence, Func&& f) \ + { \ + (f(std::get(tuple), origFunc, hook), ...); \ + } \ + \ + template \ + static void HookEach_##name(Vars&& vars, Func&& f) \ + { \ + auto tuple = std::tuple_cat(std::forward(vars)); \ + _HookEachImpl_##name(std::move(tuple), std::make_index_sequence>{}, std::forward(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*); diff --git a/SilentPatchSA/VehicleSA.cpp b/SilentPatchSA/VehicleSA.cpp index 4637ff1..fd448ca 100644 --- a/SilentPatchSA/VehicleSA.cpp +++ b/SilentPatchSA/VehicleSA.cpp @@ -114,9 +114,7 @@ WRAPPER bool CVehicle::IsLawEnforcementVehicle() { VARJMP(varIsLawEnforcementVeh auto GetFrameHierarchyId = AddressByVersion(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; } - diff --git a/SilentPatchSA/VehicleSA.h b/SilentPatchSA/VehicleSA.h index f0704bc..d0fdcb9 100644 --- a/SilentPatchSA/VehicleSA.h +++ b/SilentPatchSA/VehicleSA.h @@ -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 + static void (CVehicle::*orgDoHeadLightBeam)(int type, CMatrix& m, bool right); + + template + void DoHeadLightBeam_LightBeamFixSaveObj(int type, CMatrix& m, bool right) { - std::invoke( orgDoHeadLightBeam, this, type, m, right ); + LightbeamFix::SetCurrentVehicle(this); + std::invoke(orgDoHeadLightBeam, 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 + static void (CAutomobile::*orgAutomobilePreRender)(); - virtual void PreRender() override; + template + void PreRender_SilentPatch() + { + BeforePreRender(); + std::invoke(orgAutomobilePreRender, 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 static CVehicle* (CStoredCar::*orgRestoreCar)(); + + template + CVehicle* RestoreCar_SilentPatch() + { + return RestoreCar_LoadBombOwnership(std::invoke(orgRestoreCar, this)); + } - CVehicle* RestoreCar_SilentPatch(); +public: + HOOK_EACH_FUNC(RestoreCar, orgRestoreCar, &RestoreCar_SilentPatch); + +private: + CVehicle* RestoreCar_LoadBombOwnership(CVehicle* vehicle); }; void ReadRotorFixExceptions(const wchar_t* pPath); diff --git a/SilentPatchVC/SilentPatchVC.cpp b/SilentPatchVC/SilentPatchVC.cpp index 79a1b45..599d384 100644 --- a/SilentPatchVC/SilentPatchVC.cpp +++ b/SilentPatchVC/SilentPatchVC.cpp @@ -9,6 +9,7 @@ #include "VehicleVC.h" #include "SVF.h" +#include #include #include @@ -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 +static void (__fastcall *orgGiveWeapon)(void* ped, void*, unsigned int weapon, unsigned int ammo); + +template +static void __fastcall GiveWeapon_SP(void* ped, void*, unsigned int weapon, unsigned int ammo) +{ + orgGiveWeapon(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 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); }