From cc4e3f0c3489f14232ef900a460d337a7e93ff49 Mon Sep 17 00:00:00 2001 From: rebtd7 <59185507+rebtd7@users.noreply.github.com> Date: Sun, 5 Jan 2020 00:25:35 -0300 Subject: [PATCH] Initial FF13-2 support --- d3d9ex/AutoFix.cpp | 212 +++++++++++++++++++++++++++++++++++++++++++++ d3d9ex/Context.cpp | 132 +++------------------------- d3d9ex/Context.h | 39 ++++++--- 3 files changed, 251 insertions(+), 132 deletions(-) diff --git a/d3d9ex/AutoFix.cpp b/d3d9ex/AutoFix.cpp index 829c4e0..f113e04 100644 --- a/d3d9ex/AutoFix.cpp +++ b/d3d9ex/AutoFix.cpp @@ -25,6 +25,73 @@ void MainContext::EnableAutoFix() autofix = FINAL_FANTASY_XIII; PrintLog("AutoFix for \"Final Fantasy XIII\" enabled"); } + + if (exe_name == "ffxiii2img.exe") + { + autofix = FINAL_FANTASY_XIII2; + PrintLog("AutoFix for \"Final Fantasy XIII-2\" enabled"); + FF13_2_InitializeGameAddresses(); + FF13_2_CreateSetFrameRateCodeBlock(); + } +} + +void MainContext::FF13_2_CreateSetFrameRateCodeBlock() +{ + const int blockSize = 31; + FF13_2_SET_FRAME_RATE_INJECTED_CODE = new byte[blockSize]; + + ChangeMemoryProtectionToReadWriteExecute(FF13_2_SET_FRAME_RATE_INJECTED_CODE, blockSize); + + float frameRateConfigValue = context.config.GetFFXIIIIngameFrameRateLimit(); + if (AreAlmostTheSame(frameRateConfigValue, -1.0F) || frameRateConfigValue > FF13_2_MAX_FRAME_CAP) { + ff13_2_targetFrameRate = FF13_2_MAX_FRAME_CAP; + } + else { + ff13_2_targetFrameRate = frameRateConfigValue; + } + + // movss xmm1,[&FF13_2_30_FPS] + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 0) = 0xF3; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 1) = 0x0F; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 2) = 0x10; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 3) = 0x0D; + *(float**)(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 4) = (float*)(&FF13_2_30_FPS); + + // ucomiss xmm0,xmm1 + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 8) = 0x0F; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 9) = 0x2E; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 10) = 0xC1; + + //jna SetFrameRateVar + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 11) = 0x76; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 12) = 0x08; + + // movss xmm0,[&FF13_2_30_FPS] + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 13) = 0xF3; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 14) = 0x0F; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 15) = 0x10; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 16) = 0x05; + *(float**)(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 17) = (float*)(&ff13_2_targetFrameRate); + + // movss [ecx+04],xmm0 + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 21) = 0xF3; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 22) = 0x0F; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 23) = 0x11; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 24) = 0x41; + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 25) = 0x04; + + //jmp ffxiii2img.exe + 80261B + *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 26) = 0xE9; + *(uint32_t*)(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 27) = ff13_2_set_frame_rate_address - FF13_2_SET_FRAME_RATE_INJECTED_CODE - 26; +} + +void MainContext::FF13_2_InitializeGameAddresses() +{ + // FF13-2 uses address space layout randomization (ASLR) so we can't rely on fixed addresses without considering the base address + byte* baseAddr = (byte*)GetModuleHandle(NULL); + ff13_2_continuous_scan_instruction_address = baseAddr + 0x2A6E7F; + ff13_2_set_frame_rate_address = baseAddr + 0x802616; + ff13_2_frame_pacer_ptr_address = (float**)(baseAddr + 0x4D67208); } const std::map MainContext::behaviorflags_fixes = @@ -89,4 +156,149 @@ HRESULT APIENTRY MainContext::ApplyVertexBufferFix(IDirect3DDevice9 *pIDirect3DD } } return pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); +} + +void MainContext::FF13_OneTimeFixes() { + + bool successSoFar = true; + // The game repeatedly sets the frame rate limit. Disable the instruction that does it. + MainContext::FF13_AddHookIngameFrameRateLimitSetter(); + + successSoFar &= MainContext::FF13_SetFrameRateVariables(); + + if (successSoFar) { + context.FF13_RemoveContinuousControllerScan(); + context.FF13_FixMissingEnemyScan(); + context.didOneTimeFixes = true; + } +} + +void MainContext::FF13_RemoveContinuousControllerScan() { + // Disable continuous controller scanning. + + PrintLog("Removing game slow and synchronous controller continuous controller scanning..."); + context.ChangeMemoryProtectionToReadWriteExecute(FF13_CONTINUOUS_SCAN_INSTRUCTION_ADDRESS, 1); + // change a jne to jmp + *(byte*)FF13_CONTINUOUS_SCAN_INSTRUCTION_ADDRESS = 0xEB; +} + +void MainContext::FF13_FixMissingEnemyScan() { + // This patches the variables that eventually will turn into a RECT to be used in a IDirect3DDevice9::SetScissorRect call. + // The game incorrectly uses the same values here regardless of the resolution. + + PrintLog("Patching libra info box instructions to take in account the game resolution..."); + + const float resolutionFactor = (float)context.backbufferWidth / 1280.0F; + + const uint32_t rectHeight = (uint32_t)ceil(130.0F * resolutionFactor); + const uint32_t rectWidth = context.backbufferWidth; + const uint32_t rectPosY = (uint32_t)(496.0F * resolutionFactor); + + context.ChangeMemoryProtectionToReadWriteExecute(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS, 18); + + //push boxHeight + *(byte*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 0) = 0x68; + *(uint32_t*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 1) = rectHeight; + + // push boxWidth + *(byte*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 5) = 0x68; + *(uint32_t*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 6) = rectWidth; + + // push boxPosY + *(byte*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 10) = 0x68; + *(uint32_t*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 11) = rectPosY; + + // NOP NOP NOP + *(byte*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 15) = 0x90; + *(byte*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 16) = 0x90; + *(byte*)(FF13_ENEMY_SCAN_BOX_CODE_ADDRESS + 17) = 0x90; +} + +void MainContext::FF13_AddHookIngameFrameRateLimitSetter() { + PrintLog("Using the ingame the instruction that sets the frame rate to get the frame rate address."); + + context.ChangeMemoryProtectionToReadWriteExecute(FF13_SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS, 5); + + // patching to: mov [framePacerTargetPtr], eax + *FF13_SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS = 0xA3; + *((uint32_t*)(FF13_SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS + 1)) = (uint32_t)(&framePacerTargetPtr); +} + +bool MainContext::FF13_SetFrameRateVariables() { + if (framePacerTargetPtr) { + PrintLog("Frame pacer target frame rate is at address %x", framePacerTargetPtr); + + float* ingameFrameRateFramePacerTarget = framePacerTargetPtr; + *ingameFrameRateFramePacerTarget = MAX_FRAME_RATE_LIMIT; + PrintLog("Frame pacer disabled."); + + const float frameRateConfig = (float)context.config.GetFFXIIIIngameFrameRateLimit(); + const bool unlimitedFrameRate = AreAlmostTheSame(frameRateConfig, -1.0f); + const bool shouldSetFrameRateLimit = !AreAlmostTheSame(frameRateConfig, 0.0f); + + float frameRateLimit = 0; + + if (unlimitedFrameRate) { + frameRateLimit = MAX_FRAME_RATE_LIMIT; + } + else { + frameRateLimit = frameRateConfig; + } + + if (shouldSetFrameRateLimit) { + float* ingameFrameRateLimitPtr = framePacerTargetPtr + 1; + *ingameFrameRateLimitPtr = frameRateLimit; + PrintLog("Target frame rate set to %f", frameRateLimit); + } + } + else { + PrintLog("Unable to find frame rate pattern. This is normal if the game still hasn't completely started yet."); + } + + return framePacerTargetPtr; +} + +void MainContext::FF13_2_OneTimeFixes() { + + if (*ff13_2_frame_pacer_ptr_address) { + **ff13_2_frame_pacer_ptr_address = MAX_FRAME_RATE_LIMIT; + PrintLog("Frame pacer disabled"); + + context.FF13_2_AddHookIngameFrameRateLimitSetter(); + context.FF13_2_RemoveContinuousControllerScan(); + context.didOneTimeFixes = true; + } +} + +void MainContext::FF13_2_RemoveContinuousControllerScan() { + // Disable continuous controller scanning. + + PrintLog("Removing game slow and synchronous controller continuous controller scanning..."); + context.ChangeMemoryProtectionToReadWriteExecute(ff13_2_continuous_scan_instruction_address, 1); + // change a jne to jmp + *(byte*)ff13_2_continuous_scan_instruction_address = 0xEB; +} + +void MainContext::FF13_2_AddHookIngameFrameRateLimitSetter() { + if (context.AreAlmostTheSame(context.config.GetFFXIIIIngameFrameRateLimit(), 0.0F)) { + PrintLog("Frame rate should not be changed (config = 0)"); + return; + } + + PrintLog("Hooking the instruction that sets the frame rate..."); + + context.ChangeMemoryProtectionToReadWriteExecute(ff13_2_set_frame_rate_address, 5); + + // patching to: jmp FF13_2_SET_FRAME_RATE_INJECTED_CODE + *ff13_2_set_frame_rate_address = 0xE9; + *((uint32_t*)(ff13_2_set_frame_rate_address + 1)) = FF13_2_SET_FRAME_RATE_INJECTED_CODE - ff13_2_set_frame_rate_address - 5; +} + +void MainContext::ChangeMemoryProtectionToReadWriteExecute(void* address, const int size) { + DWORD oldProtection; + VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &oldProtection); +} + +bool MainContext::AreAlmostTheSame(float a, float b) { + return fabs(a - b) < 0.01f; } \ No newline at end of file diff --git a/d3d9ex/Context.cpp b/d3d9ex/Context.cpp index e3d216f..a6dac39 100644 --- a/d3d9ex/Context.cpp +++ b/d3d9ex/Context.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include #include "Wrapper.h" @@ -175,6 +176,16 @@ bool MainContext::CheckWindow(HWND hWnd) PrintLog("HWND 0x%p: ClassName \"%ls\", WindowName: \"%ls\"", hWnd, className.get(), windowName.get()); + if (!context.didOneTimeFixes) { + if (context.autofix == FINAL_FANTASY_XIII) { + PrintLog("Starting FFXIII one time RAM patches."); + context.FF13_OneTimeFixes(); + } + else if (context.autofix == FINAL_FANTASY_XIII2 && wcscmp(windowName.get(), L"DIEmWin") == 0) { + PrintLog("Starting FFXIII-2 one time RAM patches."); + context.FF13_2_OneTimeFixes(); + } + } bool class_found = config.GetWindowClass().compare(className.get()) == 0; bool window_found = config.GetWindowName().compare(windowName.get()) == 0; bool force = config.GetAllWindows(); @@ -298,11 +309,6 @@ HWND WINAPI MainContext::HookCreateWindowExA(DWORD dwExStyle, LPCSTR lpClassName return hWnd; } - if (context.autofix == FINAL_FANTASY_XIII && !context.didOneTimeFixes) { - PrintLog("Starting FFXIII one time RAM patches. (HookCreateWindowExA)"); - context.FFXIIIOneTimeFixes(); - } - if (context.CheckWindow(hWnd)) { context.ApplyWndProc(hWnd); @@ -321,11 +327,6 @@ HWND WINAPI MainContext::HookCreateWindowExW(DWORD dwExStyle, LPCWSTR lpClassNam return hWnd; } - if (context.autofix == FINAL_FANTASY_XIII && !context.didOneTimeFixes) { - PrintLog("Starting FFXIII one time RAM patches. (HookCreateWindowExW)"); - context.FFXIIIOneTimeFixes(); - } - if (context.CheckWindow(hWnd)) { context.ApplyWndProc(hWnd); @@ -334,114 +335,3 @@ HWND WINAPI MainContext::HookCreateWindowExW(DWORD dwExStyle, LPCWSTR lpClassNam return hWnd; } - -void MainContext::FFXIIIOneTimeFixes() { - - bool successSoFar = true; - // The game repeatedly sets the frame rate limit. Disable the instruction that does it. - successSoFar &= MainContext::FFXIIINOPIngameFrameRateLimitSetter(); - - successSoFar &= MainContext::FFXIIISetFrameRateVariables(); - - if (successSoFar) { - context.RemoveContinuousControllerScan(); - context.FixMissingEnemyScan(); - context.didOneTimeFixes = true; - } -} - -void MainContext::ChangeMemoryProtectionToReadWriteExecute(void* address, const int size) { - DWORD oldProtection; - VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &oldProtection); -} - -void MainContext::RemoveContinuousControllerScan() { - // Disable continuous controller scanning. - - PrintLog("Removing game slow and synchronous controller continuous controller scanning..."); - context.ChangeMemoryProtectionToReadWriteExecute(CONTINUOUS_SCAN_INSTRUCTION_ADDRESS, 1); - // change a jne to jmp - *(byte*)CONTINUOUS_SCAN_INSTRUCTION_ADDRESS = 0xEB; -} - -void MainContext::FixMissingEnemyScan() { - // This patches the variables that eventually will turn into a RECT to be used in a IDirect3DDevice9::SetScissorRect call. - // The game incorrectly uses the same values here regardless of the resolution. - - PrintLog("Patching libra info box instructions to take in account the game resolution..."); - - const float resolutionFactor = (float)context.backbufferWidth / 1280.0F; - - const uint32_t rectHeight = (uint32_t)ceil(130.0F * resolutionFactor); - const uint32_t rectWidth = context.backbufferWidth; - const uint32_t rectPosY = (uint32_t)(496.0F * resolutionFactor); - - context.ChangeMemoryProtectionToReadWriteExecute(ENEMY_SCAN_BOX_CODE_ADDRESS, 18); - - //push boxHeight - *(byte*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 0) = 0x68; - *(uint32_t*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 1) = rectHeight; - - // push boxWidth - *(byte*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 5) = 0x68; - *(uint32_t*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 6) = rectWidth; - - // push boxPosY - *(byte*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 10) = 0x68; - *(uint32_t*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 11) = rectPosY; - - // NOP NOP NOP - *(byte*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 15) = 0x90; - *(byte*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 16) = 0x90; - *(byte*)(ENEMY_SCAN_BOX_CODE_ADDRESS + 17) = 0x90; -} - -bool MainContext::FFXIIINOPIngameFrameRateLimitSetter() { - PrintLog("Using the ingame the instruction that sets the frame rate to get the frame rate address."); - - context.ChangeMemoryProtectionToReadWriteExecute(SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS, 5); - - // patching to: mov [framePacerTargetPtr], eax - *SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS = 0xA3; - *((uint32_t*)(SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS + 1)) = (uint32_t)(&framePacerTargetPtr); - - return true; -} - -bool MainContext::FFXIIISetFrameRateVariables() { - if (framePacerTargetPtr) { - PrintLog("Frame pacer target frame rate is at address %x", framePacerTargetPtr); - - float* ingameFrameRateFramePacerTarget = framePacerTargetPtr; - *ingameFrameRateFramePacerTarget = MAX_FRAME_RATE_LIMIT; - PrintLog("Frame pacer disabled."); - - const float frameRateConfig = (float)context.config.GetFFXIIIIngameFrameRateLimit(); - bool unlimitedFrameRate = areAlmostTheSame(frameRateConfig, -1.0f); - bool shouldSetFrameRateLimit = !areAlmostTheSame(frameRateConfig, 0.0f); - - float frameRateLimit = 0; - - if(unlimitedFrameRate){ - frameRateLimit = MAX_FRAME_RATE_LIMIT; - } - else { - frameRateLimit = frameRateConfig; - } - - if (shouldSetFrameRateLimit) { - float* ingameFrameRateLimitPtr = framePacerTargetPtr + 1; - *ingameFrameRateLimitPtr = frameRateLimit; - PrintLog("Target frame rate set to %f", frameRateLimit); - } - } - else { - PrintLog("Unable to find frame rate pattern. This is normal if the game still hasn't completely started yet."); - } - - return framePacerTargetPtr; -} - -bool MainContext::areAlmostTheSame(float a, float b) { - return fabs(a - b) < 0.01f; -} \ No newline at end of file diff --git a/d3d9ex/Context.h b/d3d9ex/Context.h index 2f799dc..957dcb1 100644 --- a/d3d9ex/Context.h +++ b/d3d9ex/Context.h @@ -64,19 +64,31 @@ private: NONE = 0, RESIDENT_EVIL_4, KINGS_BOUNTY_LEGEND, - FINAL_FANTASY_XIII + FINAL_FANTASY_XIII, + FINAL_FANTASY_XIII2, }; - void EnableAutoFix(); + void EnableAutoFix(); + void FF13_2_CreateSetFrameRateCodeBlock(); + void FF13_2_InitializeGameAddresses(); AutoFixes autofix = AutoFixes::NONE; bool didOneTimeFixes = false; const float MAX_FRAME_RATE_LIMIT = 250000.0F; - byte* SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS = (byte*)0x00E8D65F; - byte* CONTINUOUS_SCAN_INSTRUCTION_ADDRESS = (byte*)0x00820868; - byte* ENEMY_SCAN_BOX_CODE_ADDRESS = (byte*)0x0094C920; + byte* FF13_SET_FRAMERATE_INGAME_INSTRUCTION_ADDRESS = (byte*)0x00E8D65F; + byte* FF13_CONTINUOUS_SCAN_INSTRUCTION_ADDRESS = (byte*)0x00820868; + byte* FF13_ENEMY_SCAN_BOX_CODE_ADDRESS = (byte*)0x0094C920; + byte* FF13_2_SET_FRAME_RATE_INJECTED_CODE = NULL; + byte* ff13_2_continuous_scan_instruction_address; + byte* ff13_2_set_frame_rate_address; + float** ff13_2_frame_pacer_ptr_address; + float ff13_2_targetFrameRate; + + const float FF13_2_30_FPS = 30.0F; + const float FF13_2_MAX_FRAME_CAP = 1000.0F; + float* framePacerTargetPtr = NULL; UINT backbufferWidth = 0; @@ -86,13 +98,18 @@ private: static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); WNDPROC oldWndProc; - void FFXIIIOneTimeFixes(); - bool FFXIIINOPIngameFrameRateLimitSetter(); - bool FFXIIISetFrameRateVariables(); void ChangeMemoryProtectionToReadWriteExecute(void* address, const int size); - void FixMissingEnemyScan(); - void RemoveContinuousControllerScan(); - bool areAlmostTheSame(float a, float b); + + void FF13_OneTimeFixes(); + void FF13_AddHookIngameFrameRateLimitSetter(); + bool FF13_SetFrameRateVariables(); + void FF13_FixMissingEnemyScan(); + void FF13_RemoveContinuousControllerScan(); + + void FF13_2_RemoveContinuousControllerScan(); + void FF13_2_AddHookIngameFrameRateLimitSetter(); + void FF13_2_OneTimeFixes(); + bool AreAlmostTheSame(float a, float b); }; extern MainContext context; \ No newline at end of file