diff --git a/nx/include/switch/services/applet.h b/nx/include/switch/services/applet.h index dc80e104..62b305ff 100644 --- a/nx/include/switch/services/applet.h +++ b/nx/include/switch/services/applet.h @@ -133,6 +133,11 @@ typedef struct { LibAppletExitReason exitreason; ///< Set by \ref appletHolderJoin using the output from cmd GetResult, see \ref LibAppletExitReason. } AppletHolder; +/// 'pdm' ApplicationPlayStatistics +typedef struct { + u8 unk_x0[0x8]; +} AppletApplicationPlayStatistics; + Result appletInitialize(void); void appletExit(void); Result appletGetAppletResourceUserId(u64 *out); @@ -189,6 +194,17 @@ Result appletBeginBlockingHomeButton(s64 val); Result appletEndBlockingHomeButton(void); +/** + * @brief Gets ApplicationPlayStatistics. + * @note Only available with AppletType_*Application on 5.0.0+. + * @note This may return no output in some cases. + * @param stats Output \ref AppletApplicationPlayStatistics array. + * @param titleIDs Input titleIDs array. + * @param count Total entries in the input/output arrays. + * @param out Output s32. + */ +Result appletQueryApplicationPlayStatistics(AppletApplicationPlayStatistics *stats, u64 *titleIDs, s32 count, s32 *out); + /** * @brief Delay exiting until \ref appletUnlockExit is called, with a 15 second timeout once exit is requested. * @note When exit is requested \ref appletMainLoop will return false, hence any main-loop using appletMainLoop will exit. This allows the app to handle cleanup post-main-loop instead of being force-terminated. diff --git a/nx/source/services/applet.c b/nx/source/services/applet.c index be8c8ac6..6246f228 100644 --- a/nx/source/services/applet.c +++ b/nx/source/services/applet.c @@ -531,7 +531,7 @@ static Result _appletGetSessionProxy(Service* srv_out, u64 cmd_id, Handle procha ipcSendPid(&c); ipcSendHandleCopy(&c, prochandle); - if (AppletAttribute) ipcAddSendBuffer(&c, AppletAttribute, 0x80, 0); + if (AppletAttribute) ipcAddSendBuffer(&c, AppletAttribute, 0x80, BufferType_Normal); raw = serviceIpcPrepareHeader(&g_appletSrv, &c, sizeof(*raw)); @@ -1122,6 +1122,51 @@ Result appletInitializeGamePlayRecording(void) { return rc; } +//Official sw has this under 'pdm'. +Result appletQueryApplicationPlayStatistics(AppletApplicationPlayStatistics *stats, u64 *titleIDs, s32 count, s32 *out) { + IpcCommand c; + ipcInitialize(&c); + + if (!serviceIsActive(&g_appletSrv) || !_appletIsRegularApplication()) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + if (!kernelAbove500()) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + ipcAddRecvBuffer(&c, stats, count*sizeof(AppletApplicationPlayStatistics), BufferType_Normal); + ipcAddSendBuffer(&c, titleIDs, count*sizeof(u64), BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_appletIFunctions, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 110; + + Result rc = serviceIpcDispatch(&g_appletIFunctions); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + s32 out; + } *resp; + + serviceIpcParse(&g_appletIFunctions, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) *out = resp->out; + } + + return rc; +} + // ICommonStateGetter static Result _appletReceiveMessage(u32 *out) {