diff --git a/d3d9ex/AutoFix.cpp b/d3d9ex/AutoFix.cpp index bf99bd8..fe0442d 100644 --- a/d3d9ex/AutoFix.cpp +++ b/d3d9ex/AutoFix.cpp @@ -2,6 +2,7 @@ #include "Context.h" #include "IDirect3DVertexBuffer9.h" +#include void MainContext::EnableAutoFix() { @@ -24,6 +25,7 @@ void MainContext::EnableAutoFix() { autofix = FINAL_FANTASY_XIII; PrintLog("AutoFix for \"Final Fantasy XIII\" enabled"); + FF13_InitializeGameAddresses(); } if (exe_name == "ffxiii2img.exe") @@ -35,6 +37,287 @@ void MainContext::EnableAutoFix() } } +const std::map MainContext::behaviorflags_fixes = +{ + { RESIDENT_EVIL_4, D3DCREATE_SOFTWARE_VERTEXPROCESSING }, + { KINGS_BOUNTY_LEGEND, D3DCREATE_MIXED_VERTEXPROCESSING } +}; + +void MainContext::FixBehaviorFlagConflict(const DWORD flags_in, DWORD* flags_out) +{ + if (flags_in & D3DCREATE_SOFTWARE_VERTEXPROCESSING) + { + *flags_out &= ~(D3DCREATE_PUREDEVICE | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MIXED_VERTEXPROCESSING); + *flags_out |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + } + else if (flags_in & D3DCREATE_MIXED_VERTEXPROCESSING) + { + *flags_out &= ~(D3DCREATE_PUREDEVICE | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING); + *flags_out |= D3DCREATE_MIXED_VERTEXPROCESSING; + } + else if (flags_in & D3DCREATE_HARDWARE_VERTEXPROCESSING) + { + *flags_out &= ~(D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING); + *flags_out |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + } +} + +bool MainContext::ApplyBehaviorFlagsFix(DWORD* flags) +{ + if (autofix == AutoFixes::NONE) return false; + + auto&& fix = behaviorflags_fixes.find(autofix); + if (fix != behaviorflags_fixes.end()) + { + FixBehaviorFlagConflict(fix->second, flags); + return true; + } + + return false; +} + +HRESULT APIENTRY MainContext::ApplyVertexBufferFix(IDirect3DDevice9* pIDirect3DDevice9, UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) +{ + switch (autofix) { + case AutoFixes::NONE: + return pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); + case FINAL_FANTASY_XIII: + case FINAL_FANTASY_XIII2: + // Both games lock a vertex buffer 358400 before drawing any 2D element on screen (sometimes multiple times per frame) + if (Length == 358400 && Pool == D3DPOOL_MANAGED) { + Usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; + Pool = D3DPOOL_SYSTEMMEM; + + //IDirect3DVertexBuffer9* buffer = nullptr; + //HRESULT hr = pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, &buffer, NULL); + //if (FAILED(hr)) + //{ + // return pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); + //} + + //if(ppVertexBuffer) *ppVertexBuffer = new hkIDirect3DVertexBuffer9(pIDirect3DDevice9, buffer); + //return hr; + } + break; + } + + return pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); +} + +void MainContext::FF13_InitializeGameAddresses() +{ + // FF13 always seem to use the same addresses (even if you force ASLR on the OS), but we are calculating the addresses just in case... + byte* baseAddr = (byte*)GetModuleHandle(NULL); // Should be 400000 + PrintLog("Base Addr = %x", baseAddr); + + ff13_frame_pacer_ptr = (float**)(baseAddr + 0x243E34C); + ff13_set_framerate_ingame_instruction_address = baseAddr + 0xA8D65F; + ff13_continuous_scan_instruction_address = baseAddr + 0x420868; + ff13_enemy_scan_box_code_address = baseAddr + 0x54C920; + ff13_base_controller_input_address_ptr = (byte**)(baseAddr + 0x02411220); + ff13_vibration_low_set_zero_address = baseAddr + 0x4210DF; + ff13_vibration_high_set_zero_address = baseAddr + 0x4210F3; +} + +void MainContext::FF13_OneTimeFixes() { + MainContext::FF13_NOPIngameFrameRateLimitSetter(); + MainContext::FF13_SetFrameRateVariables(); + MainContext::FF13_RemoveContinuousControllerScan(); + MainContext::FF13_FixMissingEnemyScan(); + MainContext::FF13_EnableControllerVibration(); + + PrintLog("Finished FF13 One Time Fixes"); + MainContext::didOneTimeFixes = true; +} + +void MainContext::FF13_EnableControllerVibration() +{ + if (!config.GetFFXIIIEnableControllerVibration()) { + PrintLog("Vibration should not be enabled (config file)"); + return; + } + PrintLog("Enabling controller vibration..."); + ChangeMemoryProtectionToReadWriteExecute(ff13_vibration_low_set_zero_address, 5); + + *ff13_vibration_low_set_zero_address = 0x90; + *(ff13_vibration_low_set_zero_address + 1) = 0x90; + *(ff13_vibration_low_set_zero_address + 2) = 0x90; + *(ff13_vibration_low_set_zero_address + 3) = 0x90; + *(ff13_vibration_low_set_zero_address + 4) = 0x90; + + ChangeMemoryProtectionToReadWriteExecute(ff13_vibration_high_set_zero_address, 5); + *ff13_vibration_high_set_zero_address = 0x90; + *(ff13_vibration_high_set_zero_address + 1) = 0x90; + *(ff13_vibration_high_set_zero_address + 2) = 0x90; + *(ff13_vibration_high_set_zero_address + 3) = 0x90; + *(ff13_vibration_high_set_zero_address + 4) = 0x90; + xinputManager = new XInputManager(ff13_base_controller_input_address_ptr); +} + +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 rectHeight + *(byte*)(ff13_enemy_scan_box_code_address + 0) = 0x68; + *(uint32_t*)(ff13_enemy_scan_box_code_address + 1) = rectHeight; + + // push rectWidth + *(byte*)(ff13_enemy_scan_box_code_address + 5) = 0x68; + *(uint32_t*)(ff13_enemy_scan_box_code_address + 6) = rectWidth; + + // push rectPosY + *(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_NOPIngameFrameRateLimitSetter() { + PrintLog("NOPing the in-game instruction that sets the frame rate."); + + context.ChangeMemoryProtectionToReadWriteExecute(ff13_set_framerate_ingame_instruction_address, 5); + + // patching to: NOP NOP NOP NOP + *ff13_set_framerate_ingame_instruction_address = 0x90; + *(ff13_set_framerate_ingame_instruction_address + 1) = 0x90; + *(ff13_set_framerate_ingame_instruction_address + 2) = 0x90; + *(ff13_set_framerate_ingame_instruction_address + 3) = 0x90; + *(ff13_set_framerate_ingame_instruction_address + 4) = 0x90; +} + +void MainContext::FF13_SetFrameRateVariables() { + float* framePacerTargetPtr = *ff13_frame_pacer_ptr; + 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 pacer / frame rate address. This shouldn't happen! Report this."); + } +} + +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.FF13_2_EnableControllerVibration(); + PrintLog("Finished FF13-2 One Time Fixes"); + context.didOneTimeFixes = true; + } +} + +void MainContext::FF13_2_EnableControllerVibration() +{ + if (!config.GetFFXIIIEnableControllerVibration()) { + PrintLog("Vibration should not be enabled (config file)"); + return; + } + PrintLog("Enabling controller vibration..."); + ChangeMemoryProtectionToReadWriteExecute(ff13_2_vibration_low_set_zero_address, 5); + + *ff13_2_vibration_low_set_zero_address = 0x90; + *(ff13_2_vibration_low_set_zero_address + 1) = 0x90; + *(ff13_2_vibration_low_set_zero_address + 2) = 0x90; + *(ff13_2_vibration_low_set_zero_address + 3) = 0x90; + *(ff13_2_vibration_low_set_zero_address + 4) = 0x90; + + ChangeMemoryProtectionToReadWriteExecute(ff13_2_vibration_high_set_zero_address, 5); + *ff13_2_vibration_high_set_zero_address = 0x90; + *(ff13_2_vibration_high_set_zero_address + 1) = 0x90; + *(ff13_2_vibration_high_set_zero_address + 2) = 0x90; + *(ff13_2_vibration_high_set_zero_address + 3) = 0x90; + *(ff13_2_vibration_high_set_zero_address + 4) = 0x90; + + xinputManager = new XInputManager(ff13_2_base_controller_input_address_ptr); +} + +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); + PrintLog("Base Addr = %x", baseAddr); + + 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); + ff13_2_base_controller_input_address_ptr = (byte**)(baseAddr + 0x212A164); + ff13_2_vibration_low_set_zero_address = baseAddr + 0x2A7221; + ff13_2_vibration_high_set_zero_address = baseAddr + 0x2A7226; +} + +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((float)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::FF13_2_CreateSetFrameRateCodeBlock() { const int blockSize = 31; @@ -73,6 +356,7 @@ void MainContext::FF13_2_CreateSetFrameRateCodeBlock() *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 16) = 0x05; *(float**)(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 17) = (float*)(&ff13_2_targetFrameRate); + // SetFrameRateVar: // movss [ecx+04],xmm0 *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 21) = 0xF3; *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 22) = 0x0F; @@ -80,222 +364,11 @@ void MainContext::FF13_2_CreateSetFrameRateCodeBlock() *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 24) = 0x41; *(FF13_2_SET_FRAME_RATE_INJECTED_CODE + 25) = 0x04; - //jmp ffxiii2img.exe + 80261B + // 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 = -{ - { RESIDENT_EVIL_4, D3DCREATE_SOFTWARE_VERTEXPROCESSING }, - { KINGS_BOUNTY_LEGEND, D3DCREATE_MIXED_VERTEXPROCESSING } -}; - -void MainContext::FixBehaviorFlagConflict(const DWORD flags_in, DWORD* flags_out) -{ - if (flags_in & D3DCREATE_SOFTWARE_VERTEXPROCESSING) - { - *flags_out &= ~(D3DCREATE_PUREDEVICE | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MIXED_VERTEXPROCESSING); - *flags_out |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; - } - else if (flags_in & D3DCREATE_MIXED_VERTEXPROCESSING) - { - *flags_out &= ~(D3DCREATE_PUREDEVICE | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING); - *flags_out |= D3DCREATE_MIXED_VERTEXPROCESSING; - } - else if (flags_in & D3DCREATE_HARDWARE_VERTEXPROCESSING) - { - *flags_out &= ~(D3DCREATE_MIXED_VERTEXPROCESSING | D3DCREATE_SOFTWARE_VERTEXPROCESSING); - *flags_out |= D3DCREATE_HARDWARE_VERTEXPROCESSING; - } -} - -bool MainContext::ApplyBehaviorFlagsFix(DWORD* flags) -{ - if (autofix == AutoFixes::NONE) return false; - - auto && fix = behaviorflags_fixes.find(autofix); - if (fix != behaviorflags_fixes.end()) - { - FixBehaviorFlagConflict(fix->second, flags); - return true; - } - - return false; -} - -HRESULT APIENTRY MainContext::ApplyVertexBufferFix(IDirect3DDevice9* pIDirect3DDevice9, UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle) -{ - switch (autofix) { - case AutoFixes::NONE: - return pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); - case FINAL_FANTASY_XIII: - case FINAL_FANTASY_XIII2: - // Both games lock a vertex buffer 358400 before drawing any 2D element on screen (sometimes multiple times per frame) - if (Length == 358400 && Pool == D3DPOOL_MANAGED) { - Usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; - Pool = D3DPOOL_SYSTEMMEM; - - //IDirect3DVertexBuffer9* buffer = nullptr; - //HRESULT hr = pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, &buffer, NULL); - //if (FAILED(hr)) - //{ - // return pIDirect3DDevice9->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle); - //} - - //if(ppVertexBuffer) *ppVertexBuffer = new hkIDirect3DVertexBuffer9(pIDirect3DDevice9, buffer); - //return hr; - } - break; - } - - 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((float)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; @@ -303,7 +376,7 @@ void MainContext::ChangeMemoryProtectionToReadWriteExecute(void* address, const } void MainContext::PrintVersionInfo() { - PrintLog("FF13Fix 1.3.1 https://github.com/rebtd7/FF13Fix"); + PrintLog("FF13Fix 1.4.0 https://github.com/rebtd7/FF13Fix"); } bool MainContext::AreAlmostTheSame(float a, float b) { diff --git a/d3d9ex/Context.cpp b/d3d9ex/Context.cpp index 10d7ad2..27ce3c5 100644 --- a/d3d9ex/Context.cpp +++ b/d3d9ex/Context.cpp @@ -5,6 +5,7 @@ #include "Context.h" #include "IDirect3D9.h" +#include MainContext context; @@ -41,7 +42,7 @@ Config::Config() MainContext::MainContext() : oldWndProc(nullptr) { LogFile("FF13Fix.log"); - PrintVersionInfo(); + context.PrintVersionInfo(); if (config.GetAutoFix()) EnableAutoFix(); @@ -61,6 +62,8 @@ MainContext::MainContext() : oldWndProc(nullptr) MH_CreateHook(SetWindowLongW, HookSetWindowLongW, reinterpret_cast(&TrueSetWindowLongW)); MH_EnableHook(SetWindowLongW); + + } MainContext::~MainContext() @@ -178,13 +181,19 @@ 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(); + if (context.autofix == FINAL_FANTASY_XIII && wcscmp(windowName.get(), L"DIEmWin") == 0) { + const std::lock_guard lock(context.oneTimeFixesMutex); + if(!context.didOneTimeFixes){ + 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(); + const std::lock_guard lock(context.oneTimeFixesMutex); + if (!context.didOneTimeFixes) { + PrintLog("Starting FFXIII-2 one time RAM patches."); + context.FF13_2_OneTimeFixes(); + } } } bool class_found = config.GetWindowClass().compare(className.get()) == 0; diff --git a/d3d9ex/Context.h b/d3d9ex/Context.h index 464ed3a..f728eae 100644 --- a/d3d9ex/Context.h +++ b/d3d9ex/Context.h @@ -4,6 +4,7 @@ #include "d3d9.h" #include #include "SimpleIni.h" +#include "XInputManager.h" struct hkIDirect3D9; @@ -43,7 +44,8 @@ class MainContext DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); public: - MainContext(); + MainContext(); + void Foo(); ~MainContext(); bool ApplyPresentationParameters(D3DPRESENT_PARAMETERS* pPresentationParameters); @@ -68,28 +70,35 @@ private: FINAL_FANTASY_XIII2, }; - void EnableAutoFix(); - void FF13_2_CreateSetFrameRateCodeBlock(); - void FF13_2_InitializeGameAddresses(); + void EnableAutoFix(); + AutoFixes autofix = AutoFixes::NONE; + std::mutex oneTimeFixesMutex; bool didOneTimeFixes = false; const float MAX_FRAME_RATE_LIMIT = 250000.0F; - 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; + float** ff13_frame_pacer_ptr = NULL; + byte* ff13_set_framerate_ingame_instruction_address = NULL; + byte* ff13_continuous_scan_instruction_address = NULL; + byte* ff13_enemy_scan_box_code_address = NULL; + byte** ff13_base_controller_input_address_ptr = NULL; + byte* ff13_vibration_high_set_zero_address = NULL; + byte* ff13_vibration_low_set_zero_address = NULL; 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; + byte** ff13_2_base_controller_input_address_ptr = NULL; + byte* ff13_2_vibration_high_set_zero_address = NULL; + byte* ff13_2_vibration_low_set_zero_address = NULL; const float FF13_2_30_FPS = 30.0F; const float FF13_2_MAX_FRAME_CAP = 1000.0F; + XInputManager* xinputManager; - float* framePacerTargetPtr = NULL; UINT backbufferWidth = 0; void FixBehaviorFlagConflict(const DWORD flags_in, DWORD* flags_out); @@ -102,15 +111,20 @@ private: bool AreAlmostTheSame(float a, float b); void PrintVersionInfo(); - void FF13_OneTimeFixes(); - void FF13_AddHookIngameFrameRateLimitSetter(); - bool FF13_SetFrameRateVariables(); + void FF13_InitializeGameAddresses(); + void FF13_OneTimeFixes(); + void FF13_EnableControllerVibration(); + void FF13_NOPIngameFrameRateLimitSetter(); + void FF13_SetFrameRateVariables(); void FF13_FixMissingEnemyScan(); void FF13_RemoveContinuousControllerScan(); + void FF13_2_CreateSetFrameRateCodeBlock(); + void FF13_2_InitializeGameAddresses(); void FF13_2_RemoveContinuousControllerScan(); void FF13_2_AddHookIngameFrameRateLimitSetter(); - void FF13_2_OneTimeFixes(); + void FF13_2_OneTimeFixes(); + void FF13_2_EnableControllerVibration(); }; extern MainContext context; \ No newline at end of file diff --git a/d3d9ex/Settings.h b/d3d9ex/Settings.h index fd2c260..545f81d 100644 --- a/d3d9ex/Settings.h +++ b/d3d9ex/Settings.h @@ -11,6 +11,7 @@ SETTING(bool, BoolValue, ForceHideCursor, Options, false); SETTING(u32, LongValue, BehaviorFlags, Options, 0); SETTING(double, DoubleValue, FFXIIIIngameFrameRateLimit, Options, 0.0); +SETTING(bool, BoolValue, FFXIIIEnableControllerVibration, Options, true); SETTING(bool, BoolValue, Adapter, Adapter, false); SETTING(u32, LongValue, VendorId, Adapter, 0); diff --git a/d3d9ex/XInputManager.cpp b/d3d9ex/XInputManager.cpp new file mode 100644 index 0000000..dd6357a --- /dev/null +++ b/d3d9ex/XInputManager.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "XInputManager.h" +#include + +XInputManager::XInputManager(byte** base_controller_input_address_ptr) +{ + xinputThread = std::thread(&XInputManager::Run, this, base_controller_input_address_ptr); +} + +void XInputManager::Run(byte** base_controller_input_address_ptr) +{ + DWORD controllerState; + bool hasConnected = false; + for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) { + XINPUT_STATE state; + ZeroMemory(&state, sizeof(XINPUT_STATE)); + + controllerState = XInputGetState(i, &state); + if (controllerState == ERROR_SUCCESS) { + controllerId = i; + hasConnected = true; + break; + } + } + if (hasConnected) { + WaitAndSetVibrationAddress(base_controller_input_address_ptr); + VibrationLoop(); + } +} + +void XInputManager::WaitAndSetVibrationAddress(byte** base_controller_input_address_ptr) +{ + do { + std::this_thread::sleep_for(std::chrono::milliseconds(4)); + if (base_controller_input_address_ptr && *base_controller_input_address_ptr) { + vibration_address_low_frequency = (float*)(*base_controller_input_address_ptr + 0x40 + 0x60 - 0x4); + vibration_address_high_frequency = vibration_address_low_frequency + 1; + } + } while (vibration_address_low_frequency == NULL); +} + +void XInputManager::VibrationLoop() +{ + const WORD maxVibrationStrength = 65535; + bool wasVibrating = false; + SetControllerVibration(0, 0); + while (true) { + const float vibrationStrengthLowFrequency = *vibration_address_low_frequency; + const float vibrationStrengthHighFrequency = *vibration_address_high_frequency; + if (vibrationStrengthLowFrequency || vibrationStrengthHighFrequency) { + SetControllerVibration(vibrationStrengthLowFrequency * maxVibrationStrength, vibrationStrengthHighFrequency * maxVibrationStrength); + wasVibrating = true; + } + else if(wasVibrating) { + SetControllerVibration(0, 0); + wasVibrating = false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(4)); + } +} + +void XInputManager::SetControllerVibration(const WORD& leftMotorVibration, const WORD& rightMotorVibration) +{ + XINPUT_VIBRATION vibration; + ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION)); + vibration.wLeftMotorSpeed = leftMotorVibration; + vibration.wRightMotorSpeed = rightMotorVibration; + XInputSetState(controllerId, &vibration); +} diff --git a/d3d9ex/XInputManager.h b/d3d9ex/XInputManager.h new file mode 100644 index 0000000..63e961e --- /dev/null +++ b/d3d9ex/XInputManager.h @@ -0,0 +1,15 @@ +#pragma once +class XInputManager +{ + float* vibration_address_high_frequency = NULL; + float* vibration_address_low_frequency = NULL; + DWORD controllerId = -1; + std::thread xinputThread; +public: + XInputManager(byte** base_controller_input_address_ptr); + void Run(byte** base_controller_input_address_ptr); + void WaitAndSetVibrationAddress(byte** base_controller_input_address_ptr); + void VibrationLoop(); + void SetControllerVibration(const WORD& leftMotorVibration, const WORD& rightMotorVibration); +}; + diff --git a/d3d9ex/d3d9ex.vcxproj b/d3d9ex/d3d9ex.vcxproj index 3a9589f..652ff87 100644 --- a/d3d9ex/d3d9ex.vcxproj +++ b/d3d9ex/d3d9ex.vcxproj @@ -137,7 +137,7 @@ true true true - d3d9.lib;%(AdditionalDependencies) + XINPUT9_1_0.LIB;d3d9.lib;%(AdditionalDependencies) exports.def @@ -172,6 +172,7 @@ + @@ -199,6 +200,7 @@ Create Create + diff --git a/d3d9ex/d3d9ex.vcxproj.filters b/d3d9ex/d3d9ex.vcxproj.filters index 0fb9bd9..c6b1ec9 100644 --- a/d3d9ex/d3d9ex.vcxproj.filters +++ b/d3d9ex/d3d9ex.vcxproj.filters @@ -42,6 +42,9 @@ Header Files + + Header Files + @@ -65,6 +68,9 @@ Source Files + + Source Files +