libctru/libctru/source/services/apt.c
fincs b93e7f19bf
Refactor DSP<->APT interaction, see details:
- DSP wrapper is now in charge of handling sleep/awake/cancel events
  - Added DSP hook mechanism (similar to APT hook)
  - The DSP component now gets automatically unloaded/reloaded as needed
- NDSP now uses DSP hook instead of APT hook to manage lifetime
  - Moved sleep/wakeup component management logic into NDSP
- APT now calls into DSP wrapper in order to arbitrate access to the DSP
  - Launching libapplets no longer relinquishes DSP rights - this means
    music can now be played in the background during libapplet activity
2020-06-22 00:34:38 +02:00

1440 lines
35 KiB
C

/*
apt.c _ Applet/NS shell interaction
*/
#include <stdlib.h>
#include <string.h>
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/srv.h>
#include <3ds/synchronization.h>
#include <3ds/services/apt.h>
#include <3ds/services/gspgpu.h>
#include <3ds/services/ptmsysm.h> // for PtmWakeEvents
#include <3ds/allocator/mappable.h>
#include <3ds/ipc.h>
#include <3ds/env.h>
#include <3ds/thread.h>
#include <3ds/os.h>
#define APT_HANDLER_STACKSIZE (0x1000)
static int aptRefCount = 0;
static Handle aptLockHandle;
static Handle aptEvents[2];
static LightEvent aptReceiveEvent;
static LightEvent aptSleepEvent;
static Thread aptEventHandlerThread;
static bool aptEventHandlerThreadQuit;
static aptHookCookie aptFirstHook;
static aptMessageCb aptMessageFunc;
static void* aptMessageFuncData;
enum
{
// Current applet state
FLAG_ACTIVE = BIT(0),
FLAG_SLEEPING = BIT(1),
// Sleep handling flags
FLAG_ALLOWSLEEP = BIT(2),
FLAG_SHOULDSLEEP = BIT(3),
// Home button flags
FLAG_ALLOWHOME = BIT(4),
FLAG_SHOULDHOME = BIT(5),
FLAG_HOMEREJECTED = BIT(6),
// Power button flags
FLAG_POWERBUTTON = BIT(7),
FLAG_SHUTDOWN = BIT(8),
// Close handling flags
FLAG_ORDERTOCLOSE = BIT(9),
FLAG_CANCELLED = BIT(10),
// Miscellaneous
FLAG_DSPWAKEUP = BIT(29),
FLAG_CHAINLOAD = BIT(30),
FLAG_SPURIOUS = BIT(31),
};
static u8 aptHomeButtonState;
static u32 aptFlags;
static u32 aptParameters[0x1000/4];
static u8 aptChainloadFlags;
static u64 aptChainloadTid;
static u8 aptChainloadMediatype;
typedef enum
{
TR_ENABLE = 0x62,
TR_JUMPTOMENU = 0x0E,
TR_SYSAPPLET = 0x05,
TR_LIBAPPLET = 0x04,
TR_CANCELLIB = 0x03,
TR_CLOSEAPP = 0x09,
TR_APPJUMP = 0x12,
} APT_Transition;
static void aptEventHandler(void *arg);
static APT_Command aptWaitForWakeUp(APT_Transition transition);
// The following function can be overridden in order to log APT signals and notifications for debugging purposes
#ifdef LIBCTRU_APT_DEBUG
__attribute__((weak)) void _aptDebug(int a, int b) { }
#else
#define _aptDebug(a,b) ((void)0)
#endif
// APT<->DSP interaction functions (stubbed when not using DSP)
__attribute__((weak)) bool aptDspSleep(void) { return false; }
__attribute__((weak)) void aptDspWakeup(void) { }
__attribute__((weak)) void aptDspCancel(void) { }
static void aptCallHook(APT_HookType hookType)
{
aptHookCookie* c;
for (c = &aptFirstHook; c && c->callback; c = c->next)
c->callback(hookType, c->param);
}
static bool aptIsReinit(void)
{
return (envGetSystemRunFlags() & RUNFLAG_APTREINIT) != 0;
}
static bool aptIsChainload(void)
{
return (envGetSystemRunFlags() & RUNFLAG_APTCHAINLOAD) != 0;
}
static bool aptIsCrippled(void)
{
u32 flags = envGetSystemRunFlags();
return (flags & RUNFLAG_APTWORKAROUND) && !(flags & RUNFLAG_APTREINIT);
}
static Result aptGetServiceHandle(Handle* aptuHandle)
{
static const char* serviceName;
static const char* const serviceNameTable[3] = {"APT:S", "APT:A", "APT:U"};
if (serviceName)
return srvGetServiceHandleDirect(aptuHandle, serviceName);
Result ret;
int i;
for (i = 0; i < 3; i ++)
{
ret = srvGetServiceHandleDirect(aptuHandle, serviceNameTable[i]);
if (R_SUCCEEDED(ret))
{
serviceName = serviceNameTable[i];
break;
}
}
return ret;
}
static inline int countPrmWords(u32 hdr)
{
return 1 + (hdr&0x3F) + ((hdr>>6)&0x3F);
}
Result aptSendCommand(u32* aptcmdbuf)
{
Handle aptuHandle;
if (aptLockHandle) svcWaitSynchronization(aptLockHandle, U64_MAX);
Result res = aptGetServiceHandle(&aptuHandle);
if (R_SUCCEEDED(res))
{
u32* cmdbuf = getThreadCommandBuffer();
memcpy(cmdbuf, aptcmdbuf, 4*countPrmWords(aptcmdbuf[0]));
res = svcSendSyncRequest(aptuHandle);
if (R_SUCCEEDED(res))
{
memcpy(aptcmdbuf, cmdbuf, 4*countPrmWords(cmdbuf[0]));
res = aptcmdbuf[1];
}
svcCloseHandle(aptuHandle);
}
if (aptLockHandle) svcReleaseMutex(aptLockHandle);
return res;
}
static void aptInitCaptureInfo(aptCaptureBufInfo* capinfo, const GSPGPU_CaptureInfo* gspcapinfo)
{
// Fill in display-capture info for NS.
capinfo->is3D = (gspcapinfo->screencapture[0].format & 0x20) != 0;
capinfo->top.format = gspcapinfo->screencapture[0].format & 0x7;
capinfo->bottom.format = gspcapinfo->screencapture[1].format & 0x7;
u32 main_pixsz = gspGetBytesPerPixel((GSPGPU_FramebufferFormat)capinfo->top.format);
u32 sub_pixsz = gspGetBytesPerPixel((GSPGPU_FramebufferFormat)capinfo->bottom.format);
capinfo->bottom.leftOffset = 0;
capinfo->bottom.rightOffset = 0;
capinfo->top.leftOffset = sub_pixsz * 0x14000;
capinfo->top.rightOffset = capinfo->top.leftOffset;
if (capinfo->is3D)
capinfo->top.rightOffset += main_pixsz * 0x19000;
capinfo->size = main_pixsz * 0x7000 + main_pixsz * 0x19000 + capinfo->top.rightOffset;
}
Result aptInit(void)
{
Result ret=0;
if (AtomicPostIncrement(&aptRefCount)) return 0;
// Retrieve APT lock
ret = APT_GetLockHandle(0x0, &aptLockHandle);
if (R_FAILED(ret)) goto _fail;
if (aptIsCrippled()) return 0;
// Initialize APT
APT_AppletAttr attr = aptMakeAppletAttr(APTPOS_APP, false, false);
ret = APT_Initialize(envGetAptAppId(), attr, &aptEvents[0], &aptEvents[1]);
if (R_FAILED(ret)) goto _fail2;
// Initialize light events
LightEvent_Init(&aptReceiveEvent, RESET_STICKY);
LightEvent_Init(&aptSleepEvent, RESET_ONESHOT);
// Create APT event handler thread
aptEventHandlerThreadQuit = false;
aptEventHandlerThread = threadCreate(aptEventHandler, 0x0, APT_HANDLER_STACKSIZE, 0x31, -2, true);
if (!aptEventHandlerThread) goto _fail3;
// By default allow sleep mode and home button presses
aptFlags = FLAG_ALLOWSLEEP;
if (osGetSystemCoreVersion() == 2) // ... except in safe mode, which doesn't have home menu running
aptFlags |= FLAG_ALLOWHOME;
// Enable APT
ret = APT_Enable(attr);
if (R_FAILED(ret)) goto _fail3;
// If the homebrew environment requires it, chainload-to-self by default
if (aptIsChainload())
aptSetChainloaderToSelf();
// Wait for wakeup
aptWaitForWakeUp(TR_ENABLE);
// Special handling for aptReinit (aka hax 2.x):
// In certain cases when running under hax 2.x, we may receive a spurious
// second wakeup command. Therefore we must silently drop it in order to
// avoid keeping stale commands in APT's internal buffer.
if (aptIsReinit())
aptFlags |= FLAG_SPURIOUS;
return 0;
_fail3:
svcCloseHandle(aptEvents[0]);
svcCloseHandle(aptEvents[1]);
_fail2:
svcCloseHandle(aptLockHandle);
_fail:
AtomicDecrement(&aptRefCount);
return ret;
}
bool aptIsActive(void)
{
return (aptFlags & FLAG_ACTIVE) != 0;
}
bool aptShouldClose(void)
{
return (aptFlags & (FLAG_ORDERTOCLOSE|FLAG_CANCELLED)) != 0;
}
bool aptIsSleepAllowed(void)
{
return (aptFlags & FLAG_ALLOWSLEEP) != 0;
}
void aptSetSleepAllowed(bool allowed)
{
bool cur = aptIsSleepAllowed();
if (allowed && !cur)
{
aptFlags |= FLAG_ALLOWSLEEP;
APT_SleepIfShellClosed();
}
else if (!allowed && cur)
{
aptFlags &= ~FLAG_ALLOWSLEEP;
APT_ReplySleepQuery(envGetAptAppId(), APTREPLY_REJECT);
}
}
bool aptIsHomeAllowed(void)
{
return (aptFlags & FLAG_ALLOWHOME) != 0;
}
void aptSetHomeAllowed(bool allowed)
{
if (allowed)
aptFlags |= FLAG_ALLOWHOME;
else
aptFlags &= ~FLAG_ALLOWHOME;
}
bool aptShouldJumpToHome(void)
{
return aptHomeButtonState || (aptFlags & (FLAG_SHOULDHOME|FLAG_POWERBUTTON)) != 0;
}
bool aptCheckHomePressRejected(void)
{
if (aptFlags & FLAG_HOMEREJECTED)
{
aptFlags &= ~FLAG_HOMEREJECTED;
return true;
}
return false;
}
static void aptClearJumpToHome(void)
{
aptHomeButtonState = 0;
APT_UnlockTransition(0x01);
APT_SleepIfShellClosed();
}
void aptClearChainloader(void)
{
aptFlags &= ~FLAG_CHAINLOAD;
}
void aptSetChainloader(u64 programID, u8 mediatype)
{
aptFlags |= FLAG_CHAINLOAD;
aptChainloadFlags = 0;
aptChainloadTid = programID;
aptChainloadMediatype = mediatype;
}
void aptSetChainloaderToSelf(void)
{
aptFlags |= FLAG_CHAINLOAD;
aptChainloadFlags = 2;
aptChainloadTid = 0;
aptChainloadMediatype = 0;
}
extern void (*__system_retAddr)(void);
static void aptExitProcess(void)
{
APT_CloseApplication(NULL, 0, 0);
}
void aptExit(void)
{
if (AtomicDecrement(&aptRefCount)) return;
bool closeAptLock = true;
if (!aptIsCrippled())
{
bool doClose;
if (aptShouldClose())
{
// The system instructed us to close, so do just that
aptCallHook(APTHOOK_ONEXIT);
doClose = true;
}
else if (aptIsReinit())
{
// The homebrew environment expects APT to be reinitializable, so unregister ourselves without closing
APT_Finalize(envGetAptAppId());
doClose = false;
}
else if (aptFlags & FLAG_CHAINLOAD)
{
// A chainload target is configured, so perform a jump to it
// Doing this requires help from HOME menu, so ensure that it is running
bool hmRegistered;
if (R_SUCCEEDED(APT_IsRegistered(aptGetMenuAppID(), &hmRegistered)) && hmRegistered)
{
// Normal, sane chainload
u8 param[0x300] = {0};
u8 hmac[0x20] = {0};
APT_PrepareToDoApplicationJump(aptChainloadFlags, aptChainloadTid, aptChainloadMediatype);
APT_DoApplicationJump(param, sizeof(param), hmac);
}
else
{
// XX: HOME menu doesn't exist, so we need to use a workaround provided by Luma3DS
APT_Finalize(envGetAptAppId());
srvPublishToSubscriber(0x3000, 0);
}
// After a chainload has been applied, we don't need to manually close
doClose = false;
__system_retAddr = NULL;
}
else
{
// None of the other situations apply, so close anyway by default
doClose = true;
}
// If needed, perform the APT application closing sequence
if (doClose)
{
APT_PrepareToCloseApplication(true);
// APT_CloseApplication kills us if we aren't signed up for srv closing notifications, so
// defer APT_CloseApplication for as long as possible (TODO: actually use srv notif instead)
__system_retAddr = aptExitProcess;
closeAptLock = false;
srvInit(); // Keep srv initialized
}
aptEventHandlerThreadQuit = true;
svcSignalEvent(aptEvents[0]);
threadJoin(aptEventHandlerThread, U64_MAX);
int i;
for (i = 0; i < 2; i ++)
svcCloseHandle(aptEvents[i]);
}
if (closeAptLock)
svcCloseHandle(aptLockHandle);
}
void aptEventHandler(void *arg)
{
while (!aptEventHandlerThreadQuit)
{
s32 id = 0;
svcWaitSynchronizationN(&id, aptEvents, 2, 0, U64_MAX);
if (aptEventHandlerThreadQuit)
break;
// If the receive event is still signaled, sleep for a bit and retry
if (LightEvent_TryWait(&aptReceiveEvent))
{
_aptDebug(222, 0);
svcSleepThread(10000000); // 10ms
svcSignalEvent(aptEvents[id]);
continue;
}
// This is done by official sw, even though APT events are oneshot...
svcClearEvent(aptEvents[id]);
// Relay receive events to our light event
if (id == 1)
{
NS_APPID sender;
APT_Command cmd;
Result res = APT_GlanceParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, &cmd, NULL, NULL);
if (R_FAILED(res))
continue; // Official sw panics here - we instead swallow the (non-)event.
_aptDebug(2, cmd); _aptDebug(22, sender);
// NOTE: Official software handles the following parameter types here:
// - APTCMD_MESSAGE (cancelled afterwards) (we handle it in aptReceiveParameter instead)
// - APTCMD_REQUEST (cancelled afterwards) (only sent to and handled by libapplets?)
// - APTCMD_DSP_SLEEP (*NOT* cancelled afterwards)
// - APTCMD_DSP_WAKEUP (*NOT* cancelled afterwards)
// We will handle the following:
switch (cmd)
{
case APTCMD_DSP_SLEEP:
// Handle DSP sleep requests
aptDspSleep();
break;
case APTCMD_DSP_WAKEUP:
// Handle DSP wakeup requests
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
break;
case APTCMD_WAKEUP_PAUSE:
// Handle spurious APTCMD_WAKEUP_PAUSE parameters
// (see aptInit for more details on the hax 2.x spurious wakeup problem)
if (aptFlags & FLAG_SPURIOUS)
{
APT_CancelParameter(APPID_NONE, envGetAptAppId(), NULL);
aptFlags &= ~FLAG_SPURIOUS;
break;
}
// Fallthrough otherwise
default:
// Others not accounted for -> pass it on to aptReceiveParameter
LightEvent_Signal(&aptReceiveEvent);
break;
}
continue;
}
APT_Signal signal;
Result res = APT_InquireNotification(envGetAptAppId(), &signal);
if (R_FAILED(res))
continue;
_aptDebug(1, signal);
switch (signal)
{
case APTSIGNAL_HOMEBUTTON:
case APTSIGNAL_HOMEBUTTON2:
if (!aptIsActive())
break;
else if (!aptIsHomeAllowed())
{
aptFlags |= FLAG_HOMEREJECTED;
aptClearJumpToHome();
}
else if (!aptHomeButtonState)
aptHomeButtonState = signal == APTSIGNAL_HOMEBUTTON ? 1 : 2;
break;
case APTSIGNAL_SLEEP_QUERY:
{
APT_QueryReply reply;
if (aptShouldClose())
// Reject sleep if we are expected to close
reply = APTREPLY_REJECT;
else if (aptIsActive())
// Accept sleep based on user setting if we are active
reply = aptIsSleepAllowed() ? APTREPLY_ACCEPT : APTREPLY_REJECT;
else
// Accept sleep if we are inactive regardless of user setting
reply = APTREPLY_ACCEPT;
_aptDebug(10, aptFlags);
_aptDebug(11, reply);
APT_ReplySleepQuery(envGetAptAppId(), reply);
break;
}
case APTSIGNAL_SLEEP_CANCEL:
if (aptIsActive())
aptFlags &= ~FLAG_SHOULDSLEEP;
break;
case APTSIGNAL_SLEEP_ENTER:
_aptDebug(10, aptFlags);
if (aptDspSleep())
aptFlags |= FLAG_DSPWAKEUP;
if (aptIsActive())
aptFlags |= FLAG_SHOULDSLEEP;
else
// Since we are not active, this must be handled here.
APT_ReplySleepNotificationComplete(envGetAptAppId());
break;
case APTSIGNAL_SLEEP_WAKEUP:
if (aptFlags & FLAG_DSPWAKEUP)
{
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
}
if (!aptIsActive())
break;
if (aptFlags & FLAG_SLEEPING)
LightEvent_Signal(&aptSleepEvent);
else
aptFlags &= ~FLAG_SHOULDSLEEP;
break;
case APTSIGNAL_SHUTDOWN:
aptFlags |= FLAG_ORDERTOCLOSE | FLAG_SHUTDOWN;
break;
case APTSIGNAL_POWERBUTTON:
aptFlags |= FLAG_POWERBUTTON;
break;
case APTSIGNAL_POWERBUTTON2:
aptFlags &= ~FLAG_POWERBUTTON;
break;
case APTSIGNAL_TRY_SLEEP:
{
// Official software performs this APT_SleepSystem command here, although
// its purpose is unclear. For completeness' sake, we'll do it as well.
static const struct PtmWakeEvents s_sleepWakeEvents = {
.pdn_wake_events = 0,
.mcu_interupt_mask = BIT(6),
};
APT_SleepSystem(&s_sleepWakeEvents);
break;
}
case APTSIGNAL_ORDERTOCLOSE:
aptFlags |= FLAG_ORDERTOCLOSE;
break;
default:
break;
}
}
}
static Result aptReceiveParameter(APT_Command* cmd, size_t* actualSize, Handle* handle)
{
NS_APPID sender;
size_t temp_actualSize;
if (!actualSize) actualSize = &temp_actualSize;
LightEvent_Wait(&aptReceiveEvent);
LightEvent_Clear(&aptReceiveEvent);
Result res = APT_ReceiveParameter(envGetAptAppId(), aptParameters, sizeof(aptParameters), &sender, cmd, actualSize, handle);
if (R_SUCCEEDED(res) && *cmd == APTCMD_MESSAGE && aptMessageFunc)
aptMessageFunc(aptMessageFuncData, sender, aptParameters, *actualSize);
return res;
}
APT_Command aptWaitForWakeUp(APT_Transition transition)
{
APT_Command cmd;
APT_NotifyToWait(envGetAptAppId());
aptFlags &= ~FLAG_ACTIVE;
if (transition != TR_ENABLE)
APT_SleepIfShellClosed();
for (;;)
{
Result res = aptReceiveParameter(&cmd, NULL, NULL);
if (R_SUCCEEDED(res)
&& (cmd==APTCMD_WAKEUP || cmd==APTCMD_WAKEUP_PAUSE || cmd==APTCMD_WAKEUP_EXIT || cmd==APTCMD_WAKEUP_CANCEL
|| cmd==APTCMD_WAKEUP_CANCELALL || cmd==APTCMD_WAKEUP_POWERBUTTON || cmd==APTCMD_WAKEUP_JUMPTOHOME
|| cmd==APTCMD_WAKEUP_LAUNCHAPP)) break;
}
aptFlags |= FLAG_ACTIVE;
void __ctru_speedup_config();
__ctru_speedup_config();
if (transition != TR_CANCELLIB && cmd != APTCMD_WAKEUP_CANCEL && cmd != APTCMD_WAKEUP)
{
GSPGPU_AcquireRight(0);
GSPGPU_RestoreVramSysArea();
aptCallHook(APTHOOK_ONRESTORE);
}
if (cmd == APTCMD_WAKEUP_CANCEL || cmd == APTCMD_WAKEUP_CANCELALL)
{
aptDspCancel();
if (cmd == APTCMD_WAKEUP_CANCEL) // for some reason, not for CANCELALL... is this a bug in official sw?
aptFlags |= FLAG_CANCELLED;
} else if (cmd != APTCMD_WAKEUP_LAUNCHAPP)
{
aptFlags &= ~FLAG_DSPWAKEUP;
aptDspWakeup();
}
if (cmd != APTCMD_WAKEUP_JUMPTOHOME)
{
APT_UnlockTransition(0x10);
APT_SleepIfShellClosed();
} else
{
aptFlags |= FLAG_SHOULDHOME;
aptHomeButtonState = 1;
APT_LockTransition(0x01, true);
}
if (transition == TR_JUMPTOMENU || transition == TR_LIBAPPLET || transition == TR_SYSAPPLET || transition == TR_APPJUMP)
{
if (cmd != APTCMD_WAKEUP_JUMPTOHOME)
aptClearJumpToHome();
}
return cmd;
}
static void aptConvertScreenForCapture(void* dst, const void* src, u32 height, GSPGPU_FramebufferFormat format)
{
const u32 width = 240;
const u32 width_po2 = 1U << (32 - __builtin_clz(width-1)); // next_po2(240) = 256
const u32 bpp = gspGetBytesPerPixel(format);
const u32 tilesize = 8*8*bpp;
// Terrible conversion code that is also probably really slow
u8* out = (u8*)dst;
const u8* in = (u8*)src;
for (u32 tiley = 0; tiley < height; tiley += 8)
{
u32 tilex = 0;
for (tilex = 0; tilex < width; tilex += 8)
{
for (u32 y = 0; y < 8; y ++)
{
for (u32 x = 0; x < 8; x ++)
{
static const u8 morton_x[] = { 0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15 };
static const u8 morton_y[] = { 0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a };
unsigned inoff = bpp*(width*(tiley+y)+(tilex+x));
unsigned outoff = bpp*(morton_x[x] + morton_y[y]);
for (u32 c = 0; c < bpp; c ++)
out[outoff+c] = in[inoff+c];
}
}
out += tilesize;
}
for (; tilex < width_po2; tilex += 8)
out += tilesize;
}
}
static void aptScreenTransfer(NS_APPID appId, bool sysApplet)
{
// Retrieve display capture info from GSP
GSPGPU_CaptureInfo gspcapinfo = {0};
GSPGPU_ImportDisplayCaptureInfo(&gspcapinfo);
// Wait for the target applet to be registered
for (;;)
{
bool tmp;
Result res = APT_IsRegistered(appId, &tmp);
if (R_SUCCEEDED(res) && tmp) break;
svcSleepThread(10000000);
}
// Calculate the layout/size of the capture memory block
aptCaptureBufInfo capinfo;
aptInitCaptureInfo(&capinfo, &gspcapinfo);
// Request the capture memory block to be allocated
for (;;)
{
Result res = APT_SendParameter(envGetAptAppId(), appId, sysApplet ? APTCMD_SYSAPPLET_REQUEST : APTCMD_REQUEST, &capinfo, sizeof(capinfo), 0);
if (R_SUCCEEDED(res)) break;
svcSleepThread(10000000);
}
// Receive the response from APT
Handle hCapMemBlk = 0;
for (;;)
{
APT_Command cmd;
Result res = aptReceiveParameter(&cmd, NULL, &hCapMemBlk);
if (R_SUCCEEDED(res) && cmd==APTCMD_RESPONSE)
break;
}
// For library applets, we need to manually do the capture ourselves
// (this involves mapping the memory block and doing the conversion)
if (!sysApplet)
{
void* map = mappableAlloc(capinfo.size);
if (map)
{
Result res = svcMapMemoryBlock(hCapMemBlk, (u32)map, MEMPERM_READWRITE, MEMPERM_READWRITE);
if (R_SUCCEEDED(res))
{
aptConvertScreenForCapture( // Bottom screen
(u8*)map + capinfo.bottom.leftOffset,
gspcapinfo.screencapture[1].framebuf0_vaddr,
320, (GSPGPU_FramebufferFormat)capinfo.bottom.format);
aptConvertScreenForCapture( // Top screen (Left eye)
(u8*)map + capinfo.top.leftOffset,
gspcapinfo.screencapture[0].framebuf0_vaddr,
400, (GSPGPU_FramebufferFormat)capinfo.top.format);
if (capinfo.is3D)
aptConvertScreenForCapture( // Top screen (Right eye)
(u8*)map + capinfo.top.rightOffset,
gspcapinfo.screencapture[0].framebuf1_vaddr,
400, (GSPGPU_FramebufferFormat)capinfo.top.format);
svcUnmapMemoryBlock(hCapMemBlk, (u32)map);
}
mappableFree(map);
}
}
// Close the capture memory block handle
if (hCapMemBlk)
svcCloseHandle(hCapMemBlk);
// Send capture buffer information back to APT
APT_SendCaptureBufferInfo(&capinfo);
}
void aptJumpToHomeMenu(void)
{
bool sleep = aptIsSleepAllowed();
aptSetSleepAllowed(false);
aptFlags &= ~(FLAG_SHOULDHOME|FLAG_SPURIOUS); // If we haven't received a spurious wakeup by now, we probably never will (see aptInit)
APT_PrepareToJumpToHomeMenu();
aptCallHook(APTHOOK_ONSUSPEND);
GSPGPU_SaveVramSysArea();
aptScreenTransfer(aptGetMenuAppID(), true);
aptDspSleep();
GSPGPU_ReleaseRight();
APT_JumpToHomeMenu(NULL, 0, 0);
aptFlags &= ~FLAG_ACTIVE;
aptWaitForWakeUp(TR_JUMPTOMENU);
aptSetSleepAllowed(sleep);
}
void aptHandleSleep(void)
{
if (!(aptFlags & FLAG_SHOULDSLEEP))
return;
aptFlags = (aptFlags &~ FLAG_SHOULDSLEEP) | FLAG_SLEEPING;
aptCallHook(APTHOOK_ONSLEEP);
APT_ReplySleepNotificationComplete(envGetAptAppId());
LightEvent_Wait(&aptSleepEvent);
aptFlags &= ~FLAG_SLEEPING;
if (aptIsActive())
GSPGPU_SetLcdForceBlack(0);
aptCallHook(APTHOOK_ONWAKEUP);
}
bool aptMainLoop(void)
{
aptHandleSleep();
aptHandleJumpToHome();
return !aptShouldClose();
}
void aptHook(aptHookCookie* cookie, aptHookFn callback, void* param)
{
if (!callback) return;
aptHookCookie* hook = &aptFirstHook;
*cookie = *hook; // Structure copy.
hook->next = cookie;
hook->callback = callback;
hook->param = param;
}
void aptUnhook(aptHookCookie* cookie)
{
aptHookCookie* hook;
for (hook = &aptFirstHook; hook; hook = hook->next)
{
if (hook->next == cookie)
{
*hook = *cookie; // Structure copy.
break;
}
}
}
void aptSetMessageCallback(aptMessageCb callback, void* user)
{
aptMessageFunc = callback;
aptMessageFuncData = user;
}
void aptLaunchLibraryApplet(NS_APPID appId, void* buf, size_t bufsize, Handle handle)
{
bool sleep = aptIsSleepAllowed();
aptSetSleepAllowed(false);
aptFlags &= ~FLAG_SPURIOUS; // If we haven't received a spurious wakeup by now, we probably never will (see aptInit)
APT_PrepareToStartLibraryApplet(appId);
aptSetSleepAllowed(sleep);
aptCallHook(APTHOOK_ONSUSPEND);
GSPGPU_SaveVramSysArea();
aptScreenTransfer(appId, false);
GSPGPU_ReleaseRight();
aptSetSleepAllowed(false);
APT_StartLibraryApplet(appId, buf, bufsize, handle);
aptFlags &= ~FLAG_ACTIVE;
aptWaitForWakeUp(TR_LIBAPPLET);
memcpy(buf, aptParameters, bufsize);
aptSetSleepAllowed(sleep);
}
Result APT_GetLockHandle(u16 flags, Handle* lockHandle)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x1,1,0); // 0x10040
cmdbuf[1]=flags;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*lockHandle = cmdbuf[5];
return ret;
}
Result APT_Initialize(NS_APPID appId, APT_AppletAttr attr, Handle* signalEvent, Handle* resumeEvent)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x2,2,0); // 0x20080
cmdbuf[1]=appId;
cmdbuf[2]=attr;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
{
if(signalEvent) *signalEvent=cmdbuf[3];
if(resumeEvent) *resumeEvent=cmdbuf[4];
}
return ret;
}
Result APT_Finalize(NS_APPID appId)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x4,1,0); // 0x40040
cmdbuf[1]=appId;
return aptSendCommand(cmdbuf);
}
Result APT_HardwareResetAsync(void)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x4E,0,0); // 0x4E0000
return aptSendCommand(cmdbuf);
}
Result APT_Enable(APT_AppletAttr attr)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x3,1,0); // 0x30040
cmdbuf[1]=attr;
return aptSendCommand(cmdbuf);
}
Result APT_GetAppletManInfo(APT_AppletPos inpos, APT_AppletPos* outpos, NS_APPID* req_appid, NS_APPID* menu_appid, NS_APPID* active_appid)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x5,1,0); // 0x50040
cmdbuf[1]=inpos;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
{
if (outpos) *outpos =cmdbuf[2];
if (req_appid) *req_appid =cmdbuf[3];
if (menu_appid) *menu_appid =cmdbuf[4];
if (active_appid) *active_appid=cmdbuf[5];
}
return ret;
}
Result APT_GetAppletInfo(NS_APPID appID, u64* pProgramID, u8* pMediaType, bool* pRegistered, bool* pLoadState, APT_AppletAttr* pAttributes)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x6,1,0); // 0x60040
cmdbuf[1]=appID;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
{
if (pProgramID) *pProgramID =(u64)cmdbuf[2]|((u64)cmdbuf[3]<<32);
if (pMediaType) *pMediaType =cmdbuf[4];
if (pRegistered) *pRegistered=cmdbuf[5] & 0xFF;
if (pLoadState) *pLoadState =cmdbuf[6] & 0xFF;
if (pAttributes) *pAttributes=cmdbuf[7];
}
return ret;
}
Result APT_GetAppletProgramInfo(u32 id, u32 flags, u16 *titleversion)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x4D,2,0); // 0x4D0080
cmdbuf[1]=id;
cmdbuf[2]=flags;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*titleversion=cmdbuf[2];
return ret;
}
Result APT_GetProgramID(u64* pProgramID)
{
u32 cmdbuf[16];
cmdbuf[0] = IPC_MakeHeader(0x58,0,2); // 0x580002
cmdbuf[1] = IPC_Desc_CurProcessId();
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*pProgramID=((u64)cmdbuf[3]<<32)|cmdbuf[2];
return ret;
}
Result APT_IsRegistered(NS_APPID appID, bool* out)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x9,1,0); // 0x90040
cmdbuf[1]=appID;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*out=cmdbuf[2] & 0xFF;
return ret;
}
Result APT_InquireNotification(u32 appID, APT_Signal* signalType)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0xB,1,0); // 0xB0040
cmdbuf[1]=appID;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*signalType=cmdbuf[2];
return ret;
}
Result APT_PrepareToJumpToHomeMenu(void)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x2B,0,0); // 0x2B0000
return aptSendCommand(cmdbuf);
}
Result APT_JumpToHomeMenu(const void* param, size_t paramSize, Handle handle)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x2C,1,4); // 0x2C0044
cmdbuf[1]=paramSize;
cmdbuf[2]=IPC_Desc_SharedHandles(1);
cmdbuf[3]=handle;
cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[1],0);
cmdbuf[5]=(u32) param;
return aptSendCommand(cmdbuf);
}
Result APT_PrepareToJumpToApplication(bool exiting)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x23,1,0); // 0x230040
cmdbuf[1]=exiting;
return aptSendCommand(cmdbuf);
}
Result APT_JumpToApplication(const void* param, size_t paramSize, Handle handle)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x24,1,4); // 0x240044
cmdbuf[1]=paramSize;
cmdbuf[2]=IPC_Desc_SharedHandles(1);
cmdbuf[3]=handle;
cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[1],0);
cmdbuf[5]= (u32) param;
return aptSendCommand(cmdbuf);
}
Result APT_SleepSystem(const PtmWakeEvents *wakeEvents)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x42, 2, 0);
memcpy(&cmdbuf[1], wakeEvents, sizeof(PtmWakeEvents));
return aptSendCommand(cmdbuf);
}
Result APT_NotifyToWait(NS_APPID appID)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x43,1,0); // 0x430040
cmdbuf[1]=appID;
return aptSendCommand(cmdbuf);
}
Result APT_AppletUtility(int id, void* out, size_t outSize, const void* in, size_t inSize)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x4B,3,2); // 0x4B00C2
cmdbuf[1]=id;
cmdbuf[2]=inSize;
cmdbuf[3]=outSize;
cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[2],1);
cmdbuf[5]=(u32)in;
u32 saved_threadstorage[2];
u32* staticbufs = getThreadStaticBuffers();
saved_threadstorage[0]=staticbufs[0];
saved_threadstorage[1]=staticbufs[1];
staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[3],0);
staticbufs[1]=(u32)out;
Result ret = aptSendCommand(cmdbuf);
staticbufs[0]=saved_threadstorage[0];
staticbufs[1]=saved_threadstorage[1];
return R_SUCCEEDED(ret) ? cmdbuf[2] : ret;
}
Result APT_SleepIfShellClosed(void)
{
u8 out, in = 0;
return APT_AppletUtility(4, &out, sizeof(out), &in, sizeof(in));
}
Result APT_LockTransition(u32 transition, bool flag)
{
const struct
{
u32 transition;
bool flag;
u8 padding[3];
} in = { transition, flag, {0} };
u8 out;
return APT_AppletUtility(5, &out, sizeof(out), &in, sizeof(in));
}
Result APT_TryLockTransition(u32 transition, bool* succeeded)
{
return APT_AppletUtility(6, &succeeded, sizeof(succeeded), &transition, sizeof(transition));
}
Result APT_UnlockTransition(u32 transition)
{
u8 out;
return APT_AppletUtility(7, &out, sizeof(out), &transition, sizeof(transition));
}
Result APT_GlanceParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_APPID* sender, APT_Command* command, size_t* actualSize, Handle* parameter)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0xE,2,0); // 0xE0080
cmdbuf[1]=appID;
cmdbuf[2]=bufferSize;
u32 saved_threadstorage[2];
u32* staticbufs = getThreadStaticBuffers();
saved_threadstorage[0]=staticbufs[0];
saved_threadstorage[1]=staticbufs[1];
staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[2],0);
staticbufs[1]=(u32)buffer;
Result ret = aptSendCommand(cmdbuf);
staticbufs[0]=saved_threadstorage[0];
staticbufs[1]=saved_threadstorage[1];
if (R_SUCCEEDED(ret))
{
if (sender) *sender =cmdbuf[2];
if (command) *command =cmdbuf[3];
if (actualSize) *actualSize=cmdbuf[4];
if (parameter) *parameter =cmdbuf[6];
else if (cmdbuf[6]) svcCloseHandle(cmdbuf[6]);
}
return ret;
}
Result APT_ReceiveParameter(NS_APPID appID, void* buffer, size_t bufferSize, NS_APPID* sender, APT_Command* command, size_t* actualSize, Handle* parameter)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0xD,2,0); // 0xD0080
cmdbuf[1]=appID;
cmdbuf[2]=bufferSize;
u32 saved_threadstorage[2];
u32* staticbufs = getThreadStaticBuffers();
saved_threadstorage[0]=staticbufs[0];
saved_threadstorage[1]=staticbufs[1];
staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[2],0);
staticbufs[1]=(u32)buffer;
Result ret = aptSendCommand(cmdbuf);
staticbufs[0]=saved_threadstorage[0];
staticbufs[1]=saved_threadstorage[1];
if (R_SUCCEEDED(ret))
{
if (sender) *sender =cmdbuf[2];
if (command) *command =cmdbuf[3];
if (actualSize) *actualSize=cmdbuf[4];
if (parameter) *parameter =cmdbuf[6];
else if (cmdbuf[6]) svcCloseHandle(cmdbuf[6]);
}
return ret;
}
Result APT_SendParameter(NS_APPID source, NS_APPID dest, APT_Command command, const void* buffer, u32 bufferSize, Handle parameter)
{
u32 cmdbuf[16];
cmdbuf[0] = IPC_MakeHeader(0xC,4,4); // 0xC0104
cmdbuf[1] = source;
cmdbuf[2] = dest;
cmdbuf[3] = command;
cmdbuf[4] = bufferSize;
cmdbuf[5] = IPC_Desc_SharedHandles(1);
cmdbuf[6] = parameter;
cmdbuf[7] = IPC_Desc_StaticBuffer(cmdbuf[4],0);
cmdbuf[8] = (u32)buffer;
return aptSendCommand(cmdbuf);
}
Result APT_CancelParameter(NS_APPID source, NS_APPID dest, bool* success)
{
u32 cmdbuf[16];
cmdbuf[0] = IPC_MakeHeader(0xF,4,0); // 0xF0100
cmdbuf[1] = source != APPID_NONE;
cmdbuf[2] = source;
cmdbuf[3] = dest != APPID_NONE;
cmdbuf[4] = dest;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret) && success)
*success = cmdbuf[2] & 0xFF;
return ret;
}
Result APT_SendCaptureBufferInfo(const aptCaptureBufInfo* captureBuf)
{
u32 cmdbuf[16];
cmdbuf[0] = IPC_MakeHeader(0x40,1,2); // 0x400042
cmdbuf[1] = sizeof(*captureBuf);
cmdbuf[2] = IPC_Desc_StaticBuffer(cmdbuf[1],0);
cmdbuf[3] = (u32)captureBuf;
return aptSendCommand(cmdbuf);
}
Result APT_ReplySleepQuery(NS_APPID appID, APT_QueryReply reply)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x3E,2,0); // 0x3E0080
cmdbuf[1]=appID;
cmdbuf[2]=reply;
return aptSendCommand(cmdbuf);
}
Result APT_ReplySleepNotificationComplete(NS_APPID appID)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x3F,1,0); // 0x3F0040
cmdbuf[1]=appID;
return aptSendCommand(cmdbuf);
}
Result APT_PrepareToCloseApplication(bool cancelPreload)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x22,1,0); // 0x220040
cmdbuf[1]=cancelPreload;
return aptSendCommand(cmdbuf);
}
Result APT_CloseApplication(const void* param, size_t paramSize, Handle handle)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x27,1,4); // 0x270044
cmdbuf[1]=paramSize;
cmdbuf[2]=IPC_Desc_SharedHandles(1);
cmdbuf[3]=handle;
cmdbuf[4]=IPC_Desc_StaticBuffer(cmdbuf[1],0);
cmdbuf[5]= (u32) param;
return aptSendCommand(cmdbuf);
}
//See http://3dbrew.org/APT:SetApplicationCpuTimeLimit
Result APT_SetAppCpuTimeLimit(u32 percent)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x4F,2,0); // 0x4F0080
cmdbuf[1]=1;
cmdbuf[2]=percent;
return aptSendCommand(cmdbuf);
}
Result APT_GetAppCpuTimeLimit(u32 *percent)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x50,1,0); // 0x500040
cmdbuf[1]=1;
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*percent=cmdbuf[2];
return ret;
}
static Result APT_CheckNew3DS_System(bool* out)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x102,0,0); // 0x1020000
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
*out = cmdbuf[2] & 0xFF;
return ret;
}
Result APT_CheckNew3DS(bool* out)
{
static bool flagInit, flagValue;
if (!flagInit)
{
*out = false;
Result ret = APT_CheckNew3DS_System(&flagValue);
if (R_FAILED(ret)) return ret;
flagInit = true;
}
*out = flagValue;
return 0;
}
Result APT_PrepareToDoApplicationJump(u8 flags, u64 programID, u8 mediatype)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x31,4,0); // 0x310100
cmdbuf[1]=flags;
cmdbuf[2]=(u32)programID;
cmdbuf[3]=(u32)(programID>>32);
cmdbuf[4]=mediatype;
return aptSendCommand(cmdbuf);
}
Result APT_DoApplicationJump(const void* param, size_t paramSize, const void* hmac)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x32,2,4); // 0x320084
cmdbuf[1]=paramSize;
cmdbuf[2]=hmac ? 0x20 : 0;
cmdbuf[3]=IPC_Desc_StaticBuffer(cmdbuf[1],0);
cmdbuf[4]=(u32)param;
cmdbuf[5]=IPC_Desc_StaticBuffer(cmdbuf[2],2);
cmdbuf[6]=(u32)hmac;
return aptSendCommand(cmdbuf);
}
Result APT_PrepareToStartLibraryApplet(NS_APPID appID)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x18,1,0); // 0x180040
cmdbuf[1]=appID;
return aptSendCommand(cmdbuf);
}
Result APT_StartLibraryApplet(NS_APPID appID, const void* param, size_t paramSize, Handle handle)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x1E,2,4); // 0x1E0084
cmdbuf[1]=appID;
cmdbuf[2]=paramSize;
cmdbuf[3]=IPC_Desc_SharedHandles(1);
cmdbuf[4]=handle;
cmdbuf[5]=IPC_Desc_StaticBuffer(cmdbuf[2],0);
cmdbuf[6]=(u32)param;
return aptSendCommand(cmdbuf);
}
Result APT_PrepareToStartSystemApplet(NS_APPID appID)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x19,1,0); // 0x190040
cmdbuf[1]=appID;
return aptSendCommand(cmdbuf);
}
Result APT_StartSystemApplet(NS_APPID appID, const void* param, size_t paramSize, Handle handle)
{
u32 cmdbuf[16];
cmdbuf[0] = IPC_MakeHeader(0x1F,2,4); // 0x001F0084
cmdbuf[1] = appID;
cmdbuf[2] = paramSize;
cmdbuf[3] = IPC_Desc_SharedHandles(1);
cmdbuf[4] = handle;
cmdbuf[5] = IPC_Desc_StaticBuffer(cmdbuf[2],0);
cmdbuf[6] = (u32)param;
return aptSendCommand(cmdbuf);
}
Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr)
{
u32 cmdbuf[16];
cmdbuf[0] = IPC_MakeHeader(0x44,0,0); // 0x00440000
Result ret = aptSendCommand(cmdbuf);
if (R_SUCCEEDED(ret))
{
if(fontHandle) *fontHandle = cmdbuf[4];
if(mapAddr) *mapAddr = cmdbuf[2];
}
return ret;
}
Result APT_ReceiveDeliverArg(const void* param, size_t paramSize, const void* hmac, u64* sender, bool* received)
{
u32 cmdbuf[16];
cmdbuf[0]=IPC_MakeHeader(0x35,2,0); // 0x350080
cmdbuf[1]=paramSize;
cmdbuf[2]=hmac ? 0x20 : 0;
u32 saved_threadstorage[4];
u32* staticbufs = getThreadStaticBuffers();
saved_threadstorage[0]=staticbufs[0];
saved_threadstorage[1]=staticbufs[1];
saved_threadstorage[2]=staticbufs[2];
saved_threadstorage[3]=staticbufs[3];
staticbufs[0]=IPC_Desc_StaticBuffer(cmdbuf[1],0);
staticbufs[1]=(u32)param;
staticbufs[2]=IPC_Desc_StaticBuffer(cmdbuf[2],2);
staticbufs[3]=(u32)hmac;
Result ret = aptSendCommand(cmdbuf);
staticbufs[0]=saved_threadstorage[0];
staticbufs[1]=saved_threadstorage[1];
staticbufs[2]=saved_threadstorage[2];
staticbufs[3]=saved_threadstorage[3];
if (R_SUCCEEDED(ret))
{
if (sender) *sender =cmdbuf[2] | ((u64)cmdbuf[3]<<32);
if (received) *received =cmdbuf[4] & 0xFF;
}
return ret;
}