diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index eaa360e..3bc0870 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/hb.h> 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/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]; +}