Initial FF13-2 support

This commit is contained in:
rebtd7 2020-01-05 00:25:35 -03:00 committed by GitHub
parent 4133d6b03a
commit cc4e3f0c34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 251 additions and 132 deletions

View file

@ -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<const MainContext::AutoFixes, const uint32_t> 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;
}

View file

@ -1,4 +1,5 @@
#include "stdafx.h"
#include <windows.h>
#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;
}

View file

@ -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;