2014-08-27 01:20:24 +02:00
# include "StdAfx.h"
2019-12-11 21:37:18 +01:00
# include "Maths.h"
2014-08-27 01:20:24 +02:00
# include "Timer.h"
2017-09-18 22:13:02 +02:00
# include "Common.h"
2017-09-27 23:12:05 +02:00
# include "Common_ddraw.h"
2021-07-26 23:52:20 +02:00
# include "Desktop.h"
2024-05-23 20:07:34 +02:00
# include "EntityVC.h"
2018-03-27 22:28:45 +02:00
# include "ModelInfoVC.h"
2019-12-15 21:37:05 +01:00
# include "VehicleVC.h"
2019-12-25 16:02:42 +01:00
# include "SVF.h"
2024-03-09 17:04:12 +01:00
# include "RWUtils.hpp"
2024-05-09 19:11:20 +02:00
# include "TheFLAUtils.h"
2024-05-18 19:09:14 +02:00
# include "ParseUtils.hpp"
2024-05-21 18:34:44 +02:00
# include "Random.h"
2014-08-27 01:20:24 +02:00
2024-02-03 20:26:50 +01:00
# include <array>
2017-09-16 23:32:17 +02:00
# include <memory>
2019-08-24 22:35:35 +02:00
# include <Shlwapi.h>
2024-04-29 20:43:25 +02:00
# include <time.h>
2019-08-24 22:35:35 +02:00
2024-03-09 17:04:12 +01:00
# include "Utils/ModuleList.hpp"
2024-01-28 15:26:24 +01:00
# include "Utils/Patterns.h"
# include "Utils/ScopedUnprotect.hpp"
2024-03-17 20:52:52 +01:00
# include "Utils/HookEach.hpp"
2024-05-18 19:09:14 +02:00
# include "Utils/DelimStringReader.h"
2024-01-28 15:26:24 +01:00
2019-08-24 22:35:35 +02:00
# include "debugmenu_public.h"
# pragma comment(lib, "shlwapi.lib")
2017-09-16 23:32:17 +02:00
2024-05-11 12:42:28 +02:00
EXTERN_C IMAGE_DOS_HEADER __ImageBase ;
2019-12-08 20:15:25 +01:00
// ============= Mod compatibility stuff =============
namespace ModCompat
{
namespace Utils
{
template < typename AT >
HMODULE GetModuleHandleFromAddress ( AT address )
{
HMODULE result = nullptr ;
GetModuleHandleEx ( GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS , LPCTSTR ( address ) , & result ) ;
return result ;
}
}
}
2014-08-27 01:20:24 +02:00
struct RsGlobalType
{
const char * AppName ;
unsigned int unkWidth , unkHeight ;
2014-09-09 00:21:34 +02:00
signed int MaximumWidth ;
signed int MaximumHeight ;
2014-08-27 01:20:24 +02:00
unsigned int frameLimit ;
BOOL quit ;
void * ps ;
void * keyboard ;
void * mouse ;
void * pad ;
} ;
2019-08-24 22:35:35 +02:00
DebugMenuAPI gDebugMenuAPI ;
2014-08-27 01:20:24 +02:00
static RsGlobalType * RsGlobal ;
static const void * SubtitlesShadowFix_JumpBack ;
2024-05-23 20:07:34 +02:00
void * ( * GetModelInfo ) ( const char * , int * ) ;
2024-03-09 17:04:12 +01:00
// This is actually CBaseModelInfo, but we currently don't have it defined
CVehicleModelInfo * * & ms_modelInfoPtrs = * hook : : get_pattern < CVehicleModelInfo * * > ( " 8B 15 ? ? ? ? 8D 04 24 " , 2 ) ;
int32_t & numModelInfos = * hook : : get_pattern < int32_t > ( " 81 FD ? ? ? ? 7C B7 " , 2 ) ;
2014-09-14 20:02:57 +02:00
inline float GetWidthMult ( )
{
static const float & ResolutionWidthMult = * * AddressByVersion < float * * > ( 0x5FA15E , 0x5FA17E , 0x5F9DBE ) ;
return ResolutionWidthMult ;
}
inline float GetHeightMult ( )
{
static const float & ResolutionHeightMult = * * AddressByVersion < float * * > ( 0x5FA148 , 0x5FA168 , 0x5F9DA8 ) ;
return ResolutionHeightMult ;
}
2015-09-20 22:25:10 +02:00
static bool bGameInFocus = true ;
static LRESULT ( CALLBACK * * OldWndProc ) ( HWND , UINT , WPARAM , LPARAM ) ;
LRESULT CALLBACK CustomWndProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
switch ( uMsg )
{
case WM_KILLFOCUS :
bGameInFocus = false ;
break ;
case WM_SETFOCUS :
bGameInFocus = true ;
break ;
}
return ( * OldWndProc ) ( hwnd , uMsg , wParam , lParam ) ;
}
2017-09-12 22:57:30 +02:00
static auto * const pCustomWndProc = CustomWndProc ;
2015-09-20 22:25:10 +02:00
static void ( * const RsMouseSetPos ) ( RwV2d * ) = AddressByVersion < void ( * ) ( RwV2d * ) > ( 0x6030C0 , 0x6030A0 , 0x602CE0 ) ;
2018-01-10 01:08:41 +01:00
static void ( * orgConstructRenderList ) ( ) ;
2015-09-20 22:25:10 +02:00
void ResetMousePos ( )
{
if ( bGameInFocus )
{
RwV2d vecPos = { RsGlobal - > MaximumWidth * 0.5f , RsGlobal - > MaximumHeight * 0.5f } ;
RsMouseSetPos ( & vecPos ) ;
}
2018-01-10 01:08:41 +01:00
orgConstructRenderList ( ) ;
2015-09-20 22:25:10 +02:00
}
2014-08-27 01:20:24 +02:00
void __stdcall Recalculate ( float & fX , float & fY , signed int nShadow )
{
2014-09-14 20:02:57 +02:00
fX = nShadow * GetWidthMult ( ) * RsGlobal - > MaximumWidth ;
fY = nShadow * GetHeightMult ( ) * RsGlobal - > MaximumHeight ;
2014-08-27 01:20:24 +02:00
}
2024-04-06 15:14:58 +02:00
namespace PrintStringShadows
2014-08-27 01:20:24 +02:00
{
2024-04-06 15:14:58 +02:00
template < uintptr_t addr >
static const float * * margin = reinterpret_cast < const float * * > ( Memory : : DynBaseAddress ( addr ) ) ;
2014-08-27 01:20:24 +02:00
2024-04-06 15:14:58 +02:00
static void PrintString_Internal ( void ( * printFn ) ( float , float , const wchar_t * ) , float fX , float fY , float fMarginX , float fMarginY , const wchar_t * pText )
{
printFn ( fX - fMarginX + ( fMarginX * GetWidthMult ( ) * RsGlobal - > MaximumWidth ) , fY - fMarginY + ( fMarginY * GetHeightMult ( ) * RsGlobal - > MaximumHeight ) , pText ) ;
}
2017-12-23 18:38:45 +01:00
2024-04-06 15:14:58 +02:00
template < uintptr_t pFltX , uintptr_t pFltY >
struct XY
{
static inline void ( * orgPrintString ) ( float , float , const wchar_t * ) ;
static void PrintString ( float fX , float fY , const wchar_t * pText )
{
PrintString_Internal ( orgPrintString , fX , fY , * * margin < pFltX > , * * margin < pFltY > , pText ) ;
}
2014-08-27 01:20:24 +02:00
2024-04-06 15:14:58 +02:00
static void Hook ( uintptr_t addr )
{
Memory : : DynBase : : InterceptCall ( addr , orgPrintString , PrintString ) ;
}
} ;
2014-08-27 01:20:24 +02:00
2024-04-06 15:14:58 +02:00
template < uintptr_t pFltX , uintptr_t pFltY >
struct XYMinus
{
static inline void ( * orgPrintString ) ( float , float , const wchar_t * ) ;
static void PrintString ( float fX , float fY , const wchar_t * pText )
{
PrintString_Internal ( orgPrintString , fX , fY , - ( * * margin < pFltX > ) , - ( * * margin < pFltY > ) , pText ) ;
}
static void Hook ( uintptr_t addr )
{
Memory : : DynBase : : InterceptCall ( addr , orgPrintString , PrintString ) ;
}
} ;
template < uintptr_t pFltX >
struct X
{
static inline void ( * orgPrintString ) ( float , float , const wchar_t * ) ;
static void PrintString ( float fX , float fY , const wchar_t * pText )
{
PrintString_Internal ( orgPrintString , fX , fY , * * margin < pFltX > , 0.0f , pText ) ;
}
2017-12-23 18:38:45 +01:00
2024-04-06 15:14:58 +02:00
static void Hook ( uintptr_t addr )
{
Memory : : DynBase : : InterceptCall ( addr , orgPrintString , PrintString ) ;
}
} ;
template < uintptr_t pFltY >
struct Y
{
static inline void ( * orgPrintString ) ( float , float , const wchar_t * ) ;
static void PrintString ( float fX , float fY , const wchar_t * pText )
{
PrintString_Internal ( orgPrintString , fX , fY , 0.0f , * * margin < pFltY > , pText ) ;
}
static void Hook ( uintptr_t addr )
{
Memory : : DynBase : : InterceptCall ( addr , orgPrintString , PrintString ) ;
}
} ;
2024-10-04 18:15:35 +02:00
// Radar position and radardisc shadow
static const float RADARDISC_SHRINK = 2.0f ; // We are shrinking the radardisc by that
template < std : : size_t Index >
static const float * orgRadarXPos ;
template < std : : size_t Index >
static float RadarXPos_Recalculated ;
template < std : : size_t . . . I >
static void RecalculateXPositions ( std : : index_sequence < I . . . > )
{
const float multiplier = GetWidthMult ( ) * RsGlobal - > MaximumWidth ;
( ( RadarXPos_Recalculated < I > = * orgRadarXPos < I > * multiplier ) , . . . ) ;
}
template < std : : size_t Index >
static const float * orgRadarYPos ;
template < std : : size_t Index >
static float RadarYPos_Recalculated ;
template < std : : size_t . . . I >
static void RecalculateYPositions ( std : : index_sequence < I . . . > )
{
const float multiplier = GetHeightMult ( ) * RsGlobal - > MaximumHeight ;
( ( RadarYPos_Recalculated < I > = * orgRadarYPos < I > * multiplier ) , . . . ) ;
}
template < std : : size_t Index >
static const float * orgRadarXPos_RadardiscShrink ;
template < std : : size_t Index >
static float RadarXPos_Recalculated_RadardiscShrink ;
template < std : : size_t . . . I >
static void RecalculateXPositions_RadardiscShrink ( std : : index_sequence < I . . . > )
{
const float multiplier = GetWidthMult ( ) * RsGlobal - > MaximumWidth ;
( ( RadarXPos_Recalculated_RadardiscShrink < I > = ( * orgRadarXPos_RadardiscShrink < I > - RADARDISC_SHRINK ) * multiplier ) , . . . ) ;
}
template < std : : size_t Index >
static const float * orgRadarYPos_RadardiscShrink ;
template < std : : size_t Index >
static float RadarYPos_Recalculated_RadardiscShrink ;
template < std : : size_t . . . I >
static void RecalculateYPositions_RadardiscShrink ( std : : index_sequence < I . . . > )
{
const float multiplier = GetHeightMult ( ) * RsGlobal - > MaximumHeight ;
( ( RadarYPos_Recalculated_RadardiscShrink < I > = ( * orgRadarYPos_RadardiscShrink < I > - RADARDISC_SHRINK ) * multiplier ) , . . . ) ;
}
static void ( * orgDrawMap ) ( ) ;
template < std : : size_t NumXPos , std : : size_t NumYPos , std : : size_t NumXPosRadardisc , std : : size_t NumYPosRadardisc >
static void DrawMap_RecalculatePositions ( )
{
RecalculateXPositions ( std : : make_index_sequence < NumXPos > { } ) ;
RecalculateYPositions ( std : : make_index_sequence < NumYPos > { } ) ;
RecalculateXPositions_RadardiscShrink ( std : : make_index_sequence < NumXPosRadardisc > { } ) ;
RecalculateYPositions_RadardiscShrink ( std : : make_index_sequence < NumYPosRadardisc > { } ) ;
orgDrawMap ( ) ;
}
HOOK_EACH_INIT ( CalculateRadarXPos , orgRadarXPos , RadarXPos_Recalculated ) ;
HOOK_EACH_INIT ( CalculateRadarYPos , orgRadarYPos , RadarYPos_Recalculated ) ;
HOOK_EACH_INIT ( CalculateRadarXPos_RadardiscShrink , orgRadarXPos_RadardiscShrink , RadarXPos_Recalculated_RadardiscShrink ) ;
HOOK_EACH_INIT ( CalculateRadarYPos_RadardiscShrink , orgRadarYPos_RadardiscShrink , RadarYPos_Recalculated_RadardiscShrink ) ;
static CRect ScaleWidthRect ( CRect rect )
{
// Also account for a smaller radardisc
rect . x1 = ( rect . x1 + RADARDISC_SHRINK ) * GetWidthMult ( ) * RsGlobal - > MaximumWidth ;
return rect ;
}
template < std : : size_t Index >
static void ( __fastcall * orgDrawSprite ) ( void * obj , void * , const CRect & rect , const CRGBA & col1 , const CRGBA & col2 , const CRGBA & col3 , const CRGBA & col4 ) ;
template < std : : size_t Index >
static void __fastcall DrawSprite_Scale ( void * obj , void * , const CRect & rect , const CRGBA & col1 , const CRGBA & col2 , const CRGBA & col3 , const CRGBA & col4 )
{
orgDrawSprite < Index > ( obj , nullptr , ScaleWidthRect ( rect ) , col1 , col2 , col3 , col4 ) ;
}
HOOK_EACH_INIT ( DrawRadarDisc , orgDrawSprite , DrawSprite_Scale ) ;
2014-08-27 01:20:24 +02:00
}
float FixedRefValue ( )
{
return 1.0f ;
}
void __declspec ( naked ) SubtitlesShadowFix ( )
{
_asm
{
mov [ esp ] , eax
fild [ esp ]
push eax
lea eax , [ esp + 20 h - 18 h ]
push eax
lea eax , [ esp + 24 h - 14 h ]
push eax
call Recalculate
jmp SubtitlesShadowFix_JumpBack
}
}
2018-03-27 22:28:45 +02:00
void __declspec ( naked ) CreateInstance_BikeFix ( )
{
_asm
{
push eax
mov ecx , ebp
call CVehicleModelInfo : : GetExtrasFrame
retn
}
}
2017-09-18 22:13:02 +02:00
extern char * * ppUserFilesDir = AddressByVersion < char * * > ( 0x6022AA , 0x60228A , 0x601ECA ) ;
2015-04-22 15:47:56 +02:00
2015-06-29 00:53:49 +02:00
static LARGE_INTEGER FrameTime ;
2019-01-04 00:47:05 +01:00
__declspec ( safebuffers ) int32_t GetTimeSinceLastFrame ( )
2015-06-29 00:53:49 +02:00
{
LARGE_INTEGER curTime ;
QueryPerformanceCounter ( & curTime ) ;
2017-03-06 22:35:08 +01:00
return int32_t ( curTime . QuadPart - FrameTime . QuadPart ) ;
2015-06-29 00:53:49 +02:00
}
2017-03-06 22:35:08 +01:00
static int ( * RsEventHandler ) ( int , void * ) ;
int NewFrameRender ( int nEvent , void * pParam )
2015-06-29 00:53:49 +02:00
{
QueryPerformanceCounter ( & FrameTime ) ;
2017-03-06 22:35:08 +01:00
return RsEventHandler ( nEvent , pParam ) ;
2015-06-29 00:53:49 +02:00
}
2016-05-29 19:17:44 +02:00
2017-09-13 21:56:06 +02:00
static void ( * orgPickNextNodeToChaseCar ) ( void * , float , float , void * ) ;
static float PickNextNodeToChaseCarZ = 0.0f ;
static void PickNextNodeToChaseCarXYZ ( void * vehicle , const CVector & vec , void * chaseTarget )
{
PickNextNodeToChaseCarZ = vec . z ;
orgPickNextNodeToChaseCar ( vehicle , vec . x , vec . y , chaseTarget ) ;
PickNextNodeToChaseCarZ = 0.0f ;
}
2015-03-05 15:51:52 +01:00
static char aNoDesktopMode [ 64 ] ;
2014-08-27 01:20:24 +02:00
2017-06-15 17:45:51 +02:00
unsigned int __cdecl AutoPilotTimerCalculation_VC ( unsigned int nTimer , int nScaleFactor , float fScaleCoef )
{
return nTimer - static_cast < unsigned int > ( nScaleFactor * fScaleCoef ) ;
}
void __declspec ( naked ) AutoPilotTimerFix_VC ( )
{
_asm {
push dword ptr [ esp + 0xC ]
push dword ptr [ ebx + 0x10 ]
push eax
call AutoPilotTimerCalculation_VC
add esp , 0xC
mov [ ebx + 0xC ] , eax
add esp , 0x30
pop ebp
pop ebx
retn 4
}
}
2024-04-29 20:43:25 +02:00
2024-02-03 20:26:50 +01:00
namespace ZeroAmmoFix
2018-01-28 18:15:38 +01:00
{
2024-02-03 20:26:50 +01:00
template < std : : size_t Index >
2024-03-09 15:17:10 +01:00
static void ( __fastcall * orgGiveWeapon ) ( void * ped , void * , unsigned int weapon , unsigned int ammo , bool flag ) ;
2024-02-03 20:26:50 +01:00
template < std : : size_t Index >
2024-03-09 15:17:10 +01:00
static void __fastcall GiveWeapon_SP ( void * ped , void * , unsigned int weapon , unsigned int ammo , bool flag )
2024-02-03 20:26:50 +01:00
{
2024-03-09 15:17:10 +01:00
orgGiveWeapon < Index > ( ped , nullptr , weapon , std : : max ( 1u , ammo ) , flag ) ;
2024-02-03 20:26:50 +01:00
}
2024-10-04 18:15:35 +02:00
HOOK_EACH_INIT ( GiveWeapon , orgGiveWeapon , GiveWeapon_SP ) ;
2024-02-03 20:26:50 +01:00
2018-01-28 18:15:38 +01:00
}
2018-03-28 21:23:16 +02:00
2018-04-28 17:45:32 +02:00
// ============= Credits! =============
namespace Credits
{
static void ( * PrintCreditText ) ( float scaleX , float scaleY , const wchar_t * text , unsigned int & pos , float timeOffset ) ;
static void ( * PrintCreditText_Hooked ) ( float scaleX , float scaleY , const wchar_t * text , unsigned int & pos , float timeOffset ) ;
static void PrintCreditSpace ( float scale , unsigned int & pos )
{
pos + = static_cast < unsigned int > ( scale * 25.0f ) ;
}
constexpr wchar_t xvChar ( const wchar_t ch )
{
constexpr uint8_t xv = SILENTPATCH_REVISION_ID ;
return ch ^ xv ;
}
constexpr wchar_t operator " " _xv ( const char ch )
{
return xvChar ( ch ) ;
}
static void PrintSPCredits ( float scaleX , float scaleY , const wchar_t * text , unsigned int & pos , float timeOffset )
{
// Original text we intercepted
PrintCreditText_Hooked ( scaleX , scaleY , text , pos , timeOffset ) ;
PrintCreditSpace ( 1.5f , pos ) ;
{
wchar_t spText [ ] = { ' A ' _xv , ' N ' _xv , ' D ' _xv , ' \0 ' _xv } ;
for ( auto & ch : spText ) ch = xvChar ( ch ) ;
PrintCreditText ( 1.1f , 0.8f , spText , pos , timeOffset ) ;
}
PrintCreditSpace ( 1.5f , pos ) ;
{
wchar_t spText [ ] = { ' A ' _xv , ' D ' _xv , ' R ' _xv , ' I ' _xv , ' A ' _xv , ' N ' _xv , ' ' _xv , ' \' ' _xv , ' S ' _xv , ' I ' _xv , ' L ' _xv , ' E ' _xv , ' N ' _xv , ' T ' _xv , ' \' ' _xv , ' ' _xv ,
' Z ' _xv , ' D ' _xv , ' A ' _xv , ' N ' _xv , ' O ' _xv , ' W ' _xv , ' I ' _xv , ' C ' _xv , ' Z ' _xv , ' \0 ' _xv } ;
for ( auto & ch : spText ) ch = xvChar ( ch ) ;
PrintCreditText ( 1.1f , 1.1f , spText , pos , timeOffset ) ;
}
}
}
2018-03-28 21:23:16 +02:00
// ============= Keyboard latency input fix =============
namespace KeyboardInputFix
{
static void * NewKeyState ;
static void * OldKeyState ;
static void * TempKeyState ;
static constexpr size_t objSize = 0x270 ;
static void ( __fastcall * orgClearSimButtonPressCheckers ) ( void * ) ;
void __fastcall ClearSimButtonPressCheckers ( void * pThis )
{
memcpy ( OldKeyState , NewKeyState , objSize ) ;
memcpy ( NewKeyState , TempKeyState , objSize ) ;
orgClearSimButtonPressCheckers ( pThis ) ;
}
}
2019-08-24 22:35:35 +02:00
namespace Localization
{
static int8_t forcedUnits = - 1 ; // 0 - metric, 1 - imperial
bool IsMetric_LocaleBased ( )
{
if ( forcedUnits ! = - 1 ) return forcedUnits = = 0 ;
unsigned int LCData ;
if ( GetLocaleInfo ( LOCALE_USER_DEFAULT , LOCALE_IMEASURE | LOCALE_RETURN_NUMBER , reinterpret_cast < LPTSTR > ( & LCData ) , sizeof ( LCData ) / sizeof ( TCHAR ) ) ! = 0 )
{
return LCData = = 0 ;
}
// If fails, default to metric. Hopefully never fails though
return true ;
}
static void ( __thiscall * orgUpdateCompareFlag_IsMetric ) ( void * pThis , uint8_t flag ) ;
void __fastcall UpdateCompareFlag_IsMetric ( void * pThis , void * , uint8_t )
{
std : : invoke ( orgUpdateCompareFlag_IsMetric , pThis , IsMetric_LocaleBased ( ) ) ;
}
uint32_t PrefsLanguage_IsMetric ( )
{
return IsMetric_LocaleBased ( ) ;
}
}
2019-12-08 20:15:25 +01:00
// ============= Corrected FBI Washington sirens sound =============
namespace SirenSwitchingFix
{
void __declspec ( naked ) IsFBIRanchOrFBICar ( )
{
_asm
{
mov dword ptr [ esi + 1 Ch ] , 1 Ch
// al = 0 - high pitched siren
// al = 1 - normal siren
cmp dword ptr [ ebp + 14 h ] , 90 // fbiranch
je IsFBIRanchOrFBICar_HighPitchSiren
cmp dword ptr [ ebp + 14 h ] , 17 // fbicar
setne al
retn
IsFBIRanchOrFBICar_HighPitchSiren :
xor al , al
retn
}
}
}
2019-12-15 21:37:05 +01:00
// ============= Corrected siren corona placement for FBI cars and Vice Cheetah =============
namespace FBISirenCoronaFix
{
bool overridePosition ;
CVector vecOverridePosition ;
// True - don't display siren
// False - display siren
bool SetUpFBISiren ( const CVehicle * vehicle )
{
2019-12-25 16:02:42 +01:00
SVF : : Feature foundFeature = SVF : : Feature : : NO_FEATURE ;
SVF : : ForAllModelFeatures ( vehicle - > GetModelIndex ( ) , [ & ] ( SVF : : Feature f ) {
if ( f > = SVF : : Feature : : FBI_RANCHER_SIREN & & f < = SVF : : Feature : : VICE_CHEETAH_SIREN )
{
foundFeature = f ;
2024-02-01 19:07:51 +01:00
return false ;
2019-12-25 16:02:42 +01:00
}
2024-02-01 19:07:51 +01:00
return true ;
2019-12-25 16:02:42 +01:00
} ) ;
if ( foundFeature ! = SVF : : Feature : : NO_FEATURE )
2019-12-15 21:37:05 +01:00
{
2019-12-25 16:02:42 +01:00
if ( foundFeature ! = SVF : : Feature : : VICE_CHEETAH_SIREN )
2019-12-15 21:37:05 +01:00
{
2024-02-01 19:07:51 +01:00
constexpr CVector FBICAR_SIREN_POS ( 0.4f , 0.8f , 0.25f ) ;
constexpr CVector FBIRANCH_SIREN_POS ( 0.5f , 1.12f , 0.5f ) ;
2019-12-15 21:37:05 +01:00
overridePosition = true ;
2019-12-25 16:02:42 +01:00
vecOverridePosition = foundFeature = = SVF : : Feature : : FBI_WASHINGTON_SIREN ? FBICAR_SIREN_POS : FBIRANCH_SIREN_POS ;
2019-12-15 21:37:05 +01:00
}
else
{
overridePosition = false ;
}
return false ;
}
return true ;
}
CVector & __fastcall SetUpVector ( CVector & out , void * , float X , float Y , float Z )
{
if ( overridePosition )
{
out = vecOverridePosition ;
}
else
{
out = CVector ( X , Y , Z ) ;
}
return out ;
}
}
2024-02-29 21:44:18 +01:00
// ============= Fixed vehicles exploding twice if the driver leaves the car while it's exploding =============
namespace RemoveDriverStatusFix
{
__declspec ( naked ) void RemoveDriver_SetStatus ( )
{
// if (m_nStatus != STATUS_WRECKED)
// m_nStatus = STATUS_ABANDONED;
_asm
{
mov cl , [ ebx + 50 h ]
mov al , cl
and cl , 0F 8 h
cmp cl , 28 h
je DontSetStatus
and al , 7
or al , 20 h
DontSetStatus :
retn
}
}
}
2024-03-09 17:04:12 +01:00
// ============= Apply the environment mapping on extra components =============
namespace EnvMapsOnExtras
{
static void RemoveSpecularityFromAtomic ( RpAtomic * atomic )
{
RpGeometry * geometry = RpAtomicGetGeometry ( atomic ) ;
if ( geometry ! = nullptr )
{
RpGeometryForAllMaterials ( geometry , [ ] ( RpMaterial * material )
{
bool bRemoveSpecularity = false ;
// Only remove specularity from the body materials, keep glass intact.
// This is only done on a best-effort basis, as mods can fine-tune it better
// and just remove the model from the exceptions list
RwTexture * texture = RpMaterialGetTexture ( material ) ;
if ( texture ! = nullptr )
{
if ( strstr ( RwTextureGetName ( texture ) , " glass " ) = = nullptr & & strstr ( RwTextureGetMaskName ( texture ) , " glass " ) = = nullptr )
{
bRemoveSpecularity = true ;
}
}
if ( bRemoveSpecularity )
{
RpMaterialGetSurfaceProperties ( material ) - > specular = 0.0f ;
}
return material ;
} ) ;
}
}
static RpClump * ( * orgRpClumpForAllAtomics ) ( RpClump * clump , RpAtomicCallBack callback , void * data ) ;
static RpClump * RpClumpForAllAtomics_ExtraComps ( CVehicleModelInfo * modelInfo , RpAtomicCallBack callback , void * data )
{
RpClump * result = orgRpClumpForAllAtomics ( modelInfo - > m_clump , callback , data ) ;
const int32_t modelID = std : : distance ( ms_modelInfoPtrs , std : : find ( ms_modelInfoPtrs , ms_modelInfoPtrs + numModelInfos , modelInfo ) ) ;
const bool bRemoveSpecularity = ExtraCompSpecularity : : SpecularityExcluded ( modelID ) ;
for ( int32_t i = 0 ; i < modelInfo - > m_numComps ; i + + )
{
if ( bRemoveSpecularity )
{
RemoveSpecularityFromAtomic ( modelInfo - > m_comps [ i ] ) ;
}
callback ( modelInfo - > m_comps [ i ] , data ) ;
CVehicleModelInfo : : AttachCarPipeToRwObject ( reinterpret_cast < RwObject * > ( modelInfo - > m_comps [ i ] ) ) ;
}
return result ;
}
}
2024-03-18 23:47:51 +01:00
// ============= Null terminate read lines in CPlane::LoadPath =============
namespace NullTerminatedLines
{
static void * orgSscanf_LoadPath ;
__declspec ( naked ) static void sscanf1_LoadPath_Terminate ( )
{
_asm
{
mov eax , [ esp + 4 ]
mov byte ptr [ eax + ecx ] , 0
jmp [ orgSscanf_LoadPath ]
}
}
}
2024-03-21 20:44:51 +01:00
// ============= Don't reset mouse sensitivity on New Game =============
namespace MouseSensNewGame
{
static float DefaultHorizontalAccel ;
static float * fMouseAccelHorzntl ;
static void ( * orgSetDirMyDocuments ) ( ) ;
static void SetDirMyDocuments_ResetMouse ( )
{
orgSetDirMyDocuments ( ) ;
* fMouseAccelHorzntl = DefaultHorizontalAccel ;
}
}
2024-04-10 20:23:20 +02:00
// ============= Fixed pickup effects colors =============
namespace PickupEffectsFixes
{
__declspec ( naked ) static void PickUpEffects_BigDollarColor ( )
{
_asm
{
mov byte ptr [ esp + 184 h - 170 h ] , 0
mov dword ptr [ esp + 184 h - 174 h ] , 37
retn
}
}
__declspec ( naked ) static void PickUpEffects_Minigun2Glow ( )
{
_asm
{
cmp ecx , 294 // minigun2
jnz NotMinigun2
mov byte ptr [ esp + 184 h - 170 h ] , 0
xor eax , eax
jmp Return
NotMinigun2 :
lea eax , [ ecx + 1 ]
Return :
mov ebx , ecx
retn
}
}
}
2024-04-17 23:31:03 +02:00
// ============= Fixed IS_PLAYER_TARGETTING_CHAR incorrectly detecting targetting in Classic controls =============
// ============= when the player is not aiming =============
// ============= By Wesser =============
namespace IsPlayerTargettingCharFix
{
static bool * bUseMouse3rdPerson ;
static void * TheCamera ;
static bool ( __fastcall * Using1stPersonWeaponMode ) ( ) ;
__declspec ( naked ) static void IsPlayerTargettingChar_ExtraChecks ( )
{
// After this extra block of code, there is a jz Return, so set ZF to 0 here if this path is to be entered
_asm
{
test bl , bl
jnz ReturnToUpdateCompareFlag
mov eax , [ bUseMouse3rdPerson ]
cmp byte ptr [ eax ] , 0
jne CmpAndReturn
mov ecx , [ TheCamera ]
call [ Using1stPersonWeaponMode ]
test al , al
jz ReturnToUpdateCompareFlag
CmpAndReturn :
cmp byte ptr [ esp + 11 Ch - 10 Ch ] , 0
retn
ReturnToUpdateCompareFlag :
xor al , al
retn
}
}
}
2024-05-07 22:30:32 +02:00
// ============= Resetting stats and variables on New Game =============
namespace VariableResets
{
static auto TimerInitialise = reinterpret_cast < void ( * ) ( ) > ( hook : : get_pattern ( " 83 E4 F8 68 ? ? ? ? E8 " , - 6 ) ) ;
using VarVariant = std : : variant < bool * , int * > ;
std : : vector < VarVariant > GameVariablesToReset ;
static void ReInitOurVariables ( )
{
for ( const auto & var : GameVariablesToReset )
{
std : : visit ( [ ] ( auto & & v ) {
* v = { } ;
} , var ) ;
}
// Functions that should have been called by the game but aren't...
TimerInitialise ( ) ;
}
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 > ( ) ;
}
2024-10-04 18:15:35 +02:00
HOOK_EACH_INIT ( ReInitGameObjectVariables , orgReInitGameObjectVariables , ReInitGameObjectVariables ) ;
2024-05-07 22:30:32 +02:00
static void ( * orgGameInitialise ) ( const char * ) ;
void GameInitialise ( const char * path )
{
ReInitOurVariables ( ) ;
orgGameInitialise ( path ) ;
}
}
2024-05-18 19:09:14 +02:00
// ============= Disabled backface culling on detached car parts, peds and specific models =============
namespace SelectableBackfaceCulling
2024-05-17 17:35:46 +02:00
{
2024-05-18 19:09:14 +02:00
void ReadDrawBackfacesExclusions ( const wchar_t * pPath )
{
constexpr size_t SCRATCH_PAD_SIZE = 32767 ;
WideDelimStringReader reader ( SCRATCH_PAD_SIZE ) ;
GetPrivateProfileSectionW ( L " DrawBackfaces " , reader . GetBuffer ( ) , reader . GetSize ( ) , pPath ) ;
while ( const wchar_t * str = reader . GetString ( ) )
{
auto modelID = ParseUtils : : TryParseInt ( str ) ;
if ( modelID )
SVF : : RegisterFeature ( * modelID , SVF : : Feature : : DRAW_BACKFACES ) ;
else
SVF : : RegisterFeature ( ParseUtils : : ParseString ( str ) , SVF : : Feature : : DRAW_BACKFACES ) ;
}
}
2024-05-23 20:07:34 +02:00
// Only the parts of CObject we need
2024-05-17 17:35:46 +02:00
struct Object
{
std : : byte __pad [ 364 ] ;
uint8_t m_objectCreatedBy ;
bool bObjectFlag0 : 1 ;
bool bObjectFlag1 : 1 ;
bool bObjectFlag2 : 1 ;
bool bObjectFlag3 : 1 ;
bool bObjectFlag4 : 1 ;
bool bObjectFlag5 : 1 ;
bool m_bIsExploded : 1 ;
bool bUseVehicleColours : 1 ;
std : : byte __pad2 [ 22 ] ;
FLAUtils : : int16 m_wCarPartModelIndex ;
} ;
2024-05-18 19:09:14 +02:00
static void * EntityRender_Prologue_JumpBack ;
2024-05-23 20:07:34 +02:00
__declspec ( naked ) static void __fastcall EntityRender_Original ( CEntity * )
2024-05-17 17:35:46 +02:00
{
_asm
{
2024-05-18 19:09:14 +02:00
push ebx
mov ebx , ecx
cmp dword ptr [ ebx + 4 Ch ] , 0
jmp [ EntityRender_Prologue_JumpBack ]
2024-05-17 17:35:46 +02:00
}
}
2024-05-23 20:07:34 +02:00
static bool ShouldDisableBackfaceCulling ( const CEntity * entity )
2024-05-18 19:09:14 +02:00
{
const uint8_t entityType = entity - > m_nType ;
// Vehicles disable BFC elsewhere already
if ( entityType = = 2 )
{
return false ;
}
2024-05-17 17:35:46 +02:00
2024-05-18 19:09:14 +02:00
// Always disable BFC on peds
if ( entityType = = 3 )
{
return true ;
}
// For objects, do extra checks
if ( entityType = = 4 )
{
const Object * object = reinterpret_cast < const Object * > ( entity ) ;
2024-05-18 22:39:36 +02:00
if ( object - > m_wCarPartModelIndex . Get ( ) ! = - 1 & & object - > m_objectCreatedBy = = TEMP_OBJECT & & object - > bUseVehicleColours )
{
return true ;
}
2024-05-18 19:09:14 +02:00
}
// For everything else, check the exclusion list
return SVF : : ModelHasFeature ( entity - > m_modelIndex . Get ( ) , SVF : : Feature : : DRAW_BACKFACES ) ;
}
// If CEntity::Render is re-routed by another mod, we overwrite this later
2024-05-23 20:07:34 +02:00
static void ( __fastcall * orgEntityRender ) ( CEntity * obj ) = & EntityRender_Original ;
2024-05-18 19:09:14 +02:00
2024-05-23 20:07:34 +02:00
static void __fastcall EntityRender_BackfaceCulling ( CEntity * obj )
2024-05-17 17:35:46 +02:00
{
RwScopedRenderState < rwRENDERSTATECULLMODE > cullState ;
2024-05-18 19:09:14 +02:00
if ( ShouldDisableBackfaceCulling ( obj ) )
2024-05-17 17:35:46 +02:00
{
RwRenderStateSet ( rwRENDERSTATECULLMODE , reinterpret_cast < void * > ( rwCULLMODECULLNONE ) ) ;
}
2024-05-18 19:09:14 +02:00
orgEntityRender ( obj ) ;
2024-05-17 17:35:46 +02:00
}
}
2024-05-23 20:07:34 +02:00
// ============= Fix the construction site LOD losing its HQ model and showing at all times =============
namespace ConstructionSiteLODFix
2024-05-18 16:49:03 +02:00
{
2024-05-23 20:07:34 +02:00
static bool bActivateConstructionSiteFix = false ;
static int32_t MI_BLDNGST2MESH , MI_BLDNGST2MESHDAM ;
static CSimpleModelInfo * Bldngst2mesh_ModelInfo ;
static CSimpleModelInfo * Bldngst2meshDam_ModelInfo ;
static CSimpleModelInfo * LODngst2mesh_ModelInfo ;
void MatchModelIndices ( )
{
CSimpleModelInfo * Bldngst2mesh = reinterpret_cast < CSimpleModelInfo * > ( GetModelInfo ( " bldngst2mesh " , & MI_BLDNGST2MESH ) ) ;
CSimpleModelInfo * Bldngst2meshDam = reinterpret_cast < CSimpleModelInfo * > ( GetModelInfo ( " bldngst2meshdam " , & MI_BLDNGST2MESHDAM ) ) ;
CSimpleModelInfo * LODngst2mesh = reinterpret_cast < CSimpleModelInfo * > ( GetModelInfo ( " LODngst2mesh " , nullptr ) ) ;
CSimpleModelInfo * LODngst2meshDam = reinterpret_cast < CSimpleModelInfo * > ( GetModelInfo ( " LODngst2meshdam " , nullptr ) ) ;
const bool bHasBldngst2mesh = Bldngst2mesh ! = nullptr ;
const bool bHasBldngst2meshDam = Bldngst2meshDam ! = nullptr ;
const bool bHasLODngst2mesh = LODngst2mesh ! = nullptr ;
const bool bHasLODngst2meshDam = LODngst2meshDam ! = nullptr ;
// LODngst2meshdam doesn't exist in the vanilla game, so if it exists - a mod to fix this issue via
// the map modifications has been installed.
bActivateConstructionSiteFix = bHasBldngst2mesh & & bHasBldngst2meshDam & & bHasLODngst2mesh & & ! bHasLODngst2meshDam ;
Bldngst2mesh_ModelInfo = Bldngst2mesh ;
Bldngst2meshDam_ModelInfo = Bldngst2meshDam ;
LODngst2mesh_ModelInfo = LODngst2mesh ;
}
2024-05-18 19:09:14 +02:00
2024-05-23 20:07:34 +02:00
static void FixConstructionSiteModel ( int oldModelID , int newModelID )
{
if ( ! bActivateConstructionSiteFix )
{
return ;
}
if ( oldModelID = = MI_BLDNGST2MESH & & newModelID = = MI_BLDNGST2MESHDAM )
{
LODngst2mesh_ModelInfo - > m_atomics [ 2 ] = Bldngst2meshDam_ModelInfo ;
}
else if ( oldModelID = = MI_BLDNGST2MESHDAM & & newModelID = = MI_BLDNGST2MESH )
{
LODngst2mesh_ModelInfo - > m_atomics [ 2 ] = Bldngst2mesh_ModelInfo ;
}
}
template < std : : size_t Index >
static void ( __fastcall * orgReplaceWithNewModel ) ( CEntity * building , void * , int newModelID ) ;
template < std : : size_t Index >
static void __fastcall ReplaceWithNewModel_ConstructionSiteFix ( CEntity * building , void * , int newModelID )
{
const int oldModelID = building - > m_modelIndex . Get ( ) ;
orgReplaceWithNewModel < Index > ( building , nullptr , newModelID ) ;
FixConstructionSiteModel ( oldModelID , newModelID ) ;
}
2024-10-04 18:15:35 +02:00
HOOK_EACH_INIT ( ReplaceWithNewModel , orgReplaceWithNewModel , ReplaceWithNewModel_ConstructionSiteFix ) ;
2024-05-23 20:07:34 +02:00
}
namespace ModelIndicesReadyHook
{
2024-05-18 16:49:03 +02:00
static void ( * orgInitialiseObjectData ) ( const char * ) ;
static void InitialiseObjectData_ReadySVF ( const char * path )
{
orgInitialiseObjectData ( path ) ;
SVF : : MarkModelNamesReady ( ) ;
2024-05-23 20:07:34 +02:00
ConstructionSiteLODFix : : MatchModelIndices ( ) ;
2024-05-18 19:09:14 +02:00
// This is a bit dirty, but whatever
// Tooled Up in North Point Mall needs a "draw last" flag, or else our BFC changes break it very badly
// AmmuNation and other stores already have that flag, this one does not
void * model = GetModelInfo ( " mall_hardware " , nullptr ) ;
if ( model ! = nullptr )
{
uint16_t * flags = reinterpret_cast < uint16_t * > ( static_cast < char * > ( model ) + 0x42 ) ;
2024-05-18 22:39:36 +02:00
* flags | = 0x40 ;
2024-05-18 19:09:14 +02:00
}
2024-05-18 16:49:03 +02:00
}
}
2024-05-23 21:48:53 +02:00
// ============= Fix the outro splash flickering for a frame when fading in =============
namespace OutroSplashFix
{
struct RGBA
{
uint8_t r , g , b , a ;
} ;
static RGBA * ( __thiscall * orgRGBASet ) ( RGBA * , uint8_t , uint8_t , uint8_t , uint8_t ) ;
static RGBA * __fastcall RGBASet_Clamp ( RGBA * rgba , void * , int r , int g , int b , int a )
{
return orgRGBASet ( rgba , static_cast < uint8_t > ( r ) , static_cast < uint8_t > ( g ) , static_cast < uint8_t > ( b ) , static_cast < uint8_t > ( std : : clamp ( a , 0 , 255 ) ) ) ;
}
}
2024-06-05 21:53:13 +02:00
// ============= Fix Tommy not shaking his fists with brass knuckles (in all cases) and most post-GTA III weapons (when cars slow down for him) =============
namespace TommyFistShakeWithWeapons
{
struct WeaponInfo
{
std : : byte __pad [ 0x60 ] ;
uint32_t m_weaponSlot ;
} ;
static WeaponInfo * ( * GetWeaponInfo ) ( uint32_t weaponID ) ;
2024-06-07 19:47:40 +02:00
constexpr uint32_t WEAPON_CHAINSAW = 11 ;
static WeaponInfo DUMMY_INFO = [ ] {
WeaponInfo dummy ;
dummy . m_weaponSlot = 99 ;
return dummy ;
} ( ) ;
2024-06-05 21:53:13 +02:00
static bool WeaponProhibitsFistShake ( uint32_t weaponID )
{
const uint32_t weaponSlot = GetWeaponInfo ( weaponID ) - > m_weaponSlot ;
2024-06-07 19:47:40 +02:00
const bool bWeaponAllowsFistShake = ( weaponSlot = = 0 | | weaponSlot = = 1 | | weaponSlot = = 3 | | weaponSlot = = 5 ) & & weaponID ! = WEAPON_CHAINSAW ;
2024-06-05 21:53:13 +02:00
return ! bWeaponAllowsFistShake ;
}
static __declspec ( naked ) void CheckWeaponGroupHook ( )
{
_asm
{
push dword ptr [ eax ]
call WeaponProhibitsFistShake
add esp , 4
test al , al
retn
}
}
2024-06-07 19:47:40 +02:00
template < std : : size_t Index >
static WeaponInfo * ( * orgGetWeaponInfo ) ( uint32_t weaponID ) ;
template < std : : size_t Index >
static WeaponInfo * gGetWeaponInfo_ExcludeChainsaw ( uint32_t weaponID )
{
if ( weaponID = = WEAPON_CHAINSAW )
{
return & DUMMY_INFO ;
}
return orgGetWeaponInfo < Index > ( weaponID ) ;
}
2024-10-04 18:15:35 +02:00
HOOK_EACH_INIT ( ExcludeChainsaw , orgGetWeaponInfo , gGetWeaponInfo_ExcludeChainsaw ) ;
2024-06-05 21:53:13 +02:00
}
2019-08-24 22:35:35 +02:00
void InjectDelayedPatches_VC_Common ( bool bHasDebugMenu , const wchar_t * wcModulePath )
{
2019-12-15 17:04:50 +01:00
using namespace Memory ;
2024-05-16 23:43:48 +02:00
using namespace hook : : txn ;
2019-12-15 17:04:50 +01:00
2024-03-09 17:04:12 +01:00
const ModuleList moduleList ;
2024-10-04 18:15:35 +02:00
const HMODULE hGameModule = GetModuleHandle ( nullptr ) ;
2024-03-09 17:04:12 +01:00
const HMODULE skygfxModule = moduleList . Get ( L " skygfx " ) ;
if ( skygfxModule ! = nullptr )
{
auto attachCarPipe = reinterpret_cast < void ( * ) ( RwObject * ) > ( GetProcAddress ( skygfxModule , " AttachCarPipeToRwObject " ) ) ;
if ( attachCarPipe ! = nullptr )
{
CVehicleModelInfo : : AttachCarPipeToRwObject = attachCarPipe ;
}
}
2019-08-24 22:35:35 +02:00
// Locale based metric/imperial system INI/debug menu
{
using namespace Localization ;
forcedUnits = static_cast < int8_t > ( GetPrivateProfileIntW ( L " SilentPatch " , L " Units " , - 1 , wcModulePath ) ) ;
if ( bHasDebugMenu )
{
static const char * const str [ ] = { " Default " , " Metric " , " Imperial " } ;
DebugMenuEntry * e = DebugMenuAddVar ( " SilentPatch " , " Forced units " , & forcedUnits , nullptr , 1 , - 1 , 1 , str ) ;
DebugMenuEntrySetWrap ( e , true ) ;
}
}
2019-12-15 17:04:50 +01:00
// Corrected siren corona placement for emergency vehicles
2019-12-27 11:06:26 +01:00
if ( GetPrivateProfileIntW ( L " SilentPatch " , L " EnableVehicleCoronaFixes " , - 1 , wcModulePath ) = = 1 )
2019-12-15 17:04:50 +01:00
{
// Other mods might be touching it, so only patch specific vehicles if their code has not been touched at all
2024-05-16 23:43:48 +02:00
try
2019-12-15 17:04:50 +01:00
{
2024-05-16 23:43:48 +02:00
auto firetruck1 = pattern ( " 8D 8C 24 24 09 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
auto firetruck2 = pattern ( " 8D 8C 24 30 09 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
static const CVector FIRETRUCK_SIREN_POS ( 0.95f , 3.2f , 1.4f ) ;
static const float FIRETRUCK_SIREN_MINUS_X = - FIRETRUCK_SIREN_POS . x ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( firetruck1 . get < float * > ( 7 + 2 ) , & FIRETRUCK_SIREN_POS . z ) ;
Patch ( firetruck1 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & FIRETRUCK_SIREN_POS . y ) ;
Patch ( firetruck1 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & FIRETRUCK_SIREN_POS . x ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( firetruck2 . get < float * > ( 7 + 2 ) , & FIRETRUCK_SIREN_POS . z ) ;
Patch ( firetruck2 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & FIRETRUCK_SIREN_POS . y ) ;
Patch ( firetruck2 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & FIRETRUCK_SIREN_MINUS_X ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
try
{
auto ambulan1 = pattern ( " 8D 8C 24 0C 09 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
auto ambulan2 = pattern ( " 8D 8C 24 18 09 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
static const CVector AMBULANCE_SIREN_POS ( 0.7f , 0.65f , 1.55f ) ;
static const float AMBULANCE_SIREN_MINUS_X = - AMBULANCE_SIREN_POS . x ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( ambulan1 . get < float * > ( 7 + 2 ) , & AMBULANCE_SIREN_POS . z ) ;
Patch ( ambulan1 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & AMBULANCE_SIREN_POS . y ) ;
Patch ( ambulan1 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & AMBULANCE_SIREN_POS . x ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( ambulan2 . get < float * > ( 7 + 2 ) , & AMBULANCE_SIREN_POS . z ) ;
Patch ( ambulan2 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & AMBULANCE_SIREN_POS . y ) ;
Patch ( ambulan2 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & AMBULANCE_SIREN_MINUS_X ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
try
{
auto police1 = pattern ( " 8D 8C 24 DC 08 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
auto police2 = pattern ( " 8D 8C 24 E8 08 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
static const CVector POLICE_SIREN_POS ( 0.55f , - 0.4f , 0.95f ) ;
static const float POLICE_SIREN_MINUS_X = - POLICE_SIREN_POS . x ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( police1 . get < float * > ( 7 + 2 ) , & POLICE_SIREN_POS . z ) ;
Patch ( police1 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & POLICE_SIREN_POS . y ) ;
Patch ( police1 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & POLICE_SIREN_POS . x ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( police2 . get < float * > ( 7 + 2 ) , & POLICE_SIREN_POS . z ) ;
Patch ( police2 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & POLICE_SIREN_POS . y ) ;
Patch ( police2 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & POLICE_SIREN_MINUS_X ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
try
{
auto enforcer1 = pattern ( " 8D 8C 24 F4 08 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
auto enforcer2 = pattern ( " 8D 8C 24 00 09 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 " ) . get_one ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
static const CVector ENFORCER_SIREN_POS ( 0.6f , 1.05f , 1.4f ) ;
static const float ENFORCER_SIREN_MINUS_X = - ENFORCER_SIREN_POS . x ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( enforcer1 . get < float * > ( 7 + 2 ) , & ENFORCER_SIREN_POS . z ) ;
Patch ( enforcer1 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & ENFORCER_SIREN_POS . y ) ;
Patch ( enforcer1 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & ENFORCER_SIREN_POS . x ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( enforcer2 . get < float * > ( 7 + 2 ) , & ENFORCER_SIREN_POS . z ) ;
Patch ( enforcer2 . get < float * > ( 7 + 2 + ( 6 * 1 ) ) , & ENFORCER_SIREN_POS . y ) ;
Patch ( enforcer2 . get < float * > ( 7 + 2 + ( 6 * 2 ) ) , & ENFORCER_SIREN_MINUS_X ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
{
try
2019-12-15 17:04:50 +01:00
{
2024-05-16 23:43:48 +02:00
auto chopper1 = pattern ( " C7 44 24 44 00 00 E0 40 50 C7 44 24 4C 00 00 00 00 " ) . get_one ( ) ; // Front light
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
constexpr CVector CHOPPER_SEARCH_LIGHT_POS ( 0.0f , 3.0f , - 1.0f ) ; // Same as in III Aircraft (not implemented there yet!)
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( chopper1 . get < float > ( 4 ) , CHOPPER_SEARCH_LIGHT_POS . y ) ;
Patch ( chopper1 . get < float > ( 9 + 4 ) , CHOPPER_SEARCH_LIGHT_POS . z ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
try
2019-12-15 17:04:50 +01:00
{
2024-05-16 23:43:48 +02:00
auto chopper2 = pattern ( " C7 44 24 6C 00 00 10 C1 8D 44 24 5C C7 44 24 70 00 00 00 00 " ) . get_one ( ) ; // Tail light
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
constexpr CVector CHOPPER_RED_LIGHT_POS ( 0.0f , - 7.5f , 2.5f ) ; // Same as in III Aircraft
2019-12-15 17:04:50 +01:00
2024-05-16 23:43:48 +02:00
Patch ( chopper2 . get < float > ( 4 ) , CHOPPER_RED_LIGHT_POS . y ) ;
Patch ( chopper2 . get < float > ( 12 + 4 ) , CHOPPER_RED_LIGHT_POS . z ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-16 23:43:48 +02:00
try
2019-12-15 21:37:05 +01:00
{
using namespace FBISirenCoronaFix ;
2024-05-16 23:43:48 +02:00
auto viceCheetah = pattern ( " 8D 8C 24 CC 09 00 00 FF 35 ? ? ? ? FF 35 ? ? ? ? FF 35 ? ? ? ? E8 " ) . get_one ( ) ; // Siren pos
2019-12-15 21:37:05 +01:00
2024-05-16 23:43:48 +02:00
try
2019-12-15 21:37:05 +01:00
{
2024-05-16 23:43:48 +02:00
auto hasFBISiren = pattern ( " 83 E9 04 0F 84 87 0A 00 00 83 E9 10 " ) . get_one ( ) ; // Predicate for showing FBI/Vice Squad siren
2019-12-15 21:37:05 +01:00
2024-05-16 23:43:48 +02:00
Patch < uint8_t > ( hasFBISiren . get < void > ( ) , 0x55 ) ; // push ebp
InjectHook ( hasFBISiren . get < void > ( 1 ) , SetUpFBISiren , HookType : : Call ) ;
Patch ( hasFBISiren . get < void > ( 1 + 5 ) , { 0x83 , 0xC4 , 0x04 , 0x84 , 0xC0 , 0x90 } ) ; // add esp, 4 / test al, al / nop
2019-12-15 21:37:05 +01:00
2024-05-16 23:43:48 +02:00
InjectHook ( viceCheetah . get < void > ( 0x19 ) , SetUpVector ) ;
2019-12-15 21:37:05 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
static const float VICE_CHEETAH_SIREN_POS_Z = 0.25f ;
Patch ( viceCheetah . get < float * > ( 7 + 2 ) , & VICE_CHEETAH_SIREN_POS_Z ) ;
2019-12-15 21:37:05 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-15 17:04:50 +01:00
}
2024-05-09 19:11:20 +02:00
2024-05-23 20:07:34 +02:00
bool HasModelInfo = false ;
2024-05-18 16:49:03 +02:00
// Register CBaseModelInfo::GetModelInfo for SVF so we can resolve model names
try
{
2024-05-23 20:07:34 +02:00
using namespace ModelIndicesReadyHook ;
2024-05-18 16:49:03 +02:00
auto initialiseObjectData = get_pattern ( " E8 ? ? ? ? 59 E8 ? ? ? ? E8 ? ? ? ? 31 DB " ) ;
auto getModelInfo = ( void * ( * ) ( const char * , int * ) ) get_pattern ( " 57 31 FF 55 8B 6C 24 14 " , - 6 ) ;
2024-05-18 19:09:14 +02:00
GetModelInfo = getModelInfo ;
2024-05-18 16:49:03 +02:00
InterceptCall ( initialiseObjectData , orgInitialiseObjectData , InitialiseObjectData_ReadySVF ) ;
SVF : : RegisterGetModelInfoCB ( getModelInfo ) ;
2024-05-23 20:07:34 +02:00
HasModelInfo = true ;
}
TXN_CATCH ( ) ;
// Fix the construction site LOD losing its HQ model and showing at all times
if ( HasModelInfo ) try
{
using namespace ConstructionSiteLODFix ;
std : : array < void * , 3 > replaceWithNewModel = {
get_pattern ( " E8 ? ? ? ? C7 85 ? ? ? ? 00 00 00 00 83 8D ? ? ? ? FF " ) ,
get_pattern ( " DD D8 E8 ? ? ? ? 56 " , 2 ) ,
get_pattern ( " E8 ? ? ? ? FF 44 24 0C 83 C5 0C " ) ,
} ;
HookEach_ReplaceWithNewModel ( replaceWithNewModel , InterceptCall ) ;
2024-05-18 16:49:03 +02:00
}
TXN_CATCH ( ) ;
2024-10-04 18:15:35 +02:00
// Fix the radar disc shadow scaling and radar X position
try
{
// Legacy namespace name, it's OK
using namespace PrintStringShadows ;
auto draw_entity_coord_blip = pattern ( " D8 0D ? ? ? ? D8 05 ? ? ? ? DE C1 D9 5C 24 18 " ) . count ( 2 ) ;
auto draw_radar_disc1 = pattern ( " D8 25 ? ? ? ? DD DB D9 C2 D9 9C 24 ? ? ? ? DB 05 ? ? ? ? D8 0D ? ? ? ? D8 0D ? ? ? ? D8 05 ? ? ? ? D8 05 " ) . count ( 2 ) ;
auto draw_radar_disc2 = pattern ( " D8 C1 D8 05 ? ? ? ? D9 9C 24 ? ? ? ? DE D9 DD D8 " ) . count ( 2 ) ;
std : : array < float * * , 8 > radarXPos = {
get_pattern < float * > ( " D8 05 ? ? ? ? DE C1 D9 5C 24 28 " , 2 ) ,
get_pattern < float * > ( " D8 05 ? ? ? ? DE C1 D9 5C EC 30 " , 2 ) ,
get_pattern < float * > ( " D8 0D ? ? ? ? D8 05 ? ? ? ? DE C1 D9 9C C4 " , 6 + 2 ) ,
get_pattern < float * > ( " D8 0D ? ? ? ? D8 05 ? ? ? ? DE C1 D9 5C 24 08 " , 6 + 2 ) ,
get_pattern < float * > ( " D8 0D ? ? ? ? D8 05 ? ? ? ? DE C1 DD D9 DB 44 24 18 " , 6 + 2 ) ,
get_pattern < float * > ( " D8 0D ? ? ? ? D8 05 ? ? ? ? DE C1 DD DB DB 44 24 18 " , 6 + 2 ) ,
draw_entity_coord_blip . get ( 0 ) . get < float * > ( 6 + 2 ) ,
draw_entity_coord_blip . get ( 1 ) . get < float * > ( 6 + 2 ) ,
} ;
std : : array < float * * , 4 > radarXPos_RadardiscShrink = {
draw_radar_disc1 . get ( 0 ) . get < float * > ( 35 + 2 ) ,
draw_radar_disc1 . get ( 0 ) . get < float * > ( 35 + 6 + 2 ) ,
draw_radar_disc1 . get ( 1 ) . get < float * > ( 35 + 2 ) ,
draw_radar_disc1 . get ( 1 ) . get < float * > ( 35 + 6 + 2 ) ,
} ;
std : : array < float * * , 4 > radarYPos_RadardiscShrink = {
draw_radar_disc1 . get ( 0 ) . get < float * > ( 2 ) ,
draw_radar_disc1 . get ( 1 ) . get < float * > ( 2 ) ,
draw_radar_disc2 . get ( 0 ) . get < float * > ( 2 + 2 ) ,
draw_radar_disc2 . get ( 1 ) . get < float * > ( 2 + 2 ) ,
} ;
auto drawMap = get_pattern ( " 59 E8 ? ? ? ? 83 3D ? ? ? ? ? 0F 84 " , 1 ) ;
auto drawRadarDiscSprite = pattern ( " D8 05 ? ? ? ? D9 9C 24 ? ? ? ? DE D9 DD D8 E8 " ) . count ( 2 ) ;
std : : array < void * , 2 > spriteDraw = {
drawRadarDiscSprite . get ( 0 ) . get < void > ( 17 ) ,
drawRadarDiscSprite . get ( 1 ) . get < void > ( 17 ) ,
} ;
// Undo the damage caused by IVRadarScaling from the widescreen fix moving the radar way too far to the right
// It's moved from 40.0f to 71.0f, which is way too much now that we're scaling the horizontal placement correctly!
try
{
// Use exactly the same patterns as widescreen fix
float * radarPos = * get_pattern < float * > ( " D8 05 ? ? ? ? DE C1 D9 5C 24 28 " , 2 ) ;
// No need to undo CRadar::DrawYouAreHereSprite, as wsfix keeps it as 40.0f
// This hardcodes a patched constant inside so the pattern will fail to match without IV radar scaling
auto radarRing1 = pattern ( " C7 84 24 ? ? ? ? 00 00 82 42 " ) . count ( 2 ) ;
auto radarRing2 = pattern ( " D8 05 ? ? ? ? D8 05 ? ? ? ? D9 9C 24 " ) . count ( 2 ) ;
// This + radarRing1 succeeding is enough proof that IVRadarScaling is in use
if ( hGameModule = = ModCompat : : Utils : : GetModuleHandleFromAddress ( radarPos ) & & * radarPos = = ( 40.0f + 31.0f ) )
{
* radarPos = 40.0f ;
radarRing1 . for_each_result ( [ ] ( pattern_match match )
{
Patch < float > ( match . get < void > ( 7 ) , 34.0f ) ;
} ) ;
radarRing2 . for_each_result ( [ ] ( pattern_match match )
{
static float STOCK_RADAR_POS = 40.0f ;
Patch ( match . get < void > ( 2 ) , & STOCK_RADAR_POS ) ;
} ) ;
}
}
TXN_CATCH ( ) ;
auto PatchFloat = [ ] ( float * * address , const float * & org , float & replaced )
{
org = * address ;
Patch ( address , & replaced ) ;
} ;
HookEach_CalculateRadarXPos ( radarXPos , PatchFloat ) ;
HookEach_CalculateRadarXPos_RadardiscShrink ( radarXPos_RadardiscShrink , PatchFloat ) ;
HookEach_CalculateRadarYPos_RadardiscShrink ( radarYPos_RadardiscShrink , PatchFloat ) ;
HookEach_DrawRadarDisc ( spriteDraw , InterceptCall ) ;
InterceptCall ( drawMap , orgDrawMap , DrawMap_RecalculatePositions < radarXPos . size ( ) , 0 , radarXPos_RadardiscShrink . size ( ) , radarYPos_RadardiscShrink . size ( ) > ) ;
}
TXN_CATCH ( ) ;
2024-05-09 19:11:20 +02:00
FLAUtils : : Init ( moduleList ) ;
2019-08-24 22:35:35 +02:00
}
void InjectDelayedPatches_VC_Common ( )
{
std : : unique_ptr < ScopedUnprotect : : Unprotect > Protect = ScopedUnprotect : : UnprotectSectionOrFullModule ( GetModuleHandle ( nullptr ) , " .text " ) ;
// Obtain a path to the ASI
wchar_t wcModulePath [ MAX_PATH ] ;
2024-05-11 12:42:28 +02:00
GetModuleFileNameW ( reinterpret_cast < HMODULE > ( & __ImageBase ) , wcModulePath , _countof ( wcModulePath ) - 3 ) ; // Minus max required space for extension
2019-08-24 22:35:35 +02:00
PathRenameExtensionW ( wcModulePath , L " .ini " ) ;
const bool hasDebugMenu = DebugMenuLoad ( ) ;
2024-05-18 19:09:14 +02:00
SelectableBackfaceCulling : : ReadDrawBackfacesExclusions ( wcModulePath ) ;
2019-08-24 22:35:35 +02:00
InjectDelayedPatches_VC_Common ( hasDebugMenu , wcModulePath ) ;
Common : : Patches : : III_VC_DelayedCommon ( hasDebugMenu , wcModulePath ) ;
}
2021-07-26 23:52:20 +02:00
void Patch_VC_10 ( uint32_t width , uint32_t height )
2014-08-27 01:20:24 +02:00
{
2024-04-06 15:14:58 +02:00
using namespace Memory : : DynBase ;
2014-08-27 01:20:24 +02:00
2024-04-06 15:14:58 +02:00
RsGlobal = * ( RsGlobalType * * ) DynBaseAddress ( 0x602D32 ) ;
SubtitlesShadowFix_JumpBack = ( void * ) DynBaseAddress ( 0x551701 ) ;
2014-08-27 01:20:24 +02:00
InjectHook ( 0x5433BD , FixedRefValue ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( 0x5516FC , SubtitlesShadowFix , HookType : : Jump ) ;
2014-08-27 01:20:24 +02:00
Patch < BYTE > ( 0x5517C4 , 0xD9 ) ;
Patch < BYTE > ( 0x5517DF , 0xD9 ) ;
Patch < BYTE > ( 0x551832 , 0xD9 ) ;
Patch < BYTE > ( 0x551848 , 0xD9 ) ;
Patch < BYTE > ( 0x5517E2 , 0x34 - 0x14 ) ;
Patch < BYTE > ( 0x55184B , 0x34 - 0x14 ) ;
Patch < BYTE > ( 0x5517C7 , 0x28 - 0x18 ) ;
Patch < BYTE > ( 0x551835 , 0x24 - 0x18 ) ;
Patch < BYTE > ( 0x5516FB , 0x90 ) ;
2024-04-06 15:14:58 +02:00
{
using namespace PrintStringShadows ;
XY < 0x5FA1F6 , 0x5FA1D5 > : : Hook ( 0x5FA1FD ) ;
XYMinus < 0x544727 , 0x544727 > : : Hook ( 0x54474D ) ;
}
2015-03-05 15:51:52 +01:00
// Mouse fucking fix!
Patch < DWORD > ( 0x601740 , 0xC3C030 ) ;
2015-06-29 00:53:49 +02:00
// (Hopefully) more precise frame limiter
2016-09-12 00:09:21 +02:00
ReadCall ( 0x6004A2 , RsEventHandler ) ;
InjectHook ( 0x6004A2 , NewFrameRender ) ;
2015-06-29 00:53:49 +02:00
InjectHook ( 0x600449 , GetTimeSinceLastFrame ) ;
2015-09-20 22:25:10 +02:00
// RsMouseSetPos call (SA style fix)
2018-01-10 01:08:41 +01:00
ReadCall ( 0x4A5E45 , orgConstructRenderList ) ;
2015-09-20 22:25:10 +02:00
InjectHook ( 0x4A5E45 , ResetMousePos ) ;
// New wndproc
2024-04-06 15:14:58 +02:00
OldWndProc = * ( LRESULT ( CALLBACK * * * ) ( HWND , UINT , WPARAM , LPARAM ) ) DynBaseAddress ( 0x601727 ) ;
2015-09-20 22:25:10 +02:00
Patch ( 0x601727 , & pCustomWndProc ) ;
2016-03-10 23:14:25 +01:00
// Y axis sensitivity fix
// By ThirteenAG
2024-04-06 15:14:58 +02:00
float * sens = * ( float * * ) DynBaseAddress ( 0x4796E5 ) ;
2016-03-10 23:14:25 +01:00
Patch < const void * > ( 0x479410 + 0x2E0 + 0x2 , sens ) ;
Patch < const void * > ( 0x47A20E + 0x27D + 0x2 , sens ) ;
Patch < const void * > ( 0x47AE27 + 0x1CC + 0x2 , sens ) ;
Patch < const void * > ( 0x47BE8F + 0x22E + 0x2 , sens ) ;
Patch < const void * > ( 0x481AB3 + 0x4FE + 0x2 , sens ) ;
2016-03-31 22:29:31 +02:00
// Don't lock mouse Y axis during fadeins
Patch < BYTE > ( 0x47C11E , 0xEB ) ;
Patch < BYTE > ( 0x47CD94 , 0xEB ) ;
Nop ( 0x47C15A , 2 ) ;
2016-03-31 22:51:52 +02:00
// Scan for A/B drives looking for audio files
Patch < DWORD > ( 0x5D7941 , ' A ' ) ;
Patch < DWORD > ( 0x5D7B04 , ' A ' ) ;
2016-05-21 16:14:34 +02:00
// ~x~ as cyan blip
// Shared with GInput
Patch < BYTE > ( 0x550481 , 0 ) ;
Patch < BYTE > ( 0x550488 , 255 ) ;
Patch < BYTE > ( 0x55048F , 255 ) ;
Patch < BYTE > ( 0x5505FF , 0 ) ;
Patch < BYTE > ( 0x550603 , 255 ) ;
Patch < BYTE > ( 0x550607 , 255 ) ;
2016-05-29 15:01:57 +02:00
2016-05-29 16:52:25 +02:00
// Corrected crime codes
Patch < DWORD > ( 0x5FDDDB , 0xC5 ) ;
2016-05-29 15:01:57 +02:00
2016-07-27 02:19:11 +02:00
// Fixed ammo for melee weapons in cheats
Patch < BYTE > ( 0x4AED14 + 1 , 1 ) ; // katana
Patch < BYTE > ( 0x4AEB74 + 1 , 1 ) ; // chainsaw
2017-06-15 17:45:51 +02:00
// Fixed crash related to autopilot timing calculations
2024-01-28 15:26:24 +01:00
InjectHook ( 0x418FAE , AutoPilotTimerFix_VC , HookType : : Jump ) ;
2017-06-15 17:45:51 +02:00
2021-07-26 23:52:20 +02:00
Common : : Patches : : DDraw_VC_10 ( width , height , aNoDesktopMode ) ;
2014-08-27 01:20:24 +02:00
}
2021-07-26 23:52:20 +02:00
void Patch_VC_11 ( uint32_t width , uint32_t height )
2014-08-27 01:20:24 +02:00
{
2024-04-06 15:14:58 +02:00
using namespace Memory : : DynBase ;
2014-08-27 01:20:24 +02:00
2024-04-06 15:14:58 +02:00
RsGlobal = * ( RsGlobalType * * ) DynBaseAddress ( 0x602D12 ) ;
SubtitlesShadowFix_JumpBack = ( void * ) DynBaseAddress ( 0x551721 ) ;
2014-08-27 01:20:24 +02:00
InjectHook ( 0x5433DD , FixedRefValue ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( 0x55171C , SubtitlesShadowFix , HookType : : Jump ) ;
2014-08-27 01:20:24 +02:00
Patch < BYTE > ( 0x5517E4 , 0xD9 ) ;
Patch < BYTE > ( 0x5517FF , 0xD9 ) ;
Patch < BYTE > ( 0x551852 , 0xD9 ) ;
Patch < BYTE > ( 0x551868 , 0xD9 ) ;
Patch < BYTE > ( 0x551802 , 0x34 - 0x14 ) ;
Patch < BYTE > ( 0x55186B , 0x34 - 0x14 ) ;
Patch < BYTE > ( 0x5517E7 , 0x28 - 0x18 ) ;
Patch < BYTE > ( 0x551855 , 0x24 - 0x18 ) ;
Patch < BYTE > ( 0x55171B , 0x90 ) ;
2024-04-06 15:14:58 +02:00
{
using namespace PrintStringShadows ;
XY < 0x5FA216 , 0x5FA1F5 > : : Hook ( 0x5FA21D ) ;
XYMinus < 0x544747 , 0x544747 > : : Hook ( 0x54476D ) ;
}
2015-03-05 15:51:52 +01:00
// Mouse fucking fix!
Patch < DWORD > ( 0x601770 , 0xC3C030 ) ;
2015-06-29 00:53:49 +02:00
// (Hopefully) more precise frame limiter
2016-09-12 00:09:21 +02:00
ReadCall ( 0x6004C2 , RsEventHandler ) ;
InjectHook ( 0x6004C2 , NewFrameRender ) ;
2015-06-29 00:53:49 +02:00
InjectHook ( 0x600469 , GetTimeSinceLastFrame ) ;
2015-09-20 22:25:10 +02:00
// RsMouseSetPos call (SA style fix)
2018-01-10 01:08:41 +01:00
ReadCall ( 0x4A5E65 , orgConstructRenderList ) ;
2015-09-20 22:25:10 +02:00
InjectHook ( 0x4A5E65 , ResetMousePos ) ;
// New wndproc
2024-04-06 15:14:58 +02:00
OldWndProc = * ( LRESULT ( CALLBACK * * * ) ( HWND , UINT , WPARAM , LPARAM ) ) DynBaseAddress ( 0x601757 ) ;
2015-09-20 22:25:10 +02:00
Patch ( 0x601757 , & pCustomWndProc ) ;
2016-03-10 23:14:25 +01:00
// Y axis sensitivity fix
// By ThirteenAG
2024-04-06 15:14:58 +02:00
float * sens = * ( float * * ) DynBaseAddress ( 0x4796E5 ) ;
2016-03-10 23:14:25 +01:00
Patch < const void * > ( 0x479410 + 0x2E0 + 0x2 , sens ) ;
Patch < const void * > ( 0x47A20E + 0x27D + 0x2 , sens ) ;
Patch < const void * > ( 0x47AE27 + 0x1CC + 0x2 , sens ) ;
Patch < const void * > ( 0x47BE8F + 0x22E + 0x2 , sens ) ;
Patch < const void * > ( 0x481AB3 + 0x4FE + 0x2 , sens ) ;
2016-03-31 22:29:31 +02:00
// Don't lock mouse Y axis during fadeins
Patch < BYTE > ( 0x47C11E , 0xEB ) ;
Patch < BYTE > ( 0x47CD94 , 0xEB ) ;
Nop ( 0x47C15A , 2 ) ;
2016-03-31 22:51:52 +02:00
// Scan for A/B drives looking for audio files
Patch < DWORD > ( 0x5D7961 , ' A ' ) ;
Patch < DWORD > ( 0x5D7B24 , ' A ' ) ;
2016-05-21 16:14:34 +02:00
// ~x~ as cyan blip
// Shared with GInput
Patch < BYTE > ( 0x5504A1 , 0 ) ;
Patch < BYTE > ( 0x5504A8 , 255 ) ;
Patch < BYTE > ( 0x5504AF , 255 ) ;
Patch < BYTE > ( 0x55061F , 0 ) ;
Patch < BYTE > ( 0x550623 , 255 ) ;
Patch < BYTE > ( 0x550627 , 255 ) ;
2016-05-29 16:52:25 +02:00
// Corrected crime codes
Patch < DWORD > ( 0x5FDDFB , 0xC5 ) ;
2016-05-29 19:17:44 +02:00
2016-07-27 02:19:11 +02:00
// Fixed ammo for melee weapons in cheats
Patch < BYTE > ( 0x4AED34 + 1 , 1 ) ; // katana
Patch < BYTE > ( 0x4AEB94 + 1 , 1 ) ; // chainsaw
2017-06-15 17:45:51 +02:00
// Fixed crash related to autopilot timing calculations
2024-01-28 15:26:24 +01:00
InjectHook ( 0x418FAE , AutoPilotTimerFix_VC , HookType : : Jump ) ;
2017-09-18 22:13:02 +02:00
2021-07-26 23:52:20 +02:00
Common : : Patches : : DDraw_VC_11 ( width , height , aNoDesktopMode ) ;
2014-08-27 01:20:24 +02:00
}
2021-07-26 23:52:20 +02:00
void Patch_VC_Steam ( uint32_t width , uint32_t height )
2014-08-27 01:20:24 +02:00
{
2024-04-06 15:14:58 +02:00
using namespace Memory : : DynBase ;
2014-08-27 01:20:24 +02:00
2024-04-06 15:14:58 +02:00
RsGlobal = * ( RsGlobalType * * ) DynBaseAddress ( 0x602952 ) ;
SubtitlesShadowFix_JumpBack = ( void * ) DynBaseAddress ( 0x5515F1 ) ;
2014-08-27 01:20:24 +02:00
InjectHook ( 0x5432AD , FixedRefValue ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( 0x5515EC , SubtitlesShadowFix , HookType : : Jump ) ;
2014-08-27 01:20:24 +02:00
Patch < BYTE > ( 0x5516B4 , 0xD9 ) ;
Patch < BYTE > ( 0x5516CF , 0xD9 ) ;
Patch < BYTE > ( 0x551722 , 0xD9 ) ;
Patch < BYTE > ( 0x551738 , 0xD9 ) ;
Patch < BYTE > ( 0x5516D2 , 0x34 - 0x14 ) ;
Patch < BYTE > ( 0x55173B , 0x34 - 0x14 ) ;
Patch < BYTE > ( 0x5516B7 , 0x28 - 0x18 ) ;
Patch < BYTE > ( 0x551725 , 0x24 - 0x18 ) ;
Patch < BYTE > ( 0x5515EB , 0x90 ) ;
2024-04-06 15:14:58 +02:00
{
using namespace PrintStringShadows ;
XY < 0x5F9E56 , 0x5F9E35 > : : Hook ( 0x5F9E5D ) ;
XYMinus < 0x544617 , 0x544617 > : : Hook ( 0x54463D ) ;
}
2015-03-05 15:51:52 +01:00
// Mouse fucking fix!
Patch < DWORD > ( 0x6013B0 , 0xC3C030 ) ;
2015-06-29 00:53:49 +02:00
// (Hopefully) more precise frame limiter
2016-09-12 00:09:21 +02:00
ReadCall ( 0x600102 , RsEventHandler ) ;
InjectHook ( 0x600102 , NewFrameRender ) ;
2015-06-29 00:53:49 +02:00
InjectHook ( 0x6000A9 , GetTimeSinceLastFrame ) ;
2015-09-20 22:25:10 +02:00
// RsMouseSetPos call (SA style fix)
2018-01-10 01:08:41 +01:00
ReadCall ( 0x4A5D15 , orgConstructRenderList ) ;
2015-09-20 22:25:10 +02:00
InjectHook ( 0x4A5D15 , ResetMousePos ) ;
// New wndproc
2024-04-06 15:14:58 +02:00
OldWndProc = * ( LRESULT ( CALLBACK * * * ) ( HWND , UINT , WPARAM , LPARAM ) ) DynBaseAddress ( 0x601397 ) ;
2015-09-20 22:25:10 +02:00
Patch ( 0x601397 , & pCustomWndProc ) ;
2016-03-10 23:14:25 +01:00
// Y axis sensitivity fix
// By ThirteenAG
2024-04-06 15:14:58 +02:00
float * sens = * ( float * * ) DynBaseAddress ( 0x4795C5 ) ;
2016-03-10 23:14:25 +01:00
Patch < const void * > ( 0x4792F0 + 0x2E0 + 0x2 , sens ) ;
Patch < const void * > ( 0x47A0EE + 0x27D + 0x2 , sens ) ;
Patch < const void * > ( 0x47AD07 + 0x1CC + 0x2 , sens ) ;
Patch < const void * > ( 0x47BD6F + 0x22E + 0x2 , sens ) ;
Patch < const void * > ( 0x481993 + 0x4FE + 0x2 , sens ) ;
2016-03-31 22:29:31 +02:00
// Don't lock mouse Y axis during fadeins
Patch < BYTE > ( 0x47BFFE , 0xEB ) ;
Patch < BYTE > ( 0x47CC74 , 0xEB ) ;
Nop ( 0x47C03A , 2 ) ;
2016-03-31 22:51:52 +02:00
// Scan for A/B drives looking for audio files
Patch < DWORD > ( 0x5D7764 , ' A ' ) ;
2016-05-21 16:14:34 +02:00
// ~x~ as cyan blip
// Shared with GInput
Patch < BYTE > ( 0x550371 , 0 ) ;
Patch < BYTE > ( 0x550378 , 255 ) ;
Patch < BYTE > ( 0x55037F , 255 ) ;
Patch < BYTE > ( 0x5504EF , 0 ) ;
Patch < BYTE > ( 0x5504F3 , 255 ) ;
Patch < BYTE > ( 0x5504F7 , 255 ) ;
2016-05-29 16:52:25 +02:00
// Corrected crime codes
Patch < DWORD > ( 0x5FDA3B , 0xC5 ) ;
2016-05-29 19:17:44 +02:00
2016-07-27 02:19:11 +02:00
// Fixed ammo for melee weapons in cheats
Patch < BYTE > ( 0x4AEA44 + 1 , 1 ) ; // katana
Patch < BYTE > ( 0x4AEBE4 + 1 , 1 ) ; // chainsaw
2017-06-15 17:45:51 +02:00
// Fixed crash related to autopilot timing calculations
2024-01-28 15:26:24 +01:00
InjectHook ( 0x418FAE , AutoPilotTimerFix_VC , HookType : : Jump ) ;
2017-09-18 22:13:02 +02:00
2021-07-26 23:52:20 +02:00
Common : : Patches : : DDraw_VC_Steam ( width , height , aNoDesktopMode ) ;
2014-08-27 01:20:24 +02:00
}
2016-03-11 20:48:14 +01:00
void Patch_VC_JP ( )
{
2024-04-06 15:14:58 +02:00
using namespace Memory : : DynBase ;
2016-03-11 20:48:14 +01:00
// Y axis sensitivity fix
// By ThirteenAG
Patch < DWORD > ( 0x4797E7 + 0x2E0 + 0x2 , 0x94ABD8 ) ;
Patch < DWORD > ( 0x47A5E5 + 0x27D + 0x2 , 0x94ABD8 ) ;
Patch < DWORD > ( 0x47B1FE + 0x1CC + 0x2 , 0x94ABD8 ) ;
Patch < DWORD > ( 0x47C266 + 0x22E + 0x2 , 0x94ABD8 ) ;
Patch < DWORD > ( 0x481E8A + 0x4FE + 0x2 , 0x94ABD8 ) ;
}
2017-09-11 00:39:46 +02:00
void Patch_VC_Common ( )
{
using namespace Memory ;
2024-05-16 23:43:48 +02:00
using namespace hook : : txn ;
2017-09-11 00:39:46 +02:00
2024-07-09 23:30:17 +02:00
const HMODULE hGameModule = GetModuleHandle ( nullptr ) ;
2017-09-11 00:39:46 +02:00
// New timers fix
2024-05-16 23:43:48 +02:00
try
2017-09-11 00:39:46 +02:00
{
auto hookPoint = pattern ( " 83 E4 F8 89 44 24 08 C7 44 24 0C 00 00 00 00 DF 6C 24 08 " ) . get_one ( ) ;
auto jmpPoint = get_pattern ( " DD D8 E9 31 FF FF FF " ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( hookPoint . get < void > ( 0x21 ) , CTimer : : Update_SilentPatch , HookType : : Call ) ;
InjectHook ( hookPoint . get < void > ( 0x21 + 5 ) , jmpPoint , HookType : : Jump ) ;
2017-09-11 00:39:46 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-09-12 13:31:57 +02:00
2017-09-12 15:36:58 +02:00
// Alt+F4
2024-05-16 23:43:48 +02:00
try
2017-09-12 15:36:58 +02:00
{
auto addr = pattern ( " 59 59 31 C0 83 C4 70 5D 5F 5E 5B C2 10 00 " ) . count ( 2 ) ;
auto dest = get_pattern ( " 53 55 56 FF B4 24 90 00 00 00 FF 15 " ) ;
addr . for_each_result ( [ & ] ( pattern_match match ) {
2024-01-28 15:26:24 +01:00
InjectHook ( match . get < void > ( 2 ) , dest , HookType : : Jump ) ;
2017-09-12 15:36:58 +02:00
} ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-09-12 18:01:02 +02:00
// Proper panels damage
2024-05-16 23:43:48 +02:00
try
2017-09-12 18:01:02 +02:00
{
auto addr = pattern ( " C6 41 09 03 C6 41 0A 03 C6 41 0B 03 " ) . get_one ( ) ;
// or dword ptr[ecx+14], 3300000h
// nop
Patch ( addr . get < void > ( 0x18 ) , { 0x81 , 0x49 , 0x14 , 0x00 , 0x00 , 0x30 , 0x03 } ) ;
Nop ( addr . get < void > ( 0x18 + 7 ) , 13 ) ;
Nop ( addr . get < void > ( 0x33 ) , 7 ) ;
2017-09-12 22:35:45 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-09-12 22:35:45 +02:00
// Proper metric-imperial conversion constants
2024-05-16 23:43:48 +02:00
try
2017-09-12 22:35:45 +02:00
{
static const float METERS_TO_MILES = 0.0006213711922f ;
auto addr = pattern ( " 75 ? D9 05 ? ? ? ? D8 0D ? ? ? ? 6A 00 6A 00 D9 9C 24 " ) . count ( 6 ) ;
2024-05-16 23:43:48 +02:00
auto sum = get_pattern ( " D9 9C 24 A8 00 00 00 8D 84 24 A8 00 00 00 50 " , - 6 + 2 ) ;
2017-09-12 22:35:45 +02:00
addr . for_each_result ( [ & ] ( pattern_match match ) {
Patch < const void * > ( match . get < void > ( 0x8 + 2 ) , & METERS_TO_MILES ) ;
} ) ;
2017-09-12 18:01:02 +02:00
2017-09-12 22:35:45 +02:00
Patch < const void * > ( sum , & METERS_TO_MILES ) ;
2017-09-12 18:01:02 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-09-13 21:56:06 +02:00
// Improved pathfinding in PickNextNodeAccordingStrategy - PickNextNodeToChaseCar with XYZ coords
2024-05-16 23:43:48 +02:00
try
2017-09-13 21:56:06 +02:00
{
auto addr = pattern ( " E8 ? ? ? ? 50 8D 44 24 10 50 E8 " ) . get_one ( ) ;
ReadCall ( addr . get < void > ( 0x25 ) , orgPickNextNodeToChaseCar ) ;
const uintptr_t funcAddr = ( uintptr_t ) get_pattern ( " 8B 9C 24 BC 00 00 00 66 8B B3 A6 01 00 00 66 85 F6 " , - 0xA ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( funcAddr - 5 , PickNextNodeToChaseCarXYZ , HookType : : Jump ) ; // For plugin-sdk
2017-09-13 21:56:06 +02:00
// push PickNextNodeToChaseCarZ instead of 0.0f
// mov ecx, [PickNextNodeToChaseCarZ]
// mov [esp+0B8h+var_2C], ecx
Patch ( funcAddr + 0x5D , { 0x8B , 0x0D } ) ;
Patch < const void * > ( funcAddr + 0x5D + 2 , & PickNextNodeToChaseCarZ ) ;
Patch ( funcAddr + 0x5D + 6 , { 0x89 , 0x8C , 0x24 , 0x8C , 0x00 , 0x00 , 0x00 } ) ;
// lea eax, [ecx+edx*4] -> lea eax, [edx+edx*4]
Patch < uint8_t > ( funcAddr + 0x6E + 2 , 0x92 ) ;
// lea eax, [esp+20h+var_10]
// push eax
// nop...
Patch ( addr . get < void > ( 0x10 ) , { 0x83 , 0xC4 , 0x04 , 0x8D , 0x44 , 0x24 , 0x10 , 0x50 , 0xEB , 0x0A } ) ;
InjectHook ( addr . get < void > ( 0x25 ) , PickNextNodeToChaseCarXYZ ) ;
Patch < uint8_t > ( addr . get < void > ( 0x2A + 2 ) , 0xC ) ;
// push edx
// nop...
Patch < uint8_t > ( addr . get < void > ( 0x3E ) , 0x52 ) ;
Nop ( addr . get < void > ( 0x3E + 1 ) , 6 ) ;
InjectHook ( addr . get < void > ( 0x46 ) , PickNextNodeToChaseCarXYZ ) ;
Patch < uint8_t > ( addr . get < void > ( 0x4B + 2 ) , 0xC ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-09-13 21:56:06 +02:00
2017-09-26 22:21:52 +02:00
// No censorships
2024-05-16 23:43:48 +02:00
try
2017-09-26 22:21:52 +02:00
{
auto addr = get_pattern ( " 8B 43 50 85 C0 8B 53 50 74 2B 83 E8 01 " ) ;
Patch ( addr , { 0x83 , 0xC4 , 0x08 , 0x5B , 0xC3 } ) ; // add esp, 8 \ pop ebx \ retn
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-09-26 22:21:52 +02:00
2017-12-29 00:41:00 +01:00
// 014C cargen counter fix (by spaceeinstein)
2024-05-16 23:43:48 +02:00
try
2017-12-29 00:41:00 +01:00
{
auto do_processing = pattern ( " 0F B7 43 28 83 F8 FF 7D 04 66 FF 4B 28 " ) . get_one ( ) ;
Patch < uint8_t > ( do_processing . get < uint8_t * > ( 1 ) , 0xBF ) ; // movzx eax, word ptr [ebx+28h] -> movsx eax, word ptr [ebx+28h]
Patch < uint8_t > ( do_processing . get < uint8_t * > ( 7 ) , 0x74 ) ; // jge -> jz
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2017-12-29 00:41:00 +01:00
2018-01-28 18:15:38 +01:00
// Fixed ammo from SCM
2024-05-16 23:43:48 +02:00
try
2018-01-28 18:15:38 +01:00
{
2024-02-03 20:26:50 +01:00
using namespace ZeroAmmoFix ;
2018-01-28 18:15:38 +01:00
2024-02-03 20:26:50 +01:00
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 ) ;
2018-01-28 18:15:38 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2018-01-28 18:15:38 +01:00
2018-03-27 22:28:45 +02:00
// Extras working correctly on bikes
2024-05-16 23:43:48 +02:00
try
2018-03-27 22:28:45 +02:00
{
auto createInstance = get_pattern ( " 89 C1 8B 41 04 " ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( createInstance , CreateInstance_BikeFix , HookType : : Call ) ;
2018-03-27 22:28:45 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2018-03-27 22:28:45 +02:00
2018-03-28 21:23:16 +02:00
2018-04-28 17:45:32 +02:00
// Credits =)
2024-05-16 23:43:48 +02:00
try
2018-04-28 17:45:32 +02:00
{
auto renderCredits = pattern ( " 8D 44 24 28 83 C4 14 50 FF 35 ? ? ? ? E8 ? ? ? ? 8D 44 24 1C 59 59 50 FF 35 ? ? ? ? E8 ? ? ? ? 59 59 " ) . get_one ( ) ;
ReadCall ( renderCredits . get < void > ( - 50 ) , Credits : : PrintCreditText ) ;
ReadCall ( renderCredits . get < void > ( - 5 ) , Credits : : PrintCreditText_Hooked ) ;
InjectHook ( renderCredits . get < void > ( - 5 ) , Credits : : PrintSPCredits ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2018-04-28 17:45:32 +02:00
2018-03-28 21:23:16 +02:00
// Decreased keyboard input latency
2024-05-16 23:43:48 +02:00
try
2018-03-28 21:23:16 +02:00
{
using namespace KeyboardInputFix ;
auto updatePads = pattern ( " 66 8B 42 1A " ) . get_one ( ) ;
void * jmpDest = get_pattern ( " 66 A3 ? ? ? ? 5F " , 6 ) ;
void * simButtonCheckers = get_pattern ( " 56 57 B3 01 " , 0x16 ) ;
NewKeyState = * updatePads . get < void * > ( 0x27 + 1 ) ;
OldKeyState = * updatePads . get < void * > ( 4 + 1 ) ;
TempKeyState = * updatePads . get < void * > ( 0x270 + 1 ) ;
ReadCall ( simButtonCheckers , orgClearSimButtonPressCheckers ) ;
InjectHook ( simButtonCheckers , ClearSimButtonPressCheckers ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( updatePads . get < void > ( 9 ) , jmpDest , HookType : : Jump ) ;
2018-03-28 21:23:16 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2018-03-28 21:23:16 +02:00
2019-08-24 22:35:35 +02:00
// Locale based metric/imperial system
2024-05-16 23:43:48 +02:00
try
2019-08-24 22:35:35 +02:00
{
using namespace Localization ;
void * updateCompareFlag = get_pattern ( " 89 D9 6A 00 E8 ? ? ? ? 30 C0 83 C4 70 5D 5F 5E 5B C2 04 00 " , 4 ) ;
2024-05-16 23:43:48 +02:00
auto constructStatLine = pattern ( " 85 C0 74 11 83 E8 01 83 F8 03 " ) . get_one ( ) ;
2019-08-24 22:35:35 +02:00
ReadCall ( updateCompareFlag , orgUpdateCompareFlag_IsMetric ) ;
InjectHook ( updateCompareFlag , UpdateCompareFlag_IsMetric ) ;
// Stats
Nop ( constructStatLine . get < void > ( - 11 ) , 1 ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( constructStatLine . get < void > ( - 11 + 1 ) , PrefsLanguage_IsMetric , HookType : : Call ) ;
2019-08-24 22:35:35 +02:00
Nop ( constructStatLine . get < void > ( - 2 ) , 2 ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-08-24 22:35:35 +02:00
2019-12-08 20:15:25 +01:00
// Corrected FBI Washington sirens sound
// Primary siren lower pitched like in FBI Rancher and secondary siren higher pitched
2024-05-16 23:43:48 +02:00
try
2019-12-08 20:15:25 +01:00
{
using namespace SirenSwitchingFix ;
// Other mods might be touching it, so only patch specific vehicles if their code has not been touched at all
2024-05-16 23:43:48 +02:00
auto sirenPitch = pattern ( " 83 F8 17 74 32 " ) . get_one ( ) ;
2019-12-08 20:15:25 +01:00
2024-05-16 23:43:48 +02:00
InjectHook ( sirenPitch . get < void > ( 5 ) , IsFBIRanchOrFBICar , HookType : : Call ) ;
Patch ( sirenPitch . get < void > ( 5 + 5 ) , { 0x84 , 0xC0 } ) ; // test al, al
Nop ( sirenPitch . get < void > ( 5 + 5 + 2 ) , 4 ) ;
2019-12-08 20:15:25 +01:00
2024-05-16 23:43:48 +02:00
// Pitch shift FBI Washington primary siren
try
{
2019-12-08 20:15:25 +01:00
struct tVehicleSampleData {
int m_nAccelerationSampleIndex ;
char m_bEngineSoundType ;
int m_nHornSample ;
int m_nHornFrequency ;
char m_nSirenOrAlarmSample ;
int m_nSirenOrAlarmFrequency ;
char m_bDoorType ;
} ;
tVehicleSampleData * dataTable = * get_pattern < tVehicleSampleData * > ( " 8B 04 95 ? ? ? ? 89 43 1C " , 3 ) ;
// Only pitch shift if table hasn't been relocated elsewhere
2024-07-09 23:30:17 +02:00
if ( hGameModule = = ModCompat : : Utils : : GetModuleHandleFromAddress ( dataTable ) )
2019-12-08 20:15:25 +01:00
{
// fbicar frequency = fbiranch frequency
dataTable [ 17 ] . m_nSirenOrAlarmFrequency = dataTable [ 90 ] . m_nSirenOrAlarmFrequency ;
}
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-08 20:15:25 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-08 20:15:25 +01:00
2019-12-25 15:36:59 +01:00
// Allow extra6 to be picked with component rule 4 (any)
2024-05-16 23:43:48 +02:00
try
2019-12-25 15:36:59 +01:00
{
void * extraMult6 = get_pattern ( " D8 0D ? ? ? ? D9 7C 24 04 8B 44 24 04 80 4C 24 05 0C D9 6C 24 04 89 44 24 04 DB 5C 24 08 D9 6C 24 04 8B 44 24 08 83 C4 10 5D " , 2 ) ;
static const float MULT_6 = 6.0f ;
Patch ( extraMult6 , & MULT_6 ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2019-12-25 17:40:59 +01:00
// Make drive-by one shot sounds owned by the driver instead of the car
// Fixes incorrect weapon sound being used for drive-by
2024-05-16 23:43:48 +02:00
try
2019-12-25 17:40:59 +01:00
{
auto getDriverOneShot = pattern ( " FF 35 ? ? ? ? 6A 37 50 E8 ? ? ? ? 83 7E 08 00 " ) . get_one ( ) ;
// nop
// mov ecx, ebx
// call CVehicle::GetOneShotOwnerID
Patch ( getDriverOneShot . get < void > ( - 8 ) , { 0x90 , 0x89 , 0xD9 } ) ;
2024-01-28 15:26:24 +01:00
InjectHook ( getDriverOneShot . get < void > ( - 5 ) , & CVehicle : : GetOneShotOwnerID_SilentPatch , HookType : : Call ) ;
2019-12-25 17:40:59 +01:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-02-29 21:44:18 +01:00
// Fixed vehicles exploding twice if the driver leaves the car while it's exploding
2024-05-16 23:43:48 +02:00
try
2024-02-29 21:44:18 +01:00
{
using namespace RemoveDriverStatusFix ;
auto removeDriver = pattern ( " 8A 43 50 24 07 0C 20 88 43 50 E8 " ) . get_one ( ) ;
auto processCommands1 = get_pattern ( " 88 42 50 8B 33 " ) ;
auto processCommands2 = get_pattern ( " 88 42 50 8B AE " ) ;
auto removeThisPed = get_pattern ( " 88 42 50 8B 85 " ) ;
auto pedSetOutCar = get_pattern ( " 0C 20 88 47 50 8B 85 " , 2 ) ;
Nop ( removeDriver . get < void > ( ) , 2 ) ;
InjectHook ( removeDriver . get < void > ( 2 ) , RemoveDriver_SetStatus , HookType : : Call ) ;
// CVehicle::RemoveDriver already sets the status to STATUS_ABANDONED, these are redundant
Nop ( processCommands1 , 3 ) ;
Nop ( processCommands2 , 3 ) ;
Nop ( removeThisPed , 3 ) ;
Nop ( pedSetOutCar , 3 ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-03-09 17:04:12 +01:00
// Apply the environment mapping on extra components
2024-05-16 23:43:48 +02:00
try
2024-03-09 17:04:12 +01:00
{
using namespace EnvMapsOnExtras ;
auto forAllAtomics = pattern ( " 50 E8 ? ? ? ? 66 8B 4B 44 " ) . get_one ( ) ;
// push eax -> push ebx
Patch < uint8_t > ( forAllAtomics . get < void > ( ) , 0x53 ) ;
InterceptCall ( forAllAtomics . get < void > ( 1 ) , orgRpClumpForAllAtomics , RpClumpForAllAtomics_ExtraComps ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-03-17 18:15:56 +01:00
// Fix probabilities in CVehicle::InflictDamage incorrectly assuming a random range from 0 to 100.000
2024-05-16 23:43:48 +02:00
try
2024-03-17 18:15:56 +01:00
{
auto probability = get_pattern ( " 66 81 7B 5A ? ? 73 50 " , 4 ) ;
Patch < uint16_t > ( probability , 35000u / 2u ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-03-18 23:47:51 +01:00
// Null terminate read lines in CPlane::LoadPath
2024-05-16 23:43:48 +02:00
try
2024-03-18 23:47:51 +01:00
{
using namespace NullTerminatedLines ;
auto loadPath = get_pattern ( " DD D8 45 E8 " , 3 ) ;
InterceptCall ( loadPath , orgSscanf_LoadPath , sscanf1_LoadPath_Terminate ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-03-21 20:44:51 +01:00
// Don't reset mouse sensitivity on New Game
2024-05-16 23:43:48 +02:00
try
2024-03-21 20:44:51 +01:00
{
using namespace MouseSensNewGame ;
auto cameraInit = pattern ( " C7 85 14 09 00 00 00 00 00 00 C7 05 ? ? ? ? ? ? ? ? C7 05 " ) . get_one ( ) ;
auto setDirMyDocuments = get_pattern ( " 89 CD E8 ? ? ? ? 68 " , 2 ) ;
DefaultHorizontalAccel = * cameraInit . get < float > ( 20 + 2 + 4 ) ;
fMouseAccelHorzntl = * cameraInit . get < float * > ( 20 + 2 ) ;
Nop ( cameraInit . get < void > ( 20 ) , 10 ) ;
InterceptCall ( setDirMyDocuments , orgSetDirMyDocuments , SetDirMyDocuments_ResetMouse ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-04-10 20:23:20 +02:00
// Fixed pickup effects
2024-05-16 23:43:48 +02:00
try
2024-04-10 20:23:20 +02:00
{
using namespace PickupEffectsFixes ;
// Give money pickups color ID 37, like most other "generic" pickups
// Coincidentally, it's also the most likely color to be "randomly" assigned to them now
auto bigDollarColor = get_pattern ( " C6 44 24 ? 00 E9 ? ? ? ? 8D 80 00 00 00 00 0F B7 1D ? ? ? ? 39 CB 75 0C " ) ;
// Remove the glow from minigun2
auto minigun2Glow = get_pattern ( " 8D 41 01 89 CB " ) ;
InjectHook ( bigDollarColor , & PickUpEffects_BigDollarColor , HookType : : Call ) ;
InjectHook ( minigun2Glow , & PickUpEffects_Minigun2Glow , HookType : : Call ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-04-16 22:20:40 +02:00
// Fixed the muzzle flash facing the wrong direction
// By Wesser
2024-05-16 23:43:48 +02:00
try
2024-04-16 22:20:40 +02:00
{
auto fireInstantHit = pattern ( " D9 44 24 50 D8 44 24 44 " ) . get_one ( ) ;
// Replace fld [esp].vecSource with fldz, as vecEnd is already absolute
Patch ( fireInstantHit . get < void > ( ) , { 0xD9 , 0xEE , 0x90 , 0x90 } ) ;
Patch ( fireInstantHit . get < void > ( 15 ) , { 0xD9 , 0xEE , 0x90 , 0x90 } ) ;
Patch ( fireInstantHit . get < void > ( 30 ) , { 0xD9 , 0xEE , 0x90 , 0x90 } ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-04-17 23:31:03 +02:00
// Fixed IS_PLAYER_TARGETTING_CHAR incorrectly detecting targetting in Classic controls
// when the player is not aiming
// By Wesser
2024-05-16 23:43:48 +02:00
try
2024-04-17 23:31:03 +02:00
{
using namespace IsPlayerTargettingCharFix ;
auto isPlayerTargettingChar = pattern ( " 83 7C 24 ? ? A3 ? ? ? ? 0F 84 " ) . get_one ( ) ;
auto using1stPersonWeaponMode = static_cast < decltype ( Using1stPersonWeaponMode ) > ( get_pattern ( " 66 83 F8 07 74 18 " , - 7 ) ) ;
bool * useMouse3rdPerson = * get_pattern < bool * > ( " 80 3D ? ? ? ? ? 75 09 66 C7 05 ? ? ? ? ? ? 8B 35 " , 2 ) ;
void * theCamera = * get_pattern < void * > ( " B9 ? ? ? ? 31 DB E8 " , 1 ) ;
Using1stPersonWeaponMode = using1stPersonWeaponMode ;
bUseMouse3rdPerson = useMouse3rdPerson ;
TheCamera = theCamera ;
// Move mov ds:dword_784030, eax one instruction earlier so we don't need
// to include it in the patched routine
memmove ( isPlayerTargettingChar . get < void > ( ) , isPlayerTargettingChar . get < void > ( 5 ) , 5 ) ;
InjectHook ( isPlayerTargettingChar . get < void > ( 5 ) , IsPlayerTargettingChar_ExtraChecks , HookType : : Call ) ;
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-04-29 20:43:25 +02:00
// Use PS2 randomness for Rosenberg audio to hopefully bring the odds closer to PS2
// The functionality was never broken on PC - but the random distribution seemingly made it looks as if it was
2024-05-16 23:43:48 +02:00
try
2024-04-29 20:43:25 +02:00
{
2024-05-21 18:34:44 +02:00
using namespace ConsoleRandomness ;
2024-04-29 20:43:25 +02:00
auto busted_audio_rand = get_pattern ( " 80 BB 48 01 00 00 00 0F 85 ? ? ? ? E8 ? ? ? ? 25 FF FF 00 00 " , 13 ) ;
2024-05-21 18:34:44 +02:00
InjectHook ( busted_audio_rand , rand15 ) ;
2024-04-29 20:43:25 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-05-07 22:30:32 +02:00
// Reset variables on New Game
2024-05-16 23:43:48 +02:00
try
2024-05-07 22:30:32 +02:00
{
using namespace VariableResets ;
auto game_initialise = get_pattern ( " 6A 00 E8 ? ? ? ? 83 C4 0C 68 ? ? ? ? E8 ? ? ? ? 59 C3 " , 15 ) ;
std : : array < void * , 2 > reinit_game_object_variables = {
get_pattern ( " 74 05 E8 ? ? ? ? E8 ? ? ? ? 80 3D " , 7 ) ,
get_pattern ( " C6 05 ? ? ? ? ? E8 ? ? ? ? C7 05 " , 7 )
} ;
InterceptCall ( game_initialise , orgGameInitialise , GameInitialise ) ;
HookEach_ReInitGameObjectVariables ( reinit_game_object_variables , InterceptCall ) ;
// Variables to reset
GameVariablesToReset . emplace_back ( * get_pattern < bool * > ( " 7D 09 80 3D ? ? ? ? ? 74 32 " , 2 + 2 ) ) ; // Free resprays
GameVariablesToReset . emplace_back ( * get_pattern < int * > ( " 7D 78 A1 ? ? ? ? 05 " , 2 + 1 ) ) ; // LastTimeAmbulanceCreated
GameVariablesToReset . emplace_back ( * get_pattern < int * > ( " A1 ? ? ? ? 05 ? ? ? ? 39 05 ? ? ? ? 0F 86 ? ? ? ? 8B 15 " , 1 ) ) ; // LastTimeFireTruckCreated
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-05-11 23:25:06 +02:00
// Ped speech fix
// Based off Sergenaur's fix
2024-05-16 23:43:48 +02:00
try
2024-05-11 23:25:06 +02:00
{
// Remove the artificial 6s delay between any ped speech samples
2024-05-16 23:43:48 +02:00
auto delay_check = get_pattern ( " 80 BE ? ? ? ? ? 0F 85 ? ? ? ? B9 " , 7 ) ;
auto comment_delay_id1 = get_pattern ( " 0F B7 C2 DD D8 C1 E0 04 " ) ;
auto comment_delay_id2 = pattern ( " 0F B7 95 DA 05 00 00 D9 6C 24 04 " ) . get_one ( ) ;
2024-05-11 23:25:06 +02:00
2024-05-16 23:43:48 +02:00
Nop ( delay_check , 6 ) ;
2024-05-11 23:25:06 +02:00
2024-05-16 23:43:48 +02:00
// movzx eax, dx -> movzx eax, bx
Patch ( comment_delay_id1 , { 0x0F , 0xB7 , 0xC3 } ) ;
2024-05-11 23:25:06 +02:00
2024-05-16 23:43:48 +02:00
// movzx edx, word ptr [ebp+5DAh] -> movzx edx, bx \ nop
Patch ( comment_delay_id2 . get < void > ( ) , { 0x0F , 0xB7 , 0xD3 } ) ;
Nop ( comment_delay_id2 . get < void > ( 3 ) , 4 ) ;
2024-05-11 23:25:06 +02:00
}
2024-05-16 23:43:48 +02:00
TXN_CATCH ( ) ;
2024-05-17 17:35:46 +02:00
2024-05-18 19:09:14 +02:00
// Disabled backface culling on detached car parts, peds and specific models
2024-05-17 17:35:46 +02:00
try
{
2024-05-18 19:09:14 +02:00
using namespace SelectableBackfaceCulling ;
2024-05-17 17:35:46 +02:00
2024-05-18 19:09:14 +02:00
auto entity_render = pattern ( " 56 75 06 5E 5B C3 " ) . get_one ( ) ;
2024-05-17 17:35:46 +02:00
2024-05-18 19:09:14 +02:00
EntityRender_Prologue_JumpBack = entity_render . get < void > ( ) ;
2024-05-17 17:35:46 +02:00
2024-05-18 19:09:14 +02:00
// Check if CEntity::Render is already re-routed by something else
if ( * entity_render . get < uint8_t > ( - 7 ) = = 0xE9 )
2024-05-17 17:35:46 +02:00
{
2024-05-18 19:09:14 +02:00
ReadCall ( entity_render . get < void > ( - 7 ) , orgEntityRender ) ;
2024-05-17 17:35:46 +02:00
}
2024-05-18 19:09:14 +02:00
InjectHook ( entity_render . get < void > ( - 7 ) , EntityRender_BackfaceCulling , HookType : : Jump ) ;
2024-05-17 17:35:46 +02:00
}
TXN_CATCH ( ) ;
2024-05-23 21:48:53 +02:00
// Correct the duration of the outro splash to 2.5 seconds
// The outro splash displays for 150 ticks from the moment it fully fades in, with the tick cpimt supposedly incrementing every 10ms
// However, since the game is locked to 30FPS, the tick count actually increments every 33.3ms, so the splash takes around 5s
// Correct the "tick rate" to 33ms and the "tick count" to 75, so it is more or less 2.5s at 30FPS,
// and similarly long when running at 60FPS or uncapped. The original code hints at 1.5s,
// but that makes it hard to read the splash before it vanishes
//
// Also fix the splash flickering for a frame when fading in
try
{
using namespace OutroSplashFix ;
auto outro_tick_rate = get_pattern ( " 83 F8 0A 76 10 " , 2 ) ;
auto outro_tick_count = get_pattern ( " 81 3D ? ? ? ? 96 00 00 00 " , 6 ) ;
auto splash_rgba = get_pattern ( " E8 ? ? ? ? DB 05 ? ? ? ? 8D 54 24 2C " ) ;
auto alpha_clamp = get_pattern ( " 8A 83 ? ? ? ? 8D 4C 24 2C " ) ;
// Ideally, we want (time - lastTime) >= 33, but we can express the same with > 32
Patch < uint8_t > ( outro_tick_rate , 32 ) ;
Patch < uint32_t > ( outro_tick_count , 75 ) ;
InterceptCall ( splash_rgba , orgRGBASet , RGBASet_Clamp ) ;
// al -> eax
Patch < uint8_t > ( alpha_clamp , 0x8B ) ;
}
TXN_CATCH ( ) ;
2024-06-05 21:53:13 +02:00
// Fix Tommy not shaking his fists with brass knuckles (in all cases)
// and most post-GTA III weapons (when cars slow down for him)
try
{
using namespace TommyFistShakeWithWeapons ;
auto weapon_group_1a = pattern ( " 8B 40 60 59 83 F8 01 75 43 " ) . get_one ( ) ;
2024-06-07 19:47:40 +02:00
auto weapon_group_1b = pattern ( " 8B 40 60 59 83 F8 01 0F 85 ? ? ? ? 8A 85 " ) . get_one ( ) ;
2024-06-05 21:53:13 +02:00
auto slow_car_down_for_peds = pattern ( " 89 C7 8B 17 85 D2 74 19 " ) . get_one ( ) ;
auto else_jump = get_pattern ( " 0F 85 ? ? ? ? 8A 83 ? ? ? ? 24 FE " ) ;
2024-06-07 19:47:40 +02:00
std : : array < void * , 2 > exclude_chainsaw = {
weapon_group_1a . get < void > ( - 5 ) ,
weapon_group_1b . get < void > ( - 5 ) ,
} ;
2024-06-05 21:53:13 +02:00
ReadCall ( weapon_group_1a . get < void > ( - 5 ) , GetWeaponInfo ) ;
// jnz -> ja
Patch < uint8_t > ( weapon_group_1a . get < void > ( 7 ) , 0x77 ) ;
2024-06-07 19:47:40 +02:00
Patch < uint8_t > ( weapon_group_1b . get < void > ( 7 + 1 ) , 0x87 ) ;
2024-06-05 21:53:13 +02:00
Nop ( slow_car_down_for_peds . get < void > ( ) , 1 ) ;
InjectHook ( slow_car_down_for_peds . get < void > ( 1 ) , & CheckWeaponGroupHook , HookType : : Call ) ;
InjectHook ( slow_car_down_for_peds . get < void > ( 8 ) , else_jump , HookType : : Jump ) ;
2024-06-07 19:47:40 +02:00
HookEach_ExcludeChainsaw ( exclude_chainsaw , InterceptCall ) ;
2024-06-05 21:53:13 +02:00
}
TXN_CATCH ( ) ;
2024-07-09 23:30:17 +02:00
// Fix the screwdriver not making sounds on impact
2024-09-22 14:18:39 +02:00
try
2024-07-09 23:30:17 +02:00
{
void * * pedAttackJumpTable = * get_pattern < void * * > ( " 83 F8 05 77 77 FF 24 85 " , 5 + 3 ) ;
// Only make changes if the table hasn't been relocated
if ( hGameModule = = ModCompat : : Utils : : GetModuleHandleFromAddress ( pedAttackJumpTable ) )
{
// Give ASSOCGRP_SCREWDRIVER the same case as ASSOCGRP_KNIFE and others
pedAttackJumpTable [ 1 ] = pedAttackJumpTable [ 2 ] ;
}
}
2024-09-22 14:18:39 +02:00
TXN_CATCH ( ) ;
// Allow the tear gas to damage anyone (including the player), like on PS2
try
{
auto set_peds_choking = get_pattern ( " 0F 84 ? ? ? ? 8D 4B 34 D9 41 08 " ) ;
Nop ( set_peds_choking , 6 ) ;
}
TXN_CATCH ( ) ;
2017-09-11 00:39:46 +02:00
}
2014-08-27 01:20:24 +02:00
BOOL WINAPI DllMain ( HINSTANCE hinstDLL , DWORD fdwReason , LPVOID lpvReserved )
{
UNREFERENCED_PARAMETER ( hinstDLL ) ;
UNREFERENCED_PARAMETER ( lpvReserved ) ;
if ( fdwReason = = DLL_PROCESS_ATTACH )
{
2021-07-26 23:52:20 +02:00
const auto [ width , height ] = GetDesktopResolution ( ) ;
sprintf_s ( aNoDesktopMode , " Cannot find %ux%ux32 video mode " , width , height ) ;
2015-03-05 15:51:52 +01:00
2017-09-18 22:13:02 +02:00
// This scope is mandatory so Protect goes out of scope before rwcseg gets fixed
{
std : : unique_ptr < ScopedUnprotect : : Unprotect > Protect = ScopedUnprotect : : UnprotectSectionOrFullModule ( GetModuleHandle ( nullptr ) , " .text " ) ;
2018-01-14 12:04:22 +01:00
const int8_t version = Memory : : GetVersion ( ) . version ;
2021-07-26 23:52:20 +02:00
if ( version = = 0 ) Patch_VC_10 ( width , height ) ;
else if ( version = = 1 ) Patch_VC_11 ( width , height ) ;
else if ( version = = 2 ) Patch_VC_Steam ( width , height ) ;
2016-09-12 00:11:17 +02:00
2017-09-18 22:13:02 +02:00
// Y axis sensitivity only
else if ( * ( DWORD * ) 0x601048 = = 0x5E5F5D60 ) Patch_VC_JP ( ) ;
2016-03-11 20:48:14 +01:00
2017-09-18 22:13:02 +02:00
Patch_VC_Common ( ) ;
2017-09-27 23:12:05 +02:00
Common : : Patches : : III_VC_Common ( ) ;
2017-09-18 23:11:19 +02:00
Common : : Patches : : DDraw_Common ( ) ;
2019-08-24 22:35:35 +02:00
Common : : Patches : : III_VC_SetDelayedPatchesFunc ( InjectDelayedPatches_VC_Common ) ;
2017-09-18 22:13:02 +02:00
}
2014-08-27 02:44:49 +02:00
2017-09-18 22:13:02 +02:00
Common : : Patches : : FixRwcseg_Patterns ( ) ;
2014-08-27 01:20:24 +02:00
}
return TRUE ;
2018-04-20 20:33:45 +02:00
}
extern " C " __declspec ( dllexport )
uint32_t GetBuildNumber ( )
{
return ( SILENTPATCH_REVISION_ID < < 8 ) | SILENTPATCH_BUILD_ID ;
2014-08-27 01:20:24 +02:00
}