Add nim:s client implementation.
This commit is contained in:
parent
648ee46742
commit
4f2eadef96
@ -46,6 +46,7 @@ extern "C" {
|
||||
#include <3ds/services/httpc.h>
|
||||
#include <3ds/services/uds.h>
|
||||
#include <3ds/services/ndm.h>
|
||||
#include <3ds/services/nim.h>
|
||||
#include <3ds/services/nwmext.h>
|
||||
#include <3ds/services/ir.h>
|
||||
#include <3ds/services/ns.h>
|
||||
|
132
libctru/include/3ds/services/nim.h
Normal file
132
libctru/include/3ds/services/nim.h
Normal file
@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @file nim.h
|
||||
* @brief NIM (network installation management) service.
|
||||
*
|
||||
* This service is used to download and install titles from Nintendo's CDN.
|
||||
*
|
||||
* We differentiate between two different kinds of downloads:
|
||||
*
|
||||
* - "active" downloads, which are downloads started with @ref NIMS_StartDownload or @ref NIMS_StartDownloadSimple. The process must keep polling the current status using @ref NIMS_GetProgress.
|
||||
* - "tasks", which are downloads started with @ref NIMS_RegisterTask. These are only processed in sleep mode.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// FS_MediaType
|
||||
#include <3ds/services/fs.h>
|
||||
|
||||
/// Mode that NIM downloads/installs a title with.
|
||||
typedef enum
|
||||
{
|
||||
IM_DEFAULT = 0, ///< Initial installation
|
||||
IM_UNKNOWN1, ///< Unknown
|
||||
IM_UNKNOWN2, ///< Unknown
|
||||
IM_REINSTALL ///< Reinstall currently installed title; use this if the title is already installed (including updates)
|
||||
} NIM_InstallationMode;
|
||||
|
||||
/// Current state of a NIM download/installation.
|
||||
typedef enum
|
||||
{
|
||||
DS_NOT_INITIALIZED = 0, ///< Download not yet initialized
|
||||
DS_INITIALIZED, ///< Download initialized
|
||||
DS_DOWNLOAD_TMD, ///< Downloading and installing TMD
|
||||
DS_PREPARE_SAVE_DATA, ///< Initializing save data
|
||||
DS_DOWNLOAD_CONTENTS, ///< Downloading and installing contents
|
||||
DS_WAIT_COMMIT, ///< Waiting before calling AM_CommitImportTitles
|
||||
DS_COMMITTING, ///< Running AM_CommitImportTitles
|
||||
DS_FINISHED, ///< Title installation finished
|
||||
DS_VERSION_ERROR, ///< (unknown error regarding title version)
|
||||
DS_CREATE_CONTEXT, ///< Creating the .ctx file?
|
||||
DS_CANNOT_RECOVER, ///< Irrecoverable error encountered (e.g. out of space)
|
||||
DS_INVALID ///< Invalid state
|
||||
} NIM_DownloadState;
|
||||
|
||||
/// Input configuration for NIM download/installation tasks.
|
||||
typedef struct
|
||||
{
|
||||
u64 titleId; ///< Title ID
|
||||
u32 version; ///< Title version
|
||||
u32 unknown_0; ///< Always 0
|
||||
u8 ratingAge; ///< Age for the HOME Menu parental controls
|
||||
u8 mediaType; ///< Media type, see @ref FS_MediaType enum
|
||||
u8 padding[2]; ///< Padding
|
||||
u32 unknown_1; ///< Unknown input, seems to be always 0
|
||||
} NIM_TitleConfig;
|
||||
|
||||
/// Output struct for NIM downloads/installations in progress.
|
||||
typedef struct
|
||||
{
|
||||
u32 state; ///< State, see NIM_DownloadState enum
|
||||
Result lastResult; ///< Last result code in NIM
|
||||
u64 downloadedSize; ///< Amount of bytes that have been downloaded
|
||||
u64 totalSize; ///< Amount of bytes that need to be downloaded in total
|
||||
} NIM_TitleProgress;
|
||||
|
||||
/// Initializes nim:s. This uses networking and is blocking.
|
||||
Result nimsInit(void);
|
||||
|
||||
/// Exits nim:s.
|
||||
void nimsExit(void);
|
||||
|
||||
/// Gets the current nim:s session handle.
|
||||
Handle *nimsGetSessionHandle(void);
|
||||
|
||||
/**
|
||||
* @brief Checks if nim wants a system update.
|
||||
* @param want_update Set to true if a system update is required. Can be NULL.
|
||||
*/
|
||||
Result NIMS_WantUpdate(bool *want_update);
|
||||
|
||||
/**
|
||||
* @brief Makes a TitleConfig struct for use with @ref NIMS_RegisterTask, @ref NIMS_StartDownload or @ref NIMS_StartDownloadSimple.
|
||||
* @param cfg Struct to initialize.
|
||||
* @param titleId Title ID to download and install.
|
||||
* @param version Version of the title to download and install.
|
||||
* @param ratingAge Age for which the title is aged; used by parental controls in HOME Menu.
|
||||
* @param mediaType Media type of the title to download and install.
|
||||
*/
|
||||
void NIMS_MakeTitleConfig(NIM_TitleConfig *cfg, u64 titleId, u32 version, u8 ratingAge, FS_MediaType mediaType);
|
||||
|
||||
/**
|
||||
* @brief Registers a background download task with NIM. These are processed in sleep mode only.
|
||||
* @param cfg Title config to use. See @ref NIMS_MakeTitleConfig.
|
||||
* @param name Name of the title in UTF-8. Will be displayed on the HOME Menu. Maximum 73 characters.
|
||||
* @param maker Name of the maker/publisher in UTF-8. Will be displayed on the HOME Menu. Maximum 37 characters.
|
||||
*/
|
||||
Result NIMS_RegisterTask(const NIM_TitleConfig *cfg, const char *name, const char *maker);
|
||||
|
||||
/**
|
||||
* @brief Checks whether a background download task for the given title is registered with NIM.
|
||||
* @param titleId Title ID to check for.
|
||||
* @param registered Whether there is a background download task registered.
|
||||
*/
|
||||
Result NIMS_IsTaskRegistered(u64 titleId, bool *registered);
|
||||
|
||||
/**
|
||||
* @brief Unregisters a background download task.
|
||||
* @param titleId Title ID whose background download task to cancel.
|
||||
*/
|
||||
Result NIMS_UnregisterTask(u64 titleId);
|
||||
|
||||
/**
|
||||
* @brief Starts an active download with NIM. Progress can be checked with @ref NIMS_GetProcess. Do not exit the process while a download is in progress without calling @ref NIMS_CancelDownload.
|
||||
* @param cfg Title config to use. See @ref NIMS_MakeTitleConfig.
|
||||
* @param mode The installation mode to use. See @ref NIM_InstallationMode.
|
||||
*/
|
||||
Result NIMS_StartDownload(const NIM_TitleConfig *cfg, NIM_InstallationMode mode);
|
||||
|
||||
/**
|
||||
* @brief Starts an active download with NIM with default installation mode; cannot reinstall titles. Progress can be checked with @ref NIMS_GetProcess. Do not exit the process while a download is in progress without calling @ref NIMS_CancelDownload.
|
||||
* @param cfg Title config to use. See @ref NIMS_MakeTitleConfig.
|
||||
*/
|
||||
Result NIMS_StartDownloadSimple(const NIM_TitleConfig *cfg);
|
||||
|
||||
/**
|
||||
* @brief Checks the status of the current active download.
|
||||
* @param tp Title progress struct to write to. See @ref NIM_TitleProgress.
|
||||
*/
|
||||
Result NIMS_GetProgress(NIM_TitleProgress *tp);
|
||||
|
||||
/**
|
||||
* @brief Cancels the current active download with NIM.
|
||||
*/
|
||||
Result NIMS_CancelDownload(void);
|
254
libctru/source/services/nim.c
Normal file
254
libctru/source/services/nim.c
Normal file
@ -0,0 +1,254 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/srv.h>
|
||||
#include <3ds/synchronization.h>
|
||||
#include <3ds/services/nim.h>
|
||||
#include <3ds/ipc.h>
|
||||
#include <3ds/util/utf.h>
|
||||
|
||||
static Handle nimsHandle;
|
||||
static int nimsRefCount;
|
||||
|
||||
static Result NIMS_GetInitializeResult(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3f, 0, 0); // 0x003f0000
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
static Result NIMS_RegisterSelf(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3c, 0, 2); // 0x003c0002
|
||||
cmdbuf[1] = IPC_Desc_CurProcessHandle();
|
||||
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
static Result NIMS_SetTIN(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
static char attr[] = "TIN";
|
||||
static char TIN[] = "56789"; // Obtained from eShop
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0xb, 2, 4); // 0x000b0084
|
||||
cmdbuf[1] = sizeof(attr);
|
||||
cmdbuf[2] = sizeof(TIN);
|
||||
cmdbuf[3] = IPC_Desc_StaticBuffer(cmdbuf[1], 0);
|
||||
cmdbuf[4] = (u32)attr;
|
||||
cmdbuf[5] = IPC_Desc_StaticBuffer(cmdbuf[2], 1);
|
||||
cmdbuf[6] = (u32)TIN;
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
static Result NIMS_ConnectNoTicketDownload(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
static u8 buffer[0x20000];
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x57, 2, 2); // 0x00570082
|
||||
cmdbuf[1] = (u32)buffer; // What the fuck?
|
||||
cmdbuf[2] = sizeof(buffer);
|
||||
cmdbuf[3] = IPC_Desc_Buffer(sizeof(buffer), IPC_BUFFER_W);
|
||||
cmdbuf[4] = (u32)buffer;
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result nimsInit(void)
|
||||
{
|
||||
Result res;
|
||||
if (AtomicPostIncrement(&nimsRefCount)) return 0;
|
||||
res = srvGetServiceHandle(&nimsHandle, "nim:s");
|
||||
if (R_FAILED(res)) AtomicDecrement(&nimsRefCount);
|
||||
|
||||
if (R_FAILED(res = NIMS_GetInitializeResult())) {
|
||||
nimsExit();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (R_FAILED(res = NIMS_RegisterSelf())) {
|
||||
nimsExit();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (R_FAILED(res = NIMS_SetTIN())) {
|
||||
nimsExit();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (R_FAILED(res = NIMS_ConnectNoTicketDownload())) {
|
||||
nimsExit();
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void nimsExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&nimsRefCount)) return;
|
||||
svcCloseHandle(nimsHandle);
|
||||
}
|
||||
|
||||
Handle *nimsGetSessionHandle(void)
|
||||
{
|
||||
return &nimsHandle;
|
||||
}
|
||||
|
||||
Result NIMS_WantUpdate(bool *want_update)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0xa, 0, 0); //0x000a0000
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
if (want_update) *want_update = cmdbuf[2] & 0xFF;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
void NIMS_MakeTitleConfig(NIM_TitleConfig *cfg, u64 titleId, u32 version, u8 ratingAge, FS_MediaType mediaType)
|
||||
{
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
|
||||
cfg->titleId = titleId;
|
||||
cfg->version = version;
|
||||
cfg->ratingAge = ratingAge;
|
||||
cfg->mediaType = mediaType;
|
||||
}
|
||||
|
||||
Result NIMS_RegisterTask(const NIM_TitleConfig *cfg, const char *name, const char *maker)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
static uint16_t name16[0x92], maker16[0x4a];
|
||||
ssize_t units;
|
||||
|
||||
units = utf8_to_utf16(name16, (const uint8_t *)name, sizeof(name16)/sizeof(*name16));
|
||||
if (units < 0 || units > sizeof(name16)/sizeof(*name16)) return -2;
|
||||
units = utf8_to_utf16(maker16, (const uint8_t *)maker, sizeof(maker16)/sizeof(*maker16));
|
||||
if (units < 0 || units > sizeof(maker16)/sizeof(*maker16)) return -2;
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x55, 9, 6); //0x00550246
|
||||
memcpy(&cmdbuf[1], cfg, sizeof(*cfg));
|
||||
// unused: cmdbuf[7], cmdbuf[8]
|
||||
cmdbuf[9] = 0; // always 0? At least it is for eShop
|
||||
cmdbuf[10] = IPC_Desc_CurProcessHandle();
|
||||
// unused: cmdbuf[11]
|
||||
cmdbuf[12] = IPC_Desc_StaticBuffer(sizeof(name16), 1);
|
||||
cmdbuf[13] = (unsigned int)name16;
|
||||
cmdbuf[14] = IPC_Desc_StaticBuffer(sizeof(maker16), 5);
|
||||
cmdbuf[15] = (unsigned int)maker16;
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result NIMS_IsTaskRegistered(u64 titleId, bool *registered)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x6, 2, 0); //0x00060080
|
||||
cmdbuf[1] = titleId & 0xFFFFFFFF;
|
||||
cmdbuf[2] = (u32) ((titleId >> 32) & 0xFFFFFFFF);
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
if (registered) *registered = cmdbuf[2] & 0xFF;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result NIMS_UnregisterTask(u64 titleId)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x5, 2, 2); //0x00050082
|
||||
cmdbuf[1] = titleId & 0xFFFFFFFF;
|
||||
cmdbuf[2] = (u32) ((titleId >> 32) & 0xFFFFFFFF);
|
||||
cmdbuf[3] = IPC_Desc_CurProcessHandle();
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result NIMS_StartDownload(const NIM_TitleConfig *cfg, NIM_InstallationMode mode)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x42, 9, 0); //0x00420240
|
||||
memcpy(&cmdbuf[1], cfg, sizeof(*cfg));
|
||||
// unused: cmdbuf[7], cmdbuf[8]
|
||||
cmdbuf[9] = mode;
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result NIMS_StartDownloadSimple(const NIM_TitleConfig *cfg)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x1, 8, 0); //0x00010200
|
||||
memcpy(&cmdbuf[1], cfg, sizeof(*cfg));
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result NIMS_GetProgress(NIM_TitleProgress *tp)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3, 0, 0); //0x00030000
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle)))
|
||||
return ret;
|
||||
|
||||
memcpy(tp, &cmdbuf[2], sizeof(*tp));
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Result NIMS_CancelDownload(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x2, 0, 0); //0x00020000
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(nimsHandle))) return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
Loading…
Reference in New Issue
Block a user