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
This commit is contained in:
Silent 2024-10-11 23:11:39 +02:00
parent b6450d1853
commit bee5b44af1
No known key found for this signature in database
GPG Key ID: AE53149BB0C45AF1
6 changed files with 453 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<float, 6>* pBigMessageX;
static float* pOddJob2XOffset;
template<std::size_t BigMessageIndex>
struct BigMessageSlider
{
static inline bool bSlidingEnabled = false;
static inline float** pHorShadowValue;
template<std::size_t Index>
static void (*orgPrintString)(float,float,const wchar_t*);
template<std::size_t Index>
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<Index>(fX, fY, pText);
}
HOOK_EACH_INIT(PrintString, orgPrintString, PrintString_Slide);
};
struct OddJob2Slider
{
static inline bool bSlidingEnabled = false;
template<std::size_t Index>
static void (*orgPrintString)(float,float,const wchar_t*);
template<std::size_t Index>
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<Index>(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<void>(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<std::array<float, 6>*>("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<void*, 2> slidingMessage1 = {
bigMessage1ShadowPrint.get<void>(0xB),
get_pattern("E8 ? ? ? ? 83 C4 0C EB 0A C7 05 ? ? ? ? ? ? ? ? 83 C4 68"),
};
BigMessageSlider<1>::pHorShadowValue = bigMessage1ShadowPrint.get<float*>(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<float*>("D9 05 ? ? ? ? D8 E1 D9 1D ? ? ? ? E9", 2);
std::array<void*, 2> 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);
}

View File

@ -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<float, 6>* pBigMessageX;
static float* pOddJob2XOffset;
template<std::size_t BigMessageIndex>
struct BigMessageSlider
{
static inline bool bSlidingEnabled = false;
template<std::size_t Index>
static void (*orgPrintString)(float,float,const wchar_t*);
template<std::size_t Index>
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<Index>(bSlidingEnabled ? (*pBigMessageX)[BigMessageIndex] * RsGlobal->MaximumWidth / 640.0f : fX, fY, pText);
}
template<std::size_t Index>
static void (*orgSetRightJustifyWrap)(float wrap);
template<std::size_t Index>
static void SetRightJustifyWrap_Slide(float wrap)
{
orgSetRightJustifyWrap<Index>(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<std::size_t Index>
static void (*orgPrintString)(float,float,const wchar_t*);
template<std::size_t Index>
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<Index>(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<float, 6>**)(0x58C919 + 2);
std::array<uintptr_t, 1> slidingMessage1 = { 0x58D470 };
std::array<uintptr_t, 1> 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<uintptr_t, 1> 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<ScopedUnprotect::Unprotect> Protect = ScopedUnprotect::UnprotectSectionOrFullModule( hInstance, ".text" );
ScopedUnprotect::Section Protect2( hInstance, ".rdata" );
// Obtain a path to the ASI
wchar_t wcModulePath[MAX_PATH];
GetModuleFileNameW(reinterpret_cast<HMODULE>(&__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<std::array<float, 6>*>("8D 4E EC D9 05 ? ? ? ? 83 C4 04", 3 + 2);
std::array<void*, 1> slidingMessage1 = {
get_pattern("E8 ? ? ? ? 6A 00 E8 ? ? ? ? 83 C4 10 8B E5"),
};
std::array<void*, 1> 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<float*>("D9 05 ? ? ? ? DD 05 ? ? ? ? D8 F9", 2);
std::array<void*, 1> 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<uint8_t>( 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<void>(3), { 0xD9, 0x05 });
Patch(bigMessage0Threshold.get<void>(3 + 2), &f620);
}
TXN_CATCH();
}

View File

@ -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<float, 6>* pBigMessageX;
static float* pOddJob2XOffset;
template<std::size_t BigMessageIndex>
struct BigMessageSlider
{
static inline bool bSlidingEnabled = false;
template<std::size_t Index>
static void (*orgPrintString)(float,float,const wchar_t*);
template<std::size_t Index>
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<Index>(bSlidingEnabled ? (*pBigMessageX)[BigMessageIndex] * RsGlobal->MaximumWidth / 640.0f : fX, fY, pText);
}
template<std::size_t Index>
static void (*orgSetRightJustifyWrap)(float wrap);
template<std::size_t Index>
static void SetRightJustifyWrap_Slide(float wrap)
{
orgSetRightJustifyWrap<Index>(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<std::size_t Index>
static void (*orgPrintString)(float,float,const wchar_t*);
template<std::size_t Index>
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<Index>(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<std::array<float, 6>*>("DB 44 24 68 D8 1D ? ? ? ? DF E0", 4 + 2);
std::array<void*, 1> slidingMessage1 = {
get_pattern("E8 ? ? ? ? 83 C4 0C EB 0A C7 05 ? ? ? ? ? ? ? ? 83 C4 60"),
};
std::array<void*, 1> 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<float*>("D9 05 ? ? ? ? D8 E1 D9 1D ? ? ? ? E9", 2);
std::array<void*, 1> 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);
}