diff --git a/nx/include/switch/services/applet.h b/nx/include/switch/services/applet.h index 52cde06f..0b16df34 100644 --- a/nx/include/switch/services/applet.h +++ b/nx/include/switch/services/applet.h @@ -139,6 +139,12 @@ typedef struct { u8 unk_x0[0x8]; } AppletApplicationPlayStatistics; +/// Attributes for launching applications for Quest. +typedef struct { + u32 unk_x0; + u32 unk_x4; +} AppletApplicationAttributeForQuest; + /// Initialize applet, called automatically during app startup. Result appletInitialize(void); @@ -166,6 +172,23 @@ AppletThemeColorType appletGetThemeColorType(void); */ Result appletPopLaunchParameter(AppletStorage *s, AppletLaunchParameterKind kind); +/** + * @brief Requests to launch the specified application. + * @note Only available with AppletType_*Application, or AppletType_LibraryApplet on 5.0.0+. + * @param[in] titleID Application titleID. Value 0 can be used to relaunch the current application. + * @param s Optional AppletStorage object, can be NULL. This is automatically closed. When NULL on pre-4.0.0 (or with AppletType_LibraryApplet), this will internally create a tmp storage with size 0 for use with the cmd. This is the storage available to the launched application via \ref appletPopLaunchParameter with ::AppletLaunchParameterKind_Application. + */ +Result appletRequestLaunchApplication(u64 titleID, AppletStorage* s); + +/** + * @brief Requests to launch the specified application, for kiosk systems. + * @note Only available with AppletType_*Application on 3.0.0+. + * @param[in] titleID Application titleID + * @param s Optional AppletStorage object, can be NULL. This is automatically closed. When NULL on pre-4.0.0, this will internally create a tmp storage with size 0 for use with the cmd. This is the storage available to the launched application via \ref appletPopLaunchParameter with ::AppletLaunchParameterKind_Application. + * @param[in] attr Kiosk application attributes. + */ +Result appletRequestLaunchApplicationForQuest(u64 titleID, AppletStorage* s, const AppletApplicationAttributeForQuest *attr); + Result appletGetDesiredLanguage(u64 *LanguageCode); /// Only available with AppletType_*Application. diff --git a/nx/source/services/applet.c b/nx/source/services/applet.c index f498fd36..62b24a9a 100644 --- a/nx/source/services/applet.c +++ b/nx/source/services/applet.c @@ -702,6 +702,40 @@ static Result _appletCmdInU8(Service* srv, u8 inval, u64 cmd_id) { return rc; } +static Result _appletCmdInU64(Service* srv, u64 inval, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u64 inval; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->inval = inval; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + static Result _appletCmdInBool(Service* srv, bool inval, u64 cmd_id) { return _appletCmdInU8(srv, inval!=0, cmd_id); } @@ -863,6 +897,183 @@ Result appletPopLaunchParameter(AppletStorage *s, AppletLaunchParameterKind kind return rc; } +static Result _appletCreateApplicationAndPushAndRequestToStart(Service* srv, u64 cmd_id, u64 titleID, AppletStorage* s) { + IpcCommand c; + ipcInitialize(&c); + + serviceSendObject(&s->s, &c); + + struct { + u64 magic; + u64 cmd_id; + u64 titleID; + } *raw; + + raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->titleID = titleID; + + Result rc = serviceIpcDispatch(srv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(srv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _appletCreateApplicationAndPushAndRequestToStartForQuest(u64 titleID, AppletStorage* s, const AppletApplicationAttributeForQuest *attr) { //2.0.0+ + IpcCommand c; + ipcInitialize(&c); + + serviceSendObject(&s->s, &c); + + struct { + u64 magic; + u64 cmd_id; + u32 val0, val1; + u64 titleID; + } PACKED *raw; + + raw = serviceIpcPrepareHeader(&g_appletIFunctions, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 11; + raw->val0 = attr->unk_x0; + raw->val1 = attr->unk_x4; + raw->titleID = titleID; + + Result rc = serviceIpcDispatch(&g_appletIFunctions); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_appletIFunctions, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _appletCreateApplicationAndRequestToStart(u64 titleID) { //4.0.0+ + return _appletCmdInU64(&g_appletIFunctions, titleID, 12); +} + +static Result _appletCreateApplicationAndRequestToStartForQuest(u64 titleID, const AppletApplicationAttributeForQuest *attr) { //4.0.0+ + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 val0, val1; + u64 titleID; + } PACKED *raw; + + raw = serviceIpcPrepareHeader(&g_appletIFunctions, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 13; + raw->val0 = attr->unk_x0; + raw->val1 = attr->unk_x4; + raw->titleID = titleID; + + Result rc = serviceIpcDispatch(&g_appletIFunctions); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_appletIFunctions, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result appletRequestLaunchApplication(u64 titleID, AppletStorage* s) { + AppletStorage tmpstorage={0}; + Result rc=0; + bool is_libraryapplet = hosversionAtLeast(5,0,0) && __nx_applet_type == AppletType_LibraryApplet; + + if (!serviceIsActive(&g_appletSrv) || (!_appletIsApplication() && !is_libraryapplet)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + if (s && !serviceIsActive(&s->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if ((hosversionBefore(4,0,0) || is_libraryapplet) && s==NULL) { + s = &tmpstorage; + rc = appletCreateStorage(&tmpstorage, 0); + if (R_FAILED(rc)) return rc; + } + + if (is_libraryapplet) { + rc = _appletCreateApplicationAndPushAndRequestToStart(&g_appletILibraryAppletSelfAccessor, 90, titleID, s); + } + else { + if (hosversionAtLeast(4,0,0) && s==NULL) { + rc = _appletCreateApplicationAndRequestToStart(titleID); + } + else { + rc = _appletCreateApplicationAndPushAndRequestToStart(&g_appletIFunctions, 10, titleID, s); + } + } + + if (s) appletStorageClose(s); + + return rc; +} + +Result appletRequestLaunchApplicationForQuest(u64 titleID, AppletStorage* s, const AppletApplicationAttributeForQuest *attr) { + AppletStorage tmpstorage={0}; + Result rc=0; + + if (!serviceIsActive(&g_appletSrv) || !_appletIsApplication()) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + if (s && !serviceIsActive(&s->s)) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (hosversionBefore(4,0,0) && s==NULL) { + s = &tmpstorage; + rc = appletCreateStorage(&tmpstorage, 0); + if (R_FAILED(rc)) return rc; + } + + if (hosversionAtLeast(4,0,0) && s==NULL) { + rc = _appletCreateApplicationAndRequestToStartForQuest(titleID, attr); + } + else { + if (hosversionBefore(3,0,0)) rc = MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + if (R_SUCCEEDED(rc)) rc = _appletCreateApplicationAndPushAndRequestToStartForQuest(titleID, s, attr); + } + + if (s) appletStorageClose(s); + + return rc; +} + Result appletGetDesiredLanguage(u64 *LanguageCode) { if (!serviceIsActive(&g_appletSrv) || !_appletIsApplication()) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);