mirror of https://github.com/PCSX2/pcsx2.git
PCSX2: Add ability to pass launch arguments to games with CLI option '--gameargs="-arg1 -arg2 -etc (#2576)
For more information please check the PR (#2576) since it's a bit detailed.
This commit is contained in:
parent
d1dc12f6b0
commit
a3c6ad636b
|
@ -522,15 +522,35 @@ static void intExecute()
|
|||
case RESET:
|
||||
do
|
||||
execI();
|
||||
while (cpuRegs.pc != (eeloadMain ? eeloadMain : EELOAD_START));
|
||||
while (cpuRegs.pc != (g_eeloadMain ? g_eeloadMain : EELOAD_START));
|
||||
if (cpuRegs.pc == EELOAD_START)
|
||||
{
|
||||
// The EELOAD _start function is the same across all BIOS versions afaik
|
||||
u32 mainjump = memRead32(EELOAD_START + 0x9c);
|
||||
if (mainjump >> 26 == 3) // JAL
|
||||
eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU);
|
||||
} else if (cpuRegs.pc == eeloadMain)
|
||||
g_eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU);
|
||||
}
|
||||
else if (cpuRegs.pc == g_eeloadMain)
|
||||
{
|
||||
eeloadHook();
|
||||
if (g_SkipBiosHack)
|
||||
{
|
||||
// See comments on this code in iR5900-32.cpp's recRecompile()
|
||||
u32 typeAexecjump = memRead32(EELOAD_START + 0x470);
|
||||
u32 typeBexecjump = memRead32(EELOAD_START + 0x5B0);
|
||||
u32 typeCexecjump = memRead32(EELOAD_START + 0x618);
|
||||
u32 typeDexecjump = memRead32(EELOAD_START + 0x600);
|
||||
if ((typeBexecjump >> 26 == 3) || (typeCexecjump >> 26 == 3) || (typeDexecjump >> 26 == 3)) // JAL to 0x822B8
|
||||
g_eeloadExec = EELOAD_START + 0x2B8;
|
||||
else if (typeAexecjump >> 26 == 3) // JAL to 0x82170
|
||||
g_eeloadExec = EELOAD_START + 0x170;
|
||||
else
|
||||
Console.WriteLn("intExecute: Could not enable launch arguments for fast boot mode; unidentified BIOS version! Please report this to the PCSX2 developers.");
|
||||
}
|
||||
}
|
||||
else if (cpuRegs.pc == g_eeloadExec)
|
||||
eeloadHook2();
|
||||
|
||||
if (g_GameLoading)
|
||||
state = GAME_LOADING;
|
||||
else
|
||||
|
|
204
pcsx2/R5900.cpp
204
pcsx2/R5900.cpp
|
@ -56,7 +56,16 @@ static const uint eeWaitCycles = 3072;
|
|||
|
||||
bool eeEventTestIsActive = false;
|
||||
|
||||
u32 eeloadMain = 0;
|
||||
u32 g_eeloadMain = 0, g_eeloadExec = 0, g_osdsys_str = 0;
|
||||
|
||||
/* I don't know how much space for args there is in the memory block used for args in full boot mode,
|
||||
but in fast boot mode, the block we use can fit at least 16 argv pointers (varies with BIOS version).
|
||||
The second EELOAD call during full boot has three built-in arguments ("EELOAD rom0:PS2LOGO <ELF>"),
|
||||
meaning that only the first 13 game arguments supplied by the user can be added on and passed through.
|
||||
In fast boot mode, 15 arguments can fit because the only call to EELOAD is "<ELF> <<args>>". */
|
||||
const int kMaxArgs = 16;
|
||||
uptr g_argPtrs[kMaxArgs];
|
||||
#define DEBUG_LAUNCHARG 0 // show lots of helpful console messages as the launch arguments are passed to the game
|
||||
|
||||
extern SysMainMemory& GetVmMemory();
|
||||
|
||||
|
@ -110,7 +119,7 @@ void cpuReset()
|
|||
// is why I found out about this) --air
|
||||
LastELF = L"";
|
||||
|
||||
eeloadMain = 0;
|
||||
g_eeloadMain = 0, g_eeloadExec = 0, g_osdsys_str = 0;
|
||||
}
|
||||
|
||||
void cpuShutdown()
|
||||
|
@ -540,6 +549,49 @@ void __fastcall eeGameStarting()
|
|||
}
|
||||
}
|
||||
|
||||
// Count arguments, save their starting locations, and replace the space separators with null terminators so they're separate strings
|
||||
int ParseArgumentString(u32 arg_block)
|
||||
{
|
||||
if (!arg_block)
|
||||
return 0;
|
||||
|
||||
int argc = 1; // one arg is guaranteed at least
|
||||
g_argPtrs[0] = arg_block; // first arg is right here
|
||||
bool wasSpace = false; // status of last char. scanned
|
||||
int args_len = strlen((char *)PSM(arg_block));
|
||||
for (int i = 0; i < args_len; i++)
|
||||
{
|
||||
char curchar = *(char *)PSM(arg_block + i);
|
||||
if (curchar == '\0')
|
||||
break; // should never reach this
|
||||
|
||||
bool isSpace = (curchar == ' ');
|
||||
if (isSpace)
|
||||
memset(PSM(arg_block + i), 0, 1);
|
||||
else if (wasSpace) // then we're at a new arg
|
||||
{
|
||||
if (argc < kMaxArgs)
|
||||
{
|
||||
g_argPtrs[argc] = arg_block + i;
|
||||
argc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("ParseArgumentString: Discarded additional arguments beyond the maximum of %d.", kMaxArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wasSpace = isSpace;
|
||||
}
|
||||
#if DEBUG_LAUNCHARG
|
||||
// Check our args block
|
||||
Console.WriteLn("ParseArgumentString: Saving these strings:");
|
||||
for (int a = 0; a < argc; a++)
|
||||
Console.WriteLn("%p -> '%s'.", g_argPtrs[a], (char *)PSM(g_argPtrs[a]));
|
||||
#endif
|
||||
return argc;
|
||||
}
|
||||
|
||||
// Called from recompilers; __fastcall define is mandatory.
|
||||
void __fastcall eeloadHook()
|
||||
{
|
||||
|
@ -554,9 +606,71 @@ void __fastcall eeloadHook()
|
|||
int disctype = GetPS2ElfName(discelf);
|
||||
|
||||
std::string elfname;
|
||||
if (cpuRegs.GPR.n.a0.SD[0] >= 2) // argc >= 2
|
||||
elfname = (char*)PSM(memRead32(cpuRegs.GPR.n.a1.UD[0] + 4)); // argv[1]
|
||||
int argc = cpuRegs.GPR.n.a0.SD[0];
|
||||
if (argc) // calls to EELOAD *after* the first one during the startup process will come here
|
||||
{
|
||||
#if DEBUG_LAUNCHARG
|
||||
Console.WriteLn("eeloadHook: EELOAD was called with %d arguments according to $a0 and %d according to vargs block:",
|
||||
argc, memRead32(cpuRegs.GPR.n.a1.UD[0] - 4));
|
||||
for (int a = 0; a < argc; a++)
|
||||
Console.WriteLn("argv[%d]: %p -> %p -> '%s'", a, cpuRegs.GPR.n.a1.UL[0] + (a * 4),
|
||||
memRead32(cpuRegs.GPR.n.a1.UD[0] + (a * 4)), (char *)PSM(memRead32(cpuRegs.GPR.n.a1.UD[0] + (a * 4))));
|
||||
#endif
|
||||
if (argc > 1)
|
||||
elfname = (char*)PSM(memRead32(cpuRegs.GPR.n.a1.UD[0] + 4)); // argv[1] in OSDSYS's invocation "EELOAD <game ELF>"
|
||||
|
||||
// This code fires if the user chooses "full boot". First the Sony Computer Entertainment screen appears. This is the result
|
||||
// of an EELOAD call that does not want to accept launch arguments (but we patch it to do so in eeloadHook2() in fast boot
|
||||
// mode). Then EELOAD is called with the argument "rom0:PS2LOGO". At this point, we do not need any additional tricks
|
||||
// because EELOAD is now ready to accept launch arguments. So in full-boot mode, we simply wait for PS2LOGO to be called,
|
||||
// then we add the desired launch arguments. PS2LOGO passes those on to the game itself as it calls EELOAD a third time.
|
||||
if (!g_Conf->CurrentGameArgs.empty() && !strcmp(elfname.c_str(), "rom0:PS2LOGO"))
|
||||
{
|
||||
const char *argString = g_Conf->CurrentGameArgs.c_str();
|
||||
Console.WriteLn("eeloadHook: Supplying launch argument(s) '%s' to module '%s'...", argString, elfname.c_str());
|
||||
|
||||
// Join all arguments by space characters so they can be processed as one string by ParseArgumentString(), then add the
|
||||
// user's launch arguments onto the end
|
||||
u32 arg_ptr = 0;
|
||||
int arg_len = 0;
|
||||
for (int a = 0; a < argc; a++)
|
||||
{
|
||||
arg_ptr = memRead32(cpuRegs.GPR.n.a1.UD[0] + (a * 4));
|
||||
arg_len = strlen((char *)PSM(arg_ptr));
|
||||
memset(PSM(arg_ptr + arg_len), 0x20, 1);
|
||||
}
|
||||
strcpy((char *)PSM(arg_ptr + arg_len + 1), g_Conf->CurrentGameArgs.c_str());
|
||||
u32 first_arg_ptr = memRead32(cpuRegs.GPR.n.a1.UD[0]);
|
||||
#if DEBUG_LAUNCHARG
|
||||
Console.WriteLn("eeloadHook: arg block is '%s'.", (char *)PSM(first_arg_ptr));
|
||||
#endif
|
||||
argc = ParseArgumentString(first_arg_ptr);
|
||||
|
||||
// Write pointer to next slot in $a1
|
||||
for (int a = 0; a < argc; a++)
|
||||
memWrite32(cpuRegs.GPR.n.a1.UD[0] + (a * 4), g_argPtrs[a]);
|
||||
cpuRegs.GPR.n.a0.SD[0] = argc;
|
||||
#if DEBUG_LAUNCHARG
|
||||
// Check our work
|
||||
Console.WriteLn("eeloadHook: New arguments are:");
|
||||
for (int a = 0; a < argc; a++)
|
||||
Console.WriteLn("argv[%d]: %p -> '%s'", a, memRead32(cpuRegs.GPR.n.a1.UD[0] + (a * 4)),
|
||||
(char *)PSM(memRead32(cpuRegs.GPR.n.a1.UD[0] + (a * 4))));
|
||||
#endif
|
||||
}
|
||||
// else it's presumed that the invocation is "EELOAD <game ELF> <<launch args>>", coming from PS2LOGO, and we needn't do
|
||||
// anything more
|
||||
}
|
||||
#if DEBUG_LAUNCHARG
|
||||
// This code fires in full/fast boot mode when EELOAD is called the first/only time. When EELOAD is not given any arguments,
|
||||
// it calls rom0:OSDSYS by default, which displays the Sony Computer Entertainment screen. OSDSYS then calls "EELOAD
|
||||
// rom0:PS2LOGO" and we end up above.
|
||||
else
|
||||
Console.WriteLn("eeloadHook: EELOAD was called with no arguments.");
|
||||
#endif
|
||||
|
||||
// If "fast boot" was chosen, then on EELOAD's first call we won't yet know what the game's ELF is. Find the name and write it
|
||||
// into EELOAD's memory.
|
||||
if (g_SkipBiosHack && elfname.empty())
|
||||
{
|
||||
std::string elftoload;
|
||||
|
@ -571,37 +685,21 @@ void __fastcall eeloadHook()
|
|||
elftoload = discelf.ToUTF8();
|
||||
}
|
||||
|
||||
// When fast-booting, we insert the game's ELF name into EELOAD so that the game is called instead of the default call of
|
||||
// "rom0:OSDSYS"; any launch arguments supplied by the user will be inserted into EELOAD later by eeloadHook2()
|
||||
if (!elftoload.empty())
|
||||
{
|
||||
#if 0
|
||||
// FIXME: you'd think that changing argc and argv would work but no, need to work out why not
|
||||
// This method would support adding command line arguments to homebrew (and is generally less hacky)
|
||||
// It works if you hook on rom0:PS2LOGO loading, but not on the first call with no arguments
|
||||
cpuRegs.GPR.n.a0.SD[0] = 2; // argc = 2
|
||||
// argv[0] = "EELOAD"
|
||||
strcpy((char*)PSM(cpuRegs.GPR.n.a1.UD[0] + 0x40), "EELOAD");
|
||||
memWrite32(cpuRegs.GPR.n.a1.UD[0] + 0, cpuRegs.GPR.n.a1.UD[0] + 0x40);
|
||||
// argv[1] = elftoload
|
||||
strcpy((char*)PSM(cpuRegs.GPR.n.a1.UD[0] + 0x47), elftoload.c_str());
|
||||
memWrite32(cpuRegs.GPR.n.a1.UD[0] + 4, cpuRegs.GPR.n.a1.UD[0] + 0x47);
|
||||
memWrite32(cpuRegs.GPR.n.a1.UD[0] + 8, 0);
|
||||
g_GameLoading = true;
|
||||
return;
|
||||
#else
|
||||
// The strings are all 64-bit aligned. Why? I don't know, but they are
|
||||
for (u32 osdsys_str = EELOAD_START; osdsys_str < EELOAD_START + EELOAD_SIZE; osdsys_str += 8) {
|
||||
if (!strcmp((char*)PSM(osdsys_str), "rom0:OSDSYS")) {
|
||||
for (u32 osdsys_ptr = osdsys_str - 4; osdsys_ptr >= EELOAD_START; osdsys_ptr -= 4) {
|
||||
if (memRead32(osdsys_ptr) == osdsys_str) {
|
||||
strcpy((char*)PSM(cpuRegs.GPR.n.a1.UD[0] + 0x40), elftoload.c_str());
|
||||
memWrite32(osdsys_ptr, cpuRegs.GPR.n.a1.UD[0] + 0x40);
|
||||
g_GameLoading = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Find and save location of default/fallback call "rom0:OSDSYS"; to be used later by eeloadHook2()
|
||||
for (g_osdsys_str = EELOAD_START; g_osdsys_str < EELOAD_START + EELOAD_SIZE; g_osdsys_str += 8) // strings are 64-bit aligned
|
||||
{
|
||||
if (!strcmp((char*)PSM(g_osdsys_str), "rom0:OSDSYS"))
|
||||
{
|
||||
// Overwrite OSDSYS with game's ELF name
|
||||
strcpy((char*)PSM(g_osdsys_str), elftoload.c_str());
|
||||
g_GameLoading = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -609,6 +707,50 @@ void __fastcall eeloadHook()
|
|||
g_GameLoading = true;
|
||||
}
|
||||
|
||||
// Called from recompilers; __fastcall define is mandatory.
|
||||
// Only called if g_SkipBiosHack is true
|
||||
void __fastcall eeloadHook2()
|
||||
{
|
||||
if (g_Conf->CurrentGameArgs.empty())
|
||||
return;
|
||||
|
||||
if (!g_osdsys_str)
|
||||
{
|
||||
Console.WriteLn("eeloadHook2: Called before \"rom0:OSDSYS\" was found by eeloadHook()!");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *argString = g_Conf->CurrentGameArgs.c_str();
|
||||
Console.WriteLn("eeloadHook2: Supplying launch argument(s) '%s' to ELF '%s'.", argString, (char *)PSM(g_osdsys_str));
|
||||
|
||||
// Add args string after game's ELF name that was written over "rom0:OSDSYS" by eeloadHook(). In between the ELF name and args
|
||||
// string we insert a space character so that ParseArgumentString() has one continuous string to process.
|
||||
int game_len = strlen((char *)PSM(g_osdsys_str));
|
||||
memset(PSM(g_osdsys_str + game_len), 0x20, 1);
|
||||
strcpy((char *)PSM(g_osdsys_str + game_len + 1), g_Conf->CurrentGameArgs.c_str());
|
||||
#if DEBUG_LAUNCHARG
|
||||
Console.WriteLn("eeloadHook2: arg block is '%s'.", (char *)PSM(g_osdsys_str));
|
||||
#endif
|
||||
int argc = ParseArgumentString(g_osdsys_str);
|
||||
|
||||
// Back up 4 bytes from start of args block for every arg + 4 bytes for start of argv pointer block, write pointers
|
||||
uptr block_start = g_osdsys_str - (argc * 4);
|
||||
for (int a = 0; a < argc; a++)
|
||||
{
|
||||
#if DEBUG_LAUNCHARG
|
||||
Console.WriteLn("eeloadHook2: Writing address %p to location %p.", g_argPtrs[a], block_start + (a * 4));
|
||||
#endif
|
||||
memWrite32(block_start + (a * 4), g_argPtrs[a]);
|
||||
}
|
||||
|
||||
// Save argc and argv as incoming arguments for EELOAD function which calls ExecPS2()
|
||||
#if DEBUG_LAUNCHARG
|
||||
Console.WriteLn("eeloadHook2: Saving %d and %p in $a0 and $a1.", argc, block_start);
|
||||
#endif
|
||||
cpuRegs.GPR.n.a0.SD[0] = argc;
|
||||
cpuRegs.GPR.n.a1.UD[0] = block_start;
|
||||
}
|
||||
|
||||
inline bool isBranchOrJump(u32 addr)
|
||||
{
|
||||
u32 op = memRead32(addr);
|
||||
|
|
|
@ -273,10 +273,11 @@ const u32 EEKERNEL_START = 0;
|
|||
const u32 EENULL_START = 0x81FC0;
|
||||
const u32 EELOAD_START = 0x82000;
|
||||
const u32 EELOAD_SIZE = 0x20000; // overestimate for searching
|
||||
extern u32 eeloadMain;
|
||||
extern u32 g_eeloadMain, g_eeloadExec;
|
||||
|
||||
extern void __fastcall eeGameStarting();
|
||||
extern void __fastcall eeloadHook();
|
||||
extern void __fastcall eeloadHook2();
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// R5900cpu
|
||||
|
|
|
@ -288,6 +288,8 @@ public:
|
|||
|
||||
wxString ElfFile;
|
||||
|
||||
wxString GameLaunchArgs;
|
||||
|
||||
// Specifies the CDVD source type to use when AutoRunning
|
||||
CDVD_SourceType CdvdSource;
|
||||
|
||||
|
|
|
@ -315,6 +315,7 @@ public:
|
|||
wxString CurrentELF;
|
||||
wxString CurrentIRX;
|
||||
CDVD_SourceType CdvdSource;
|
||||
wxString CurrentGameArgs;
|
||||
|
||||
// Memorycard options - first 2 are default slots, last 6 are multitap 1 and 2
|
||||
// slots (3 each)
|
||||
|
|
|
@ -243,6 +243,7 @@ void Pcsx2App::OnInitCmdLine( wxCmdLineParser& parser )
|
|||
parser.AddSwitch( wxEmptyString,L"nohacks", _("disables all speedhacks") );
|
||||
parser.AddOption( wxEmptyString,L"gamefixes", _("use the specified comma or pipe-delimited list of gamefixes.") + fixlist, wxCMD_LINE_VAL_STRING );
|
||||
parser.AddSwitch( wxEmptyString,L"fullboot", _("disables fast booting") );
|
||||
parser.AddOption( wxEmptyString,L"gameargs", _("passes the specified space-delimited string of launch arguments to the game"), wxCMD_LINE_VAL_STRING);
|
||||
|
||||
parser.AddOption( wxEmptyString,L"cfgpath", _("changes the configuration file path"), wxCMD_LINE_VAL_STRING );
|
||||
parser.AddOption( wxEmptyString,L"cfg", _("specifies the PCSX2 configuration file to use"), wxCMD_LINE_VAL_STRING );
|
||||
|
@ -365,6 +366,10 @@ bool Pcsx2App::OnCmdLineParsed( wxCmdLineParser& parser )
|
|||
}
|
||||
}
|
||||
|
||||
wxString game_args;
|
||||
if (parser.Found(L"gameargs", &game_args) && !game_args.IsEmpty())
|
||||
Startup.GameLaunchArgs = game_args;
|
||||
|
||||
if( parser.Found(L"usecd") )
|
||||
{
|
||||
Startup.CdvdSource = CDVD_SourceType::Plugin;
|
||||
|
@ -507,6 +512,7 @@ bool Pcsx2App::OnInit()
|
|||
if (Startup.CdvdSource == CDVD_SourceType::Iso)
|
||||
SysUpdateIsoSrcFile( Startup.IsoFile );
|
||||
sApp.SysExecute( Startup.CdvdSource );
|
||||
g_Conf->CurrentGameArgs = Startup.GameLaunchArgs;
|
||||
}
|
||||
else if ( Startup.SysAutoRunElf )
|
||||
{
|
||||
|
|
|
@ -1652,15 +1652,33 @@ static void __fastcall recRecompile( const u32 startpc )
|
|||
|
||||
pxAssert(s_pCurBlockEx);
|
||||
|
||||
if (HWADDR(startpc) == EELOAD_START) {
|
||||
// The EELOAD _start function is the same across all BIOS versions afaik
|
||||
if (HWADDR(startpc) == EELOAD_START)
|
||||
{
|
||||
// The EELOAD _start function is the same across all BIOS versions
|
||||
u32 mainjump = memRead32(EELOAD_START + 0x9c);
|
||||
if (mainjump >> 26 == 3) // JAL
|
||||
eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU);
|
||||
g_eeloadMain = ((EELOAD_START + 0xa0) & 0xf0000000U) | (mainjump << 2 & 0x0fffffffU);
|
||||
}
|
||||
|
||||
if (eeloadMain && HWADDR(startpc) == HWADDR(eeloadMain)) {
|
||||
if (g_eeloadMain && HWADDR(startpc) == HWADDR(g_eeloadMain))
|
||||
{
|
||||
xFastCall((void*)eeloadHook);
|
||||
if (g_SkipBiosHack)
|
||||
{
|
||||
// There are four known versions of EELOAD, identifiable by the location of the 'jal' to the EELOAD function which
|
||||
// calls ExecPS2(). The function itself is at the same address in all BIOSs after v1.00-v1.10.
|
||||
u32 typeAexecjump = memRead32(EELOAD_START + 0x470); // v1.00, v1.01?, v1.10?
|
||||
u32 typeBexecjump = memRead32(EELOAD_START + 0x5B0); // v1.20, v1.50, v1.60 (3000x models)
|
||||
u32 typeCexecjump = memRead32(EELOAD_START + 0x618); // v1.60 (3900x models)
|
||||
u32 typeDexecjump = memRead32(EELOAD_START + 0x600); // v1.70, v1.90, v2.00, v2.20, v2.30
|
||||
if ((typeBexecjump >> 26 == 3) || (typeCexecjump >> 26 == 3) || (typeDexecjump >> 26 == 3)) // JAL to 0x822B8
|
||||
g_eeloadExec = EELOAD_START + 0x2B8;
|
||||
else if (typeAexecjump >> 26 == 3) // JAL to 0x82170
|
||||
g_eeloadExec = EELOAD_START + 0x170;
|
||||
else // There might be other types of EELOAD, because these models' BIOSs have not been examined: 18000, 3500x, 3700x,
|
||||
// 5500x, and 7900x. However, all BIOS versions have been examined except for v1.01 and v1.10.
|
||||
Console.WriteLn("recRecompile: Could not enable launch arguments for fast boot mode; unidentified BIOS version! Please report this to the PCSX2 developers.");
|
||||
}
|
||||
|
||||
// On fast/full boot this will have a crc of 0x0. But when the game/elf itself is
|
||||
// recompiled (below - ElfEntry && g_GameLoading), then the crc would be from the elf.
|
||||
|
@ -1670,6 +1688,9 @@ static void __fastcall recRecompile( const u32 startpc )
|
|||
doPlace0Patches();
|
||||
g_patchesNeedRedo = 0;
|
||||
}
|
||||
|
||||
if (g_eeloadExec && HWADDR(startpc) == HWADDR(g_eeloadExec))
|
||||
xFastCall((void*)eeloadHook2);
|
||||
|
||||
// this is the only way patches get applied, doesn't depend on a hack
|
||||
if (g_GameLoading && HWADDR(startpc) == ElfEntry) {
|
||||
|
|
Loading…
Reference in New Issue