Skip to content
Open
Changes from all commits
Commits
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
218 changes: 209 additions & 9 deletions MGSFPSUnlock/MGS2/mgs2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,52 @@ class MGS2FramerateUnlocker
// Function delegates
typedef __int64 __fastcall ActDashFireDelegate(__int64 a1);
typedef __int64 __fastcall CreateDebrisTexDelegate(__int64 a1, float* a2, float* a3, unsigned int a4, int a5, int a6, float a7);
typedef __int64 __fastcall GetTimeBaseDelegate(struct _exception* a1);
typedef __int64 __fastcall GetTimeBaseDelegate(int a1);
typedef __int64 __fastcall sub_144A0Delegate(int a1);
typedef __int64 __fastcall GetTargetFpsDelegate(int a1);
typedef __int64 __fastcall sub_142C0Delegate(int a1);
typedef __int64 __fastcall sub_15CDF0Delegate(__int64 a1);
typedef __int64 __fastcall sub_4CACF0Delegate(__int64 a1, int* a2, int a3, int a4, int a5, __int16 a6, float a7, int a8, int a9);
typedef __int64 __fastcall sub_6ED70Delegate(unsigned __int8* a1, __int64 a2);
typedef __int64 __fastcall sub_53EFF0Delegate(__int64 a1);
typedef __int64 __fastcall sub_4AB220Delegate(__int64 a1);
typedef __int64 __fastcall sub_14410Delegate(__int64 a1);
typedef __int64 __fastcall sub_14300Delegate(__int64 a1);
typedef __int64 __fastcall sub_6EB70Delegate(unsigned int a1, int a2, unsigned int a3);
typedef __int64 __fastcall sub_407980Delegate(__int64 a1, __int64 a2, int a3);
typedef __int64 __fastcall sub_162890Delegate(__int64 a1, __int16 a2, __int16 a3, __int16 a4);

// Game data structure
struct GameVariables
{
int* timeBase;
double* actorWaitValue;
int* cutsceneFlag;
int* demoCutsceneFlag;

float* frameScaleFactorA;
float* frameScaleFactorB; // sadly reused by non-frame related funcs too
float* frameScaleFactorC;
float* frameScaleFactorD;
};

// Original function pointers
GetTimeBaseDelegate* GetTimeBase;
ActDashFireDelegate* ActDashFire;
CreateDebrisTexDelegate* CreateDebrisTex;
sub_144A0Delegate* sub_144A0;
GetTargetFpsDelegate* GetTargetFps;
sub_142C0Delegate* sub_142C0;
sub_15CDF0Delegate* sub_15CDF0;
sub_4CACF0Delegate* sub_4CACF0;
sub_6ED70Delegate* sub_6ED70;
sub_53EFF0Delegate* sub_53EFF0;
sub_4AB220Delegate* sub_4AB220;
sub_14410Delegate* sub_14410;
sub_14300Delegate* sub_14300;
sub_6EB70Delegate* sub_6EB70;
sub_407980Delegate* sub_407980;
sub_162890Delegate* sub_162890;

// Game data
GameVariables gameVars;
Expand All @@ -56,9 +88,148 @@ class MGS2FramerateUnlocker

static __int64 __fastcall ActDashFireHook(__int64 a1);
static __int64 __fastcall CreateDebrisTexHook(__int64 a1, float* a2, float* a3, unsigned int a4, int a5, int a6, float a7);
static __int64 __fastcall GetTimeBaseHook(struct _exception* a1);
static __int64 __fastcall GetTimeBaseHook(int a1);
static __int64 __fastcall sub_144A0Hook(int a1);
static __int64 __fastcall GetTargetFpsHook(int a1);
static __int64 __fastcall sub_142C0Hook(int a1);
static __int64 __fastcall sub_15CDF0Hook(__int64 a1);
static __int64 __fastcall sub_4CACF0Hook(__int64 a1, int* a2, int a3, int a4, int a5, __int16 a6, float a7, int a8, int a9);
static __int64 __fastcall sub_6ED70Hook(unsigned __int8* a1, __int64 a2);
static __int64 __fastcall sub_53EFF0Hook(__int64 a1);
static __int64 __fastcall sub_4AB220Hook(__int64 a1);
static __int64 __fastcall sub_14410Hook(__int64 a1);
static __int64 __fastcall sub_14300Hook(__int64 a1);
static __int64 __fastcall sub_6EB70Hook(unsigned int a1, int a2, unsigned int a3);
static __int64 __fastcall sub_407980Hook(__int64 a1, __int64 a2, int a3);
static __int64 __fastcall sub_162890Hook(__int64 a1, __int16 a2, __int16 a3, __int16 a4);
};

__int64 __fastcall MGS2FramerateUnlocker::sub_144A0Hook(int a1)
{
float scale = DEFAULT_FPS / Config.targetFramerate;

*instance->gameVars.frameScaleFactorA = -2.7222223f * (scale * scale);

return 0;
}

__int64 __fastcall MGS2FramerateUnlocker::GetTargetFpsHook(int a1)
{
return Config.targetFramerate;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_142C0Hook(int a1)
{
return (a1 + 4 * a1 + 1) / instance->timeBase;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_15CDF0Hook(__int64 a1)
{
auto result = instance->sub_15CDF0(a1);

auto v1 = *(__int64*)(a1 + 32);
if ((*(uint8_t*)(*(__int64*)(v1 + 4992) + 68LL) & 2) == 0)
{
if ((*(__int64*)(v1 + 5104) & 0x100040000000LL) == 0)
{
auto v4 = *(__int64*)(a1 + 8);
auto v6 = *(float*)(v4 + 20) + 16.0f;
auto scale = DEFAULT_FPS / Config.targetFramerate;
*(float*)(v4 + 20) = v6 - (16.0f * scale);
}
}

return result;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_4CACF0Hook(__int64 a1, int* a2, int a3, int a4, int a5, __int16 a6, float a7, int a8, int a9)
{
a7 = a7 * (DEFAULT_FPS / Config.targetFramerate);
return instance->sub_4CACF0(a1, a2, a3, a4, a5, a6, a7, a8, a9);
}

__int64 __fastcall MGS2FramerateUnlocker::sub_6ED70Hook(unsigned __int8* a1, __int64 a2)
{
auto v6 = instance->sub_6ED70(a1, a2);

*(float*)(v6 + 164) = DEFAULT_FPS / Config.targetFramerate;

return v6;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_53EFF0Hook(__int64 a1) // fps cam speed?
{
*instance->gameVars.frameScaleFactorB = DEFAULT_FPS / Config.targetFramerate;

auto result = instance->sub_53EFF0(a1);

*instance->gameVars.frameScaleFactorB = 1.2f;

return result;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_4AB220Hook(__int64 a1) // bandana physics?
{
*instance->gameVars.frameScaleFactorB = DEFAULT_FPS / Config.targetFramerate;
*instance->gameVars.frameScaleFactorC = *instance->gameVars.frameScaleFactorB * *instance->gameVars.frameScaleFactorB;

auto result = instance->sub_4AB220(a1);

*instance->gameVars.frameScaleFactorB = 1.2f;
*instance->gameVars.frameScaleFactorC = *instance->gameVars.frameScaleFactorB * *instance->gameVars.frameScaleFactorB;

return result;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_14410Hook(__int64 a1)
{
return 5 * a1 / instance->timeBase;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_14300Hook(__int64 a1)
{
return (instance->timeBase * a1) / 5;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_6EB70Hook(unsigned int a1, int a2, unsigned int a3)
{
auto result = instance->sub_6EB70(a1, a2, a3);

if (result && a2 < 0)
{
*(float*)(result + 164) = DEFAULT_FPS / Config.targetFramerate;
}

return result;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_407980Hook(__int64 a1, __int64 a2, int a3) // throw items?
{
*instance->gameVars.frameScaleFactorB = (DEFAULT_FPS / Config.targetFramerate) * 1.2f;
*instance->gameVars.frameScaleFactorD = (DEFAULT_FPS / Config.targetFramerate) * 2.5f;

a3 = (int)(a3 * (DEFAULT_FPS / Config.targetFramerate));

auto result = instance->sub_407980(a1, a2, a3);

*instance->gameVars.frameScaleFactorB = 1.2f;
*instance->gameVars.frameScaleFactorD = 2.5f;

return result;
}

__int64 __fastcall MGS2FramerateUnlocker::sub_162890Hook(__int64 a1, __int16 a2, __int16 a3, __int16 a4)
{
auto result = instance->sub_162890(a1, a2, a3, a4);

int v6 = a4 * (Config.targetFramerate / 120.0f);

*(short*)(a1 + 4922) = v6;
*(short*)(a1 + 4930) = v6;

return result;
}

MGS2FramerateUnlocker* MGS2FramerateUnlocker::instance = nullptr;

MGS2FramerateUnlocker::MGS2FramerateUnlocker()
Expand Down Expand Up @@ -107,7 +278,7 @@ __int64 __fastcall MGS2FramerateUnlocker::CreateDebrisTexHook(__int64 a1, float*
return result;
}

__int64 __fastcall MGS2FramerateUnlocker::GetTimeBaseHook(struct _exception* a1)
__int64 __fastcall MGS2FramerateUnlocker::GetTimeBaseHook(int a1)
{
return instance->timeBase;
}
Expand Down Expand Up @@ -145,8 +316,15 @@ bool MGS2FramerateUnlocker::InitializeOffsets()
instance->gameVars.demoCutsceneFlag = reinterpret_cast<int*>(GameBase + 0x16CA5D8);
break;
case 0x2000000010000:
instance->gameVars.timeBase = reinterpret_cast<int*>(GameBase + 0xAA9F94);
instance->gameVars.cutsceneFlag = reinterpret_cast<int*>(GameBase + 0xAA9F94);
instance->gameVars.demoCutsceneFlag = reinterpret_cast<int*>(GameBase + 0x16CB5D8);

instance->gameVars.frameScaleFactorA = reinterpret_cast<float*>(GameBase + 0x16ED790);
instance->gameVars.frameScaleFactorB = reinterpret_cast<float*>(GameBase + 0x725314);
instance->gameVars.frameScaleFactorC = reinterpret_cast<float*>(GameBase + 0x725318);
instance->gameVars.frameScaleFactorD = reinterpret_cast<float*>(GameBase + 0x725508);

break;
default:
spdlog::error("Unsupported game version: {:#x}", Config.gameVersion);
Expand All @@ -167,11 +345,24 @@ bool MGS2FramerateUnlocker::InitializeOffsets()

DWORD oldProtect;
VirtualProtect(instance->gameVars.actorWaitValue, 8, PAGE_READWRITE, &oldProtect);
VirtualProtect(instance->gameVars.frameScaleFactorB, 4, PAGE_READWRITE, &oldProtect);
VirtualProtect(instance->gameVars.frameScaleFactorC, 4, PAGE_READWRITE, &oldProtect);
VirtualProtect((LPVOID)(GameBase + 0x53F3F3), 2, PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect((LPVOID)(GameBase + 0x4AB2EE), 1, PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect((LPVOID)(GameBase + 0x16EE43), 1, PAGE_EXECUTE_READWRITE, &oldProtect);
//VirtualProtect((LPVOID)(GameBase + 0x407778), 1, PAGE_EXECUTE_READWRITE, &oldProtect);
//VirtualProtect((LPVOID)(GameBase + 0x40781b), 1, PAGE_EXECUTE_READWRITE, &oldProtect);
//40781b
*(uint8_t*)(GameBase + 0x53F3F3) = 0x90;
*(uint8_t*)(GameBase + 0x53F3F4) = 0x90;
*(uint8_t*)(GameBase + 0x4AB2EE) = 0x00;
*(uint8_t*)(GameBase + 0x16EE43) = 0x00;
//*(uint8_t*)(GameBase + 0x407778) = 0x01;
//*(uint8_t*)(GameBase + 0x40781b) = 0x00;
spdlog::info("InitializeOffsets() finished.");
return true;
}


bool MGS2FramerateUnlocker::InstallHooks()
{
spdlog::info("Installing hooks");
Expand Down Expand Up @@ -200,6 +391,20 @@ bool MGS2FramerateUnlocker::InstallHooks()
MH_CreateHook(getTimeBaseOffset, GetTimeBaseHook, reinterpret_cast<void**>(&GetTimeBase));
MH_CreateHook(createDebrisTexOffset, CreateDebrisTexHook, reinterpret_cast<void**>(&CreateDebrisTex));
MH_CreateHook(actDashFireOffset, ActDashFireHook, reinterpret_cast<void**>(&ActDashFire));
//MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0xC780), Is50HzHook, reinterpret_cast<void**>(&Is50Hz));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x144A0), sub_144A0Hook, reinterpret_cast<void**>(&sub_144A0));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x143F0), GetTargetFpsHook, reinterpret_cast<void**>(&GetTargetFps));
//MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x142C0), sub_142C0Hook, reinterpret_cast<void**>(&sub_142C0));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x15CDF0), sub_15CDF0Hook, reinterpret_cast<void**>(&sub_15CDF0));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x4CACF0), sub_4CACF0Hook, reinterpret_cast<void**>(&sub_4CACF0));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x6ED70), sub_6ED70Hook, reinterpret_cast<void**>(&sub_6ED70));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x53EFF0), sub_53EFF0Hook, reinterpret_cast<void**>(&sub_53EFF0));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x4AB220), sub_4AB220Hook, reinterpret_cast<void**>(&sub_4AB220));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x14410), sub_14410Hook, reinterpret_cast<void**>(&sub_14410));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x14300), sub_14300Hook, reinterpret_cast<void**>(&sub_14300));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x6EB70), sub_6EB70Hook, reinterpret_cast<void**>(&sub_6EB70));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x407980), sub_407980Hook, reinterpret_cast<void**>(&sub_407980));
MH_CreateHook((LPVOID)((uintptr_t)GameModule + 0x162890), sub_162890Hook, reinterpret_cast<void**>(&sub_162890));

if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK)
{
Expand Down Expand Up @@ -238,11 +443,6 @@ bool MGS2FramerateUnlocker::Initialize()
{
spdlog::info("Initializing framerate unlocker...");

if (Config.targetFramerate > DEFAULT_FPS) { //A LOT more work is needed before we support unlocked framerate.
Config.targetFramerate = DEFAULT_FPS;
spdlog::warn("MGS2 currently only supports framerate related bugfixes and not fully unlocked FPS. Setting target to {}.", DEFAULT_FPS);
}

if (!InitializeOffsets())
{
spdlog::error("Failed to initialize offsets");
Expand Down