diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 082d5877a5..1835efb753 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -76,6 +76,8 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "YugiohSaveFix", &flags_.YugiohSaveFix); CheckSetting(iniFile, gameID, "ForceUMDDelay", &flags_.ForceUMDDelay); CheckSetting(iniFile, gameID, "ForceMax60FPS", &flags_.ForceMax60FPS); + CheckSetting(iniFile, gameID, "GoWFramerateHack60", &flags_.GoWFramerateHack60); + CheckSetting(iniFile, gameID, "GoWFramerateHack30", &flags_.GoWFramerateHack30); CheckSetting(iniFile, gameID, "JitInvalidationHack", &flags_.JitInvalidationHack); CheckSetting(iniFile, gameID, "HideISOFiles", &flags_.HideISOFiles); CheckSetting(iniFile, gameID, "MoreAccurateVMMUL", &flags_.MoreAccurateVMMUL); diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 7499dcd9f4..2cd9cf042b 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -66,6 +66,8 @@ struct CompatFlags { bool YugiohSaveFix; bool ForceUMDDelay; bool ForceMax60FPS; + bool GoWFramerateHack60; + bool GoWFramerateHack30; bool JitInvalidationHack; bool HideISOFiles; bool MoreAccurateVMMUL; diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 039c26ecc4..915ab466f9 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -25,6 +25,7 @@ #include "Common/Log.h" #include "Common/Swap.h" #include "Core/Config.h" +#include "Core/System.h" #include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/MemBlockInfo.h" #include "Core/Debugger/SymbolMap.h" @@ -34,6 +35,7 @@ #include "Core/MIPS/MIPSAnalyst.h" #include "Core/HLE/ReplaceTables.h" #include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceDisplay.h" #include "GPU/Math3D.h" #include "GPU/GPU.h" @@ -1338,6 +1340,27 @@ static int Hook_soltrigger_render_ucschar() { return 0; } +static int Hook_gow_fps_hack() { + if (PSP_CoreParameter().compat.flags().GoWFramerateHack60 || PSP_CoreParameter().compat.flags().GoWFramerateHack30) { + if (PSP_CoreParameter().compat.flags().GoWFramerateHack30) { + __DisplayWaitForVblanks("vblank start waited", 2); + } else { + __DisplayWaitForVblanks("vblank start waited", 1); + } + } + return 0; +} + +static int Hook_gow_vortex_hack() { + if (PSP_CoreParameter().compat.flags().GoWFramerateHack60) { + // from my tests both ==0x3F800000 and !=0x3F800000 takes around 1:40-1:50, that seems to match correct behaviour + if (currentMIPS->r[MIPS_REG_S1] == 0 && currentMIPS->r[MIPS_REG_A0] == 0xC0 && currentMIPS->r[MIPS_REG_T4] != 0x3F800000) { + currentMIPS->r[MIPS_REG_S1] = 1; + } + } + return 0; +} + #define JITFUNC(f) (&MIPSComp::MIPSFrontendInterface::f) // Can either replace with C functions or functions emitted in Asm/ArmAsm. @@ -1454,6 +1477,8 @@ static const ReplacementTableEntry entries[] = { { "worms_copy_normalize_alpha", &Hook_worms_copy_normalize_alpha, 0, REPFLAG_HOOKENTER, 0x0CC }, { "openseason_data_decode", &Hook_openseason_data_decode, 0, REPFLAG_HOOKENTER, 0x2F0 }, { "soltrigger_render_ucschar", &Hook_soltrigger_render_ucschar, 0, REPFLAG_HOOKENTER, 0 }, + { "gow_fps_hack", &Hook_gow_fps_hack, 0, REPFLAG_HOOKEXIT , 0 }, + { "gow_vortex_hack", &Hook_gow_vortex_hack, 0, REPFLAG_HOOKENTER, 0x60 }, {} }; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index bd1de18342..114573e494 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -732,6 +732,10 @@ static int DisplayWaitForVblanks(const char *reason, int vblanks, bool callbacks return hleLogSuccessVerboseI(SCEDISPLAY, 0, "waiting for %d vblanks", vblanks); } +void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks) { + DisplayWaitForVblanks(reason, vblanks, callbacks); +} + static u32 sceDisplaySetMode(int displayMode, int displayWidth, int displayHeight) { if (displayMode != PSP_DISPLAY_MODE_LCD || displayWidth != 480 || displayHeight != 272) { WARN_LOG_REPORT(SCEDISPLAY, "Video out requested, not supported: mode=%d size=%d,%d", displayMode, displayWidth, displayHeight); diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index f1911a49ec..bb134af815 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -33,3 +33,4 @@ void __DisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync); void __DisplaySetWasPaused(); void Register_sceDisplay_driver(); +void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks = false); diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 6cc740246a..63599a758c 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -507,6 +507,8 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0xfe5dd338ab862291, 216, "memset", }, // Metal Gear Solid: Peace Walker demo { 0xffc8f5f8f946152c, 192, "dl_write_light_color", }, { 0x249a3c5981c73480, 1472, "openseason_data_decode", }, // Open Season + { 0x795d940ad0a605f8, 40, "gow_fps_hack", }, // God of War (all) + { 0x4c75043b7b0c643b, 512, "gow_vortex_hack", } // God of War: Ghost of Sparta vortex timer hack, avoids softlock #8299 }; namespace MIPSAnalyst { diff --git a/assets/compat.ini b/assets/compat.ini index 7683300327..1e2c6214ed 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -818,10 +818,9 @@ ULJS00119 = true ULKS46167 = true NPJH50017 = true -[ForceMax60FPS] -# The GOW games are very heavy and render as fast as they can. They benefit greatly from -# capping the framerate at 60fps. - +[GoWFramerateHack60] +# Replaces ForceMax60FPS for GOW games, should provide smoother experience +# Also works around softlock in GOW:GOS , see #8299 # GOW : Ghost of Sparta UCUS98737 = true UCAS40323 = true @@ -855,6 +854,47 @@ UCUS98705 = true UCED00971 = true UCUS98713 = true +[GoWFramerateHack30] +# As the 60 fps version, but makes GOW games run on a potato, +# Doesn't suffer from softlock #8299 +# disabled by default since most people wouldn't need it +# GOW : Ghost of Sparta +# UCUS98737 = true +# UCAS40323 = true +# NPHG00092 = true +# NPEG00044 = true +# NPEG00045 = true +# NPJG00120 = true +# NPUG80508 = true +# UCJS10114 = true +# UCES01401 = true +# UCES01473 = true +# GOW : Ghost of Sparta Demo +# NPEG90035 = true +# NPUG70125 = true +# NPJG90095 = true +# GOW : Chains Of Olympus +# UCAS40198 = true +# UCUS98653 = true +# UCES00842 = true +# ULJM05438 = true +# ULJM05348 = true +# UCKS45084 = true +# NPUG80325 = true +# NPEG00023 = true +# NPHG00027 = true +# NPHG00028 = true +# NPJH50170 = true +# UCET00844 = true +# GOW: Chains of Olympus Demo +# UCUS98705 = true +# UCED00971 = true +# UCUS98713 = true + +[ForceMax60FPS] +# Some games are very heavy and render as fast as they can. They benefit greatly from +# capping the framerate at 60fps. + # F1 2006 has extremely long loading times if we don't limit the framerate. UCES00238 = true UCJS10045 = true