diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index b002661..5244f28 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -63,6 +63,7 @@ extern "C" { #include <3ds/services/news.h> #include <3ds/services/qtm.h> #include <3ds/services/srvpm.h> +#include <3ds/services/loader.h> #include <3ds/services/y2r.h> #include <3ds/services/mcuhwc.h> #include <3ds/services/hb.h> @@ -78,6 +79,8 @@ extern "C" { #include <3ds/applets/swkbd.h> #include <3ds/applets/error.h> +#include <3ds/applets/miiselector.h> + #include <3ds/sdmc.h> #include <3ds/romfs.h> #include <3ds/font.h> diff --git a/libctru/include/3ds/applets/miiselector.h b/libctru/include/3ds/applets/miiselector.h new file mode 100644 index 0000000..6792635 --- /dev/null +++ b/libctru/include/3ds/applets/miiselector.h @@ -0,0 +1,90 @@ +/** + * @file miiselector.h + * @brief Mii Selector Applet (appletEd). + */ + +#pragma once + +/// Magic value needed to launch the applet. +#define MIISELECTOR_MAGIC 0x13DE28CF + +/// Maximum length of title to be displayed at the top of the Mii selector applet +#define MIISELECTOR_TITLE_LEN 64 + +/// Number of Guest Miis available for selection +#define MIISELECTOR_GUESTMII_SLOTS 6 + +/// Maximum number of user Miis available for selection +#define MIISELECTOR_USERMII_SLOTS 100 + +/// Parameter structure passed to AppletEd +typedef struct +{ + char enable_cancel_button; ///< Enables canceling of selection if nonzero. + char enable_selecting_guests; ///< Makes Guets Miis selectable if nonzero. + char show_on_top_screen; ///< Shows applet on top screen if nonzero, + ///< otherwise show it on the bottom screen. + char _unk0x3[5]; ///< @private + u16 title[MIISELECTOR_TITLE_LEN]; ///< UTF16-LE string displayed at the top of the applet. If + ///< set to the empty string, a default title is displayed. + char _unk0x88[4]; ///< @private + char show_guest_page; ///< If nonzero, the applet shows a page with Guest + ///< Miis on launch. + char _unk0x8D[3]; ///< @private + u32 initial_index; ///< Index of the initially selected Mii. If + ///< @ref MiiSelectorConf.show_guest_page is + ///< set, this is the index of a Guest Mii, + ///< otherwise that of a user Mii. + char mii_guest_whitelist[MIISELECTOR_GUESTMII_SLOTS]; ///< Each byte set to a nonzero value + ///< enables its corresponding Guest + ///< Mii to be enabled for selection. + char mii_whitelist[MIISELECTOR_USERMII_SLOTS]; ///< Each byte set to a nonzero value enables + ///< its corresponding user Mii to be enabled + ///< for selection. + u16 _unk0xFE; ///< @private + u32 magic; ///< Will be set to @ref MIISELECTOR_MAGIC before launching the + ///< applet. +} MiiSelectorConf; + +/// Size of the data describing a single Mii +#define MIISELECTOR_MIIDATA_SIZE 0x5c + +/// Maximum length of the localized name of a Guest Mii +#define MIISELECTOR_GUESTMII_NAME_LEN 12 + +/// Structure written by AppletEd +typedef struct +{ + u32 no_mii_selected; ///< 0 if a Mii was selected, 1 if the selection was + ///< canceled. + u32 guest_mii_was_selected; ///< 1 if a Guest Mii was selected, 0 otherwise. + u32 guest_mii_index; ///< Index of the selected Guest Mii, + ///< 0xFFFFFFFF if no guest was selected. + char mii[MIISELECTOR_MIIDATA_SIZE]; ///< Data of selected Mii. + u16 _pad0x68; ///< @private + u16 checksum; ///< Checksum of the returned Mii data. + ///< Stored as a big-endian value; use + ///< @ref miiSelectorChecksumIsValid to + ///< verify. + u16 guest_mii_name[MIISELECTOR_GUESTMII_NAME_LEN]; ///< Localized name of a Guest Mii, + ///< if one was selected (UTF16-LE + ///< string). Zeroed otherwise. +} MiiSelectorReturn; + +/** + * @brief Launch the Mii selector library applet + * + * @param conf Configuration determining how the applet should behave + * @param returnbuf Data returned by the applet + */ +Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn* returnbuf); + +/** + * @brief Verifies that the Mii data returned from the applet matches its + * checksum + * + * @param returnbuffer Buffer filled by Mii selector applet + * + * @return `true` if `returnbuf->checksum` is the same as the one computed from `returnbuf` + */ +bool miiSelectorChecksumIsValid(const MiiSelectorReturn *returnbuf); diff --git a/libctru/include/3ds/gpu/enums.h b/libctru/include/3ds/gpu/enums.h index 5082b33..e9b0bdb 100644 --- a/libctru/include/3ds/gpu/enums.h +++ b/libctru/include/3ds/gpu/enums.h @@ -385,7 +385,7 @@ typedef enum /// Light distance attenuation disable bits in GPUREG_LIGHT_CONFIG1. #define GPU_LC1_ATTNBIT(n) BIT((n)+24) /// Creates a light permutation parameter. -#define GPU_LIGHTPERM(i,n) ((n) << (i)) +#define GPU_LIGHTPERM(i,n) ((n) << ((i)*4)) /// Creates a light LUT input parameter. #define GPU_LIGHTLUTINPUT(i,n) ((n) << ((i)*4)) /// Creates a light LUT index parameter. diff --git a/libctru/include/3ds/gpu/gpu.h b/libctru/include/3ds/gpu/gpu.h index d43b0a0..e3bfb44 100644 --- a/libctru/include/3ds/gpu/gpu.h +++ b/libctru/include/3ds/gpu/gpu.h @@ -56,12 +56,6 @@ static inline void GPUCMD_GetBuffer(u32** addr, u32* size, u32* offset) */ void GPUCMD_AddRawCommands(const u32* cmd, u32 size); -/// Executes the GPU command buffer. -DEPRECATED void GPUCMD_Run(void); - -/// Flushes linear memory and executes the GPU command buffer. -DEPRECATED void GPUCMD_FlushAndRun(void); - /** * @brief Adds a GPU command to the current command buffer. * @param header Header of the command. @@ -77,9 +71,6 @@ void GPUCMD_Add(u32 header, const u32* param, u32 paramlength); */ void GPUCMD_Split(u32** addr, u32* size); -/// Finalizes the GPU command buffer. -DEPRECATED void GPUCMD_Finalize(void); - /** * @brief Converts a 32-bit float to a 16-bit float. * @param f Float to convert. diff --git a/libctru/include/3ds/services/gspgpu.h b/libctru/include/3ds/services/gspgpu.h index 8c359a6..6636c46 100644 --- a/libctru/include/3ds/services/gspgpu.h +++ b/libctru/include/3ds/services/gspgpu.h @@ -218,3 +218,8 @@ Result GSPGPU_UnregisterInterruptRelayQueue(void); /// Triggers a handling of commands written to shared memory. Result GSPGPU_TriggerCmdReqQueue(void); +/** + * @brief Sets 3D_LEDSTATE to the input state value. + * @param disable False = 3D LED enable, true = 3D LED disable. + */ +Result GSPGPU_SetLedForceOff(bool disable); \ No newline at end of file diff --git a/libctru/include/3ds/services/gsplcd.h b/libctru/include/3ds/services/gsplcd.h index fdb219e..f044da9 100644 --- a/libctru/include/3ds/services/gsplcd.h +++ b/libctru/include/3ds/services/gsplcd.h @@ -19,6 +19,12 @@ Result gspLcdInit(void); /// Exits GSPLCD. void gspLcdExit(void); +/// Powers on both backlights. +Result GSPLCD_PowerOnAllBacklights(void); + +/// Powers off both backlights. +Result GSPLCD_PowerOffAllBacklights(void); + /** * @brief Powers on the backlight. * @param screen Screen to power on. @@ -31,6 +37,12 @@ Result GSPLCD_PowerOnBacklight(u32 screen); */ Result GSPLCD_PowerOffBacklight(u32 screen); +/** + * @brief Sets 3D_LEDSTATE to the input state value. + * @param disable False = 3D LED enable, true = 3D LED disable. + */ +Result GSPLCD_SetLedForceOff(bool disable); + /** * @brief Gets the LCD screens' vendors. Stubbed on old 3ds. * @param vendor Pointer to output the screen vendors to. diff --git a/libctru/include/3ds/services/ir.h b/libctru/include/3ds/services/ir.h index ee7250d..9521316 100644 --- a/libctru/include/3ds/services/ir.h +++ b/libctru/include/3ds/services/ir.h @@ -87,7 +87,7 @@ Result IRU_GetBitRate(u8 *out); Result IRU_SetIRLEDState(u32 value); /** - * @brief Gets the IR KED state. + * @brief Gets the IR LED state. * @param out Pointer to write the IR LED state to. */ Result IRU_GetIRLEDRecvState(u32 *out); diff --git a/libctru/include/3ds/services/loader.h b/libctru/include/3ds/services/loader.h new file mode 100644 index 0000000..e7f9fd2 --- /dev/null +++ b/libctru/include/3ds/services/loader.h @@ -0,0 +1,45 @@ +/** + * @file loader.h + * @brief LOADER Service + */ + +#pragma once + +#include <3ds/exheader.h> +#include <3ds/services/fs.h> + +/// Initializes LOADER. +Result loaderInit(void); + +/// Exits LOADER. +void loaderExit(void); + +/** + * @brief Loads a program and returns a process handle to the newly created process. + * @param[out] process Pointer to output the process handle to. + * @param programHandle The handle of the program to load. + */ +Result LOADER_LoadProcess(Handle* process, u64 programHandle); + +/** + * @brief Registers a program (along with its update). + * @param[out] programHandle Pointer to output the program handle to. + * @param titleId The program's title ID. + * @param mediaType The program's media type. + * @param updateTitleId The program update's title ID. + * @param updateMediaType The program update's media type. + */ +Result LOADER_RegisterProgram(u64* programHandle, u64 titleId, FS_MediaType mediaType, u64 updateTitleId, FS_MediaType updateMediaType); + +/** + * @brief Registers a program (along with its update). + * @param programHandle The handle of the program to unregister. + */ +Result LOADER_UnregisterProgram(u64 programHandle); + +/** + * @brief Retrives a program's main NCCH extended header info (SCI + ACI, see @ref ExHeader_Info). + * @param[out] exheaderInfo Pointer to output the main NCCH extended header info. + * @param programHandle The handle of the program to unregister + */ +Result LOADER_GetProgramInfo(ExHeader_Info* exheaderInfo, u64 programHandle); diff --git a/libctru/source/applets/miiselector.c b/libctru/source/applets/miiselector.c new file mode 100644 index 0000000..365dea2 --- /dev/null +++ b/libctru/source/applets/miiselector.c @@ -0,0 +1,57 @@ +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/services/apt.h> + +#include <3ds/applets/miiselector.h> + +#include // for memcpy + +Result miiSelectorLaunch(const MiiSelectorConf *conf, MiiSelectorReturn *returnbuf) +{ + union { + MiiSelectorConf config; + MiiSelectorReturn ret; + } ctx; + + memcpy(&ctx.config, conf, sizeof(MiiSelectorConf)); + ctx.config.magic = MIISELECTOR_MAGIC; + + Result ret = aptLaunchLibraryApplet(APPID_APPLETED, &ctx.config, sizeof(MiiSelectorConf), 0); + if(R_SUCCEEDED(ret) && returnbuf) { + memcpy(returnbuf, &ctx.ret, sizeof(MiiSelectorReturn)); + } + + return ret; +} + +static u16 crc16_ccitt(void const *buf, size_t len, uint32_t starting_val) +{ + if(buf == NULL) { + return -1; + } + + u8 const *cbuf = buf; + u32 crc = starting_val; + + static const u16 POLY = 0x1021; + + for(size_t i = 0; i < len; i++) { + for(int bit = 7; bit >= 0; bit--) { + crc = ((crc << 1) | ((cbuf[i] >> bit) & 0x1)) ^ (crc & 0x8000 ? POLY : 0); + } + } + + for(int _ = 0; _ < 16; _++) { + crc = (crc << 1) ^ (crc & 0x8000 ? POLY : 0); + } + + return (u16)(crc & 0xffff); +} + +bool miiSelectorChecksumIsValid(const MiiSelectorReturn *returnbuf) +{ + u16 computed = + crc16_ccitt(&returnbuf->mii, sizeof(returnbuf->mii) + sizeof(returnbuf->_pad0x68), 0x0000); + u16 chk_little_endian = __builtin_bswap16(returnbuf->checksum); + return computed == chk_little_endian; +} diff --git a/libctru/source/gpu/gpu.c b/libctru/source/gpu/gpu.c index db77129..3b7afd2 100644 --- a/libctru/source/gpu/gpu.c +++ b/libctru/source/gpu/gpu.c @@ -22,21 +22,6 @@ void GPUCMD_AddRawCommands(const u32* cmd, u32 size) gpuCmdBufOffset+=size; } -void GPUCMD_Run(void) -{ - GX_ProcessCommandList(gpuCmdBuf, gpuCmdBufOffset*4, GX_CMDLIST_FLUSH); -} - -extern u32 __ctru_linear_heap; -extern u32 __ctru_linear_heap_size; - -void GPUCMD_FlushAndRun(void) -{ - //take advantage of GX_FlushCacheRegions to flush gsp heap - GX_FlushCacheRegions(gpuCmdBuf, gpuCmdBufOffset*4, (u32 *) __ctru_linear_heap, __ctru_linear_heap_size, NULL, 0); - GX_ProcessCommandList(gpuCmdBuf, gpuCmdBufOffset*4, 0x0); -} - void GPUCMD_Add(u32 header, const u32* param, u32 paramlength) { if(!paramlength)paramlength=1; @@ -74,15 +59,6 @@ void GPUCMD_Split(u32** addr, u32* size) gpuCmdBufOffset = 0; } -void GPUCMD_Finalize(void) -{ - GPUCMD_AddMaskedWrite(GPUREG_PRIMITIVE_CONFIG, 0x8, 0x00000000); - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_FLUSH, 0x00000001); - GPUCMD_AddWrite(GPUREG_FRAMEBUFFER_INVALIDATE, 0x00000001); - GPUCMD_AddWrite(GPUREG_FINALIZE, 0x12345678); - GPUCMD_AddWrite(GPUREG_FINALIZE, 0x12345678); //not the cleanest way of guaranteeing 0x10-byte size but whatever good enough for now -} - static inline u32 floatrawbits(float f) { union { float f; u32 i; } s; diff --git a/libctru/source/services/gspgpu.c b/libctru/source/services/gspgpu.c index 8d86689..1247267 100644 --- a/libctru/source/services/gspgpu.c +++ b/libctru/source/services/gspgpu.c @@ -430,3 +430,15 @@ Result GSPGPU_RestoreVramSysArea(void) return cmdbuf[1]; } +Result GSPGPU_SetLedForceOff(bool disable) +{ + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x1C,1,0); // 0x1C0040 + cmdbuf[1] = disable & 0xFF; + + Result ret=0; + if (R_FAILED(ret = svcSendSyncRequest(gspGpuHandle))) return ret; + + return cmdbuf[1]; +} \ No newline at end of file diff --git a/libctru/source/services/gsplcd.c b/libctru/source/services/gsplcd.c index 0183e56..06ab178 100644 --- a/libctru/source/services/gsplcd.c +++ b/libctru/source/services/gsplcd.c @@ -26,6 +26,30 @@ void gspLcdExit(void) svcCloseHandle(gspLcdHandle); } +Result GSPLCD_PowerOnAllBacklights(void) +{ + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x0F,0,0); // 0x0F0000 + + Result ret=0; + if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret; + + return cmdbuf[1]; +} + +Result GSPLCD_PowerOffAllBacklights(void) +{ + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x10,0,0); // 0x100000 + + Result ret=0; + if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret; + + return cmdbuf[1]; +} + Result GSPLCD_PowerOnBacklight(u32 screen) { u32 *cmdbuf = getThreadCommandBuffer(); @@ -52,6 +76,19 @@ Result GSPLCD_PowerOffBacklight(u32 screen) return cmdbuf[1]; } +Result GSPLCD_SetLedForceOff(bool disable) +{ + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x13,1,0); // 0x130040 + cmdbuf[1] = disable & 0xFF; + + Result ret=0; + if (R_FAILED(ret = svcSendSyncRequest(gspLcdHandle))) return ret; + + return cmdbuf[1]; +} + Result GSPLCD_GetVendors(u8 *vendors) { u32 *cmdbuf = getThreadCommandBuffer(); diff --git a/libctru/source/services/loader.c b/libctru/source/services/loader.c new file mode 100644 index 0000000..5d6235a --- /dev/null +++ b/libctru/source/services/loader.c @@ -0,0 +1,93 @@ +#include <3ds/services/loader.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/srv.h> +#include <3ds/synchronization.h> +#include <3ds/ipc.h> + +static Handle loaderHandle; +static int loaderRefCount; + +Result loaderInit(void) +{ + Result res; + if (AtomicPostIncrement(&loaderRefCount)) return 0; + res = srvGetServiceHandle(&loaderHandle, "Loader"); + if (R_FAILED(res)) AtomicDecrement(&loaderRefCount); + return res; +} + +void loaderExit(void) +{ + if (AtomicDecrement(&loaderRefCount)) return; + svcCloseHandle(loaderHandle); +} + +Result LOADER_LoadProcess(Handle* process, u64 programHandle) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(1, 2, 0); // 0x10080 + cmdbuf[1] = (u32)programHandle; + cmdbuf[2] = (u32)(programHandle >> 32); + + if(R_FAILED(ret = svcSendSyncRequest(loaderHandle))) return ret; + + *process = cmdbuf[3]; + + return (Result)cmdbuf[1]; +} + +Result LOADER_RegisterProgram(u64* programHandle, u64 titleId, FS_MediaType mediaType, u64 updateTitleId, FS_MediaType updateMediaType) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(2, 8, 0); // 0x20200 + cmdbuf[1] = (u32)titleId; + cmdbuf[2] = (u32)(titleId >> 32); + cmdbuf[3] = mediaType; + + cmdbuf[5] = (u32)updateTitleId; + cmdbuf[6] = (u32)(updateTitleId >> 32); + cmdbuf[7] = updateMediaType; + + if(R_FAILED(ret = svcSendSyncRequest(loaderHandle))) return ret; + + *programHandle = ((u64)cmdbuf[2] << 32) | cmdbuf[3]; + + return (Result)cmdbuf[1]; +} + +Result LOADER_UnregisterProgram(u64 programHandle) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(3, 2, 0); // 0x30080 + cmdbuf[1] = (u32)programHandle; + cmdbuf[2] = (u32)(programHandle >> 32); + + if(R_FAILED(ret = svcSendSyncRequest(loaderHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result LOADER_GetProgramInfo(ExHeader_Info* exheaderInfo, u64 programHandle) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + u32 *staticbufs = getThreadStaticBuffers(); + + cmdbuf[0] = IPC_MakeHeader(4, 2, 0); // 0x40080 + cmdbuf[1] = (u32)programHandle; + cmdbuf[2] = (u32)(programHandle >> 32); + + staticbufs[0] = IPC_Desc_StaticBuffer(0x400, 0); + staticbufs[1] = (u32)exheaderInfo; + + if(R_FAILED(ret = svcSendSyncRequest(loaderHandle))) return ret; + + return (Result)cmdbuf[1]; +}