Compare commits

...

21 commits

Author SHA1 Message Date
Tomasak
202101e4f7
More backface culling models (#30)
* two more bfc objects

it never ends

* Update SilentPatchVC.ini

* Update SilentPatchVC.ini
2024-11-02 17:26:22 +01:00
Silent
52deecd5ac
VC: Fix an incorrect vertex setup for the outline of a destination blip
Fixes #66
2024-11-02 17:22:03 +01:00
Silent
7d055f9264
III/VC: Two more minor scaling fixes
III: The position of the dialog question text in the main menu now
scales to resolution correctly.
VC: The vertical offset of the weapon name text in Ammu-Nation
now scales to resolution correctly.
2024-11-01 19:44:40 +01:00
Silent
fd67cb4064
III/VC: Port the inner text box background padding fix from San Andreas
Fixes #48
2024-11-01 19:00:01 +01:00
Silent
7d1bd8a9ac
Fix warnings 2024-11-01 17:53:46 +01:00
Silent
75a3f16ffb
III/VC: Improve the way RwD3D8Get/SetRenderState is located
Improves compatibility with other mods and provides a safe fallback.

Fixes #65
2024-11-01 17:33:33 +01:00
Silent
2f79eb1c8f
SA: Fix the driving school cones fix so it doesn't leave cones from "Cone Coil" and "Burn and Lap" behind
Fixes #62
2024-10-31 23:27:53 +01:00
Silent
3017790d44
Fix Hydra's jet thrusters not displaying due to an uninitialized variable in RwMatrix
By B1ack_Wh1te

Fixes #57
2024-10-31 19:07:22 +01:00
Silent
9f705a3dc3
VC: Improve compatibility between the radar placement fix and plugin-sdk
plugin-sdk had to reimplement an inlined
CRadar::TransformRadarPointToScreenSpace, so we need to patch
a stock global variable so this reimplementation can be aware
of our fixes.

Fixes #43
2024-10-30 21:11:06 +01:00
Silent
5aaec719ed
Fixup logo in ReadMe 2024-10-30 19:44:38 +01:00
Silent
cdcf979a54
SA: Fix wheels detaching after they were made invisible when the car explodes
It was fine in the stock game, regressed once SP made the game pick
a random wheel to detach when exploding.

Fixes #47
2024-10-30 19:44:13 +01:00
Silent
2de40406d2
SA: Add even more preemptive checks
Since we are checking the adjacent fixes,
check this one just in case too.
2024-10-30 18:42:25 +01:00
Silent
09bb67220e
SA: Fix incompatibilities with wshps.asi
* Map Screen cursor fix is now split between the cursor thickness
   and the cursor positioning fix. The latter gets out of the way
   for the ws fix.
* Text background padding not scaling to resolution now gives
   priority to the same fix in the widescreen fix.

Fixes #33
2024-10-30 18:28:53 +01:00
Silent
6a524dcf46
VC: When excluding extras from envmap fixes, also set the envmap coefficient to 0.0
Fixes #40
2024-10-29 21:56:38 +01:00
Silent
17baa64613
SA: Use DWLP_USER in the resolution dialog
GWLP_USERDATA was unlikely to cause issues, but it's technically
reserved since we didn't create the window class ourselves.
DWLP_USER is guaranteed to be free for us to use.
2024-10-28 22:36:50 +01:00
Silent
f1d85b825b
SA: Clean CVehicleModelInfo fields from ::Init so they are cleaned up after modloader loads the ASI
Fixes #44
2024-10-28 20:47:34 +01:00
Silent
4cb6edad7c
SA: Also scale the default size of the gamepad crosshair
Fixes #29
2024-10-28 19:51:57 +01:00
Silent
6e5b41c958
SA: Fix focus issues with the resolution selection dialog
Now requests focus the proper way from WM_INITDIALOG,
instead of a manual SetFocus call.

Fixes #49
2024-10-28 19:27:26 +01:00
Silent
a07d7936da
SA: Improve mod compatibility of the new resolution dialog
Should resolve compatibility issues with Renderhook
2024-10-28 18:56:52 +01:00
Silent
e8b3d3f531
SA: Fix EU 1.0 incompatibilities 2024-10-25 21:36:10 +02:00
Silent
d74b104473
Fix a typo 2024-10-25 18:03:34 +02:00
20 changed files with 761 additions and 228 deletions

View file

@ -48,6 +48,8 @@ All the remaining, non-critical fixes.
* The radar's horizontal position and its disc texture now scale to resolution correctly, resolving color bleed issues at high resolutions.
* Credits now scale to resolution correctly, and they don't cut to an empty screen at the very end anymore.
* Mission title and 'Mission Passed' texts now stay on screen for the same duration, regardless of screen resolution.
* The inner padding of the text boxes with a background now scales to resolution correctly.
* The position of the dialog question text in the main menu now scales to resolution correctly.
* `FILE_FLAG_NO_BUFFERING` flag has been removed from IMG reading functions - speeding up streaming.
* Free resprays will not carry on a New Game now.
* Fixed ambulance and firetruck dispatch timers - they reset on New Game now.
@ -63,7 +65,7 @@ All the remaining, non-critical fixes.
* Drivers now behave correctly when shot at. In the PC versions, they would always speed away, but now they can also do nothing, like in the PS2 version. Additionally, a third, previously inaccessible behavior where drivers abandon the car and flee on foot is now working correctly.
* Dodo keyboard controls are now active when the All Cars Fly cheat is enabled.
* Temporary pickups (like money) are now properly cleaned up if there are too many of them, fixing a possible object leak.
* Car reflections are now displayed correctly in the Steam version (integrated Steam Car Colour Fix from **Sergenaur**).
* Car reflections are now displayed correctly in the Steam version (integrated Steam Car Colour Fix from **Sergeanur**).
* Made Claude sit when riding in a Speeder, the same as **Fire_Head**'s SitInBoat. This change also applies to Skimmer if III Aircraft is installed.
* All FBI Kurumas now spawn in a dark grey color with unpainted bumpers, rather than pitch black.
* Detached limbs now have properly working LODs, instead of rendering the normal and low-detail models at the same time.

View file

@ -102,6 +102,7 @@ All the remaining, non-critical fixes.
* The boundaries of the cursor on the Map screen, and the cursor itself now scale to resolution correctly (contributed by **Wesser**).
* The inner padding of the text boxes with a background now scales to resolution correctly (contributed by **Wesser**).
* Nitrous will no longer regenerate faster when reversing the car (contributed by **Wesser**).
* Hydra's jet thrusters no longer randomly fail to appear (contributed by **B1ack_Wh1te**).
* Detached vehicle parts will now keep the same color and lighting as the vehicle they came from.
* Detached vehicle parts are now rendered from both sides.
* Resolved single-pixel wide seams showing on the Map screen with Anti-Aliasing enabled.

View file

@ -44,6 +44,9 @@ All the remaining, non-critical fixes.
* The radar's horizontal position, the disc texture, and the shadow now scale to resolution correctly. The radar disc was also shrunk slightly to fix gaps and make the icons sit better on the edge.
* Credits now scale to resolution correctly.
* Mission title and 'Mission Passed' texts now stay on screen for the same duration, regardless of screen resolution.
* The inner padding of the text boxes with a background now scales to resolution correctly.
* The vertical offset of the weapon name text in Ammu-Nation now scales to resolution correctly.
* The downward-pointing destination blip in the Map Legend now displays with a correct outline.
* `FILE_FLAG_NO_BUFFERING` flag has been removed from IMG reading functions - speeding up streaming.
* Free resprays will not carry on a New Game now.
* Fixed ambulance and firetruck dispatch timers - they reset on New Game now.
@ -63,7 +66,7 @@ All the remaining, non-critical fixes.
* Fixed an issue where Tommy wouldn't shake his fist at incoming traffic when holding Brass Knuckles, yet would do it when holding a chainsaw.
* Fixed an issue where Tommy wouldn't shake his fist at stopped traffic when holding any weapons introduced in Vice City.
* Hitting vehicles and objects with a screwdriver now produces an impact sound.
* Pedestrians and Tommy are now much more talkative, like in the PS2 version (integrated Ped Speech Patch from **Sergenaur**).
* Pedestrians and Tommy are now much more talkative, like in the PS2 version (integrated Ped Speech Patch from **Sergeanur**).
* Tear gas can now deal damage to Tommy and other mission characters, like in the PS2 version.
* ⚙️ Fixed siren corona placements in Police, Firetruck, Ambulance, Enforcer, Vice Cheetah, and FBI Washington.
* ⚙️ Added siren corona to FBI Washington.

View file

@ -56,17 +56,30 @@ carsontrack
car_fucko03
chairsntable
chairsntableml
ci_alights1_01
ci_astudlights
ci_backlot1
ci_backlot2
ci_backlot3
ci_backlotprops1
ci_backlotprops2
ci_backlotprops3
ci_blights1_01
ci_camera1_01
ci_flatprop01
ci_flatprop02
ci_fountain
ci_industrial1
ci_lander
ci_lights1_01
ci_manintstuff
ci_mansion1
ci_offices
ci_redhead1_01
ci_setshad1
ci_tankship
ci_watertank
ci_windmach
cl_curtains
cl_tbl_chait_tmp
cl_tbl_chait_tmpb

View file

@ -1,7 +1,7 @@
# SilentPatch
<p align="center">
<img height="350" src="https://i.imgur.com/sCDzq12.png" alt="Logo">
<img src="https://i.imgur.com/sCDzq12.png" alt="Logo">
</p>
SilentPatch for the 3D-era Grand Theft Auto games is the first and flagship release of the "SilentPatch family", providing numerous fixes for this beloved franchise.
@ -36,10 +36,11 @@ for both first-time players and the old guard returning for yet another playthro
SilentPatch includes code contributions from:
* aap
* B1ack_Wh1te
* DK22Pac
* Fire_Head
* Nick007J
* NTAuthority
* Sergenaur
* Sergeanur
* spaceeinstein
* Wesser

View file

@ -7,6 +7,7 @@
#include "SVF.h"
#include "ParseUtils.hpp"
#include "Random.h"
#include "RWGTA.h"
#include "Utils/DelimStringReader.h"
@ -195,6 +196,8 @@ namespace Common {
using namespace Memory;
using namespace hook::txn;
const bool HasRwD3D8 = RWGTA::Patches::TryLocateRwD3D8();
// Delayed patching
try
{
@ -249,7 +252,7 @@ namespace Common {
// Fixed static shadows not rendering under fire and pickups
try
if (HasRwD3D8) try
{
using namespace StaticShadowAlphaFix;

View file

@ -4,6 +4,7 @@
#define RwEngineInstance (*rwengine)
#include <rwcore.h>
#include "RWGTA.h"
// GTA versions of RenderWare functions/macros for GTA III/Vice City
// since we cannot use RwEngineInstance directly
@ -39,14 +40,16 @@ void** rwengine = []() -> void** {
return nullptr;
}();
static void* varRwD3D8SetRenderState = Memory::ReadCallFrom( hook::get_pattern( "0F 8C ? ? ? ? 6A 05 6A 19", 10 ) );
WRAPPER RwBool RwD3D8SetRenderState(RwUInt32 state, RwUInt32 value) { VARJMP(varRwD3D8SetRenderState); }
static decltype(::RwD3D8SetRenderState)* fnRwD3D8SetRenderState;
RwBool RwD3D8SetRenderState(RwUInt32 state, RwUInt32 value)
{
return fnRwD3D8SetRenderState(state, value);
}
static RwUInt32* _rwD3D8RenderStates = *static_cast<RwUInt32**>(Memory::ReadCallFrom( hook::get_pattern( "0F 8C ? ? ? ? 6A 05 6A 19", 10 ), 8 + 3 ));
static decltype(::RwD3D8GetRenderState)* fnRwD3D8GetRenderState;
void RwD3D8GetRenderState(RwUInt32 state, void* value)
{
RwUInt32* valuePtr = static_cast<RwUInt32*>(value);
*valuePtr = _rwD3D8RenderStates[ 2 * state ];
fnRwD3D8GetRenderState(state, value);
}
RwReal RwIm2DGetNearScreenZ()
@ -65,4 +68,37 @@ RwBool RwRenderStateSet(RwRenderState state, void *value)
}
// Unreachable stub
RwBool RwMatrixDestroy(RwMatrix* mpMat) { assert(!"Unreachable!"); return TRUE; }
RwBool RwMatrixDestroy(RwMatrix* /*mpMat*/) { assert(!"Unreachable!"); return TRUE; }
bool RWGTA::Patches::TryLocateRwD3D8() try
{
using namespace Memory;
using namespace hook::txn;
auto pfnRwD3D8SetRenderState = [] {
try {
// Everything except for III Steam
return static_cast<decltype(RwD3D8SetRenderState)*>(get_pattern("39 0C C5 ? ? ? ? 74 31", -8));
} catch (const hook::txn_exception&) {
// III Steam
return static_cast<decltype(RwD3D8SetRenderState)*>(get_pattern("8B 0C C5 ? ? ? ? 3B CA", -8));
}
}();
auto pfnRwD3D8GetRenderState = [] {
try {
// Everything except for III Steam
return static_cast<decltype(RwD3D8GetRenderState)*>(get_pattern("8B 0C C5 ? ? ? ? 89 0A C3", -8));
} catch (const hook::txn_exception&) {
// III Steam
return static_cast<decltype(RwD3D8GetRenderState)*>(get_pattern("8B 04 C5 ? ? ? ? 89 02 C3", -8));
}
}();
fnRwD3D8SetRenderState = pfnRwD3D8SetRenderState;
fnRwD3D8GetRenderState = pfnRwD3D8GetRenderState;
return true;
}
catch (const hook::txn_exception&)
{
return false;
}

6
SilentPatch/RWGTA.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
namespace RWGTA::Patches
{
bool TryLocateRwD3D8();
}

@ -1 +1 @@
Subproject commit d70ae68a8e20c4cbd43f60e45586ce65304c1741
Subproject commit 0c6cb62fbee9ff278cc9ff61b4b8b39403812945

View file

@ -1099,6 +1099,84 @@ namespace BrakelightsDummy
}
}
// ============= Fix text background padding not scaling to resolution =============
namespace TextRectPaddingScalingFixes
{
template<std::size_t Index>
static const float* orgPaddingXSize;
template<std::size_t Index>
static float PaddingXSize_Recalculated;
template<std::size_t... I>
static void RecalculateXSize(std::index_sequence<I...>)
{
const float multiplier = GetWidthMult() * RsGlobal->MaximumWidth;
((PaddingXSize_Recalculated<I> = *orgPaddingXSize<I> * multiplier), ...);
}
template<std::size_t Index>
static const float* orgPaddingYSize;
template<std::size_t Index>
static float PaddingYSize_Recalculated;
template<std::size_t... I>
static void RecalculateYSize(std::index_sequence<I...>)
{
const float multiplier = GetHeightMult() * RsGlobal->MaximumHeight;
((PaddingYSize_Recalculated<I> = *orgPaddingYSize<I> * multiplier), ...);
}
static void (*orgGetTextRect)(CRect*, float, float, void*);
template<std::size_t NumXPadding, std::size_t NumYPadding>
static void GetTextRect_Recalculate(CRect* a1, float a2, float a3, void* a4)
{
RecalculateXSize(std::make_index_sequence<NumXPadding>{});
RecalculateYSize(std::make_index_sequence<NumYPadding>{});
orgGetTextRect(a1, a2, a3, a4);
}
HOOK_EACH_INIT(PaddingXSize, orgPaddingXSize, PaddingXSize_Recalculated);
HOOK_EACH_INIT(PaddingYSize, orgPaddingYSize, PaddingYSize_Recalculated);
template<std::size_t Index>
static const float* orgWrapX;
template<std::size_t Index>
static float WrapX_Recalculated;
template<std::size_t... I>
static void RecalculateWrapX(std::index_sequence<I...>)
{
const float multiplier = GetWidthMult() * RsGlobal->MaximumWidth;
((WrapX_Recalculated<I> = *orgWrapX<I> * multiplier), ...);
}
static void (*orgSetJustifyOff)();
template<std::size_t NumXWrap>
static void SetJustifyOff_Recalculate()
{
RecalculateWrapX(std::make_index_sequence<NumXWrap>{});
orgSetJustifyOff();
}
HOOK_EACH_INIT(WrapX, orgWrapX, WrapX_Recalculated);
}
// ============= Fix menu texts not scaling to resolution =============
namespace MenuManagerScalingFixes
{
static void (*orgPrintString)(float,float,const wchar_t*);
static void PrintString_Scale(float fX, float fY, const wchar_t* pText)
{
orgPrintString(fX * GetWidthMult() * RsGlobal->MaximumWidth, fY * GetHeightMult() * RsGlobal->MaximumHeight, pText);
}
}
namespace ModelIndicesReadyHook
{
static void (*orgInitialiseObjectData)(const char*);
@ -1493,6 +1571,65 @@ void InjectDelayedPatches_III_Common( bool bHasDebugMenu, const wchar_t* wcModul
}
TXN_CATCH();
// Fix text background padding not scaling to resolution
try
{
using namespace TextRectPaddingScalingFixes;
auto getTextRect = get_pattern("FF 74 24 54 FF 74 24 54 50 E8 ? ? ? ? 83 C4 10", 9);
auto rectWidth1 = pattern("D8 25 ? ? ? ? D9 18 89 54 24 18 8B 44 24 34 DB 44 24 18 D8 44 24 38 D8 05").get_one();
auto rectWidth2 = pattern("D8 25 ? ? ? ? D9 18 D9 05 ? ? ? ? D8 0D ? ? ? ? 8B 44 24 34 D8 44 24 38 D8 05").get_one();
auto rectWidth3 = get_pattern<float*>("D8 25 ? ? ? ? 8B 44 24 34 D9 18", 2);
auto rectHeight1 = pattern("D8 05 ? ? ? ? D9 58 04 D9 44 24 3C D8 25").count(2);
auto rectHeight2 = pattern("D9 05 ? ? ? ? D8 44 24 3C DE C1 D8 05").get_one();
// SetWrapx on the help boxes includes an unscaled -4.0f probably to work together with this padding,
// so treat it as part of the same fix
auto setJustifyOff_helpBox = get_pattern("59 E8 ? ? ? ? D9 EE", 1);
std::array<float**, 5> paddingXSizes = {
rectWidth1.get<float*>(2),
rectWidth1.get<float*>(0x18 + 2),
rectWidth2.get<float*>(2),
rectWidth2.get<float*>(0x1C + 2),
rectWidth3
};
std::array<float**, 6> paddingYSizes = {
rectHeight1.get(0).get<float*>(2),
rectHeight1.get(0).get<float*>(0xD + 2),
rectHeight1.get(1).get<float*>(2),
rectHeight1.get(1).get<float*>(0xD + 2),
rectHeight2.get<float*>(2),
rectHeight2.get<float*>(0xC + 2),
};
std::array<float**, 1> wrapxWidth = {
get_pattern<float*>("D8 25 ? ? ? ? D9 1C 24 DD D8", 2),
};
HookEach_PaddingXSize(paddingXSizes, PatchFloat);
HookEach_PaddingYSize(paddingYSizes, PatchFloat);
InterceptCall(getTextRect, orgGetTextRect, GetTextRect_Recalculate<paddingXSizes.size(), paddingYSizes.size()>);
HookEach_WrapX(wrapxWidth, PatchFloat);
InterceptCall(setJustifyOff_helpBox, orgSetJustifyOff, SetJustifyOff_Recalculate<wrapxWidth.size()>);
}
TXN_CATCH();
// ============= Fix menu texts not scaling to resolution =============
try
{
using namespace MenuManagerScalingFixes;
auto printStringMenuText = get_pattern("E8 ? ? ? ? 83 C4 0C DB 05 ? ? ? ? 50");
InterceptCall(printStringMenuText, orgPrintString, PrintString_Scale);
}
TXN_CATCH();
FLAUtils::Init(moduleList);
}
@ -2276,10 +2413,10 @@ void Patch_III_Common()
// Fix dark car reflections in the Steam EXE
// Based off Sergenaur's fix
// Based off Sergeanur's fix
try
{
// This will only pass on the Steam EXE, and if Sergenaur's standalone fix isn't present
// This will only pass on the Steam EXE, and if Sergeanur's standalone fix isn't present
auto reflection = pattern("A1 ? ? ? ? 85 C0 74 34").get_one();
// xor eax, eax \ nop

View file

@ -73,6 +73,7 @@
<ClInclude Include="..\SilentPatch\Maths.h" />
<ClInclude Include="..\SilentPatch\ParseUtils.hpp" />
<ClInclude Include="..\SilentPatch\Random.h" />
<ClInclude Include="..\SilentPatch\RWGTA.h" />
<ClInclude Include="..\SilentPatch\StdAfx.h" />
<ClInclude Include="..\SilentPatch\StoredCar.h" />
<ClInclude Include="..\SilentPatch\SVF.h" />

View file

@ -113,6 +113,9 @@
<ClInclude Include="..\SilentPatch\Random.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SilentPatch\RWGTA.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\SilentPatch\SilentPatch.rc">

View file

@ -6,7 +6,7 @@
<SILENTPATCH_EXT>.asi</SILENTPATCH_EXT>
<SILENTPATCH_FULL_NAME>SilentPatch for GTA III</SILENTPATCH_FULL_NAME>
<SILENTPATCH_REVISION_ID>9</SILENTPATCH_REVISION_ID>
<SILENTPATCH_BUILD_ID>0</SILENTPATCH_BUILD_ID>
<SILENTPATCH_BUILD_ID>1</SILENTPATCH_BUILD_ID>
<SILENTPATCH_COPYRIGHT>2013-2024</SILENTPATCH_COPYRIGHT>
</PropertyGroup>
<PropertyGroup />

View file

@ -603,7 +603,7 @@ static hook::pattern MakeScriptPattern(bool isMission, std::string_view bytes)
begin = uintptr_t(ScriptSpace);
end = begin + ScriptFileSize;
}
return hook::make_range_pattern(begin, end, bytes).count_hint(100);
return hook::make_range_pattern(begin, end, bytes);
}
static void MountainCloudBoysFix()
@ -631,7 +631,10 @@ static void SupplyLinesFix( bool isBeefyBaron )
static void DrivingSchoolConesFix()
{
auto pattern = MakeScriptPattern(true, "04 00 02 20 03 04 00 D6 00 04 00 1A 00 04 2E 02 20 03 4D 00 01 60 75 FF FF BE 00 08 01 07 24 03 20 03 2E 80 08 00 02 20 03 04 01");
if (pattern.size() == 1) // Only destroy as many cones as were created
auto coneCoilConeCount = MakeScriptPattern(true, "1A 00 04 17 02 20 03");
auto burnAndLapConeCount = MakeScriptPattern(true, "1A 00 04 23 02 20 03");
// Only destroy as many cones as were created, and correct trafficcone_counter for "Cone Coil" and "Burn and Lap"
if (pattern.size() == 1 && coneCoilConeCount.size() == 1 && burnAndLapConeCount.size() == 1)
{
const uint8_t gotoSkipAssignment[] = { 0x02, 0x00, 0x01, 0x8B, 0x75, 0xFF, 0xFF };
memcpy(pattern.get(0).get<void>(0), gotoSkipAssignment, sizeof(gotoSkipAssignment));
@ -646,6 +649,15 @@ static void DrivingSchoolConesFix()
// Also set trafficcone_counter to 0 so the first destruction doesn't happen
int32_t* trafficcone_counter = reinterpret_cast<int32_t*>(ScriptSpace+800);
*trafficcone_counter = 0;
// Correct the final trafficcone_counter in Cone Coil
// 23 -> 30
*coneCoilConeCount.get(0).get<int8_t>(3) = 30;
// Correct the final trafficcone_counter in Burn and Lap
// 35 -> 42
*burnAndLapConeCount.get(0).get<int8_t>(3) = 42;
}
}
@ -1167,10 +1179,10 @@ void DrawScriptSpritesAndRectangles( uint8_t arg )
bool ReadDoubleRearWheels(const wchar_t* pPath);
bool __stdcall CheckDoubleRWheelsList( void* modelInfo, uint8_t* handlingData );
CVehicleModelInfo* (__thiscall *orgVehicleModelInfoCtor)(CVehicleModelInfo*);
CVehicleModelInfo* __fastcall VehicleModelInfoCtor(CVehicleModelInfo* me)
CVehicleModelInfo* (__thiscall *orgVehicleModelInfoInit)(CVehicleModelInfo*);
CVehicleModelInfo* __fastcall VehicleModelInfoInit(CVehicleModelInfo* me)
{
orgVehicleModelInfoCtor(me);
orgVehicleModelInfoInit(me);
// Hack to satisfy some null checks
static uintptr_t DUMMY;
@ -2629,16 +2641,23 @@ namespace NewResolutionSelectionDialog
}
}
static RwSubSystemInfo* (*orgRwEngineGetSubSystemInfo)(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex);
static RwSubSystemInfo *RwEngineGetSubSystemInfo_GetFriendlyNames(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex)
{
static const auto friendlyNames = FriendlyMonitorNames::GetNamesForDevicePaths();
// If we can't do any our work, fall back to the original game functions that may already by customized by other mods
if (*ppRWD3D9 == nullptr)
{
return orgRwEngineGetSubSystemInfo(subSystemInfo, subSystemIndex);
}
D3DADAPTER_IDENTIFIER9 identifier;
if (FAILED((*ppRWD3D9)->GetAdapterIdentifier(subSystemIndex, 0, &identifier)))
{
return nullptr;
return orgRwEngineGetSubSystemInfo(subSystemInfo, subSystemIndex);
}
static const auto friendlyNames = FriendlyMonitorNames::GetNamesForDevicePaths();
// If we can't find the friendly name, either because it doesn't exist or we're on an ancient Windows, fall back to the device name
auto it = friendlyNames.find(identifier.DeviceName);
if (it != friendlyNames.end())
@ -2654,8 +2673,15 @@ namespace NewResolutionSelectionDialog
}
static size_t MenuManagerAdapterOffset = 0xDC;
static RwInt32 (*orgRwEngineGetCurrentSubSystem)();
static RwInt32 RwEngineGetCurrentSubSystem_FromSettings()
{
// If we can't do any our work, fall back to the original game functions that may already by customized by other mods
if (*ppRWD3D9 == nullptr)
{
return orgRwEngineGetCurrentSubSystem();
}
RwInt32 subSystem = *reinterpret_cast<RwInt32*>(static_cast<char*>(FrontEndMenuManager) + MenuManagerAdapterOffset);
if (subSystem > 0)
{
@ -2705,7 +2731,7 @@ namespace NewResolutionSelectionDialog
if (msg == WM_INITDIALOG)
{
const WrappedDialocFunc* data = reinterpret_cast<WrappedDialocFunc*>(lParam);
SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(data->lpDialogFunc));
SetWindowLongPtr(window, DWLP_USER, reinterpret_cast<LONG_PTR>(data->lpDialogFunc));
data->lpDialogFunc(window, msg, wParam, data->dwInitParam);
@ -2716,14 +2742,15 @@ namespace NewResolutionSelectionDialog
// The game inits the selected resolution weirdly, and corrects it in the IDOK handler
// so let's invoke it manually (bleh)
data->lpDialogFunc(window, WM_COMMAND, IDOK, 0);
return TRUE;
return FALSE;
}
HMODULE hGameModule = GetModuleHandle(nullptr);
SendMessage(window, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(LoadIcon(hGameModule, MAKEINTRESOURCE(100))));
CreateNewButtonTooltip(hGameModule, window);
// Return TRUE instead of FALSE on init, we want keyboard focus
// Return TRUE instead of FALSE on init, as we removed a manual SetFocus from the init function
// and we want to rely on Windows to give us focus.
return TRUE;
}
@ -2743,7 +2770,7 @@ namespace NewResolutionSelectionDialog
}
}
DLGPROC origProc = reinterpret_cast<DLGPROC>(GetWindowLongPtr(window, GWLP_USERDATA));
DLGPROC origProc = reinterpret_cast<DLGPROC>(GetWindowLongPtr(window, DWLP_USER));
if (origProc != nullptr)
{
return origProc(window, msg, wParam, lParam);
@ -2806,6 +2833,12 @@ namespace NewResolutionSelectionDialog
return result;
}
static auto* const pDialogBoxParamA_New = &DialogBoxParamA_New;
static HWND WINAPI SetFocus_NOP(HWND)
{
return nullptr;
}
static auto* const pSetFocus_NOP = &SetFocus_NOP;
}
@ -3052,9 +3085,7 @@ namespace MapScreenScalingFixes
((CursorYSize_Recalculated<I> = ScaleY(*orgCursorYSize<I>)), ...);
}
static void (*orgLimitToMap)(float* x, float* y);
template<std::size_t NumXSize, std::size_t NumYSize>
static void (*orgLimitToMap_Scale)(float* x, float* y);
static void LimitToMap_Scale(float* x, float* y)
{
// LimitToMap assumes it's given scaled coordinates, but then the caller assumes it returns unscaled coordinates.
@ -3064,12 +3095,17 @@ namespace MapScreenScalingFixes
*x *= XScale;
*y *= YScale;
orgLimitToMap(x, y);
orgLimitToMap_Scale(x, y);
*x /= XScale;
*y /= YScale;
}
// This is also a comfortable spot to scale the cursor dimensions
static void (*orgLimitToMap_RecalculateSizes)(float* x, float* y);
template<std::size_t NumXSize, std::size_t NumYSize>
static void LimitToMap_RecalculateSizes(float* x, float* y)
{
orgLimitToMap_RecalculateSizes(x, y);
RecalculateXSize(std::make_index_sequence<NumXSize>{});
RecalculateYSize(std::make_index_sequence<NumYSize>{});
}
@ -3186,6 +3222,30 @@ namespace NitrousReverseRechargeFix
}
// ============= Fix Hydra's jet thrusters not displaying due to an uninitialized variable in RwMatrix =============
// By B1ack_Wh1te
namespace JetThrustersFix
{
// These are technically CMatrix, but for simplicity we use RwMatrix here
template<std::size_t Index>
static RwMatrix* (*orgMatrixMultiply)(RwMatrix* out, const RwMatrix* lhs, const RwMatrix* rhs);
template<std::size_t Index>
static RwMatrix* MatrixMultiply_ZeroFlags(RwMatrix* out, const RwMatrix* lhs, const RwMatrix* rhs)
{
RwMatrix* result = orgMatrixMultiply<Index>(out, lhs, rhs);
// Technically, this should be the same as RwMatrixUpdate, but this variable is on the stack
// and completely uninitialized, so zero it completely for consistent results.
rwMatrixSetFlags(result, 0);
return result;
}
HOOK_EACH_INIT(MatrixMultiply, orgMatrixMultiply, MatrixMultiply_ZeroFlags);
}
// ============= LS-RP Mode stuff =============
namespace LSRPMode
{
@ -3418,25 +3478,25 @@ void InstallMemValidator()
using namespace Memory;
// TEST: Validate memory
InjectHook( 0x824257, malloc_validator, HookType::Jump );
InjectHook( 0x824269, realloc_validator, HookType::Jump );
InjectHook( 0x824416, calloc_validator, HookType::Jump );
InjectHook( 0x82413F, free_validator, HookType::Jump );
InjectHook( 0x828C4A, _msize_validator, HookType::Jump );
InjectHook( AddressByRegion_10(0x824257), malloc_validator, HookType::Jump );
InjectHook( AddressByRegion_10(0x824269), realloc_validator, HookType::Jump );
InjectHook( AddressByRegion_10(0x824416), calloc_validator, HookType::Jump );
InjectHook( AddressByRegion_10(0x82413F), free_validator, HookType::Jump );
InjectHook( AddressByRegion_10(0x828C4A), _msize_validator, HookType::Jump );
InjectHook( 0x82119A, _new, HookType::Jump );
InjectHook( 0x8214BD, _delete, HookType::Jump );
InjectHook( AddressByRegion_10(0x82119A), _new, HookType::Jump );
InjectHook( AddressByRegion_10(0x8214BD), _delete, HookType::Jump );
InjectHook( 0x72F420, &CDebugMemoryMgr::Malloc, HookType::Jump );
InjectHook( 0x72F430, &CDebugMemoryMgr::Free, HookType::Jump );
InjectHook( 0x72F440, &CDebugMemoryMgr::Realloc, HookType::Jump );
InjectHook( 0x72F460, &CDebugMemoryMgr::Calloc, HookType::Jump );
InjectHook( 0x72F4C0, &CDebugMemoryMgr::MallocAlign, HookType::Jump );
InjectHook( 0x72F4F0, &CDebugMemoryMgr::AlignedFree, HookType::Jump );
InjectHook( AddressByRegion_10(0x72F420), &CDebugMemoryMgr::Malloc, HookType::Jump );
InjectHook( AddressByRegion_10(0x72F430), &CDebugMemoryMgr::Free, HookType::Jump );
InjectHook( AddressByRegion_10(0x72F440), &CDebugMemoryMgr::Realloc, HookType::Jump );
InjectHook( AddressByRegion_10(0x72F460), &CDebugMemoryMgr::Calloc, HookType::Jump );
InjectHook( AddressByRegion_10(0x72F4C0), &CDebugMemoryMgr::MallocAlign, HookType::Jump );
InjectHook( AddressByRegion_10(0x72F4F0), &CDebugMemoryMgr::AlignedFree, HookType::Jump );
PutStaticValidator( 0xAAE950, 0xB4C310 ); // CStore
PutStaticValidator( 0xA9AE00, 0xA9AE58 ); // fx_c
PutStaticValidator( AddressByRegion_10(0xAAE950), AddressByRegion_10(0xB4C310) ); // CStore
PutStaticValidator( AddressByRegion_10(0xA9AE00), AddressByRegion_10(0xA9AE58) ); // fx_c
}
#endif
@ -4160,6 +4220,16 @@ BOOL InjectDelayedPatches_10()
const bool bHasDebugMenu = DebugMenuLoad();
auto PatchFloat = [](float** address, const float*& org, float& replaced)
{
org = *address;
Patch(address, &replaced);
};
const std::initializer_list<uint8_t> fadd = { 0xD8, 0x05 };
const std::initializer_list<uint8_t> fsub = { 0xD8, 0x25 };
const std::initializer_list<uint8_t> fld = { 0xD9, 0x05 };
#ifdef _DEBUG
if ( bHasDebugMenu )
{
@ -4622,6 +4692,63 @@ BOOL InjectDelayedPatches_10()
}
// Fix Map screen boundaries and the cursor not scaling to resolution
// Debugged by Wesser
// Moved here for compatibility with wshps.asi
{
using namespace MapScreenScalingFixes;
// Even though those two patch the same function, treating them as separate patches makes retaining compatibility
// with the widescreen fix easy.
if (MemEquals(0x588251, fld) && MemEquals(0x588265, fadd) && MemEquals(0x5882A8, fld) && MemEquals(0x5882C6, fadd))
{
std::array<float**, 2> cursorXSizes = { (float**)(0x588251 + 2), (float**)(0x588265 + 2) };
std::array<float**, 2> cursorYSizes = { (float**)(0x5882A8 + 2), (float**)(0x5882C6 + 2) };
HookEach_CursorXSize(cursorXSizes, PatchFloat);
HookEach_CursorYSize(cursorYSizes, PatchFloat);
InterceptCall(0x58822D, orgLimitToMap_RecalculateSizes, LimitToMap_RecalculateSizes<cursorXSizes.size(), cursorYSizes.size()>);
}
// Only patch this function if wshps.asi hasn't changed the way it's being called
// The expected code:
// lea edx, [esp+70h+a1.y]
// push edx
// lea eax, [esp+74h+a1]
// push eax
if (MemEquals(0x588223, {0x8D, 0x54, 0x24, 0x4C, 0x52, 0x8D, 0x44, 0x24, 0x4C, 0x50 }))
{
InterceptCall(0x58822D, orgLimitToMap_Scale, LimitToMap_Scale);
}
}
// Fix text background padding not scaling to resolution
// Debugged by Wesser
// Moved here for compatibility with wshps.asi
{
using namespace TextRectPaddingScalingFixes;
// Verify that all fadd and fsub instructions are intact
// Patterns would do it for us for free, but 1.0 does not use them...
if (MemEquals(0x71A653, fadd) && MemEquals(0x71A66B, fadd) && MemEquals(0x71A69D, fsub) && MemEquals(0x71A6AB, fadd) &&
MemEquals(0x71A6BF, fsub) && MemEquals(0x71A6EC, fadd))
{
std::array<float**, 4> paddingXSizes = {
(float**)(0x71A653 + 2), (float**)(0x71A66B + 2),
(float**)(0x71A69D + 2), (float**)(0x71A6AB + 2),
};
std::array<float**, 2> paddingYSizes = {
(float**)(0x71A6BF + 2), (float**)(0x71A6EC + 2),
};
HookEach_PaddingXSize(paddingXSizes, PatchFloat);
HookEach_PaddingYSize(paddingYSizes, PatchFloat);
InterceptCall(0x71A631, orgProcessCurrentString, ProcessCurrentString_Scale<paddingXSizes.size(), paddingYSizes.size()>);
}
}
#ifndef NDEBUG
if ( const int QPCDays = GetPrivateProfileIntW(L"Debug", L"AddDaysToQPC", 0, wcModulePath); QPCDays != 0 )
{
@ -4643,11 +4770,16 @@ BOOL InjectDelayedPatches_10()
BOOL InjectDelayedPatches_11()
{
#ifdef NDEBUG
MessageBoxW( nullptr, L"You're using a 1.01 executable which is no longer supported by SilentPatch!\n\n"
const int messageResult = MessageBoxW( nullptr, L"You're using a 1.01 executable which is no longer supported by SilentPatch!\n\n"
L"Since this EXE is used by only a few people, I recommend downgrading back to 1.0 - you gain full compatibility with mods "
L"and any relevant fixes 1.01 brings are backported to 1.0 by SilentPatch anyway.\n\n"
L"To downgrade to 1.0, find a 1.0 EXE online and replace your current game executable with it.",
L"To downgrade to 1.0, find a 1.0 EXE online and replace your current game executable with it. Do you want to continue?\n\n"
L"Pressing No will close the game. Press Yes to proceed to the game anyway.",
L"SilentPatch", MB_OK | MB_ICONWARNING );
if (messageResult == IDNO)
{
return TRUE;
}
#endif
if ( !IsAlreadyRunning() )
@ -4815,11 +4947,10 @@ BOOL InjectDelayedPatches_Steam()
{
const int messageResult = MessageBoxW( nullptr, L"You're using a 3.0 executable which is no longer supported by SilentPatch!\n\n"
L"Since this is an old Steam EXE, by now you should have either downgraded to 1.0 or started using an up to date version. It is recommended to "
L"verify your game's cache on Steam and then downgrade it to 1.0. Do you want to download San Andreas Downgrader now?\n\n"
L"Pressing Yes will close the game and open your web browser. Press No to proceed to the game anyway.", L"SilentPatch", MB_YESNO | MB_ICONWARNING );
if ( messageResult == IDYES )
L"verify your game's cache on Steam and then downgrade it to 1.0. Do you want to continue?\n\n"
L"Pressing No will close the game. Press Yes to proceed to the game anyway.", L"SilentPatch", MB_YESNO | MB_ICONWARNING );
if (messageResult == IDNO)
{
ShellExecuteW( nullptr, L"open", L"http://gtaforums.com/topic/753764-/", nullptr, nullptr, SW_SHOWNORMAL );
return TRUE;
}
}
@ -5002,6 +5133,12 @@ BOOL InjectDelayedPatches_NewBinaries()
const bool bHasDebugMenu = DebugMenuLoad();
auto PatchDouble = [](double** address, const double*& org, double& replaced)
{
org = *address;
Patch(address, &replaced);
};
// Race condition in CdStream fixed
// Not taking effect with modloader
//if ( !ModCompat::ModloaderCdStreamRaceConditionAware( modloaderModule ) )
@ -5098,6 +5235,61 @@ BOOL InjectDelayedPatches_NewBinaries()
TXN_CATCH();
// Fix Map screen boundaries and the cursor not scaling to resolution
// Debugged by Wesser
// Moved here for compatibility with wshps.asi
{
using namespace MapScreenScalingFixes;
// These two are entirely separate fixes
try
{
// Updated the pattern to also ensure this function's arguments are not changed by other mods
auto limitToMap = get_pattern("8D 45 F0 50 8B CA 51 E8 ? ? ? ? DB 05 ? ? ? ? 83 C4 1C", 7);
InterceptCall(limitToMap, orgLimitToMap_Scale, LimitToMap_Scale);
}
TXN_CATCH();
// Cursor X/Y scaling need to be done differently than in 1.0, as the game has one fld1 for width and one fld1 for height
try
{
auto scaleX = pattern("D9 E8 DC E9 D9 C9 D9 5D 94").get_one();
auto scaleY = pattern("D9 E8 DC E9 D9 C9 D9 5D B0").get_one();
Nop(scaleX.get<void>(), 1);
InjectHook(scaleX.get<void>(1), ScaleX_NewBinaries, HookType::Call);
Nop(scaleY.get<void>(), 1);
InjectHook(scaleY.get<void>(1), ScaleY_NewBinaries, HookType::Call);
}
TXN_CATCH();
}
// Fix text background padding not scaling to resolution
// Debugged by Wesser
// Moved here for compatibility with wshps.asi
try
{
using namespace TextRectPaddingScalingFixes;
auto processCurrentString = get_pattern("E8 ? ? ? ? DD 05 ? ? ? ? 8B 4D 08");
// In new binaries, 4.0f is shared for width and height.
// Make height determine the scale, so it works nicer in widescreen.
auto paddingSize = pattern("DD 05 ? ? ? ? DC E9 D9 C9 D9 19").count(2);
std::array<double**, 3> paddingYSizes = {
get_pattern<double*>("D8 CA DD 05 ? ? ? ? DC C1", 2 + 2),
paddingSize.get(0).get<double*>(2),
paddingSize.get(1).get<double*>(2),
};
HookEach_PaddingYSize_Double(paddingYSizes, PatchDouble);
InterceptCall(processCurrentString, orgProcessCurrentString, ProcessCurrentString_Scale_NewBinaries<paddingYSizes.size()>);
}
TXN_CATCH();
return FALSE;
}
return TRUE;
@ -5579,9 +5771,7 @@ void Patch_SA_10(HINSTANCE hInstance)
// Properly initialize all CVehicleModelInfo fields
ReadCall( 0x4C75E4, orgVehicleModelInfoCtor );
InjectHook( 0x4C75E4, VehicleModelInfoCtor );
InterceptCall(0x4C7633, orgVehicleModelInfoInit, VehicleModelInfoInit);
// Animated Phoenix hood scoop
{
@ -6140,15 +6330,16 @@ void Patch_SA_10(HINSTANCE hInstance)
{
using namespace NewResolutionSelectionDialog;
ppRWD3D9 = *(IDirect3D9***)(0x7F6312 + 1);
ppRWD3D9 = *AddressByRegion_10<IDirect3D9***>(0x7F6312 + 1);
FrontEndMenuManager = *(void**)(0x4054DB + 1);
orgGetDocumentsPath = (char*(*)())0x744FB0;
orgGetDocumentsPath = AddressByRegion_10<char*(*)()>(0x744FB0);
Patch(0x0746241 + 2, &pDialogBoxParamA_New);
Patch(AddressByRegion_10(0x746241 + 2), &pDialogBoxParamA_New);
Patch(AddressByRegion_10(0x745DB3 + 2), &pSetFocus_NOP);
InjectHook(0x7461D8, RwEngineGetSubSystemInfo_GetFriendlyNames);
InjectHook(0x7461ED, RwEngineGetCurrentSubSystem_FromSettings);
InterceptCall(AddressByRegion_10(0x7461D8), orgRwEngineGetSubSystemInfo, RwEngineGetSubSystemInfo_GetFriendlyNames);
InterceptCall(AddressByRegion_10(0x7461ED), orgRwEngineGetCurrentSubSystem, RwEngineGetCurrentSubSystem_FromSettings);
}
@ -6229,14 +6420,14 @@ void Patch_SA_10(HINSTANCE hInstance)
};
// Triangular gamepad crosshairs - their size needs to scale to screen *height*
std::array<float**, 11> triangleSizes = {
std::array<float**, 13> triangleSizes = {
// Co-op offscreen crosshair
(float**)(0x7436F1 + 2), (float**)(0x7436FF + 2), (float**)(0x74370D + 2), (float**)(0x74374B + 2),
(float**)(0x743797 + 2), (float**)(0x7437D0 + 2), (float**)(0x7437FB + 2), (float**)(0x743819 + 2),
(float**)(0x74386F + 2),
// Regular crosshair
(float**)(0x743259 + 2), (float**)(0x743266 + 2),
(float**)(0x743212 + 2), (float**)(0x74321E + 2), (float**)(0x743259 + 2), (float**)(0x743266 + 2),
};
HookEach_RenderOneXLUSprite_Rotate_Aspect(renderRotateAspect, InterceptCall);
@ -6246,39 +6437,6 @@ void Patch_SA_10(HINSTANCE hInstance)
}
// Fix Map screen boundaries and the cursor not scaling to resolution
// Debugged by Wesser
{
using namespace MapScreenScalingFixes;
std::array<float**, 2> cursorXSizes = { (float**)(0x588251 + 2), (float**)(0x588265 + 2) };
std::array<float**, 2> cursorYSizes = { (float**)(0x5882A8 + 2), (float**)(0x5882C6 + 2) };
HookEach_CursorXSize(cursorXSizes, PatchFloat);
HookEach_CursorYSize(cursorYSizes, PatchFloat);
InterceptCall(0x58822D, orgLimitToMap, LimitToMap_Scale<cursorXSizes.size(), cursorYSizes.size()>);
}
// Fix text background padding not scaling to resolution
// Debugged by Wesser
{
using namespace TextRectPaddingScalingFixes;
std::array<float**, 4> paddingXSizes = {
(float**)(0x71A653 + 2), (float**)(0x71A66B + 2),
(float**)(0x71A69D + 2), (float**)(0x71A6AB + 2),
};
std::array<float**, 2> paddingYSizes = {
(float**)(0x71A6BF + 2), (float**)(0x71A6EC + 2),
};
HookEach_PaddingXSize(paddingXSizes, PatchFloat);
HookEach_PaddingYSize(paddingYSizes, PatchFloat);
InterceptCall(0x71A631, orgProcessCurrentString, ProcessCurrentString_Scale<paddingXSizes.size(), paddingYSizes.size()>);
}
// Fix nitrous recharging faster when reversing the car
// By Wesser
{
@ -6289,6 +6447,16 @@ void Patch_SA_10(HINSTANCE hInstance)
}
// Fix Hydra's jet thrusters not displaying due to an uninitialized variable in RwMatrix
// By B1ack_Wh1te
{
using namespace JetThrustersFix;
std::array<uintptr_t, 4> matrixMult = { 0x6CA09F, 0x6CA122, 0x6CA1B2, 0x6CA242 };
HookEach_MatrixMultiply(matrixMult, InterceptCall);
}
#if FULL_PRECISION_D3D
// Test - full precision D3D device
Patch<uint8_t>( 0x7F672B+1, *(uint8_t*)(0x7F672B+1) | D3DCREATE_FPU_PRESERVE );
@ -8308,6 +8476,7 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance)
return get_pattern("53 FF 15 ? ? ? ? 85 C0", 1 + 2);
}
}();
auto setFocus = get_pattern("53 FF 15 ? ? ? ? 5F", 1 + 2);
auto rRwEngineGetSubSystemInfo = get_pattern("E8 ? ? ? ? 46 83 C4 08 83 C7 50");
auto rwEngineGetCurrentSubSystem = get_pattern("7C EA E8 ? ? ? ? A3", 2);
@ -8319,9 +8488,10 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance)
orgGetDocumentsPath = static_cast<char*(*)()>(get_pattern( "8D 45 FC 50 68 19 00 02 00", -6 ));
Patch(dialogBoxParam, &pDialogBoxParamA_New);
Patch(setFocus, &pSetFocus_NOP);
InjectHook(rRwEngineGetSubSystemInfo, RwEngineGetSubSystemInfo_GetFriendlyNames);
InjectHook(rwEngineGetCurrentSubSystem, RwEngineGetCurrentSubSystem_FromSettings);
InterceptCall(rRwEngineGetSubSystemInfo, orgRwEngineGetSubSystemInfo, RwEngineGetSubSystemInfo_GetFriendlyNames);
InterceptCall(rwEngineGetCurrentSubSystem, orgRwEngineGetCurrentSubSystem, RwEngineGetCurrentSubSystem_FromSettings);
}
TXN_CATCH();
@ -8432,6 +8602,7 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance)
// Triangular gamepad crosshairs - their size needs to scale to screen *height*
auto regularCrosshair = pattern("D8 0D ? ? ? ? D9 5D F4 D9 46 08 DC 0D ? ? ? ? D8 45 F4").get_one();
auto defaultCrosshairSize = pattern("DD 05 ? ? ? ? D8 C9 D9 5D F4 DC 0D ? ? ? ? D9 5D E8").get_one();
std::array<float**, 3> triangleSizes = {
// Co-op offscreen crosshair
get_pattern<float*>("D9 5D CC D9 05 ? ? ? ? D9 C0 D9 45 FC", 3 + 2),
@ -8440,8 +8611,10 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance)
// Regular crosshair (float)
regularCrosshair.get<float*>(2),
};
std::array<double**, 1> triangleSizesDouble = {
std::array<double**, 3> triangleSizesDouble = {
regularCrosshair.get<double*>(0xC + 2),
defaultCrosshairSize.get<double*>(2),
defaultCrosshairSize.get<double*>(0xB + 2),
};
HookEach_RenderOneXLUSprite_Rotate_Aspect(renderRotateAspect, InterceptCall);
@ -8453,52 +8626,6 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance)
TXN_CATCH();
// Fix Map screen boundaries and the cursor not scaling to resolution
// Debugged by Wesser
try
{
using namespace MapScreenScalingFixes;
auto limitToMap = get_pattern("E8 ? ? ? ? DB 05 ? ? ? ? 83 C4 1C");
// Cursor X/Y scaling need to be done differently than in 1.0, as the game has one fld1 for width and one fld1 for height
auto scaleX = pattern("D9 E8 DC E9 D9 C9 D9 5D 94").get_one();
auto scaleY = pattern("D9 E8 DC E9 D9 C9 D9 5D B0").get_one();
InterceptCall(limitToMap, orgLimitToMap, LimitToMap_Scale<0, 0>);
Nop(scaleX.get<void>(), 1);
InjectHook(scaleX.get<void>(1), ScaleX_NewBinaries, HookType::Call);
Nop(scaleY.get<void>(), 1);
InjectHook(scaleY.get<void>(1), ScaleY_NewBinaries, HookType::Call);
}
TXN_CATCH();
// Fix text background padding not scaling to resolution
// Debugged by Wesser
try
{
using namespace TextRectPaddingScalingFixes;
auto processCurrentString = get_pattern("E8 ? ? ? ? DD 05 ? ? ? ? 8B 4D 08");
// In new binaries, 4.0f is shared for width and height.
// Make height determine the scale, so it works nicer in widescreen.
auto paddingSize = pattern("DD 05 ? ? ? ? DC E9 D9 C9 D9 19").count(2);
std::array<double**, 3> paddingYSizes = {
get_pattern<double*>("D8 CA DD 05 ? ? ? ? DC C1", 2 + 2),
paddingSize.get(0).get<double*>(2),
paddingSize.get(1).get<double*>(2),
};
HookEach_PaddingYSize_Double(paddingYSizes, PatchDouble);
InterceptCall(processCurrentString, orgProcessCurrentString, ProcessCurrentString_Scale_NewBinaries<paddingYSizes.size()>);
}
TXN_CATCH();
// Fix nitrous recharging faster when reversing the car
// By Wesser
try
@ -8510,6 +8637,25 @@ void Patch_SA_NewBinaries_Common(HINSTANCE hInstance)
InjectHook(getGasPedal.get<void>(1), &NitrousControl_DontRechargeWhenReversing_NewBinaries, HookType::Call);
}
TXN_CATCH();
// Fix Hydra's jet thrusters not displaying due to an uninitialized variable in RwMatrix
// By B1ack_Wh1te
try
{
using namespace JetThrustersFix;
auto thrust = pattern("D9 5D DC E8 ? ? ? ? 83 C4 0C").count(4);
std::array<void*, 4> matrixMult = {
thrust.get(0).get<void>(3),
thrust.get(1).get<void>(3),
thrust.get(2).get<void>(3),
thrust.get(3).get<void>(3),
};
HookEach_MatrixMultiply(matrixMult, InterceptCall);
}
TXN_CATCH();
}

View file

@ -582,66 +582,56 @@ void CAutomobile::AfterPreRender()
}
}
void CAutomobile::HideDestroyedWheels_SilentPatch(void (CAutomobile::*spawnFlyingComponentCB)(int, unsigned int), int nodeID, unsigned int modelID)
void CAutomobile::HideDestroyedWheels_SilentPatch(void (CAutomobile::*spawnFlyingComponentCB)(int, unsigned int), int, unsigned int modelID)
{
auto hideWheel = [this](int nodeID)
auto trySpawnAndHideWheel = [this, spawnFlyingComponentCB, modelID](int nodeID)
{
bool bHasWheel = false;
RwFrame* wheelNode = m_pCarNode[nodeID];
if (wheelNode != nullptr)
{
RwFrameForAllObjects(wheelNode, [&bHasWheel](RwObject* object)
bool bWheelVisible = false;
RwFrameForAllObjects(wheelNode, [&bWheelVisible](RwObject* object) -> RwObject*
{
if ((rwObjectGetFlags(object) & rpATOMICRENDER) != 0)
{
rwObjectSetFlags(object, 0);
bHasWheel = true;
bWheelVisible = true;
return nullptr;
}
return object;
});
if (bWheelVisible)
{
std::invoke(spawnFlyingComponentCB, this, nodeID, modelID);
RwFrameForAllObjects(wheelNode, [](RwObject* object)
{
rwObjectSetFlags(object, 0);
return object;
});
}
}
return bHasWheel;
};
if (m_DamageManager.GetWheelStatus(0) == 2)
{
if (hideWheel(5))
{
std::invoke(spawnFlyingComponentCB, this, 5, modelID);
}
trySpawnAndHideWheel(5);
}
if (m_DamageManager.GetWheelStatus(2) == 2)
{
if (hideWheel(2))
{
std::invoke(spawnFlyingComponentCB, this, 2, modelID);
}
trySpawnAndHideWheel(2);
}
// For rear wheels, also hide and spawn the middle wheel (if it exists)
if (m_DamageManager.GetWheelStatus(1) == 2)
{
if (hideWheel(6))
{
std::invoke(spawnFlyingComponentCB, this, 6, modelID);
}
if (hideWheel(7))
{
std::invoke(spawnFlyingComponentCB, this, 7, modelID);
}
trySpawnAndHideWheel(6);
trySpawnAndHideWheel(7);
}
if (m_DamageManager.GetWheelStatus(3) == 2)
{
if (hideWheel(3))
{
std::invoke(spawnFlyingComponentCB, this, 3, modelID);
}
if (hideWheel(4))
{
std::invoke(spawnFlyingComponentCB, this, 4, modelID);
}
trySpawnAndHideWheel(3);
trySpawnAndHideWheel(4);
}
}

View file

@ -6,7 +6,7 @@
<SILENTPATCH_EXT>.asi</SILENTPATCH_EXT>
<SILENTPATCH_FULL_NAME>SilentPatch for San Andreas</SILENTPATCH_FULL_NAME>
<SILENTPATCH_REVISION_ID>33</SILENTPATCH_REVISION_ID>
<SILENTPATCH_BUILD_ID>0</SILENTPATCH_BUILD_ID>
<SILENTPATCH_BUILD_ID>1</SILENTPATCH_BUILD_ID>
<SILENTPATCH_COPYRIGHT>2014-2024</SILENTPATCH_COPYRIGHT>
</PropertyGroup>
<PropertyGroup />

View file

@ -211,32 +211,9 @@ namespace PrintStringShadows
namespace RadardiscFixes
{
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), ...);
}
static float orgRadarXPosVal;
static float* orgRadarXPosPtr;
template<std::size_t Index>
static const float* orgRadarXPos_RadardiscShrink;
@ -265,18 +242,15 @@ namespace RadardiscFixes
}
static void (*orgDrawMap)();
template<std::size_t NumXPos, std::size_t NumYPos, std::size_t NumXPosRadardisc, std::size_t NumYPosRadardisc>
template<std::size_t NumXPosRadardisc, std::size_t NumYPosRadardisc>
static void DrawMap_RecalculatePositions()
{
RecalculateXPositions(std::make_index_sequence<NumXPos>{});
RecalculateYPositions(std::make_index_sequence<NumYPos>{});
*orgRadarXPosPtr = orgRadarXPosVal * GetWidthMult() * RsGlobal->MaximumWidth;
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);
@ -579,6 +553,117 @@ namespace MinimalHUD
}
// ============= Fix text background padding not scaling to resolution =============
namespace TextRectPaddingScalingFixes
{
template<std::size_t Index>
static const float* orgPaddingXSize;
template<std::size_t Index>
static float PaddingXSize_Recalculated;
template<std::size_t... I>
static void RecalculateXSize(std::index_sequence<I...>)
{
const float multiplier = GetWidthMult() * RsGlobal->MaximumWidth;
((PaddingXSize_Recalculated<I> = *orgPaddingXSize<I> * multiplier), ...);
}
template<std::size_t Index>
static const float* orgPaddingYSize;
template<std::size_t Index>
static float PaddingYSize_Recalculated;
template<std::size_t... I>
static void RecalculateYSize(std::index_sequence<I...>)
{
const float multiplier = GetHeightMult() * RsGlobal->MaximumHeight;
((PaddingYSize_Recalculated<I> = *orgPaddingYSize<I> * multiplier), ...);
}
static void (*orgGetTextRect)(CRect*, float, float, void*);
template<std::size_t NumXPadding, std::size_t NumYPadding>
static void GetTextRect_Recalculate(CRect* a1, float a2, float a3, void* a4)
{
RecalculateXSize(std::make_index_sequence<NumXPadding>{});
RecalculateYSize(std::make_index_sequence<NumYPadding>{});
orgGetTextRect(a1, a2, a3, a4);
}
HOOK_EACH_INIT(PaddingXSize, orgPaddingXSize, PaddingXSize_Recalculated);
HOOK_EACH_INIT(PaddingYSize, orgPaddingYSize, PaddingYSize_Recalculated);
template<std::size_t Index>
static const float* orgWrapX;
template<std::size_t Index>
static float WrapX_Recalculated;
template<std::size_t... I>
static void RecalculateWrapX(std::index_sequence<I...>)
{
const float multiplier = GetWidthMult() * RsGlobal->MaximumWidth;
((WrapX_Recalculated<I> = *orgWrapX<I> * multiplier), ...);
}
static void (*orgSetJustifyOff)();
template<std::size_t NumXWrap>
static void SetJustifyOff_Recalculate()
{
RecalculateWrapX(std::make_index_sequence<NumXWrap>{});
orgSetJustifyOff();
}
HOOK_EACH_INIT(WrapX, orgWrapX, WrapX_Recalculated);
}
// ============= Fix ammunation text (big message type 3) Y position offset not scaling to resolution =============
namespace BigMessage3ScalingFixes
{
template<std::size_t Index>
static const float* orgOffsetY;
template<std::size_t Index>
static float OffsetY_Recalculated;
template<std::size_t... I>
static void RecalculateYOffset(std::index_sequence<I...>)
{
const float multiplier = GetHeightMult() * RsGlobal->MaximumHeight;
((OffsetY_Recalculated<I> = *orgOffsetY<I> * multiplier), ...);
}
static void (*orgSetDropColor)(const CRGBA&);
template<std::size_t NumXOffsets>
static void SetDropColor_Scale(const CRGBA& color)
{
RecalculateYOffset(std::make_index_sequence<NumXOffsets>{});
orgSetDropColor(color);
}
HOOK_EACH_INIT(MessageYOffset, orgOffsetY, OffsetY_Recalculated);
}
// ============= Fix an incorrect vertex setup for the outline of a destination blip in the Map Legend =============
namespace LegendBlipFix
{
static void (*orgDraw2DPolygon)(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, const CRGBA& color);
static void Draw2DPolygon_FixVertices(float x1, float y1, float x2, float y2, float /*x3*/, float y3, float x4, float y4, const CRGBA& color)
{
// In this setup, x3 is incorrect - it should have been (X + scaleX(14.0f)) but is (X + scaleX(2.0f)), same as x4
// We can recover the correct dimensions from x1 (bottom center) and x4 (top left):
// x3 = x1 + (x1 - x4)
// Write it out in full like this (without simplifying), so we know (x1 - x4) is done at a correct floating point precision.
const float x3 = x1 + (x1 - x4);
orgDraw2DPolygon(x1, y1, x2, y2, x3, y3, x4, y4, color);
}
}
float FixedRefValue()
{
return 1.0f;
@ -884,6 +969,9 @@ namespace RemoveDriverStatusFix
// ============= Apply the environment mapping on extra components =============
namespace EnvMapsOnExtras
{
static RpMaterial* (*RpMatFXMaterialSetEnvMapCoefficient)(RpMaterial* material, RwReal coef);
static int (*RpMatFXMaterialGetEffects)(const RpMaterial* material);
static void RemoveSpecularityFromAtomic(RpAtomic* atomic)
{
RpGeometry* geometry = RpAtomicGetGeometry(atomic);
@ -907,6 +995,10 @@ namespace EnvMapsOnExtras
if (bRemoveSpecularity)
{
if (RpMatFXMaterialGetEffects(material) == 2) // rpMATFXEFFECTENVMAP
{
RpMatFXMaterialSetEnvMapCoefficient(material, 0.0f);
}
RpMaterialGetSurfaceProperties(material)->specular = 0.0f;
}
return material;
@ -1530,21 +1622,9 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule
{
using namespace RadardiscFixes;
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),
@ -1567,13 +1647,18 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule
drawRadarDiscSprite.get(1).get<void>(17),
};
// Use exactly the same patterns as widescreen fix
float* radarPos = *get_pattern<float*>("D8 05 ? ? ? ? DE C1 D9 5C 24 28", 2);
std::array<float**, 2> youAreHereSize = {
get_pattern<float*>("DD D9 D9 05 ? ? ? ? D8 C9 D9 7C 24 04", 2 + 2),
get_pattern<float*>("8B 5C 24 18 D8 0D ? ? ? ? D8 0D ? ? ? ? D9 7C 24 04", 10 + 2),
};
// 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!
// This is removed from the most up-to-date widescreen fix, but keep it so we don't break with older builds.
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
@ -1597,11 +1682,33 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule
}
TXN_CATCH();
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()>);
// Normally we would "wrap" the global variable 40.0f used as a X radar position, but that causes issues with plugin-sdk.
// Vice City inlined CRadar::TransformRadarPointToScreenSpace and plugin-sdk reimplements it using this global directly, so we need to patch it.
// Therefore, we instead do the following:
// 1. Patch the float directly, reading the original value once and rescaling as usual in-place.
// 2. If CRadar::DrawYouAreHereSprite still points at the same global variable, give it a dedicated one.
// Otherwise do nothing, as some other mod (like the wsfix above) may have already done it.
// 3. If we can't safely change the radar position because it was relocated, bail out of the fix entirely, just to be safe.
// Missing out on a fix is better than breaking something.
if (hGameModule == ModCompat::Utils::GetModuleHandleFromAddress(radarPos))
{
static float fYouAreHereSize;
orgRadarXPosVal = fYouAreHereSize = *radarPos;
orgRadarXPosPtr = radarPos;
for (float** val : youAreHereSize)
{
if (*val == radarPos)
{
Patch(val, &fYouAreHereSize);
}
}
HookEach_CalculateRadarXPos_RadardiscShrink(radarXPos_RadardiscShrink, PatchFloat);
HookEach_CalculateRadarYPos_RadardiscShrink(radarYPos_RadardiscShrink, PatchFloat);
HookEach_DrawRadarDisc(spriteDraw, InterceptCall);
InterceptCall(drawMap, orgDrawMap, DrawMap_RecalculatePositions<radarXPos_RadardiscShrink.size(), radarYPos_RadardiscShrink.size()>);
}
}
TXN_CATCH();
@ -1912,6 +2019,70 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule
}
TXN_CATCH();
// Fix text background padding not scaling to resolution
try
{
using namespace TextRectPaddingScalingFixes;
auto getTextRect = get_pattern("FF 74 24 54 FF 74 24 54 50 E8 ? ? ? ? 83 C4 10", 9);
auto rectWidth1 = pattern("D8 25 ? ? ? ? D9 1E D9 44 24 38 D8 05 ? ? ? ? D8 05").get_one();
auto rectWidth2 = pattern("D8 25 ? ? ? ? D9 1E D9 05 ? ? ? ? D8 0D ? ? ? ? D8 44 24 38 D8 05").get_one();
auto rectWidth3 = get_pattern<float*>("D8 25 ? ? ? ? 0F BF C5 D9 1E", 2);
auto rectHeight1 = pattern("D8 05 ? ? ? ? D9 5E 04 D9 44 24 3C D8 25").count(2);
auto rectHeight2 = pattern("D9 05 ? ? ? ? D8 44 24 3C DE C1 D8 05").get_one();
// SetWrapx on the help boxes includes an unscaled -4.0f probably to work together with this padding,
// so treat it as part of the same fix
auto setJustifyOff_helpBox = get_pattern("59 E8 ? ? ? ? D9 EE", 1);
std::array<float**, 5> paddingXSizes = {
rectWidth1.get<float*>(2),
rectWidth1.get<float*>(0x12 + 2),
rectWidth2.get<float*>(2),
rectWidth2.get<float*>(0x18 + 2),
rectWidth3
};
std::array<float**, 6> paddingYSizes = {
rectHeight1.get(0).get<float*>(2),
rectHeight1.get(0).get<float*>(0xD + 2),
rectHeight1.get(1).get<float*>(2),
rectHeight1.get(1).get<float*>(0xD + 2),
rectHeight2.get<float*>(2),
rectHeight2.get<float*>(0xC + 2),
};
std::array<float**, 1> wrapxWidth = {
get_pattern<float*>("D8 25 ? ? ? ? D9 1C 24 DD D8", 2),
};
HookEach_PaddingXSize(paddingXSizes, PatchFloat);
HookEach_PaddingYSize(paddingYSizes, PatchFloat);
InterceptCall(getTextRect, orgGetTextRect, GetTextRect_Recalculate<paddingXSizes.size(), paddingYSizes.size()>);
HookEach_WrapX(wrapxWidth, PatchFloat);
InterceptCall(setJustifyOff_helpBox, orgSetJustifyOff, SetJustifyOff_Recalculate<wrapxWidth.size()>);
}
TXN_CATCH();
// Fix ammunation text (big message type 3) Y position offset not scaling to resolution
try
{
using namespace BigMessage3ScalingFixes;
auto setDropColor = get_pattern("E8 ? ? ? ? 59 8D 4C 24 40");
std::array<float**, 1> YOffset = {
get_pattern<float*>("D8 25 ? ? ? ? D9 1C 24 A1", 2),
};
HookEach_MessageYOffset(YOffset, PatchFloat);
InterceptCall(setDropColor, orgSetDropColor, SetDropColor_Scale<YOffset.size()>);
}
TXN_CATCH();
FLAUtils::Init(moduleList);
}
@ -2495,10 +2666,15 @@ void Patch_VC_Common()
using namespace EnvMapsOnExtras;
auto forAllAtomics = pattern("50 E8 ? ? ? ? 66 8B 4B 44").get_one();
auto setEnvMapCoefficient = reinterpret_cast<decltype(RpMatFXMaterialSetEnvMapCoefficient)>(get_pattern("8B 44 24 14 81 E2 FF 00 00 00 8D 14 52 8D 0C D6 89 41 08", -0x48));
auto getEffects = reinterpret_cast<decltype(RpMatFXMaterialGetEffects)>(get_pattern("8B 04 01 85 C0 75 01", -0xA));
// push eax -> push ebx
Patch<uint8_t>(forAllAtomics.get<void>(), 0x53);
InterceptCall(forAllAtomics.get<void>(1), orgRpClumpForAllAtomics, RpClumpForAllAtomics_ExtraComps);
RpMatFXMaterialSetEnvMapCoefficient = setEnvMapCoefficient;
RpMatFXMaterialGetEffects = getEffects;
}
TXN_CATCH();
@ -2634,7 +2810,7 @@ void Patch_VC_Common()
// Ped speech fix
// Based off Sergenaur's fix
// Based off Sergeanur's fix
try
{
// Remove the artificial 6s delay between any ped speech samples
@ -2755,6 +2931,17 @@ void Patch_VC_Common()
Nop(set_peds_choking, 6);
}
TXN_CATCH();
// Fix an incorrect vertex setup for the outline of a destination blip in the Map Legend
try
{
using namespace LegendBlipFix;
auto draw2dPolygon = get_pattern("E8 ? ? ? ? D9 EE D9 EE D9 EE DB 05 ? ? ? ? 89 5C 24 24");
InterceptCall(draw2dPolygon, orgDraw2DPolygon, Draw2DPolygon_FixVertices);
}
TXN_CATCH();
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)

View file

@ -166,6 +166,7 @@
<ClInclude Include="..\SilentPatch\Maths.h" />
<ClInclude Include="..\SilentPatch\ParseUtils.hpp" />
<ClInclude Include="..\SilentPatch\Random.h" />
<ClInclude Include="..\SilentPatch\RWGTA.h" />
<ClInclude Include="..\SilentPatch\RWUtils.hpp" />
<ClInclude Include="..\SilentPatch\StdAfx.h" />
<ClInclude Include="..\SilentPatch\StoredCar.h" />

View file

@ -75,6 +75,9 @@
<ClInclude Include="EntityVC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SilentPatch\RWGTA.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\SilentPatch\Timer.cpp">

View file

@ -6,7 +6,7 @@
<SILENTPATCH_EXT>.asi</SILENTPATCH_EXT>
<SILENTPATCH_FULL_NAME>SilentPatch for Vice City</SILENTPATCH_FULL_NAME>
<SILENTPATCH_REVISION_ID>11</SILENTPATCH_REVISION_ID>
<SILENTPATCH_BUILD_ID>0</SILENTPATCH_BUILD_ID>
<SILENTPATCH_BUILD_ID>1</SILENTPATCH_BUILD_ID>
<SILENTPATCH_COPYRIGHT>2013-2024</SILENTPATCH_COPYRIGHT>
</PropertyGroup>
<PropertyGroup />