Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/plugins/GTAIV.EFLC.FusionFix.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ FpsLimit = -2 // used when FPS Limit menu toggle
CutsceneFpsLimit = 0 // custom FPS Limit value in cutscene
LoadingFpsLimit = 30 // used to avoid game freeze on loading (e.g. Off Route mission)
UnlockFramerateDuringLoadscreens = 1 // game loads faster when using frame limiter
MinigamesFpsLimit = 30 // used to avoid possible issue for selected minigames: Pool, Air Hockey, Arm Wrestling, Bowling, Darts, Drinking
MinigamesFpsLimit = 30 // used to avoid possible issue for specified minigames in MinigamesList
MinigamesList = pool_game, air_hockey, arm_wrestling, tenpinbowl, darts, drinking

[MISC]
DefaultCameraAngleInTLaD = 1 // enforces the default IV camera angle on bikes in TLaD
Expand Down
Binary file modified data/update/GTAIV.EFLC.FusionFix/GTAIV.FusionFix/computermain.sco
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified data/update/GTAIV.EFLC.FusionFix/TBOGT.FusionFix/computermain.sco
Binary file not shown.
Binary file not shown.
Binary file modified data/update/GTAIV.EFLC.FusionFix/TLAD.FusionFix/computermain.sco
Binary file not shown.
16 changes: 16 additions & 0 deletions source/comvars.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,7 @@ export namespace CTimer
uint8_t* m_UserPause = nullptr;
uint8_t* m_CodePause = nullptr;
int32_t* m_snTimeInMilliseconds = nullptr;
int32_t* m_snTimeInMillisecondsPauseMode = nullptr;
}

export namespace CTimeCycle
Expand Down Expand Up @@ -2505,6 +2506,12 @@ export bool IsKeyboardKeyPressed(int vkeycode, int type = 1, const char* hint =
}
}

export namespace CPhysical
{
float* (__fastcall* getAngularVelocity)(void*, void*, float*) = nullptr;
void (__fastcall* TransformOffsetToWorldSpace)(float*, void*, float*, float*, char, int) = nullptr;
}

export enum eControllerButtons
{
BUTTON_BUMPER_LEFT = 4,
Expand Down Expand Up @@ -2564,6 +2571,9 @@ public:
pattern = find_pattern("A1 ? ? ? ? A3 ? ? ? ? EB 3A", "A1 ? ? ? ? 39 05 ? ? ? ? 76 1F");
CTimer::m_snTimeInMilliseconds = *pattern.get_first<int32_t*>(1);

pattern = find_pattern("89 0D ? ? ? ? F3 0F 11 05 ? ? ? ? A3 ? ? ? ? E8 ? ? ? ? F3 0F 10 0D ? ? ? ? F3 0F 10 44 24 ? 84 C0 74 ? 0F 2F C1 77 ? EB ? 0F 2F C1 76 ? 0F 28 C8 F3 0F 10 05 ? ? ? ? 0F 2F C1 77 03 0F 28 C8 80 3D", "89 0D ? ? ? ? D9 2C 24 E8 ? ? ? ? 84 C0 F3 0F 10 05 ? ? ? ? F3 0F 10 4C 24 ? 74 ? 0F 2F C8 77 ? EB ? 0F 2F C8 76 ? 0F 28 C1 F3 0F 10 0D ? ? ? ? 0F 2F C8 77 03 0F 28 C1 80 3D");
CTimer::m_snTimeInMillisecondsPauseMode = *pattern.get_first<int32_t*>(2);

pattern = find_pattern("83 3D ? ? ? ? ? 74 17 8B 4D 14", "83 3D ? ? ? ? ? 74 15 8B 44 24 1C", "83 3D ? ? ? ? ? 74 EF");
rage::grcDevice::ms_pD3DDevice = *pattern.get_first<IDirect3DDevice9**>(2);

Expand Down Expand Up @@ -2824,5 +2834,11 @@ public:
pattern = find_pattern("B9 ? ? ? ? E8 ? ? ? ? 84 C0 74 ? C6 86", "B9 ? ? ? ? E8 ? ? ? ? 84 C0 74 ? C6 86");
KeyboardBuffer = *pattern.get_first<void**>(1);
pIsKeyboardKeyPressed = (decltype(pIsKeyboardKeyPressed))injector::GetBranchDestination(pattern.get_first(5)).as_int();

pattern = find_pattern("E8 ? ? ? ? F3 0F 10 40 ? F3 0F 10 48 ? 8B 08 F3 0F 11 87 ? ? ? ? F3 0F 10 45", "E8 ? ? ? ? D9 00 F3 0F 10 40 ? F3 0F 10 48 ? D9 9E ? ? ? ? F3 0F 11 86 ? ? ? ? F3 0F 10 5D");
CPhysical::getAngularVelocity = (decltype(CPhysical::getAngularVelocity))injector::GetBranchDestination(pattern.get_first(0)).as_int();

pattern = find_pattern("E8 ? ? ? ? F3 0F 10 B7 ? ? ? ? F3 0F 10 BF ? ? ? ? F3 0F 10 AF ? ? ? ? F3 0F 10 97", "E8 ? ? ? ? F3 0F 10 A6 ? ? ? ? F3 0F 10 6B");
CPhysical::TransformOffsetToWorldSpace = (decltype(CPhysical::TransformOffsetToWorldSpace))injector::GetBranchDestination(pattern.get_first(0)).as_int();
}
} Common;
9 changes: 1 addition & 8 deletions source/fixes.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export module fixes;

import common;
import comvars;
import settings;
import natives;
import settings;
import shaders;

class Fixes
Expand Down Expand Up @@ -828,13 +828,6 @@ public:
injector::WriteMemory<uint8_t>(pattern.get_first(0), 0xEB, true); // jz -> jmp
}

// Radar zoom (T hotkey) 30fps cap fix
{
auto pattern = find_pattern("83 F9 ? 0F 86 ? ? ? ? F3 0F 10 15", "83 F8 1E 0F 86 ? ? ? ? F3 0F 10 0D ? ? ? ? 0F 2E C1");
if (!pattern.empty())
injector::WriteMemory<uint8_t>(pattern.get_first(2), 15, true);
}

// Radar zoom stays for a bit
{
auto pattern = find_pattern("E8 ? ? ? ? 84 C0 74 ? F3 0F 10 05 ? ? ? ? F3 0F 11 44 24 ? 6A");
Expand Down
43 changes: 29 additions & 14 deletions source/framelimit.ixx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module;

#include <common.hxx>
#include <regex>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib") // needed for timeBeginPeriod()/timeEndPeriod()

Expand All @@ -18,15 +19,7 @@ float fScriptCutsceneFpsLimit;
float fScriptCutsceneFovLimit;
float fLoadingFpsLimit;
float fMinigamesFpsLimit;

std::vector<std::string> minigamesNames = {
"pool_game",
"air_hockey",
"arm_wrestling",
"tenpinbowl",
"darts",
"drinking",
};
std::vector<std::string> minigamesNames;

class FrameLimiter
{
Expand Down Expand Up @@ -125,7 +118,8 @@ private:
bool __cdecl sub_411F50(uint32_t* a1, uint32_t* a2)
{
bLoadingShown = false;
if (!a1[2] && !a2[2]) {
if (!a1[2] && !a2[2])
{
bLoadingShown = (*a1 == *a2) && *a1;
return *a1 == *a2;
}
Expand Down Expand Up @@ -153,7 +147,8 @@ void __cdecl sub_855640()

if ((CMenuManager::bLoadscreenShown && !*CMenuManager::bLoadscreenShown && !bLoadingShown) || !bUnlockFramerateDuringLoadscreens)
{
if (preset && *preset >= FusionFixSettings.FpsCaps.eCustom) {
if (preset && *preset >= FusionFixSettings.FpsCaps.eCustom)
{
if (fFpsLimit != 0.0f || (*preset > FusionFixSettings.FpsCaps.eCustom && *preset < int32_t(FusionFixSettings.FpsCaps.data.size())))
FpsLimiter.Sync();
}
Expand Down Expand Up @@ -205,6 +200,24 @@ public:
fScriptCutsceneFovLimit = static_cast<float>(iniReader.ReadInteger("FRAMELIMIT", "ScriptCutsceneFovLimit", 0));
fLoadingFpsLimit = static_cast<float>(iniReader.ReadInteger("FRAMELIMIT", "LoadingFpsLimit", 30));
fMinigamesFpsLimit = static_cast<float>(iniReader.ReadInteger("FRAMELIMIT", "MinigamesFpsLimit", 30));
std::string minigamesList = iniReader.ReadString("FRAMELIMIT", "MinigamesList", "");
try
{
std::regex re("[^a-zA-Z0-9_]+");
std::sregex_token_iterator iter(minigamesList.begin(), minigamesList.end(), re, -1);
std::sregex_token_iterator end;
for (; iter != end; ++iter)
{
std::string token = iter->str();
if (!token.empty())
{
minigamesNames.push_back(token);
}
}
} catch (const std::exception&)
{
minigamesNames = { "pool_game", "air_hockey", "arm_wrestling", "tenpinbowl", "darts", "drinking" };
}
bUnlockFramerateDuringLoadscreens = iniReader.ReadInteger("FRAMELIMIT", "UnlockFramerateDuringLoadscreens", 0) != 0;

//if (fFpsLimit || fCutsceneFpsLimit || fScriptCutsceneFpsLimit)
Expand Down Expand Up @@ -273,7 +286,8 @@ public:
injector::WriteMemory(pattern.get_first(0), 0x901CC483, true); //nop + add esp,1C
injector::MakeJMP(pattern.get_first(4), sub_855640, true); // + jmp

FusionFixSettings.SetCallback("PREF_FPS_LIMIT_PRESET", [](int32_t value) {
FusionFixSettings.SetCallback("PREF_FPS_LIMIT_PRESET", [](int32_t value)
{
auto mode = (nFrameLimitType == 2) ? FrameLimiter::FPSLimitMode::FPS_ACCURATE : FrameLimiter::FPSLimitMode::FPS_REALTIME;
if (value > FusionFixSettings.FpsCaps.eCustom && value < int32_t(FusionFixSettings.FpsCaps.data.size()))
FpsLimiter.Init(mode, (float)FusionFixSettings.FpsCaps.data[value]);
Expand Down Expand Up @@ -342,13 +356,14 @@ public:
pattern = find_pattern("FF 05 ? ? ? ? C3 E8", "83 05 ? ? ? ? ? C3 E8 ? ? ? ? 8B C8");
if (!pattern.empty())
{
static auto SET_MINIGAME_IN_PROGRESS_HOOK = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs) {
static auto SET_MINIGAME_IN_PROGRESS_HOOK = safetyhook::create_mid(pattern.get_first(), [](SafetyHookContext& regs)
{
auto curThread = (rage::scrThread*)regs.eax;
if (curThread)
{
std::string curScript = curThread->m_szProgramName;
std::transform(curScript.begin(), curScript.end(), curScript.begin(), [](unsigned char c) { return std::tolower(c); });

if (std::any_of(std::begin(minigamesNames), std::end(minigamesNames), [&curScript](const auto& i) { return i == curScript; }))
bNeedsToLimitFpsForThisMinigame = true;
else
Expand Down
Loading