From bee5b44af1ad24f555515725a81d7b80895f8f48 Mon Sep 17 00:00:00 2001 From: Silent Date: Fri, 11 Oct 2024 23:11:39 +0200 Subject: [PATCH] III/VC/SA: Fix sliding text leftovers causing texts to stay on screen for too long at high resolutions Also introduced new INI/debug mode options to re-enable those cut features in all 3 games: * SlidingMissionTitleText * SlidingOddJobText --- Config/SilentPatchIII.ini | 5 + Config/SilentPatchSA.ini | 5 + Config/SilentPatchVC.ini | 5 + SilentPatchIII/SilentPatchIII.cpp | 122 +++++++++++++++++++ SilentPatchSA/SilentPatchSA.cpp | 196 +++++++++++++++++++++++++++++- SilentPatchVC/SilentPatchVC.cpp | 123 ++++++++++++++++++- 6 files changed, 453 insertions(+), 3 deletions(-) diff --git a/Config/SilentPatchIII.ini b/Config/SilentPatchIII.ini index 9471cad..265fca1 100644 --- a/Config/SilentPatchIII.ini +++ b/Config/SilentPatchIII.ini @@ -7,6 +7,11 @@ Units=-1 ; it also corrects taxi sign light placement on Taxi ; and fixes placement of the search light of the search light on the police chopper. EnableVehicleCoronaFixes=1 +; Enables the sliding mission title text, based on remnants of this feature in the game code. +SlidingMissionTitleText=0 +; Enables the sliding odd job text, based on remnants of this feature in the game code. +; This behaviour was visible in one of the beta clips released for GTA III. +SlidingOddJobText=0 [ExtraCompSpecularityExceptions] ; Put model IDs or names (one ID/name per line) here to forcibly disable specularity on their extras diff --git a/Config/SilentPatchSA.ini b/Config/SilentPatchSA.ini index fd7e867..1c87921 100644 --- a/Config/SilentPatchSA.ini +++ b/Config/SilentPatchSA.ini @@ -27,6 +27,11 @@ TrueInvicibility=0 ; Allows to override units used by the game - 0 forces metric units, 1 forces imperial units, ; -1 makes the game use units based on system locale Units=-1 +; Enables the sliding mission title text, based on remnants of this feature in the game code. +SlidingMissionTitleText=0 +; Enables the sliding odd job text, based on remnants of this feature in the game code. +; This behaviour was visible in one of the beta clips released for GTA III. +SlidingOddJobText=0 [RotorFixExceptions] ; Put model IDs or names (one ID/name per line) here to exclude them from using blurred rotors diff --git a/Config/SilentPatchVC.ini b/Config/SilentPatchVC.ini index 5cd54d0..43d5dad 100644 --- a/Config/SilentPatchVC.ini +++ b/Config/SilentPatchVC.ini @@ -7,6 +7,11 @@ Units=-1 ; it also adds a siren to FBI Washington, corrects taxi sign light placement on Taxi ; and fixes placement of the search light and rear rotor light on Police Maverick. EnableVehicleCoronaFixes=1 +; Enables the sliding mission title text, based on remnants of this feature in the game code. +SlidingMissionTitleText=0 +; Enables the sliding odd job text, based on remnants of this feature in the game code. +; This behaviour was visible in one of the beta clips released for GTA III. +SlidingOddJobText=0 [ExtraCompSpecularityExceptions] ; Put model IDs or names (one ID/name per line) here to forcibly disable specularity on their extras diff --git a/SilentPatchIII/SilentPatchIII.cpp b/SilentPatchIII/SilentPatchIII.cpp index 8d297de..26ec32c 100644 --- a/SilentPatchIII/SilentPatchIII.cpp +++ b/SilentPatchIII/SilentPatchIII.cpp @@ -1010,6 +1010,67 @@ namespace CreditsScalingFixes HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_ScaleY); } + +// ============= Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature ============= +namespace SlidingTextsScalingFixes +{ + static const unsigned int FIXED_RES_WIDTH_SCALE = 640; + + static std::array* pBigMessageX; + static float* pOddJob2XOffset; + + template + struct BigMessageSlider + { + static inline bool bSlidingEnabled = false; + + static inline float** pHorShadowValue; + + template + static void (*orgPrintString)(float,float,const wchar_t*); + + template + static void PrintString_Slide(float fX, float fY, const wchar_t* pText) + { + if (bSlidingEnabled) + { + // We divide by a constant 640.0, because the X position is meant to slide across the entire screen + fX = (*pBigMessageX)[BigMessageIndex] * RsGlobal->MaximumWidth / 640.0f; + // The first draws are shadows, add the shadow offset manually. We know this function is called BEFORE the one fixing the shadow scale, + // so we're fine with this approach. + if constexpr (Index == 0) + { + fX += **pHorShadowValue; + } + } + orgPrintString(fX, fY, pText); + } + + HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide); + }; + + struct OddJob2Slider + { + static inline bool bSlidingEnabled = false; + + template + static void (*orgPrintString)(float,float,const wchar_t*); + + template + static void PrintString_Slide(float fX, float fY, const wchar_t* pText) + { + // We divide by a constant 640.0, because the X position is meant to slide across the entire screen + if (bSlidingEnabled) + { + fX -= *pOddJob2XOffset * RsGlobal->MaximumWidth / 640.0f; + } + orgPrintString(fX, fY, pText); + } + + HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide); + }; +} + namespace ModelIndicesReadyHook { static void (*orgInitialiseObjectData)(const char*); @@ -1310,6 +1371,67 @@ void InjectDelayedPatches_III_Common( bool bHasDebugMenu, const wchar_t* wcModul } TXN_CATCH(); + + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature + // Also since we're touching it, optionally allow to re-enable this feature. + try + { + using namespace SlidingTextsScalingFixes; + + // "Unscale" text sliding thresholds, so texts don't stay on screen longer at high resolutions + auto scalingThreshold = pattern("A1 ? ? ? ? 59 83 C0 EC").count(2); + + scalingThreshold.for_each_result([](pattern_match match) + { + Patch(match.get(1), &FIXED_RES_WIDTH_SCALE); + }); + + // Optional sliding texts + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingMissionTitleText", -1, wcModulePath); INIoption != -1) try + { + // We need to manually add the shadow offset in this case + pBigMessageX = *get_pattern*>("DB 44 24 20 D8 1D ? ? ? ? DF E0", 4 + 2); + + auto bigMessage1ShadowPrint = pattern("D8 05 ? ? ? ? D9 1C 24 DD D8 E8 ? ? ? ? D9 05 ? ? ? ? D9 7C 24 0C").get_one(); + + std::array slidingMessage1 = { + bigMessage1ShadowPrint.get(0xB), + get_pattern("E8 ? ? ? ? 83 C4 0C EB 0A C7 05 ? ? ? ? ? ? ? ? 83 C4 68"), + }; + + BigMessageSlider<1>::pHorShadowValue = bigMessage1ShadowPrint.get(2); + BigMessageSlider<1>::bSlidingEnabled = INIoption != 0; + BigMessageSlider<1>::HookEach_PrintString(slidingMessage1, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding mission title text", &BigMessageSlider<1>::bSlidingEnabled, nullptr); + } + } + TXN_CATCH(); + + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingOddJobText", -1, wcModulePath); INIoption != -1) try + { + pOddJob2XOffset = *get_pattern("D9 05 ? ? ? ? D8 E1 D9 1D ? ? ? ? E9", 2); + + std::array slidingOddJob2 = { + get_pattern("E8 ? ? ? ? 83 C4 0C 8D 4C 24 5C"), + get_pattern("E8 ? ? ? ? 83 C4 0C 66 83 3D ? ? ? ? ? 0F 84"), + }; + + OddJob2Slider::bSlidingEnabled = INIoption != 0; + OddJob2Slider::HookEach_PrintString(slidingOddJob2, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding odd job text", &OddJob2Slider::bSlidingEnabled, nullptr); + } + } + TXN_CATCH(); + } + TXN_CATCH(); + + FLAUtils::Init(moduleList); } diff --git a/SilentPatchSA/SilentPatchSA.cpp b/SilentPatchSA/SilentPatchSA.cpp index ad8837f..e859083 100644 --- a/SilentPatchSA/SilentPatchSA.cpp +++ b/SilentPatchSA/SilentPatchSA.cpp @@ -2841,6 +2841,65 @@ namespace CreditsScalingFixes } +// ============= Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature ============= +namespace SlidingTextsScalingFixes +{ + static const unsigned int FIXED_RES_WIDTH_SCALE = 640; + + static std::array* pBigMessageX; + static float* pOddJob2XOffset; + + template + struct BigMessageSlider + { + static inline bool bSlidingEnabled = false; + + template + static void (*orgPrintString)(float,float,const wchar_t*); + + template + static void PrintString_Slide(float fX, float fY, const wchar_t* pText) + { + // We divide by a constant 640.0, because the X position is meant to slide across the entire screen + orgPrintString(bSlidingEnabled ? (*pBigMessageX)[BigMessageIndex] * RsGlobal->MaximumWidth / 640.0f : fX, fY, pText); + } + + template + static void (*orgSetRightJustifyWrap)(float wrap); + + template + static void SetRightJustifyWrap_Slide(float wrap) + { + orgSetRightJustifyWrap(bSlidingEnabled ? ScaleX(-500.0f) : wrap); + } + + HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide); + HOOK_EACH_INIT(RightJustifyWrap, orgSetRightJustifyWrap, SetRightJustifyWrap_Slide); + }; + + struct OddJob2Slider + { + static inline bool bSlidingEnabled = false; + + template + static void (*orgPrintString)(float,float,const wchar_t*); + + template + static void PrintString_Slide(float fX, float fY, const wchar_t* pText) + { + // We divide by a constant 640.0, because the X position is meant to slide across the entire screen + if (bSlidingEnabled) + { + fX -= *pOddJob2XOffset * RsGlobal->MaximumWidth / 640.0f; + } + orgPrintString(fX, fY, pText); + } + + HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide); + }; +} + + // ============= LS-RP Mode stuff ============= namespace LSRPMode { @@ -4227,6 +4286,47 @@ BOOL InjectDelayedPatches_10() } + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature + // Also since we're touching it, optionally allow to re-enable this feature. + + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingMissionTitleText", -1, wcModulePath); INIoption != -1) + { + using namespace SlidingTextsScalingFixes; + + pBigMessageX = *(std::array**)(0x58C919 + 2); + + std::array slidingMessage1 = { 0x58D470 }; + + std::array textWrapFix = { 0x58D2A9 }; + + BigMessageSlider<1>::bSlidingEnabled = INIoption != 0; + BigMessageSlider<1>::HookEach_PrintString(slidingMessage1, InterceptCall); + BigMessageSlider<1>::HookEach_RightJustifyWrap(textWrapFix, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding mission title text", &BigMessageSlider<1>::bSlidingEnabled, nullptr); + } + } + + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingOddJobText", -1, wcModulePath); INIoption != -1) + { + using namespace SlidingTextsScalingFixes; + + pOddJob2XOffset = *(float**)(0x58D077 + 2); + + std::array slidingOddJob2 = { 0x58D21A }; + + OddJob2Slider::bSlidingEnabled = INIoption != 0; + OddJob2Slider::HookEach_PrintString(slidingOddJob2, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding odd job text", &OddJob2Slider::bSlidingEnabled, nullptr); + } + } + + #ifndef NDEBUG if ( const int QPCDays = GetPrivateProfileIntW(L"Debug", L"AddDaysToQPC", 0, wcModulePath); QPCDays != 0 ) { @@ -4589,7 +4689,7 @@ BOOL InjectDelayedPatches_Steam() return TRUE; } -BOOL InjectDelayedPatches_Newsteam() +BOOL InjectDelayedPatches_NewBinaries() { if ( !IsAlreadyRunning() ) { @@ -4600,6 +4700,13 @@ BOOL InjectDelayedPatches_Newsteam() std::unique_ptr Protect = ScopedUnprotect::UnprotectSectionOrFullModule( hInstance, ".text" ); ScopedUnprotect::Section Protect2( hInstance, ".rdata" ); + // Obtain a path to the ASI + wchar_t wcModulePath[MAX_PATH]; + GetModuleFileNameW(reinterpret_cast(&__ImageBase), wcModulePath, _countof(wcModulePath) - 3); // Minus max required space for extension + PathRenameExtensionW(wcModulePath, L".ini"); + + const bool bHasDebugMenu = DebugMenuLoad(); + // Race condition in CdStream fixed // Not taking effect with modloader //if ( !ModCompat::ModloaderCdStreamRaceConditionAware( modloaderModule ) ) @@ -4648,6 +4755,54 @@ BOOL InjectDelayedPatches_Newsteam() } + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature + // Also since we're touching it, optionally allow to re-enable this feature. + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingMissionTitleText", -1, wcModulePath); INIoption != -1) try + { + using namespace SlidingTextsScalingFixes; + + pBigMessageX = *get_pattern*>("8D 4E EC D9 05 ? ? ? ? 83 C4 04", 3 + 2); + + std::array slidingMessage1 = { + get_pattern("E8 ? ? ? ? 6A 00 E8 ? ? ? ? 83 C4 10 8B E5"), + }; + + std::array textWrapFix = { + get_pattern("E8 ? ? ? ? 6A 02 E8 ? ? ? ? 6A 03"), + }; + + BigMessageSlider<1>::bSlidingEnabled = INIoption != 0; + BigMessageSlider<1>::HookEach_PrintString(slidingMessage1, InterceptCall); + BigMessageSlider<1>::HookEach_RightJustifyWrap(textWrapFix, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding mission title text", &BigMessageSlider<1>::bSlidingEnabled, nullptr); + } + } + TXN_CATCH(); + + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingOddJobText", -1, wcModulePath); INIoption != -1) try + { + using namespace SlidingTextsScalingFixes; + + pOddJob2XOffset = *get_pattern("D9 05 ? ? ? ? DD 05 ? ? ? ? D8 F9", 2); + + std::array slidingOddJob2 = { + get_pattern("DB 45 08 D9 1C 24 E8 ? ? ? ? 83 C4 0C 8B E5 5D C3", 6), + }; + + OddJob2Slider::bSlidingEnabled = INIoption != 0; + OddJob2Slider::HookEach_PrintString(slidingOddJob2, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding odd job text", &OddJob2Slider::bSlidingEnabled, nullptr); + } + } + TXN_CATCH(); + + return FALSE; } return TRUE; @@ -5719,6 +5874,22 @@ void Patch_SA_10(HINSTANCE hInstance) } + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature + // Also since we're touching it, optionally allow to re-enable this feature. + { + using namespace SlidingTextsScalingFixes; + + // "Unscale" text sliding thresholds, so texts don't stay on screen longer at high resolutions + Patch(0x58D2E9 + 1, &FIXED_RES_WIDTH_SCALE); + + // Replace dword ptr [esp+0Ch+X], eax \ dword ptr [esp+0Ch+X] + // with a constant fld [620.0] + static const float f620 = FIXED_RES_WIDTH_SCALE - 20.0f; + Patch(0x58C90E, { 0x90, 0x90, 0xD9, 0x05 }); + Patch(0x58C90E + 4, &f620); + } + + #if FULL_PRECISION_D3D // Test - full precision D3D device Patch( 0x7F672B+1, *(uint8_t*)(0x7F672B+1) | D3DCREATE_FPU_PRESERVE ); @@ -6486,7 +6657,7 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance) { void* isAlreadyRunning = get_pattern( "85 C0 74 08 33 C0 8B E5 5D C2 10 00", -5 ); ReadCall( isAlreadyRunning, IsAlreadyRunning ); - InjectHook(isAlreadyRunning, InjectDelayedPatches_Newsteam); + InjectHook(isAlreadyRunning, InjectDelayedPatches_NewBinaries); } TXN_CATCH(); @@ -7777,6 +7948,27 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance) Patch(addr, &FIXED_RES_HEIGHT_SCALE); } } + + + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature + // Also since we're touching it, optionally allow to re-enable this feature. + try + { + using namespace SlidingTextsScalingFixes; + + // "Unscale" text sliding thresholds, so texts don't stay on screen longer at high resolutions + auto bigMessage0Threshold = pattern("83 C4 04 89 4D F4 DB 45 F4").get_one(); + auto bigMessage1Threshold = get_pattern("A1 ? ? ? ? D9 05 ? ? ? ? 83 C0 EC", 1); + + Patch(bigMessage1Threshold, &FIXED_RES_WIDTH_SCALE); + + // Replace dword ptr [ebp+X], eax \ dword ptr [ebp+X] + // with a constant fld [620.0] + static const float f620 = FIXED_RES_WIDTH_SCALE - 20.0f; + Patch(bigMessage0Threshold.get(3), { 0xD9, 0x05 }); + Patch(bigMessage0Threshold.get(3 + 2), &f620); + } + TXN_CATCH(); } diff --git a/SilentPatchVC/SilentPatchVC.cpp b/SilentPatchVC/SilentPatchVC.cpp index 29b8672..f5fa5eb 100644 --- a/SilentPatchVC/SilentPatchVC.cpp +++ b/SilentPatchVC/SilentPatchVC.cpp @@ -467,6 +467,65 @@ namespace CreditsScalingFixes HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_ScaleY); } + +// ============= Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature ============= +namespace SlidingTextsScalingFixes +{ + static const unsigned int FIXED_RES_WIDTH_SCALE = 640; + + static std::array* pBigMessageX; + static float* pOddJob2XOffset; + + template + struct BigMessageSlider + { + static inline bool bSlidingEnabled = false; + + template + static void (*orgPrintString)(float,float,const wchar_t*); + + template + static void PrintString_Slide(float fX, float fY, const wchar_t* pText) + { + // We divide by a constant 640.0, because the X position is meant to slide across the entire screen + orgPrintString(bSlidingEnabled ? (*pBigMessageX)[BigMessageIndex] * RsGlobal->MaximumWidth / 640.0f : fX, fY, pText); + } + + template + static void (*orgSetRightJustifyWrap)(float wrap); + + template + static void SetRightJustifyWrap_Slide(float wrap) + { + orgSetRightJustifyWrap(bSlidingEnabled ? -500.0f * GetWidthMult() * RsGlobal->MaximumWidth : wrap); + } + + HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide); + HOOK_EACH_INIT(RightJustifyWrap, orgSetRightJustifyWrap, SetRightJustifyWrap_Slide); + }; + + struct OddJob2Slider + { + static inline bool bSlidingEnabled = false; + + template + static void (*orgPrintString)(float,float,const wchar_t*); + + template + static void PrintString_Slide(float fX, float fY, const wchar_t* pText) + { + // We divide by a constant 640.0, because the X position is meant to slide across the entire screen + if (bSlidingEnabled) + { + fX -= *pOddJob2XOffset * RsGlobal->MaximumWidth / 640.0f; + } + orgPrintString(fX, fY, pText); + } + + HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide); + }; +} + float FixedRefValue() { return 1.0f; @@ -1250,7 +1309,7 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule static const char * const str[] = { "Default", "Metric", "Imperial" }; DebugMenuEntry *e = DebugMenuAddVar( "SilentPatch", "Forced units", &forcedUnits, nullptr, 1, -1, 1, str ); DebugMenuEntrySetWrap(e, true); - } + } } @@ -1628,6 +1687,68 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule } TXN_CATCH(); + + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature + // Also since we're touching it, optionally allow to re-enable this feature. + try + { + using namespace SlidingTextsScalingFixes; + + // "Unscale" text sliding thresholds, so texts don't stay on screen longer at high resolutions + void* scalingThreshold[] = { + get_pattern("A1 ? ? ? ? 59 83 C0 EC", 1), + get_pattern("59 A1 ? ? ? ? 83 C0 EC", 1 + 1) + }; + + for (void* addr : scalingThreshold) + { + Patch(addr, &FIXED_RES_WIDTH_SCALE); + } + + // Optional sliding texts + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingMissionTitleText", -1, wcModulePath); INIoption != -1) try + { + pBigMessageX = *get_pattern*>("DB 44 24 68 D8 1D ? ? ? ? DF E0", 4 + 2); + + std::array slidingMessage1 = { + get_pattern("E8 ? ? ? ? 83 C4 0C EB 0A C7 05 ? ? ? ? ? ? ? ? 83 C4 60"), + }; + + std::array textWrapFix = { + get_pattern("E8 ? ? ? ? 59 E8 ? ? ? ? 6A 00 E8 ? ? ? ? 59 8B 0D"), + }; + + BigMessageSlider<1>::bSlidingEnabled = INIoption != 0; + BigMessageSlider<1>::HookEach_PrintString(slidingMessage1, InterceptCall); + BigMessageSlider<1>::HookEach_RightJustifyWrap(textWrapFix, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding mission title text", &BigMessageSlider<1>::bSlidingEnabled, nullptr); + } + } + TXN_CATCH(); + + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"SlidingOddJobText", -1, wcModulePath); INIoption != -1) try + { + pOddJob2XOffset = *get_pattern("D9 05 ? ? ? ? D8 E1 D9 1D ? ? ? ? E9", 2); + + std::array slidingOddJob2 = { + get_pattern("E8 ? ? ? ? 83 C4 0C 66 83 3D ? ? ? ? ? 0F 84"), + }; + + OddJob2Slider::bSlidingEnabled = INIoption != 0; + OddJob2Slider::HookEach_PrintString(slidingOddJob2, InterceptCall); + + if (bHasDebugMenu) + { + DebugMenuAddVar("SilentPatch", "Sliding odd job text", &OddJob2Slider::bSlidingEnabled, nullptr); + } + } + TXN_CATCH(); + } + TXN_CATCH(); + FLAUtils::Init(moduleList); }