diff --git a/pcsx2/Patch.cpp b/pcsx2/Patch.cpp index 08a8ed90c1..aead3227a4 100644 --- a/pcsx2/Patch.cpp +++ b/pcsx2/Patch.cpp @@ -27,11 +27,15 @@ #include #include +// This is a declaration for PatchMemory.cpp::_ApplyPatch where we're (patch.cpp) +// the only consumer, so it's not made public via Patch.h +// Applies a single patch line to emulation memory regardless of its "place" value. +extern void _ApplyPatch(IniPatch *p); + + IniPatch Patch[ MAX_PATCH ]; -IniPatch Cheat[ MAX_CHEAT ]; int patchnumber = 0; -int cheatnumber = 0; wxString strgametitle; @@ -49,13 +53,6 @@ static const PatchTextTable commands_patch[] = { 0, wxEmptyString, NULL } // Array Terminator }; -static const PatchTextTable commands_cheat[] = -{ - { 1, L"comment", PatchFunc::comment }, - { 2, L"patch", PatchFunc::cheat }, - { 0, wxEmptyString, NULL } // Array Terminator -}; - static const PatchTextTable dataType[] = { { 1, L"byte", NULL }, @@ -75,7 +72,7 @@ static const PatchTextTable cpuCore[] = // IniFile Functions. -void inifile_trim( wxString& buffer ) +static void inifile_trim( wxString& buffer ) { buffer.Trim(false); // trims left side. @@ -112,7 +109,7 @@ static int PatchTableExecute( const ParsedAssignmentString& set, const PatchText } // This routine is for executing the commands of the ini file. -void inifile_command(bool isCheat, const wxString& cmd) +static void inifile_command(const wxString& cmd) { ParsedAssignmentString set( cmd ); @@ -120,7 +117,7 @@ void inifile_command(bool isCheat, const wxString& cmd) // would make more sense... --air if (set.rvalue.IsEmpty()) set.rvalue = set.lvalue; - /*int code = */PatchTableExecute(set, isCheat ? commands_cheat : commands_patch); + /*int code = */PatchTableExecute(set, commands_patch); } // This routine receives a string containing patches, trims it, @@ -128,19 +125,18 @@ void inifile_command(bool isCheat, const wxString& cmd) void TrimPatches(wxString& s) { wxStringTokenizer tkn( s, L"\n" ); - + while(tkn.HasMoreTokens()) { - inifile_command(0, tkn.GetNextToken()); + inifile_command(tkn.GetNextToken()); } } -// This routine loads patches from the game database +// This routine loads patches from the game database (but not the config/game fixes/hacks) // Returns number of patches loaded -int InitPatches(const wxString& crc, const Game_Data& game) +int LoadPatchesFromGamesDB(const wxString& crc, const Game_Data& game) { bool patchFound = false; wxString patch; - patchnumber = 0; if (game.IsOk()) { @@ -153,9 +149,9 @@ int InitPatches(const wxString& crc, const Game_Data& game) patchFound = true; } } - + if (patchFound) TrimPatches(patch); - + return patchnumber; } @@ -163,7 +159,7 @@ void inifile_processString(const wxString& inStr) { wxString str(inStr); inifile_trim(str); - if (!str.IsEmpty()) inifile_command(1, str); + if (!str.IsEmpty()) inifile_command(str); } // This routine receives a file from inifile_read, trims it, @@ -176,20 +172,14 @@ void inifile_process(wxTextFile &f1 ) } } -void ResetPatchesCount() +void ForgetLoadedPatches() { patchnumber = 0; } - -void ResetCheatsCount() +static int _LoadPatchFiles(const wxDirName& folderName, wxString& fileSpec, const wxString& friendlyName, int& numberFoundPatchFiles) { - cheatnumber = 0; -} - -static int LoadCheatsFiles(const wxDirName& folderName, wxString& fileSpec, const wxString& friendlyName, int& numberFoundCheatsFiles) -{ - numberFoundCheatsFiles = 0; + numberFoundPatchFiles = 0; if (!folderName.Exists()) { Console.WriteLn(Color_Red, L"The %s folder ('%s') is inaccessible. Skipping...", WX_STR(friendlyName), WX_STR(folderName.ToString())); @@ -197,72 +187,72 @@ static int LoadCheatsFiles(const wxDirName& folderName, wxString& fileSpec, cons } wxDir dir(folderName.ToString()); - int before = cheatnumber; + int before = patchnumber; wxString buffer; wxTextFile f; bool found = dir.GetFirst(&buffer, L"*", wxDIR_FILES); while (found) { if (buffer.Upper().Matches(fileSpec.Upper())) { PatchesCon->WriteLn(Color_Green, L"Found %s file: '%s'", WX_STR(friendlyName), WX_STR(buffer)); - int before = cheatnumber; + int before = patchnumber; f.Open(Path::Combine(dir.GetName(), buffer)); inifile_process(f); f.Close(); - int loaded = cheatnumber - before; + int loaded = patchnumber - before; PatchesCon->WriteLn((loaded ? Color_Green : Color_Gray), L"Loaded %d %s from '%s' at '%s'", - loaded, WX_STR(friendlyName), WX_STR(buffer), WX_STR(folderName.ToString())); - numberFoundCheatsFiles ++; + loaded, WX_STR(friendlyName), WX_STR(buffer), WX_STR(folderName.ToString())); + numberFoundPatchFiles++; } found = dir.GetNext(&buffer); } - return cheatnumber - before; + return patchnumber - before; } -// This routine loads cheats from a zip file -// Returns number of cheats loaded -// Note: Should be called after InitPatches() -// Note: only load cheats from the root folder of the zip -int LoadCheatsFromZip(wxString gameCRC, const wxString& cheatsArchiveFilename) { - gameCRC.MakeUpper(); +// This routine loads patches from a zip file +// Returns number of patches loaded +// Note: does not reset previously loaded patches (use ForgetLoadedPatches() for that) +// Note: only load patches from the root folder of the zip +int LoadPatchesFromZip(wxString gameCRC, const wxString& patchesArchiveFilename) { + gameCRC.MakeUpper(); - int before = cheatnumber; + int before = patchnumber; - std::unique_ptr entry; - wxFFileInputStream in(cheatsArchiveFilename); - wxZipInputStream zip(in); - while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) - { - wxString name = entry->GetName(); - name.MakeUpper(); - if (name.Find(gameCRC) == 0 && name.Find(L".PNACH")+6u == name.Length()) { - PatchesCon->WriteLn(Color_Green, L"Loading patch '%s' from archive '%s'", - WX_STR(entry->GetName()), WX_STR(cheatsArchiveFilename)); - wxTextInputStream pnach(zip); - while (!zip.Eof()) { - inifile_processString(pnach.ReadLine()); - } - } - } - return cheatnumber - before; + std::unique_ptr entry; + wxFFileInputStream in(patchesArchiveFilename); + wxZipInputStream zip(in); + while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) + { + wxString name = entry->GetName(); + name.MakeUpper(); + if (name.Find(gameCRC) == 0 && name.Find(L".PNACH")+6u == name.Length()) { + PatchesCon->WriteLn(Color_Green, L"Loading patch '%s' from archive '%s'", + WX_STR(entry->GetName()), WX_STR(patchesArchiveFilename)); + wxTextInputStream pnach(zip); + while (!zip.Eof()) { + inifile_processString(pnach.ReadLine()); + } + } + } + return patchnumber - before; } -// This routine loads cheats from *.pnach files -// Returns number of cheats loaded -// Note: Should be called after InitPatches() -int LoadCheats(wxString name, const wxDirName& folderName, const wxString& friendlyName) +// This routine loads patches from *.pnach files +// Returns number of patches loaded +// Note: does not reset previously loaded patches (use ForgetLoadedPatches() for that) +int LoadPatchesFromDir(wxString name, const wxDirName& folderName, const wxString& friendlyName) { int loaded = 0; - int numberFoundCheatsFiles; + int numberFoundPatchFiles; wxString filespec = name + L"*.pnach"; - loaded += LoadCheatsFiles(folderName, filespec, friendlyName, numberFoundCheatsFiles); + loaded += _LoadPatchFiles(folderName, filespec, friendlyName, numberFoundPatchFiles); - // This message _might_ be buggy. This function (LoadCheats) loads from an explicit folder. + // This comment _might_ be buggy. This function (LoadPatchesFromDir) loads from an explicit folder. // This folder can be cheats or cheats_ws at either the default location or a custom one. // This check only tests the default cheats folder, so the message it produces is possibly misleading. - if (folderName.ToString().IsSameAs(PathDefs::GetCheats().ToString()) && numberFoundCheatsFiles == 0) { + if (folderName.ToString().IsSameAs(PathDefs::GetCheats().ToString()) && numberFoundPatchFiles == 0) { wxString pathName = Path::Combine(folderName, name.MakeUpper() + L".pnach"); PatchesCon->WriteLn(Color_Gray, L"Not found %s file: %s", WX_STR(friendlyName), WX_STR(pathName)); } @@ -311,7 +301,6 @@ namespace PatchFunc const wxString& WriteValue() const { return m_pieces[4]; } }; - template void patchHelper(const wxString& cmd, const wxString& param) { // Error Handling Note: I just throw simple wxStrings here, and then catch them below and // format them into more detailed cmd+data+error printouts. If we want to add user-friendly @@ -324,17 +313,17 @@ namespace PatchFunc try { - if (isCheat && cheatnumber >= MAX_CHEAT) - throw wxString( L"Maximum number of cheats reached" ); - if(!isCheat && patchnumber >= MAX_PATCH) + if(patchnumber >= MAX_PATCH) throw wxString( L"Maximum number of patches reached" ); - IniPatch& iPatch = isCheat ? Cheat[cheatnumber] : Patch[patchnumber]; + IniPatch& iPatch = Patch[patchnumber]; PatchPieces pieces(param); iPatch.enabled = 0; iPatch.placetopatch = StrToU32(pieces.PlaceToPatch(), 10); + if (iPatch.placetopatch >= _PPT_END_MARKER) + throw wxsFormat(L"Invalid 'place' value '%s' (0 - once on startup, 1: continuously)", WX_STR(pieces.PlaceToPatch())); iPatch.cpu = (patch_cpu_type)PatchTableExecute(pieces.CpuType(), cpuCore); iPatch.addr = StrToU32(pieces.MemAddr(), 16); iPatch.type = (patch_data_type)PatchTableExecute(pieces.OperandSize(), dataType); @@ -348,8 +337,7 @@ namespace PatchFunc iPatch.enabled = 1; // omg success!! - if (isCheat) cheatnumber++; - else patchnumber++; + patchnumber++; } catch( wxString& exmsg ) { @@ -357,12 +345,11 @@ namespace PatchFunc Console.Indent().Error( exmsg ); } } - void patch(const wxString& cmd, const wxString& param) { patchHelper<0>(cmd, param); } - void cheat(const wxString& cmd, const wxString& param) { patchHelper<1>(cmd, param); } + void patch(const wxString& cmd, const wxString& param) { patchHelper(cmd, param); } } // This is for applying patches directly to memory -void ApplyPatch(patch_place_type place) +void ApplyLoadedPatches(patch_place_type place) { for (int i = 0; i < patchnumber; i++) { @@ -370,13 +357,3 @@ void ApplyPatch(patch_place_type place) _ApplyPatch(&Patch[i]); } } - -// This is for applying cheats directly to memory -void ApplyCheat(patch_place_type place) -{ - for (int i = 0; i < cheatnumber; i++) - { - if (Cheat[i].placetopatch == place) - _ApplyPatch(&Cheat[i]); - } -} diff --git a/pcsx2/Patch.h b/pcsx2/Patch.h index f2148fb27d..0155b265b0 100644 --- a/pcsx2/Patch.h +++ b/pcsx2/Patch.h @@ -15,11 +15,30 @@ #pragma once +// Note about terminology: +// "patch" in pcsx2 terminology is a single pnach style patch line, e.g. patch=1,EE,001110e0,word,00000000 +// Such patches can appear in several places: +// - At .pnach files where each file could have several such patches: +// - At the "cheats" folder +// - UI name: "Cheats", controlled via system -> enable cheats +// - At the "cheats_ws" folder or inside "cheats_ws.zip" (the zip also called "widescreen cheats DB") +// - the latter is searched if the former is not found for a CRC +// - UI name: "Widescreen hacks/patches", controlled via system -> enable widescreen patches +// - At GameIndex.DBF inside a [patches] section +// - UI name: "Patches", controlled via system -> enable automatic game fixes +// - note that automatic game fixes also controls automatic config changes from GameIndex.dbf (UI name: "fixes") +// +// So, if the console title contains the following suffix: "... [2 Fixes] [1 Patches] [0 Cheats] [6 widescreen hacks]" then: +// - The 2 fixes are configuration (not patches) fixes applied from the Games DB (automatic game fixes). +// - The 1 Patch is one uncommented pnach-style patch line from the GamesDB (automatic game fixes). +// - The 0 cheats - cheats are enabled but nothing found/loaded from the "cheats" folder. +// - The 6 widescreen patches are 6 pnach-style patch lines loaded either from cheats_ws folder or from cheats_ws.zip + + #include "Pcsx2Defs.h" #include "SysForwardDefs.h" -#define MAX_PATCH 512 -#define MAX_CHEAT 1024 +#define MAX_PATCH 2048 enum patch_cpu_type { NO_CPU, @@ -53,9 +72,12 @@ enum patch_data_type { // - There's no "place" value which indicates to apply both once on startup // and then also continuously, however such behavior can be achieved by // duplicating the line where one has a 0 place and the other has a 1 place. +// FIXME: Do we want to apply the place=1 patches also on startup? (when the 0 patches are applied) enum patch_place_type { PPT_ONCE_ON_LOAD = 0, - PPT_CONTINUOUSLY = 1 + PPT_CONTINUOUSLY = 1, + + _PPT_END_MARKER }; typedef void PATCHTABLEFUNC( const wxString& text1, const wxString& text2 ); @@ -76,21 +98,30 @@ namespace PatchFunc PATCHTABLEFUNC comment; PATCHTABLEFUNC gametitle; PATCHTABLEFUNC patch; - PATCHTABLEFUNC cheat; } -extern void ResetPatchesCount(); -extern void ResetCheatsCount(); -extern int LoadCheats(wxString name, const wxDirName& folderName, const wxString& friendlyName); -extern int LoadCheatsFromZip(wxString gameCRC, const wxString& cheatsArchiveFilename); -extern void inifile_command(bool isCheat, const wxString& cmd); -extern void inifile_trim(wxString& buffer); +// The following LoadPatchesFrom* functions: +// - do not reset/unload previously loaded patches (use ForgetLoadedPatches() for that) +// - do not actually patch the emulation memory (that happens at ApplyLoadedPatches(...) ) +extern int LoadPatchesFromGamesDB(const wxString& name, const Game_Data& game); +extern int LoadPatchesFromDir(wxString name, const wxDirName& folderName, const wxString& friendlyName); +extern int LoadPatchesFromZip(wxString gameCRC, const wxString& cheatsArchiveFilename); -extern int InitPatches(const wxString& name, const Game_Data& game); -extern int AddPatch(int Mode, int Place, int Address, int Size, u64 data); -extern void ResetPatch(void); -extern void ApplyPatch(patch_place_type place); -extern void ApplyCheat(patch_place_type place); -extern void _ApplyPatch(IniPatch *p); +// Patches the emulation memory by applying all the loaded patches with a specific place value. +// Note: unless you know better, there's no need to check whether or not different patch sources +// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches, +// because on boot or on any configuration change --> all the loaded patches are invalidated, +// and then it loads only the ones which are enabled according to the current config +// (this happens at AppCoreThread::ApplySettings(...) ) +extern void ApplyLoadedPatches(patch_place_type place); + +// Empties the patches store ("unload" the patches) but doesn't touch the emulation memory. +// Following ApplyLoadedPatches calls will do nothing until some LoadPatchesFrom* are invoked. +extern void ForgetLoadedPatches(); extern const IConsoleWriter *PatchesCon; + +// The following prototypes seem unused in PCSX2, but maybe part of the cheats browser? +// regardless, they don't seem to have an implementation anywhere. +// extern int AddPatch(int Mode, int Place, int Address, int Size, u64 data); +// extern void ResetPatch(void); diff --git a/pcsx2/Patch_Memory.cpp b/pcsx2/Patch_Memory.cpp index f40b33c5ed..086e581a66 100644 --- a/pcsx2/Patch_Memory.cpp +++ b/pcsx2/Patch_Memory.cpp @@ -424,6 +424,8 @@ void handle_extended_t(IniPatch *p) } } +// Only used from Patch.cpp and we don't export this in any h file. +// Patch.cpp itself declares this prototype, so make sure to keep in sync. void _ApplyPatch(IniPatch *p) { if (p->enabled == 0) return; diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index 881ca03f43..467afb9f01 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -198,8 +198,7 @@ void SysCoreThread::_reset_stuff_as_needed() m_resetVirtualMachine = false; m_resetVsyncTimers = false; - ResetPatchesCount(); - ResetCheatsCount(); + ForgetLoadedPatches(); } if( m_resetVsyncTimers ) @@ -228,9 +227,7 @@ void SysCoreThread::DoCpuReset() // void SysCoreThread::VsyncInThread() { - if (EmuConfig.EnablePatches) ApplyPatch(PPT_CONTINUOUSLY); - if (EmuConfig.EnableCheats) ApplyCheat(PPT_CONTINUOUSLY); - if (EmuConfig.EnableWideScreenPatches) ApplyCheat(PPT_CONTINUOUSLY); + ApplyLoadedPatches(PPT_CONTINUOUSLY); } void SysCoreThread::GameStartingInThread() @@ -241,8 +238,7 @@ void SysCoreThread::GameStartingInThread() symbolMap.UpdateActiveSymbols(); sApp.PostAppMethod(&Pcsx2App::resetDebugger); - if (EmuConfig.EnablePatches) ApplyPatch(PPT_ONCE_ON_LOAD); - if (EmuConfig.EnableCheats) ApplyCheat(PPT_ONCE_ON_LOAD); + ApplyLoadedPatches(PPT_ONCE_ON_LOAD); } bool SysCoreThread::StateCheckInThread() diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 11373dc56b..21f3c2689f 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -54,7 +54,7 @@ public: bool AllowCancelOnExit() const { return false; } bool IsCriticalEvent() const { return m_IsCritical; } - + SysExecEvent_InvokeCoreThreadMethod( FnPtr_CoreThreadMethod method, bool critical=false ) { m_method = method; @@ -113,7 +113,7 @@ void AppCoreThread::Reset() GetSysExecutorThread().PostEvent( SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Reset) ); return; } - + _parent::Reset(); } @@ -203,7 +203,7 @@ void AppCoreThread::OnResumeReady() { wxGetApp().SysApplySettings(); wxGetApp().PostMethod( AppSaveSettings ); - + sApp.PostAppMethod( &Pcsx2App::leaveDebugMode ); _parent::OnResumeReady(); } @@ -232,7 +232,7 @@ static int loadGameSettings(Pcsx2Config& dest, const Game_Data& game) { ++gf; } } - + if (game.keyExists("vuRoundMode")) { SSE_RoundMode vuRM = (SSE_RoundMode)game.getInt("vuRoundMode"); @@ -359,6 +359,8 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src ) curGameKey = newGameKey; + ForgetLoadedPatches(); + if (!curGameKey.IsEmpty()) { if (IGameDatabase* GameDB = AppHost_GetGameDatabase() ) @@ -375,7 +377,7 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src ) if (fixup.EnablePatches) { - if (int patches = InitPatches(gameCRC, game)) + if (int patches = LoadPatchesFromGamesDB(gameCRC, game)) { gamePatch.Printf(L" [%d Patches]", patches); PatchesCon->WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", patches); @@ -400,8 +402,6 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src ) gameName = L"Booting PS2 BIOS... "; } - ResetCheatsCount(); - //Till the end of this function, entry CRC will be 00000000 if (!gameCRC.Length()) { @@ -411,20 +411,21 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src ) // regular cheat patches if (fixup.EnableCheats) - gameCheats.Printf(L" [%d Cheats]", LoadCheats(gameCRC, GetCheatsFolder(), L"Cheats")); + gameCheats.Printf(L" [%d Cheats]", LoadPatchesFromDir(gameCRC, GetCheatsFolder(), L"Cheats")); // wide screen patches if (fixup.EnableWideScreenPatches) { - if (int numberLoadedWideScreenPatches = LoadCheats(gameCRC, GetCheatsWsFolder(), L"Widescreen hacks")) + if (int numberLoadedWideScreenPatches = LoadPatchesFromDir(gameCRC, GetCheatsWsFolder(), L"Widescreen hacks")) { gameWsHacks.Printf(L" [%d widescreen hacks]", numberLoadedWideScreenPatches); + Console.WriteLn(Color_Gray, "Found ws patches at cheats_ws --> skipping cheats_ws.zip"); } else { // No ws cheat files found at the cheats_ws folder, try the ws cheats zip file. wxString cheats_ws_archive = Path::Combine(PathDefs::GetProgramDataDir(), wxFileName(L"cheats_ws.zip")); - int numberDbfCheatsLoaded = LoadCheatsFromZip(gameCRC, cheats_ws_archive); + int numberDbfCheatsLoaded = LoadPatchesFromZip(gameCRC, cheats_ws_archive); PatchesCon->WriteLn(Color_Green, "(Wide Screen Cheats DB) Patches Loaded: %d", numberDbfCheatsLoaded); gameWsHacks.Printf(L" [%d widescreen hacks]", numberDbfCheatsLoaded); } @@ -517,7 +518,7 @@ void AppCoreThread::GameStartingInThread() _reset_stuff_as_needed(); ClearMcdEjectTimeoutNow(); // probably safe to do this when a game boots, eliminates annoying prompts m_ExecMode = ExecMode_Opened; - + _parent::GameStartingInThread(); } @@ -552,7 +553,7 @@ void AppCoreThread::DoCpuExecute() Console.Error( ex.FormatMessage() ); // [TODO] : Debugger Hook! - + if( ++m_except_threshold > 6 ) { // If too many TLB Misses occur, we're probably going to crash and @@ -611,7 +612,7 @@ void SysExecEvent_CoreThreadClose::InvokeEvent() ScopedCoreThreadClose closed_core; _post_and_wait(closed_core); closed_core.AllowResume(); -} +} void SysExecEvent_CoreThreadPause::InvokeEvent() @@ -621,7 +622,7 @@ void SysExecEvent_CoreThreadPause::InvokeEvent() ScopedCoreThreadPause paused_core; _post_and_wait(paused_core); - // All plugins should be initialized and opened upon resuming from + // All plugins should be initialized and opened upon resuming from // a paused state. If the thread that puased us changed plugin status, it should // have used Close instead. if( CorePluginsAreOpen ) @@ -638,7 +639,7 @@ void SysExecEvent_CoreThreadPause::InvokeEvent() paused_core.AllowResume(); #endif -} +} // -------------------------------------------------------------------------------------- @@ -713,7 +714,7 @@ ScopedCoreThreadClose::ScopedCoreThreadClose() m_alreadyScoped = true; return; } - + if( !PostToSysExec(new SysExecEvent_CoreThreadClose()) ) { m_alreadyStopped = CoreThread.IsClosed(); diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp index a177dbf405..0ab577a73a 100644 --- a/pcsx2/x86/ix86-32/iR5900-32.cpp +++ b/pcsx2/x86/ix86-32/iR5900-32.cpp @@ -1618,8 +1618,7 @@ static void __fastcall recRecompile( const u32 startpc ) // First tentative was to call eeGameStarting directly (not through the // recompiler) but it crashes some games (GT4, DMC3). It is either a // thread issue or linked to the various components reset. - if (EmuConfig.EnablePatches) ApplyPatch(PPT_ONCE_ON_LOAD); - if (EmuConfig.EnableCheats) ApplyCheat(PPT_ONCE_ON_LOAD); + ApplyLoadedPatches(PPT_ONCE_ON_LOAD); } g_branch = 0;