patches: simplify by unifying patches/cheats (effectively no-op)

patches and cheats are exactly the same (pnach style patch line) but we
stored two sets of them depending on their source: "patches" for
GameIndex.dbf patches, and "cheats" for all the rest (cheats, widescreen,
etc).

Unify patches and cheats and keep only "patches", cleanup and rename the
public API at Patch.h, and add documentation.

Also: add some console messages on invalid "place" value, and when we skip
searching cheats_ws.zip because a pnach file was found at cheats_ws dir.

Also: removed checks before applying different kinds of patches/cheats
because we don't need them (we didn't have disabled patches loaded anyway).

The checks removal _shouldn't_ have any effect, except that the checks were
wrong and accidentally prevented loading widescreen hacks which have a place
value of 0. No one probably noticed it since all the widescreen patches
which I looked at have a place value of 1. So now ws patches with place=0
would load correctly too. If we'll ever have such.
This commit is contained in:
Avi Halachmi (:avih) 2016-07-27 17:59:00 +03:00
parent eb1e890278
commit cff8cb137c
6 changed files with 134 additions and 128 deletions

View File

@ -27,11 +27,15 @@
#include <wx/txtstrm.h>
#include <wx/zipstrm.h>
// 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<wxZipEntry> 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<wxZipEntry> 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<bool isCheat>
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]);
}
}

View File

@ -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 <CRC>.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);

View File

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

View File

@ -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()

View File

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

View File

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