3ds now has proper core launching
This commit is contained in:
parent
1a6f572405
commit
052de6bcd2
|
@ -181,7 +181,7 @@ static unsigned swap_interval = 1;
|
||||||
static const bool video_threaded = false;
|
static const bool video_threaded = false;
|
||||||
|
|
||||||
#if defined(HAVE_THREADS)
|
#if defined(HAVE_THREADS)
|
||||||
#if defined(GEKKO) || defined(PSP) || defined(_3DS)
|
#if defined(GEKKO) || defined(PSP)
|
||||||
/* For single-core consoles right now it's better to have this be disabled. */
|
/* For single-core consoles right now it's better to have this be disabled. */
|
||||||
static const bool threaded_data_runloop_enable = false;
|
static const bool threaded_data_runloop_enable = false;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -10,11 +10,13 @@ extern const loaderFuncs_s loader_Ninjhax1;
|
||||||
extern const loaderFuncs_s loader_Ninjhax2;
|
extern const loaderFuncs_s loader_Ninjhax2;
|
||||||
extern const loaderFuncs_s loader_Rosalina;
|
extern const loaderFuncs_s loader_Rosalina;
|
||||||
|
|
||||||
static argData_s newProgramArgs;;//the argv variable must remain in memory even when the application exits for the new program to load properly
|
static void (*launch_3dsx)(const char* path, argData_s* args, executableMetadata_s* em);
|
||||||
|
|
||||||
|
|
||||||
int exec_3dsx(const char* path, const char* args){
|
static int exec_3dsx_actual(const char* path, const char* args, bool appendPath){
|
||||||
struct stat sBuff;
|
struct stat sBuff;
|
||||||
|
argData_s newProgramArgs;
|
||||||
|
char* writeableString[0x400];
|
||||||
bool fileExists;
|
bool fileExists;
|
||||||
bool inited;
|
bool inited;
|
||||||
|
|
||||||
|
@ -29,39 +31,50 @@ int exec_3dsx(const char* path, const char* args){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else if(S_ISDIR(sBuff.st_mode)){
|
else if(S_ISDIR(sBuff.st_mode)){
|
||||||
errno = EINVAL;;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(args == NULL || args[0] == '\0')
|
//args the string functions write to the passed string, this will cause a write to read only memory sot the string must be cloned
|
||||||
args = path;
|
memset(newProgramArgs.buf, '\0', sizeof(newProgramArgs.buf));
|
||||||
|
newProgramArgs.dst = (char*)&newProgramArgs.buf[1];
|
||||||
int argsSize = strlen(args);
|
if(appendPath){
|
||||||
strncpy((char*)newProgramArgs.buf , args, ENTRY_ARGBUFSIZE);
|
strcpy(writeableString, path);
|
||||||
if(argsSize >= ENTRY_ARGBUFSIZE)
|
launchAddArg(&newProgramArgs, writeableString);
|
||||||
((char*)&newProgramArgs.buf[0])[ENTRY_ARGBUFSIZE - 1] = '\0';
|
}
|
||||||
newProgramArgs.dst = (char*)newProgramArgs.buf + (argsSize < ENTRY_ARGBUFSIZE ? argsSize : ENTRY_ARGBUFSIZE);
|
if(args != NULL && args[0] != '\0'){
|
||||||
|
strcpy(writeableString, args);
|
||||||
|
launchAddArgsFromString(&newProgramArgs, writeableString);
|
||||||
|
}
|
||||||
|
|
||||||
inited = loader_Rosalina.init();
|
inited = loader_Rosalina.init();
|
||||||
if(inited){
|
launch_3dsx = loader_Rosalina.launchFile;
|
||||||
loader_Rosalina.launchFile(path, &newProgramArgs, NULL);
|
|
||||||
//exit(0);
|
if(!inited){
|
||||||
|
inited = loader_Ninjhax2.init();
|
||||||
|
launch_3dsx = loader_Ninjhax2.launchFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!inited){
|
||||||
|
inited = loader_Ninjhax1.init();
|
||||||
|
launch_3dsx = loader_Ninjhax1.launchFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
inited = loader_Ninjhax2.init();
|
|
||||||
if(inited){
|
if(inited){
|
||||||
loader_Ninjhax2.launchFile(path, &newProgramArgs, NULL);
|
osSetSpeedupEnable(false);
|
||||||
//exit(0);
|
launch_3dsx(path, &newProgramArgs, NULL);
|
||||||
}
|
exit(0);
|
||||||
|
|
||||||
inited = loader_Ninjhax1.init();
|
|
||||||
if(inited){
|
|
||||||
loader_Ninjhax1.launchFile(path, &newProgramArgs, NULL);
|
|
||||||
//exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//should never be reached
|
//should never be reached
|
||||||
//errno = ENOTSUP;
|
errno = ENOTSUP;
|
||||||
//return -1;
|
return -1;
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
int exec_3dsx(const char* path, const char* args){
|
||||||
|
return exec_3dsx_actual(path, args, true/*appendPath*/);
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec_3dsx_no_path_in_args(const char* path, const char* args){
|
||||||
|
return exec_3dsx_actual(path, args, false/*appendPath*/);
|
||||||
}
|
}
|
|
@ -2,7 +2,8 @@
|
||||||
#define EXEC_3DSX_H
|
#define EXEC_3DSX_H
|
||||||
|
|
||||||
//since 3dsx programs are not guaranteed access to the OS, the 3dsx bootloader run by the exploit must run the next program
|
//since 3dsx programs are not guaranteed access to the OS, the 3dsx bootloader run by the exploit must run the next program
|
||||||
//your program must call this then exit gracefully to work, exit() also doesnt work
|
//your program must have no extra threads running when this is called, these limits do not apply to exec_cia
|
||||||
|
int exec_3dsx_no_path_in_args(const char* path, const char* args);
|
||||||
int exec_3dsx(const char* path, const char* args);
|
int exec_3dsx(const char* path, const char* args);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -14,6 +14,9 @@ typedef struct{
|
||||||
}ciaParam;
|
}ciaParam;
|
||||||
|
|
||||||
|
|
||||||
|
char argvHmac[0x20] = {0x1d, 0x78, 0xff, 0xb9, 0xc5, 0xbc, 0x78, 0xb7, 0xac, 0x29, 0x1d, 0x3e, 0x16, 0xd0, 0xcf, 0x53, 0xef, 0x12, 0x58, 0x83, 0xb6, 0x9e, 0x2f, 0x79, 0x47, 0xf9, 0x35, 0x61, 0xeb, 0x50, 0xd7, 0x67};
|
||||||
|
|
||||||
|
|
||||||
static void errorAndQuit(const char* errorStr){
|
static void errorAndQuit(const char* errorStr){
|
||||||
errorConf error;
|
errorConf error;
|
||||||
|
|
||||||
|
@ -23,10 +26,12 @@ static void errorAndQuit(const char* errorStr){
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isCiaInstalled(u64 titleId){
|
static int isCiaInstalled(u64 titleId, u16 version){
|
||||||
u32 titlesToRetrieve;
|
u32 titlesToRetrieve;
|
||||||
u32 titlesRetrieved;
|
u32 titlesRetrieved;
|
||||||
u64* titleIds;
|
u64* titleIds;
|
||||||
|
bool titleExists = false;
|
||||||
|
AM_TitleEntry titleInfo;
|
||||||
Result failed;
|
Result failed;
|
||||||
|
|
||||||
failed = AM_GetTitleCount(MEDIATYPE_SD, &titlesToRetrieve);
|
failed = AM_GetTitleCount(MEDIATYPE_SD, &titlesToRetrieve);
|
||||||
|
@ -43,12 +48,22 @@ static int isCiaInstalled(u64 titleId){
|
||||||
|
|
||||||
for(u32 titlesToCheck = 0; titlesToCheck < titlesRetrieved; titlesToCheck++){
|
for(u32 titlesToCheck = 0; titlesToCheck < titlesRetrieved; titlesToCheck++){
|
||||||
if(titleIds[titlesToCheck] == titleId){
|
if(titleIds[titlesToCheck] == titleId){
|
||||||
free(titleIds);
|
titleExists = true;
|
||||||
return 1;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(titleIds);
|
free(titleIds);
|
||||||
|
|
||||||
|
if(titleExists){
|
||||||
|
failed = AM_GetTitleInfo(MEDIATYPE_SD, 1 /*titleCount*/, &titleId, &titleInfo);
|
||||||
|
if(R_FAILED(failed))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(titleInfo.version == version)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,17 +115,6 @@ static int installCia(Handle ciaFile){
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 getCiaTitleId(Handle ciaFile){
|
|
||||||
Result failed;
|
|
||||||
AM_TitleEntry ciaInfo;
|
|
||||||
|
|
||||||
failed = AM_GetCiaFileInfo(MEDIATYPE_SD, &ciaInfo, ciaFile);
|
|
||||||
if(R_FAILED(failed))
|
|
||||||
return 0x0000000000000000;
|
|
||||||
|
|
||||||
return ciaInfo.titleID;
|
|
||||||
}
|
|
||||||
|
|
||||||
int exec_cia(const char* path, const char* args){
|
int exec_cia(const char* path, const char* args){
|
||||||
struct stat sBuff;
|
struct stat sBuff;
|
||||||
bool fileExists;
|
bool fileExists;
|
||||||
|
@ -134,14 +138,12 @@ int exec_cia(const char* path, const char* args){
|
||||||
inited = R_SUCCEEDED(amInit()) && R_SUCCEEDED(fsInit());
|
inited = R_SUCCEEDED(amInit()) && R_SUCCEEDED(fsInit());
|
||||||
if(inited){
|
if(inited){
|
||||||
Result res;
|
Result res;
|
||||||
int error;
|
AM_TitleEntry ciaInfo;
|
||||||
FS_Archive ciaArchive;
|
FS_Archive ciaArchive;
|
||||||
Handle ciaFile;
|
Handle ciaFile;
|
||||||
u64 titleId;
|
|
||||||
int ciaInstalled;
|
int ciaInstalled;
|
||||||
ciaParam param;
|
ciaParam param;
|
||||||
int argsLength;
|
int argsLength;
|
||||||
extern char __argv_hmac[0x20];
|
|
||||||
|
|
||||||
//open cia file
|
//open cia file
|
||||||
res = FSUSER_OpenArchive(&ciaArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
|
res = FSUSER_OpenArchive(&ciaArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
|
||||||
|
@ -151,12 +153,12 @@ int exec_cia(const char* path, const char* args){
|
||||||
res = FSUSER_OpenFile(&ciaFile, ciaArchive, fsMakePath(PATH_ASCII, path + 5/*skip "sdmc:"*/), FS_OPEN_READ, 0);
|
res = FSUSER_OpenFile(&ciaFile, ciaArchive, fsMakePath(PATH_ASCII, path + 5/*skip "sdmc:"*/), FS_OPEN_READ, 0);
|
||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
errorAndQuit("Cant open CIA file.");
|
errorAndQuit("Cant open CIA file.");
|
||||||
|
|
||||||
|
res = AM_GetCiaFileInfo(MEDIATYPE_SD, &ciaInfo, ciaFile);
|
||||||
|
if(R_FAILED(res))
|
||||||
|
errorAndQuit("Cant get CIA file info.");
|
||||||
|
|
||||||
titleId = getCiaTitleId(ciaFile);
|
ciaInstalled = isCiaInstalled(ciaInfo.titleID, ciaInfo.version);
|
||||||
if(titleId == 0x0000000000000000)
|
|
||||||
errorAndQuit("Cant get CIA file title id.");
|
|
||||||
|
|
||||||
ciaInstalled = isCiaInstalled(titleId);
|
|
||||||
if(ciaInstalled == -1){
|
if(ciaInstalled == -1){
|
||||||
//error
|
//error
|
||||||
errorAndQuit("Could not read title id list.");
|
errorAndQuit("Could not read title id list.");
|
||||||
|
@ -164,7 +166,6 @@ int exec_cia(const char* path, const char* args){
|
||||||
else if(ciaInstalled == 0){
|
else if(ciaInstalled == 0){
|
||||||
//not installed
|
//not installed
|
||||||
int error = installCia(ciaFile);
|
int error = installCia(ciaFile);
|
||||||
|
|
||||||
if(error == -1)
|
if(error == -1)
|
||||||
errorAndQuit("Cant install CIA.");
|
errorAndQuit("Cant install CIA.");
|
||||||
}
|
}
|
||||||
|
@ -180,7 +181,6 @@ int exec_cia(const char* path, const char* args){
|
||||||
bool inSingleQuotes = false;
|
bool inSingleQuotes = false;
|
||||||
bool inDoubleQuotes = false;
|
bool inDoubleQuotes = false;
|
||||||
int argStringLength = strlen(args);
|
int argStringLength = strlen(args);
|
||||||
int argPtr;
|
|
||||||
param.argc = 0;
|
param.argc = 0;
|
||||||
argsLength = 0;
|
argsLength = 0;
|
||||||
|
|
||||||
|
@ -204,11 +204,11 @@ int exec_cia(const char* path, const char* args){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = APT_PrepareToDoApplicationJump(0, titleId, 0x1);
|
res = APT_PrepareToDoApplicationJump(0, ciaInfo.titleID, 0x1);
|
||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
errorAndQuit("CIA cant run, cant prepare.");
|
errorAndQuit("CIA cant run, cant prepare.");
|
||||||
|
|
||||||
res = APT_DoApplicationJump(¶m, sizeof(param.argc) + argsLength, __argv_hmac);
|
res = APT_DoApplicationJump(¶m, sizeof(param.argc) + argsLength, argvHmac);
|
||||||
if(R_FAILED(res))
|
if(R_FAILED(res))
|
||||||
errorAndQuit("CIA cant run, cant jump.");
|
errorAndQuit("CIA cant run, cant jump.");
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,66 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
size_t launchAddArg(argData_s* ad, const char* arg)
|
||||||
|
{
|
||||||
|
size_t len = strlen(arg)+1;
|
||||||
|
if ((ad->dst+len) >= (char*)(ad+1)) return len; // Overflow
|
||||||
|
ad->buf[0]++;
|
||||||
|
strcpy(ad->dst, arg);
|
||||||
|
ad->dst += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void launchAddArgsFromString(argData_s* ad, char* arg)
|
||||||
|
{
|
||||||
|
char c, *pstr, *str=arg, *endarg = arg+strlen(arg);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
c = *str++;
|
||||||
|
} while ((c == ' ' || c == '\t') && str < endarg);
|
||||||
|
|
||||||
|
pstr = str-1;
|
||||||
|
|
||||||
|
if (c == '\"')
|
||||||
|
{
|
||||||
|
pstr++;
|
||||||
|
while(*str++ != '\"' && str < endarg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (c == '\'')
|
||||||
|
{
|
||||||
|
pstr++;
|
||||||
|
while(*str++ != '\'' && str < endarg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
c = *str++;
|
||||||
|
} while (c != ' ' && c != '\t' && str < endarg);
|
||||||
|
}
|
||||||
|
|
||||||
|
str--;
|
||||||
|
|
||||||
|
if (str == (endarg - 1))
|
||||||
|
{
|
||||||
|
if(*str == '\"' || *str == '\'')
|
||||||
|
*(str++) = 0;
|
||||||
|
else
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(str++) = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
launchAddArg(ad, pstr);
|
||||||
|
|
||||||
|
} while(str<endarg);
|
||||||
|
}
|
||||||
|
|
||||||
Handle launchOpenFile(const char* path)
|
Handle launchOpenFile(const char* path)
|
||||||
{
|
{
|
||||||
if (strncmp(path, "sdmc:/", 6) == 0)
|
if (strncmp(path, "sdmc:/", 6) == 0)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
extern void (*__system_retAddr)(void);
|
extern void (*__system_retAddr)(void);
|
||||||
|
@ -21,4 +23,7 @@ typedef struct
|
||||||
void (* useTitle)(u64 tid, u8 mediatype);
|
void (* useTitle)(u64 tid, u8 mediatype);
|
||||||
} loaderFuncs_s;
|
} loaderFuncs_s;
|
||||||
|
|
||||||
|
size_t launchAddArg(argData_s* ad, const char* arg);
|
||||||
|
void launchAddArgsFromString(argData_s* ad, char* arg);
|
||||||
|
|
||||||
Handle launchOpenFile(const char* path);
|
Handle launchOpenFile(const char* path);
|
|
@ -65,7 +65,7 @@ static void get_first_valid_core(char* path_return)
|
||||||
{
|
{
|
||||||
DIR* dir;
|
DIR* dir;
|
||||||
struct dirent* ent;
|
struct dirent* ent;
|
||||||
const char* extension = envIsHomebrew() ? "3dsx" : "cia";
|
const char* extension = envIsHomebrew() ? ".3dsx" : ".cia";
|
||||||
|
|
||||||
path_return[0] = '\0';
|
path_return[0] = '\0';
|
||||||
|
|
||||||
|
@ -78,8 +78,8 @@ static void get_first_valid_core(char* path_return)
|
||||||
break;
|
break;
|
||||||
if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension))
|
if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension))
|
||||||
{
|
{
|
||||||
strcpy(path_return, "sdmc:/retroarch/cores");
|
strcpy(path_return, "sdmc:/retroarch/cores");
|
||||||
strcat(path_return, "/");
|
strcat(path_return, "/");
|
||||||
strcat(path_return, ent->d_name);
|
strcat(path_return, ent->d_name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ static void frontend_ctr_exec(const char* path, bool should_load_game)
|
||||||
DEBUG_VAR(path);
|
DEBUG_VAR(path);
|
||||||
DEBUG_STR(path);
|
DEBUG_STR(path);
|
||||||
|
|
||||||
strlcpy(args, elf_path_cst, sizeof(args));
|
strncpy(args, elf_path_cst, sizeof(args));
|
||||||
|
|
||||||
RARCH_LOG("Attempt to load core: [%s].\n", path);
|
RARCH_LOG("Attempt to load core: [%s].\n", path);
|
||||||
#ifndef IS_SALAMANDER
|
#ifndef IS_SALAMANDER
|
||||||
|
@ -205,10 +205,10 @@ static void frontend_ctr_exec(const char* path, bool should_load_game)
|
||||||
struct stat sbuff;
|
struct stat sbuff;
|
||||||
bool file_exists;
|
bool file_exists;
|
||||||
|
|
||||||
fileExists = stat(path, &sBuff) == 0;
|
file_exists = stat(path, &sbuff) == 0;
|
||||||
if (!fileExists)
|
if (!file_exists)
|
||||||
{
|
{
|
||||||
char core_path[512];
|
char core_path[PATH_MAX];
|
||||||
|
|
||||||
/* find first valid core and load it if the target core doesnt exist */
|
/* find first valid core and load it if the target core doesnt exist */
|
||||||
get_first_valid_core(&core_path[0]);
|
get_first_valid_core(&core_path[0]);
|
||||||
|
@ -225,14 +225,23 @@ static void frontend_ctr_exec(const char* path, bool should_load_game)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (envIsHomebrew())
|
if (envIsHomebrew())
|
||||||
error = exec_3dsx(path, args);
|
{
|
||||||
|
exec_3dsx_no_path_in_args(path, args);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
error = exec_cia(path, args);
|
{
|
||||||
|
RARCH_LOG("\n");
|
||||||
|
RARCH_LOG("\n");
|
||||||
|
RARCH_LOG("Warning:\n");
|
||||||
|
RARCH_LOG("First core launch may take 20 seconds!\n");
|
||||||
|
RARCH_LOG("Do not force quit before then or your memory card may be corrupted!\n");
|
||||||
|
RARCH_LOG("\n");
|
||||||
|
RARCH_LOG("\n");
|
||||||
|
exec_cia(path, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);//couldnt launch new core, but context is corrupt so we have to quit
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error)
|
|
||||||
RARCH_LOG("Cant execute new core:%s.\n", strerror(errno));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef IS_SALAMANDER
|
#ifndef IS_SALAMANDER
|
||||||
|
|
Loading…
Reference in New Issue