From c3e110988a16f6f16ff6067dafe2fc03289bc90b Mon Sep 17 00:00:00 2001 From: yellows8 Date: Wed, 8 Mar 2023 16:31:44 -0500 Subject: [PATCH] pdm: Updated/fixed sysver compat, updated structs. --- nx/include/switch/services/pdm.h | 130 +++++++++++++++++------- nx/source/services/pdm.c | 165 ++++++++++++++++++++++++++++--- 2 files changed, 243 insertions(+), 52 deletions(-) diff --git a/nx/include/switch/services/pdm.h b/nx/include/switch/services/pdm.h index f49b38f0..d5f8442b 100644 --- a/nx/include/switch/services/pdm.h +++ b/nx/include/switch/services/pdm.h @@ -38,41 +38,71 @@ typedef enum { PdmPlayLogPolicy_Unknown3 = 3, ///< [10.0.0+] The cmds which require ::PdmPlayLogPolicy_All, now also allow value 3 if the cmd input flag is set. } PdmPlayLogPolicy; -/// AppletEvent. +/// AppletEventV1. AppletEvent for [1.0.0-15.0.1], converted to \ref PdmAppletEvent when needed. /// Timestamp format, converted from PosixTime: total minutes since epoch UTC 1999/12/31 00:00:00. /// See \ref pdmPlayTimestampToPosix. typedef struct { u64 program_id; ///< ProgramId. u32 entry_index; ///< Entry index. - u32 timestampUser; ///< See PdmPlayEvent::timestampUser, with the above timestamp format. - u32 timestampNetwork; ///< See PdmPlayEvent::timestampNetwork, with the above timestamp format. - u8 eventType; ///< \ref PdmAppletEventType + u32 timestamp_user; ///< See PdmPlayEvent::timestamp_user, with the above timestamp format. + u32 timestamp_network; ///< See PdmPlayEvent::timestamp_network, with the above timestamp format. + u8 event_type; ///< \ref PdmAppletEventType u8 pad[3]; ///< Padding. +} PdmAppletEventV1; + +/// AppletEvent. AppletEvent for [16.0.0+], converted from \ref PdmAppletEventV1 on [1.0.0-15.0.1]. +typedef struct { + u64 program_id; ///< ProgramId. + u32 entry_index; ///< Entry index. + u32 pad; ///< Padding + u64 timestamp_user; ///< See PdmPlayEvent::timestamp_user. + u64 timestamp_network; ///< See PdmPlayEvent::timestamp_network. + u8 event_type; ///< \ref PdmAppletEventType + u8 pad2[7]; ///< Padding. } PdmAppletEvent; -/// PlayStatistics +/// PlayStatisticsV1. PlayStatistics for [1.0.0-15.0.1], converted to \ref PdmPlayStatistics when needed. typedef struct { - u64 application_id; ///< ApplicationId. + u64 program_id; ///< ProgramId. - u32 first_entry_index; ///< Entry index for the first time the application was played. - u32 first_timestampUser; ///< See PdmAppletEvent::timestampUser. This is for the first time the application was played. - u32 first_timestampNetwork; ///< See PdmAppletEvent::timestampNetwork. This is for the first time the application was played. + u32 first_entry_index; ///< Entry index for the first time the program was played. + u32 first_timestamp_user; ///< See PdmAppletEventV1::timestamp_user. This is for the first time the program was played. + u32 first_timestamp_network; ///< See PdmAppletEventV1::timestamp_network. This is for the first time the program was played. - u32 last_entry_index; ///< Entry index for the last time the application was played. - u32 last_timestampUser; ///< See PdmAppletEvent::timestampUser. This is for the last time the application was played. - u32 last_timestampNetwork; ///< See PdmAppletEvent::timestampNetwork. This is for the last time the application was played. + u32 last_entry_index; ///< Entry index for the last time the program was played. + u32 last_timestamp_user; ///< See PdmAppletEventV1::timestamp_user. This is for the last time the program was played. + u32 last_timestamp_network; ///< See PdmAppletEventV1::timestamp_network. This is for the last time the program was played. - u32 playtimeMinutes; ///< Total play-time in minutes. - u32 totalLaunches; ///< Total times the application was launched. + u32 playtime_minutes; ///< Total play-time in minutes. + u32 total_launches; ///< Total times the program was launched. +} PdmPlayStatisticsV1; + +/// PlayStatistics. PlayStatistics for [16.0.0+], converted from \ref PdmPlayStatisticsV1 on [1.0.0-15.0.1]. +typedef struct { + u64 program_id; ///< ProgramId. + + u32 first_entry_index; ///< Entry index for the first time the program was played. + u32 pad; ///< Padding + u64 first_timestamp_user; ///< See PdmAppletEvent::timestamp_user. This is for the first time the program was played, in PosixTime. + u64 first_timestamp_network; ///< See PdmAppletEvent::timestamp_network. This is for the first time the program was played, in PosixTime. + + u32 last_entry_index; ///< Entry index for the last time the program was played. + u32 pad2; ///< Padding + u64 last_timestamp_user; ///< See PdmAppletEvent::timestamp_user. This is for the last time the program was played, in PosixTime. + u64 last_timestamp_network; ///< See PdmAppletEvent::timestamp_network. This is for the last time the program was played, in PosixTime. + + u64 playtime; ///< Total play-time in nanoseconds. + u32 total_launches; ///< Total times the program was launched. + u32 pad3; ///< Padding } PdmPlayStatistics; /// LastPlayTime. /// This contains data from the last time the application was played. typedef struct { u64 application_id; ///< ApplicationId. - u32 timestampUser; ///< See PdmAppletEvent::timestampUser. - u32 timestampNetwork; ///< See PdmAppletEvent::timestampNetwork. - u32 lastPlayedMinutes; ///< Total minutes since the application was last played. + u32 timestamp_user; ///< See PdmAppletEventV1::timestamp_user. + u32 timestamp_network; ///< See PdmAppletEventV1::timestamp_network. + u32 last_played_minutes; ///< Total minutes since the application was last played. u8 flag; ///< Flag indicating whether the above field is set. u8 pad[3]; ///< Padding. } PdmLastPlayTime; @@ -98,10 +128,10 @@ typedef struct { u32 data; } unk_x8; - u8 appletId; ///< \ref AppletId - u8 storageId; ///< \ref NcmStorageId - u8 logPolicy; ///< \ref PdmPlayLogPolicy - u8 eventType; ///< \ref PdmAppletEventType + u8 applet_id; ///< \ref AppletId + u8 storage_id; ///< \ref NcmStorageId + u8 log_policy; ///< \ref PdmPlayLogPolicy + u8 event_type; ///< \ref PdmAppletEventType u8 unused[0xc]; ///< Unused. } applet; @@ -114,34 +144,59 @@ typedef struct { struct { u8 value; ///< Input value from the pdm:ntfy command. u8 unused[0x1b]; ///< Unused. - } powerStateChange; + } power_state_change; struct { u8 value; ///< Input value from the pdm:ntfy command. u8 unused[0x1b]; ///< Unused. - } operationModeChange; + } operation_mode_change; u8 data[0x1c]; - } eventData; ///< ProgramId/ApplicationId/userId stored within here have the u32 low/high swapped in each u64. + } event_data; ///< ProgramId/ApplicationId/userId stored within here have the u32 low/high swapped in each u64. - u8 playEventType; ///< \ref PdmPlayEventType. Controls which struct in the above eventData is used. ::PdmPlayEventType_Initialize doesn't use eventData. + u8 play_event_type; ///< \ref PdmPlayEventType. Controls which struct in the above event_data is used. ::PdmPlayEventType_Initialize doesn't use event_data. u8 pad[3]; ///< Padding. - u64 timestampUser; ///< PosixTime timestamp from StandardUserSystemClock. - u64 timestampNetwork; ///< PosixTime timestamp from StandardNetworkSystemClock. - u64 timestampSteady; ///< Timestamp in seconds derived from StandardSteadyClock. + u64 timestamp_user; ///< PosixTime timestamp from StandardUserSystemClock. + u64 timestamp_network; ///< PosixTime timestamp from StandardNetworkSystemClock. + u64 timestamp_steady; ///< Timestamp in seconds derived from StandardSteadyClock. } PdmPlayEvent; -/// AccountEvent +/// AccountEventV3. AccountEvent for [3.0.0-9.2.0], converted to \ref PdmAccountEvent when needed. typedef struct { AccountUid uid; ///< \ref AccountUid u32 entry_index; ///< Entry index. u8 pad[4]; ///< Padding. - u64 timestampUser; ///< See PdmPlayEvent::timestampUser. - u64 timestampNetwork; ///< See PdmPlayEvent::timestampNetwork. - u64 timestampSteady; ///< See PdmPlayEvent::timestampSteady. - u8 type; ///< See PdmPlayEvent::eventData::account::type. - u8 pad_x31[7]; ///< Padding. + u64 timestamp_user; ///< See PdmPlayEvent::timestamp_user. + u64 timestamp_network; ///< See PdmPlayEvent::timestamp_network. + u64 timestamp_steady; ///< See PdmPlayEvent::timestamp_steady. + u8 type; ///< See PdmPlayEvent::event_data::account::type. + u8 pad2[7]; ///< Padding. +} PdmAccountEventV3; + +/// AccountEventV10. AccountEvent for [10.0.0-15.0.1], converted to \ref PdmAccountEvent when needed. +typedef struct { + AccountUid uid; ///< \ref AccountUid + u64 program_id; ///< ProgramId + u32 entry_index; ///< Entry index. + u8 pad[4]; ///< Padding. + u64 timestamp_user; ///< See PdmPlayEvent::timestamp_user. + u64 timestamp_network; ///< See PdmPlayEvent::timestamp_network. + u64 timestamp_steady; ///< See PdmPlayEvent::timestamp_steady. + u8 type; ///< See PdmPlayEvent::event_data::account::type. + u8 pad2[7]; ///< Padding. +} PdmAccountEventV10; + +/// AccountEvent. AccountEvent for [16.0.0+], converted from the older structs when needed. +typedef struct { + AccountUid uid; ///< \ref AccountUid + u64 program_id; ///< [10.0.0+] ProgramId + u32 entry_index; ///< Entry index. + u8 pad[4]; ///< Padding. + u64 timestamp_user; ///< See PdmPlayEvent::timestamp_user. + u64 timestamp_network; ///< See PdmPlayEvent::timestamp_network. + u8 type; ///< See PdmPlayEvent::event_data::account::type. + u8 pad2[7]; ///< Padding. } PdmAccountEvent; /// AccountPlayEvent. @@ -157,8 +212,8 @@ typedef struct { /// ApplicationPlayStatistics typedef struct { u64 application_id; ///< ApplicationId. - u64 totalPlayTime; ///< Total play-time in nanoseconds. - u64 totalLaunches; ///< Total times the application was launched. + u64 playtime; ///< Total play-time in nanoseconds. + u64 total_launches; ///< Total times the application was launched. } PdmApplicationPlayStatistics; /// Initialize pdm:qry. @@ -226,6 +281,7 @@ Result pdmqryGetAvailablePlayEventRange(s32 *total_entries, s32 *start_entry_ind /** * @brief Gets a list of \ref PdmAccountEvent. + * @note Only available with [3.0.0+]. * @param[in] entry_index Start entry index. * @param[out] events Output \ref PdmAccountEvent array. * @param[in] count Max entries in the output array. @@ -265,7 +321,7 @@ Result pdmqryGetAvailableAccountPlayEventRange(AccountUid uid, s32 *total_entrie Result pdmqryQueryRecentlyPlayedApplication(AccountUid uid, bool flag, u64 *application_ids, s32 count, s32 *total_out); /** - * @brief Gets an Event which is signaled when logging a new \ref PdmPlayEvent which would be available via \ref pdmqryQueryAccountEvent, where PdmPlayEvent::eventData::account::type is 0. + * @brief Gets an Event which is signaled when logging a new \ref PdmPlayEvent which would be available via \ref pdmqryQueryAccountEvent, where PdmPlayEvent::event_data::account::type is 0. * @note Only available with [6.0.0-14.1.2]. * @note The Event must be closed by the user once finished with it. * @param[out] out_event Output Event with autoclear=false. diff --git a/nx/source/services/pdm.c b/nx/source/services/pdm.c index 00b1db94..673ad63c 100644 --- a/nx/source/services/pdm.c +++ b/nx/source/services/pdm.c @@ -20,6 +20,60 @@ Service* pdmqryGetServiceSession(void) { return &g_pdmqrySrv; } +static void _pdmConvertAppletEventFromV1(const PdmAppletEventV1 *in, PdmAppletEvent *out) { + memset(out, 0, sizeof(*out)); + + out->program_id = in->program_id; + + out->entry_index = in->entry_index; + out->timestamp_user = pdmPlayTimestampToPosix(in->timestamp_user); + out->timestamp_network = pdmPlayTimestampToPosix(in->timestamp_network); + + out->event_type = in->event_type; +} + +static void _pdmConvertPlayStatisticsFromV1(const PdmPlayStatisticsV1 *in, PdmPlayStatistics *out) { + memset(out, 0, sizeof(*out)); + + out->program_id = in->program_id; + + out->first_entry_index = in->first_entry_index; + out->first_timestamp_user = pdmPlayTimestampToPosix(in->first_timestamp_user); + out->first_timestamp_network = pdmPlayTimestampToPosix(in->first_timestamp_network); + + out->last_entry_index = in->last_entry_index; + out->last_timestamp_user = pdmPlayTimestampToPosix(in->last_timestamp_user); + out->last_timestamp_network = pdmPlayTimestampToPosix(in->last_timestamp_network); + + out->playtime = ((u64)in->playtime_minutes) * 60 * 1000000000UL; + out->total_launches = in->total_launches; +} + +static void _pdmConvertAccountEventFromV3(const PdmAccountEventV3 *in, PdmAccountEvent *out) { + memset(out, 0, sizeof(*out)); + + out->uid = in->uid; + + out->entry_index = in->entry_index; + out->timestamp_user = in->timestamp_user; + out->timestamp_network = in->timestamp_network; + + out->type = in->type; +} + +static void _pdmConvertAccountEventFromV10(const PdmAccountEventV10 *in, PdmAccountEvent *out) { + memset(out, 0, sizeof(*out)); + + out->uid = in->uid; + out->program_id = in->program_id; + + out->entry_index = in->entry_index; + out->timestamp_user = in->timestamp_user; + out->timestamp_network = in->timestamp_network; + + out->type = in->type; +} + static Result _pdmCmdGetEvent(Service* srv, Event* out_event, bool autoclear, u32 cmd_id) { Handle event = INVALID_HANDLE; Result rc = serviceDispatch(srv, cmd_id, @@ -41,28 +95,49 @@ static Result _pdmqryQueryEvent(s32 entry_index, void* events, size_t entrysize, } Result pdmqryQueryAppletEvent(s32 entry_index, bool flag, PdmAppletEvent *events, s32 count, s32 *total_out) { + Result rc=0; + if (hosversionBefore(10,0,0)) { - return serviceDispatchInOut(&g_pdmqrySrv, 0, entry_index, *total_out, + rc = serviceDispatchInOut(&g_pdmqrySrv, 0, entry_index, *total_out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, .buffers = { { events, count*sizeof(PdmAppletEvent) } }, ); } + else { + const struct { + u8 flag; + u8 pad[3]; + s32 entry_index; + } in = { flag!=0, {0}, entry_index }; - const struct { - u8 flag; - u8 pad[3]; - s32 entry_index; - } in = { flag!=0, {0}, entry_index }; + size_t entrysize = hosversionBefore(16,0,0) ? sizeof(PdmAppletEventV1) : sizeof(PdmAppletEvent); + rc = serviceDispatchInOut(&g_pdmqrySrv, 0, in, *total_out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { events, count*entrysize } }, + ); + } - return serviceDispatchInOut(&g_pdmqrySrv, 0, in, *total_out, - .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, - .buffers = { { events, count*sizeof(PdmAppletEvent) } }, - ); + if (R_SUCCEEDED(rc) && hosversionBefore(16,0,0)) { + s32 end = count; + if (total_out && *total_out < end) end = *total_out; + PdmAppletEventV1 *v1_in = (PdmAppletEventV1*)events; + for (s32 i=end-1; i>=0; i--) { // V1 is smaller than latest, so loop backwards. + PdmAppletEventV1 tmp = v1_in[i]; + _pdmConvertAppletEventFromV1(&tmp, &events[i]); + } + } + + return rc; } Result pdmqryQueryPlayStatisticsByApplicationId(u64 application_id, bool flag, PdmPlayStatistics *stats) { + PdmPlayStatisticsV1 tmp={}; + Result rc=0; + if (hosversionBefore(10,0,0)) { - return serviceDispatchInOut(&g_pdmqrySrv, 4, application_id, *stats); + rc = serviceDispatchInOut(&g_pdmqrySrv, 4, application_id, tmp); + if (R_SUCCEEDED(rc)) _pdmConvertPlayStatisticsFromV1(&tmp, stats); + return rc; } const struct { @@ -71,17 +146,28 @@ Result pdmqryQueryPlayStatisticsByApplicationId(u64 application_id, bool flag, P u64 application_id; } in = { flag!=0, {0}, application_id }; - return serviceDispatchInOut(&g_pdmqrySrv, 4, in, *stats); + if (hosversionBefore(16,0,0)) { + Result rc = serviceDispatchInOut(&g_pdmqrySrv, 4, in, tmp); + if (R_SUCCEEDED(rc)) _pdmConvertPlayStatisticsFromV1(&tmp, stats); + } + else + rc = serviceDispatchInOut(&g_pdmqrySrv, 4, in, *stats); + return rc; } Result pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(u64 application_id, AccountUid uid, bool flag, PdmPlayStatistics *stats) { + PdmPlayStatisticsV1 tmp={}; + Result rc=0; + if (hosversionBefore(10,0,0)) { const struct { u64 application_id; AccountUid uid; } in = { application_id, uid }; - return serviceDispatchInOut(&g_pdmqrySrv, 5, in, *stats); + rc = serviceDispatchInOut(&g_pdmqrySrv, 5, in, tmp); + if (R_SUCCEEDED(rc)) _pdmConvertPlayStatisticsFromV1(&tmp, stats); + return rc; } const struct { @@ -91,7 +177,13 @@ Result pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(u64 application_ AccountUid uid; } in = { flag!=0, {0}, application_id, uid }; - return serviceDispatchInOut(&g_pdmqrySrv, 5, in, *stats); + if (hosversionBefore(16,0,0)) { + Result rc = serviceDispatchInOut(&g_pdmqrySrv, 5, in, tmp); + if (R_SUCCEEDED(rc)) _pdmConvertPlayStatisticsFromV1(&tmp, stats); + } + else + rc = serviceDispatchInOut(&g_pdmqrySrv, 5, in, *stats); + return rc; } Result pdmqryQueryLastPlayTime(bool flag, PdmLastPlayTime *playtimes, const u64 *application_ids, s32 count, s32 *total_out) { @@ -142,7 +234,50 @@ Result pdmqryGetAvailablePlayEventRange(s32 *total_entries, s32 *start_entry_ind } Result pdmqryQueryAccountEvent(s32 entry_index, PdmAccountEvent *events, s32 count, s32 *total_out) { - return _pdmqryQueryEvent(entry_index, events, sizeof(PdmAccountEvent), count, total_out, 10); + if (hosversionBefore(3,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + size_t entrysize = sizeof(PdmAccountEventV3); + u32 ver=0; + if (hosversionBetween(10, 16)) { + entrysize = sizeof(PdmAccountEventV10); + ver=1; + } + else if (hosversionAtLeast(16,0,0)) { + entrysize = sizeof(PdmAccountEvent); + ver=2; + } + + Result rc=0; + if (ver!=1) rc = _pdmqryQueryEvent(entry_index, events, entrysize, count, total_out, 10); + if (R_SUCCEEDED(rc) && ver==0) { + s32 end = count; + if (total_out && *total_out < end) end = *total_out; + PdmAccountEventV3 *v3_in = (PdmAccountEventV3*)events; + for (s32 i=0; icur_count) tmp_out = cur_count; + if (total_out) *total_out += tmp_out; + for (s32 j=0; j