Skip to content

Commit 0076007

Browse files
committed
Add ptm:plays service
1 parent 0a37639 commit 0076007

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

libctru/include/3ds.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ extern "C" {
6868
#include <3ds/services/ptmsysm.h>
6969
#include <3ds/services/ptmgets.h>
7070
#include <3ds/services/ptmsets.h>
71+
#include <3ds/services/ptmplays.h>
7172
#include <3ds/services/pxidev.h>
7273
#include <3ds/services/pxipm.h>
7374
#include <3ds/services/soc.h>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* @file ptmplays.h
3+
* @brief PTMPLAYS service. Has access to ptm:u commands, but this is not exposed here.
4+
*/
5+
#pragma once
6+
7+
#include <3ds/types.h>
8+
9+
/// Maximum number of play events that can be stored.
10+
#define PTM_MAX_PLAY_EVENTS 0x11D28u
11+
12+
/// Invalid title ID constant for PTM.
13+
#define PTM_INVALID_TITLE_ID 0xFFFFFFFFFFFFFFFFull
14+
15+
/// Play event types. "Applet" includes "HOME Menu".
16+
typedef enum PtmPlayEventType {
17+
PTMPLAYEVENT_APPLICATION_LAUNCH = 0, ///< Application launch (special meaning for invalid TitleId upon launching DSi title)
18+
PTMPLAYEVENT_APPLICATION_EXIT = 1, ///< Application exit (likewise)
19+
PTMPLAYEVENT_APPLET_LAUNCH = 2, ///< Applet launch
20+
PTMPLAYEVENT_APPLET_EXIT = 3, ///< Applet exit
21+
PTMPLAYEVENT_JUMP_TO_APPLICATION = 4, ///< Jump to application
22+
PTMPLAYEVENT_LEAVE_APPLICATION = 5, ///< Leave application
23+
PTMPLAYEVENT_JUMP_TO_APPLET = 6, ///< Jump to applet
24+
PTMPLAYEVENT_LEAVE_APPLET = 7, ///< Leave applet
25+
PTMPLAYEVENT_SHELL_CLOSE = 8, ///< Shell close (no TitleId)
26+
PTMPLAYEVENT_SHELL_OPEN = 9, ///< Shell open (no TitleId)
27+
PTMPLAYEVENT_SYSTEM_SHUTDOWN = 10, ///< System shutdown (no TitleId)
28+
PTMPLAYEVENT_USER_TIME_CHANGE_OLD_TIME = 11, ///< User time change - old time (no TitleId, old user time stored in \ref minutesSince2000)
29+
PTMPLAYEVENT_USER_TIME_CHANGE_NEW_TIME = 12, ///< User time change - new time (no TitleId, new user time stored in \ref minutesSince2000)
30+
} PtmPlayEventType;
31+
32+
/// Play event entry.
33+
typedef struct PtmPlayEvent {
34+
u32 tidHigh; ///< Upper 32 bits of title ID
35+
u32 tidLow; ///< Lower 32 bits of title ID
36+
PtmPlayEventType type : 4; ///< Event type
37+
u32 minutesSince2000 : 28; ///< Number of minutes elapsed since 2000-01-01 00:00
38+
} PtmPlayEvent;
39+
40+
/// Get TitleID from PtmPlayEvent.
41+
static inline u64 ptmGetPlayEventTitleId(PtmPlayEvent event) {
42+
return ((u64)(event.tidHigh) << 32) | event.tidLow;
43+
}
44+
45+
/// Initializes PTMPLAYS.
46+
Result ptmPlaysInit(void);
47+
48+
/// Exits PTMPLAYS.
49+
void ptmPlaysExit(void);
50+
51+
/**
52+
* @brief Gets a pointer to the current ptm:plays session handle.
53+
* @return A pointer to the current ptm:plays session handle.
54+
*/
55+
Handle *ptmPlaysGetSessionHandle(void);
56+
57+
/**
58+
* @brief Gets the PlayEvent history (ring buffer).
59+
* @param[out] outEndIndex The pointer to write the end index to (ring buffer).
60+
* @param[out] outEvents The pointer to write the event entries to.
61+
* @param[in] startIndex The start index (offset) to read from (ring buffer).
62+
* @param[in] maxEvents The maximum number of events to write to outEvents.
63+
*/
64+
Result PTMPLAYS_GetPlayHistory(s32 *outEndIndex, PtmPlayEvent *outEvents, s32 startIndex, s32 maxEvents);
65+
66+
/**
67+
* @brief Gets the start index of the PlayEvent history (ring buffer).
68+
* @param[out] outStartIndex The pointer to write the start index to (ring buffer).
69+
*/
70+
Result PTMPLAYS_GetPlayHistoryStart(s32 *outStartIndex);
71+
72+
/**
73+
* @brief Gets the number of entries of the PlayEvent history (ring buffer).
74+
* @param[out] outNumEvents The pointer to write the start index to (ring buffer).
75+
*/
76+
Result PTMPLAYS_GetPlayHistorySize(s32 *outNumEvents);
77+
78+
/**
79+
* @brief Computes (start + numEvents) % PTM_MAX_PLAY_EVENTS with positive remainder.
80+
* @param[out] outIndex Result of the index computation.
81+
*/
82+
Result PTMPLAYS_CalcPlayHistoryStart(s32 *outIndex, s32 start, s32 numEvents);

libctru/source/services/ptmplays.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include <string.h>
2+
#include <3ds/types.h>
3+
#include <3ds/result.h>
4+
#include <3ds/svc.h>
5+
#include <3ds/srv.h>
6+
#include <3ds/synchronization.h>
7+
#include <3ds/ipc.h>
8+
9+
#include <3ds/services/ptmplays.h>
10+
11+
static Handle ptmPlaysHandle;
12+
static int ptmPlaysRefCount;
13+
14+
Result ptmPlaysInit(void)
15+
{
16+
if (AtomicPostIncrement(&ptmPlaysRefCount)) return 0;
17+
Result res = srvGetServiceHandle(&ptmPlaysHandle, "ptm:plays");
18+
if (R_FAILED(res)) AtomicDecrement(&ptmPlaysRefCount);
19+
return res;
20+
}
21+
22+
void ptmPlaysExit(void)
23+
{
24+
if (AtomicDecrement(&ptmPlaysRefCount)) return;
25+
svcCloseHandle(ptmPlaysHandle);
26+
}
27+
28+
Handle *ptmPlaysGetSessionHandle(void)
29+
{
30+
return &ptmPlaysHandle;
31+
}
32+
33+
Result PTMPLAYS_GetPlayHistory(s32 *outEndIndex, PtmPlayEvent *outEvents, s32 startIndex, s32 maxEvents)
34+
{
35+
Result ret;
36+
u32 *cmdbuf = getThreadCommandBuffer();
37+
cmdbuf[0] = IPC_MakeHeader(0x807,2,2); // 0x08070282
38+
39+
cmdbuf[1] = (u32)startIndex;
40+
cmdbuf[2] = (u32)maxEvents;
41+
cmdbuf[3] = IPC_Desc_Buffer(sizeof(PtmPlayEvent) * maxEvents, IPC_BUFFER_W);
42+
cmdbuf[4] = (u32)outEvents;
43+
44+
if(R_FAILED(ret = svcSendSyncRequest(ptmPlaysHandle)))return ret;
45+
46+
*outEndIndex = (s32)cmdbuf[2];
47+
48+
return (Result)cmdbuf[1];
49+
}
50+
51+
Result PTMPLAYS_GetPlayHistoryStart(s32 *outStartIndex)
52+
{
53+
Result ret;
54+
u32 *cmdbuf = getThreadCommandBuffer();
55+
cmdbuf[0] = IPC_MakeHeader(0x808,0,0); // 0x08080000
56+
57+
if(R_FAILED(ret = svcSendSyncRequest(ptmPlaysHandle)))return ret;
58+
59+
*outStartIndex = (s32)cmdbuf[2];
60+
61+
return (Result)cmdbuf[1];
62+
}
63+
64+
Result PTMPLAYS_GetPlayHistorySize(s32 *outNumEvents)
65+
{
66+
Result ret;
67+
u32 *cmdbuf = getThreadCommandBuffer();
68+
cmdbuf[0] = IPC_MakeHeader(0x809,0,0); // 0x08090000
69+
70+
if(R_FAILED(ret = svcSendSyncRequest(ptmPlaysHandle)))return ret;
71+
72+
*outNumEvents = (s32)cmdbuf[2];
73+
74+
return (Result)cmdbuf[1];
75+
}
76+
77+
Result PTMPLAYS_CalcPlayHistoryStart(s32 *outIndex, s32 start, s32 numEvents)
78+
{
79+
Result ret;
80+
u32 *cmdbuf = getThreadCommandBuffer();
81+
cmdbuf[0] = IPC_MakeHeader(0x80A,2,0); // 0x08090000
82+
83+
cmdbuf[1] = (u32)start;
84+
cmdbuf[2] = (u32)numEvents;
85+
86+
if(R_FAILED(ret = svcSendSyncRequest(ptmPlaysHandle)))return ret;
87+
88+
*outIndex = (s32)cmdbuf[2];
89+
90+
return (Result)cmdbuf[1];
91+
}

0 commit comments

Comments
 (0)