rosalina: implement dirty hb chainload
This commit is contained in:
parent
04bd881cfa
commit
d3e62df769
@ -129,16 +129,21 @@ Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_Syste
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid)
|
Result GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags)
|
||||||
{
|
{
|
||||||
ProcessList_Lock(&g_manager.processList);
|
ProcessList_Lock(&g_manager.processList);
|
||||||
Result res;
|
Result res;
|
||||||
|
|
||||||
|
memset(outProgramInfo, 0, sizeof(FS_ProgramInfo));
|
||||||
if (g_manager.runningApplicationData != NULL) {
|
if (g_manager.runningApplicationData != NULL) {
|
||||||
*outTitleId = g_manager.runningApplicationData->titleId;
|
ProcessData *app = g_manager.runningApplicationData;
|
||||||
*outPid = g_manager.runningApplicationData->pid;
|
outProgramInfo->programId = app->titleId;
|
||||||
|
outProgramInfo->mediaType = app->mediaType;
|
||||||
|
*outPid = app->pid;
|
||||||
|
*outLaunchFlags = app->launchFlags;
|
||||||
res = 0;
|
res = 0;
|
||||||
} else {
|
} else {
|
||||||
*outTitleId = 0;
|
*outPid = 0;
|
||||||
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
|
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
|
||||||
}
|
}
|
||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
|
@ -12,4 +12,4 @@ Result listMergeUniqueDependencies(ProcessData **procs, u64 *dependencies, u32 *
|
|||||||
Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_SystemInfoFlags *outSiFlags, const FS_ProgramInfo *programInfo);
|
Result GetTitleExHeaderFlags(ExHeader_Arm11CoreInfo *outCoreInfo, ExHeader_SystemInfoFlags *outSiFlags, const FS_ProgramInfo *programInfo);
|
||||||
|
|
||||||
// Custom
|
// Custom
|
||||||
Result GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid);
|
Result GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
|
||||||
|
@ -56,10 +56,12 @@ static Result loadWithoutDependencies(Handle *outDebug, ProcessData **outProcess
|
|||||||
process->pid = pid;
|
process->pid = pid;
|
||||||
process->titleId = exheaderInfo->aci.local_caps.title_id;;
|
process->titleId = exheaderInfo->aci.local_caps.title_id;;
|
||||||
process->programHandle = programHandle;
|
process->programHandle = programHandle;
|
||||||
|
process->launchFlags = launchFlags; // not in official PM
|
||||||
process->flags = 0; // will be filled later
|
process->flags = 0; // will be filled later
|
||||||
process->terminatedNotificationVariation = (launchFlags & 0xF0) >> 4;
|
process->terminatedNotificationVariation = (launchFlags & 0xF0) >> 4;
|
||||||
process->terminationStatus = TERMSTATUS_RUNNING;
|
process->terminationStatus = TERMSTATUS_RUNNING;
|
||||||
process->refcount = 1;
|
process->refcount = 1;
|
||||||
|
process->mediaType = programInfo->mediaType; // not in official PM
|
||||||
|
|
||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
svcSignalEvent(g_manager.newProcessEvent);
|
svcSignalEvent(g_manager.newProcessEvent);
|
||||||
@ -135,6 +137,11 @@ static Result loadWithDependencies(Handle *outDebug, ProcessData **outProcessDat
|
|||||||
process->flags |= PROCESSFLAG_DEPENDENCIES_LOADED;
|
process->flags |= PROCESSFLAG_DEPENDENCIES_LOADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (launchFlags & PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING) {
|
||||||
|
// See no evil
|
||||||
|
numUnique = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Official pm does this:
|
Official pm does this:
|
||||||
for each dependency:
|
for each dependency:
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
#include <3ds/services/fs.h>
|
#include <3ds/services/fs.h>
|
||||||
#include "process_data.h"
|
#include "process_data.h"
|
||||||
|
|
||||||
|
/// Custom launch flags for PM launch commands.
|
||||||
|
enum {
|
||||||
|
PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING = BIT(24),
|
||||||
|
};
|
||||||
|
|
||||||
Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);
|
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);
|
||||||
Result LaunchApp(const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result LaunchApp(const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
|
@ -52,7 +52,7 @@ void initSystem()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "pm:app", 3, pmAppHandleCommands, false },
|
{ "pm:app", 4, pmAppHandleCommands, false },
|
||||||
{ "pm:dbg", 2, pmDbgHandleCommands, false },
|
{ "pm:dbg", 2, pmDbgHandleCommands, false },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
@ -69,3 +69,27 @@ Result UnregisterProcess(u64 titleId)
|
|||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result PrepareToChainloadHomebrew(u64 titleId)
|
||||||
|
{
|
||||||
|
// Note: I'm allowing this command to be called for non-applications, maybe that'll be useful
|
||||||
|
// in the future...
|
||||||
|
|
||||||
|
ProcessData *foundProcess = NULL;
|
||||||
|
Result res;
|
||||||
|
ProcessList_Lock(&g_manager.processList);
|
||||||
|
foundProcess = ProcessList_FindProcessByTitleId(&g_manager.processList, titleId & ~N3DS_TID_MASK);
|
||||||
|
if (foundProcess != NULL) {
|
||||||
|
// Clear the "notify on termination, don't cleanup" flag, so that for ex. APT isn't notified & no need for UnregisterProcess,
|
||||||
|
// and the "dependencies loaded" flag, so that the dependencies aren't killed (for ex. when
|
||||||
|
// booting hbmenu instead of Home Menu, in which case the same title is going to be launched...)
|
||||||
|
|
||||||
|
foundProcess->flags &= ~(PROCESSFLAG_DEPENDENCIES_LOADED | PROCESSFLAG_NOTIFY_TERMINATION);
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
|
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
@ -21,3 +21,4 @@ extern Manager g_manager;
|
|||||||
void Manager_Init(void *procBuf, size_t numProc);
|
void Manager_Init(void *procBuf, size_t numProc);
|
||||||
void Manager_RegisterKips(void);
|
void Manager_RegisterKips(void);
|
||||||
Result UnregisterProcess(u64 titleId);
|
Result UnregisterProcess(u64 titleId);
|
||||||
|
Result PrepareToChainloadHomebrew(u64 titleId);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "launch.h"
|
#include "launch.h"
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
void pmDbgHandleCommands(void *ctx)
|
void pmDbgHandleCommands(void *ctx)
|
||||||
{
|
{
|
||||||
@ -11,10 +12,10 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
u32 cmdhdr = cmdbuf[0];
|
u32 cmdhdr = cmdbuf[0];
|
||||||
|
|
||||||
FS_ProgramInfo programInfo;
|
FS_ProgramInfo programInfo;
|
||||||
Handle debug;
|
|
||||||
|
|
||||||
u64 titleId;
|
u64 titleId;
|
||||||
|
Handle debug;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
|
u32 launchFlags;
|
||||||
|
|
||||||
switch (cmdhdr >> 16) {
|
switch (cmdhdr >> 16) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -40,12 +41,11 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
|
|
||||||
// Custom
|
// Custom
|
||||||
case 0x100:
|
case 0x100:
|
||||||
titleId = 0;
|
cmdbuf[1] = GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
|
||||||
pid = 0xFFFFFFFF;
|
cmdbuf[0] = IPC_MakeHeader(0x100, 7, 0);
|
||||||
cmdbuf[1] = GetCurrentAppTitleIdAndPid(&titleId, &pid);
|
memcpy(cmdbuf + 2, &programInfo, sizeof(FS_ProgramInfo));
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x100, 4, 0);
|
cmdbuf[6] = pid;
|
||||||
memcpy(cmdbuf + 2, &titleId, 8);
|
cmdbuf[7] = launchFlags;
|
||||||
cmdbuf[4] = pid;
|
|
||||||
break;
|
break;
|
||||||
case 0x101:
|
case 0x101:
|
||||||
cmdbuf[1] = DebugNextApplicationByForce(cmdbuf[1] != 0);
|
cmdbuf[1] = DebugNextApplicationByForce(cmdbuf[1] != 0);
|
||||||
@ -59,7 +59,11 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
||||||
cmdbuf[3] = debug;
|
cmdbuf[3] = debug;
|
||||||
break;
|
break;
|
||||||
|
case 0x103:
|
||||||
|
memcpy(&titleId, cmdbuf + 1, 8);
|
||||||
|
cmdbuf[1] = PrepareToChainloadHomebrew(titleId);
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x103, 1, 0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||||
cmdbuf[1] = 0xD900182F;
|
cmdbuf[1] = 0xD900182F;
|
||||||
|
@ -29,10 +29,12 @@ typedef struct ProcessData {
|
|||||||
u32 pid;
|
u32 pid;
|
||||||
u64 titleId;
|
u64 titleId;
|
||||||
u64 programHandle;
|
u64 programHandle;
|
||||||
|
u32 launchFlags;
|
||||||
u8 flags;
|
u8 flags;
|
||||||
u8 terminatedNotificationVariation;
|
u8 terminatedNotificationVariation;
|
||||||
TerminationStatus terminationStatus;
|
TerminationStatus terminationStatus;
|
||||||
u8 refcount;
|
u8 refcount;
|
||||||
|
FS_MediaType mediaType;
|
||||||
} ProcessData;
|
} ProcessData;
|
||||||
|
|
||||||
typedef struct ProcessList {
|
typedef struct ProcessList {
|
||||||
|
@ -33,4 +33,5 @@
|
|||||||
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
||||||
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
||||||
|
|
||||||
|
void HBLDR_RestartHbApplication(void *p);
|
||||||
void HBLDR_HandleCommands(void *ctx);
|
void HBLDR_HandleCommands(void *ctx);
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
#include <3ds/services/pmapp.h>
|
#include <3ds/services/pmapp.h>
|
||||||
#include <3ds/services/pmdbg.h>
|
#include <3ds/services/pmdbg.h>
|
||||||
|
|
||||||
Result PMDBG_GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid);
|
/// Custom launch flags for PM launch commands.
|
||||||
|
enum {
|
||||||
|
PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING = BIT(24),
|
||||||
|
};
|
||||||
|
|
||||||
|
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
|
||||||
Result PMDBG_DebugNextApplicationByForce(bool debug);
|
Result PMDBG_DebugNextApplicationByForce(bool debug);
|
||||||
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
|
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId);
|
||||||
|
@ -178,6 +178,31 @@ static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HBLDR_RestartHbApplication(void *p)
|
||||||
|
{
|
||||||
|
(void)p;
|
||||||
|
// Don't crash if we fail
|
||||||
|
|
||||||
|
FS_ProgramInfo programInfo;
|
||||||
|
u32 pid;
|
||||||
|
u32 launchFlags;
|
||||||
|
|
||||||
|
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
|
||||||
|
if (R_FAILED(res)) return;
|
||||||
|
res = PMDBG_PrepareToChainloadHomebrew(programInfo.programId);
|
||||||
|
if (R_FAILED(res)) return;
|
||||||
|
res = PMAPP_TerminateCurrentApplication(3 * 1000 * 1000 *1000LL); // 3s, like what NS uses
|
||||||
|
if (R_FAILED(res)) return;
|
||||||
|
if (R_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
svcSleepThread(100 * 1000 * 1000LL);
|
||||||
|
res = PMAPP_LaunchTitle(&programInfo, PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING | launchFlags);
|
||||||
|
} while (res == (Result)0xC8A05BF0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HBLDR_HandleCommands(void *ctx)
|
void HBLDR_HandleCommands(void *ctx)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
@ -135,7 +135,7 @@ void initSystem(void)
|
|||||||
if (R_FAILED(stealFsReg()) || R_FAILED(fsRegSetupPermissions()) || R_FAILED(fsInit()))
|
if (R_FAILED(stealFsReg()) || R_FAILED(fsRegSetupPermissions()) || R_FAILED(fsInit()))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
if (R_FAILED(pmDbgInit()))
|
if (R_FAILED(pmAppInit()) || R_FAILED(pmDbgInit()))
|
||||||
svcBreak(USERBREAK_PANIC);
|
svcBreak(USERBREAK_PANIC);
|
||||||
|
|
||||||
// **** DO NOT init services that don't come from KIPs here ****
|
// **** DO NOT init services that don't come from KIPs here ****
|
||||||
@ -192,6 +192,12 @@ static void handleNextApplicationDebuggedByForce(u32 notificationId)
|
|||||||
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
|
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handleRestartHbAppNotification(u32 notificationId)
|
||||||
|
{
|
||||||
|
(void)notificationId;
|
||||||
|
TaskRunner_RunTask(HBLDR_RestartHbApplication, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
@ -202,6 +208,7 @@ static const ServiceManagerNotificationEntry notifications[] = {
|
|||||||
//{ 0x103 , relinquishConnectionSessions }, // Sleep mode entry <=== causes issues
|
//{ 0x103 , relinquishConnectionSessions }, // Sleep mode entry <=== causes issues
|
||||||
{ 0x1000, handleNextApplicationDebuggedByForce },
|
{ 0x1000, handleNextApplicationDebuggedByForce },
|
||||||
{ 0x2000, relinquishConnectionSessions },
|
{ 0x2000, relinquishConnectionSessions },
|
||||||
|
{ 0x3000, handleRestartHbAppNotification },
|
||||||
{ 0x000, NULL },
|
{ 0x000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1860,12 +1860,16 @@ static void Cheat_LoadCheatsIntoMemory(u64 titleId)
|
|||||||
|
|
||||||
static u32 Cheat_GetCurrentProcessAndTitleId(u64* titleId)
|
static u32 Cheat_GetCurrentProcessAndTitleId(u64* titleId)
|
||||||
{
|
{
|
||||||
|
FS_ProgramInfo programInfo;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
Result res = PMDBG_GetCurrentAppTitleIdAndPid(titleId, &pid);
|
u32 launchFlags;
|
||||||
|
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
*titleId = 0;
|
*titleId = 0;
|
||||||
return 0xFFFFFFFF;
|
return 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*titleId = programInfo.programId;
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,16 +52,17 @@ Menu miscellaneousMenu = {
|
|||||||
void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void)
|
||||||
{
|
{
|
||||||
Result res;
|
Result res;
|
||||||
u64 titleId = 0;
|
|
||||||
char failureReason[64];
|
char failureReason[64];
|
||||||
|
|
||||||
if(HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID)
|
if(HBLDR_3DSX_TID == HBLDR_DEFAULT_3DSX_TID)
|
||||||
{
|
{
|
||||||
|
FS_ProgramInfo progInfo;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
res = PMDBG_GetCurrentAppTitleIdAndPid(&titleId, &pid);
|
u32 launchFlags;
|
||||||
|
res = PMDBG_GetCurrentAppInfo(&progInfo, &pid, &launchFlags);
|
||||||
if(R_SUCCEEDED(res))
|
if(R_SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
HBLDR_3DSX_TID = titleId;
|
HBLDR_3DSX_TID = progInfo.programId;
|
||||||
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
|
miscellaneousMenu.items[0].title = "Switch the hb. title to hblauncher_loader";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -10,15 +10,16 @@
|
|||||||
#include <3ds/services/pmdbg.h>
|
#include <3ds/services/pmdbg.h>
|
||||||
#include <3ds/ipc.h>
|
#include <3ds/ipc.h>
|
||||||
|
|
||||||
Result PMDBG_GetCurrentAppTitleIdAndPid(u64 *outTitleId, u32 *outPid)
|
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags)
|
||||||
{
|
{
|
||||||
Result ret = 0;
|
Result ret = 0;
|
||||||
u32 *cmdbuf = getThreadCommandBuffer();
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x100, 0, 0);
|
cmdbuf[0] = IPC_MakeHeader(0x100, 0, 0);
|
||||||
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
|
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
|
||||||
|
|
||||||
memcpy(outTitleId, cmdbuf + 2, 8);
|
memcpy(outProgramInfo, cmdbuf + 2, sizeof(FS_ProgramInfo));
|
||||||
*outPid = cmdbuf[4];
|
*outPid = cmdbuf[6];
|
||||||
|
*outLaunchFlags = cmdbuf[7];
|
||||||
return cmdbuf[1];
|
return cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,3 +48,16 @@ Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInf
|
|||||||
*outDebug = cmdbuf[3];
|
*outDebug = cmdbuf[3];
|
||||||
return (Result)cmdbuf[1];
|
return (Result)cmdbuf[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId)
|
||||||
|
{
|
||||||
|
Result ret = 0;
|
||||||
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
|
|
||||||
|
cmdbuf[0] = IPC_MakeHeader(0x103, 2, 0);
|
||||||
|
memcpy(&cmdbuf[1], &titleId, 8);
|
||||||
|
|
||||||
|
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
|
||||||
|
|
||||||
|
return (Result)cmdbuf[1];
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user