From 30f88a308b42ee1c6f700dbe6168dc3e02ac2cda Mon Sep 17 00:00:00 2001 From: "Claude Opus 5.5" Date: Fri, 30 Oct 2026 09:31:01 +0000 Subject: [PATCH 1/3] nn_nim: Add the title package task API nn_nim is the downloader the eShop handed its purchases to. A title package task registered with it is downloaded and installed by the system in the background, listed in the Download Management, and kept going in standby. The exports are already in nn_nim.def, but there was no header for them. This adds Initialize, Finalize, RegisterTitlePackageTask, UnregisterTitlePackageTask, GetNumTitlePackages, ListTitlePackagesStatically and GetTitlePackageInfos, with TitlePackageTaskConfig and TitlePackageInfo. The layouts follow Cemu, corrected where a 5.5.5E console disagreed: the word Cemu calls region or language related is the title version (0 there makes the system request tmd.0), ListTitlePackagesStatically returns package IDs rather than title IDs (the title ID is refused with 105-0259), and a package info starts with the config it was registered with. Fields that were neither observed nor named by Cemu stay unknown bytes, and the ones only Cemu names say so. ResultError is left out: Cemu emulates it as 8 bytes, but its real size is unconfirmed and a wrong size here would corrupt the stack of every caller. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KUHQm9dhEc1v1icAZBZA4f --- include/nn/nim.h | 8 + include/nn/nim/nim_cpp.h | 206 ++++++++++++++++++ .../test_compile_headers_list.h | 1 + 3 files changed, 215 insertions(+) create mode 100644 include/nn/nim.h create mode 100644 include/nn/nim/nim_cpp.h diff --git a/include/nn/nim.h b/include/nn/nim.h new file mode 100644 index 000000000..4e1e7487e --- /dev/null +++ b/include/nn/nim.h @@ -0,0 +1,8 @@ +#pragma once + +/** + * \defgroup nn_nim nn_nim + * System downloader: title package tasks, as listed in the Download Management. + */ + +#include diff --git a/include/nn/nim/nim_cpp.h b/include/nn/nim/nim_cpp.h new file mode 100644 index 000000000..1036012b3 --- /dev/null +++ b/include/nn/nim/nim_cpp.h @@ -0,0 +1,206 @@ +#pragma once +#include +#include + +/** + * \defgroup nn_nim_cpp Title Package API + * \ingroup nn_nim + * Hands titles to the system downloader (see nn::nim). + * + * A title package task registered here is downloaded and installed by the + * system itself, in the background, and is listed in the Download Management. + * With the standby functions of the console enabled it also runs while the + * console is switched off. This is the downloader the eShop handed its + * purchases to. + * + * The layouts come from Cemu and from testing on a 5.5.5E console. Fields + * named here were observed to behave as documented on that console, or are + * marked as coming from Cemu; everything else is left as unknown bytes. + * @{ + */ + +#ifdef __cplusplus + +namespace nn +{ + +namespace nim +{ + +//! A title package task, see \link RegisterTitlePackageTask \endlink. +struct WUT_PACKED TitlePackageTaskConfig +{ + //! Title ID of the title to download. + uint64_t titleId; + //! Version of the title to download. + //! Cemu calls this word region or language related, but the system builds + //! the path of the TMD from it: 0 makes it request tmd.0, which fails with + //! 105-0955 for a title that has no version 0. + uint32_t titleVersion; + //! Type of the title, a value of nn::Cafe::TitleType. + //! The values are undocumented. Seen on a 5.5.5E console: + //! - 0: application. Works for a game, and a demo installs as a demo + //! with it. + //! - 1: update, carried by an update package the system registered on its + //! own. Cemu also marks 1 as an update. + //! - 2: additional content. A demo registered with 2 was listed as + //! additional content and installed without an entry on the Wii U Menu. + //! + //! This is not the category byte of the title ID: 0x0C and 0x0E are + //! accepted, but left the Download Management blank. + uint8_t titleType; + //! Device to install to. 1 installs to the internal memory. An update the + //! system registered on its own, for a game stored on a USB drive, + //! carried 2 here and in \link applicationBoxDevice2 \endlink. + uint8_t applicationBoxDevice1; + WUT_UNKNOWN_BYTES(1); + //! Device to install to, set like \link applicationBoxDevice1 \endlink. + uint8_t applicationBoxDevice2; + WUT_UNKNOWN_BYTES(5); + //! Unknown. Cemu sets it to 1, and so did every config tested on hardware. + uint8_t unk_0x15; + //! What happens once the download is complete. + //! - 1: the title is installed right away, the way the eShop installs a purchase. + //! - 2: background install policy. The install waits for the next start of + //! the title, which a title that is not installed yet cannot have. + //! + //! 0 has not been observed. + uint8_t postDownloadAction; + WUT_UNKNOWN_BYTES(1); +}; +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x00, titleId); +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x08, titleVersion); +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x0C, titleType); +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x0D, applicationBoxDevice1); +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x0F, applicationBoxDevice2); +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x15, unk_0x15); +WUT_CHECK_OFFSET(TitlePackageTaskConfig, 0x16, postDownloadAction); +WUT_CHECK_SIZE(TitlePackageTaskConfig, 0x18); + +//! State of a title package, see \link GetTitlePackageInfos \endlink. +struct WUT_PACKED TitlePackageInfo +{ + //! The config the package was registered with. For a package registered + //! with \link RegisterTitlePackageTask \endlink these bytes equal the + //! config that was passed in. + TitlePackageTaskConfig config; + // On a 5.5.5E console 0x18 to 0x3F held three pairs of equal values for + // a finished download: two 64-bit byte counts matching the download size + // at 0x18 and 0x20, two smaller 64-bit byte counts at 0x28 and 0x30, and + // two 32-bit counts at 0x38 and 0x3C (7 and 7 for a DLC with 7 + // contents). Cemu reads 0x28 to 0x3F as download progress. Which value + // of a pair is the total and which the progress is not established. + WUT_UNKNOWN_BYTES(0x28); + // Cemu writes a result-like value at 0x40. Not verified on hardware. + WUT_UNKNOWN_BYTES(0x08); + //! State of the package. According to Cemu, values 0 to 7 are allowed, + //! 0 shows no progress bar and 3 shows one. A finished download waiting + //! to be installed read 5 on a 5.5.5E console. + uint8_t state; + WUT_UNKNOWN_BYTES(0x07); +}; +WUT_CHECK_OFFSET(TitlePackageInfo, 0x00, config); +WUT_CHECK_OFFSET(TitlePackageInfo, 0x48, state); +WUT_CHECK_SIZE(TitlePackageInfo, 0x50); + +/** + * Initializes nn_nim. Call it before any other function here. + */ +nn::Result +Initialize() + asm("Initialize__Q2_2nn3nimFv"); + +/** + * Finalizes nn_nim. + */ +nn::Result +Finalize() + asm("Finalize__Q2_2nn3nimFv"); + +/** + * Registers a title package task. The system downloads the title and, as + * \link TitlePackageTaskConfig::postDownloadAction \endlink says, installs it. + * + * Before it takes the task the system fetches the icon of the title from + * idbe-wup.cdn.nintendo.net, and a failure there refuses the task with + * 105-1017. It installs with the ticket that is on the console, so a title + * without one is downloaded and then fails to install with 105-3683. + * + * \param config + * The task to register. + * + * \param contents + * Indices of the contents to download, or \c nullptr to let the system pick + * them. A list of every content of the TMD was refused as a usage error + * (0xC0512E80) on a 5.5.5E console, while \c nullptr was accepted. + * + * \param numContents + * Number of entries in \p contents. + */ +nn::Result +RegisterTitlePackageTask(const TitlePackageTaskConfig &config, + const uint16_t *contents, + uint32_t numContents) + asm("RegisterTitlePackageTask__Q2_2nn3nimFRCQ3_2nn3nim22TitlePackageTaskConfigPCUsUi"); + +/** + * Removes a title package, along with what it downloaded so far. + * + * \param packageId + * ID of the package as \link ListTitlePackagesStatically \endlink returns it. + * This is not the title ID: the title ID was refused with 105-0259 + * (TITLE_PACKAGE_NOT_FOUND) on a console that listed the package of that very + * title as 0x1F. + */ +nn::Result +UnregisterTitlePackageTask(uint64_t packageId) + asm("UnregisterTitlePackageTask__Q2_2nn3nimFUL"); + +/** + * Gets the number of title packages the system holds, those of the eShop + * included. + */ +uint32_t +GetNumTitlePackages() + asm("GetNumTitlePackages__Q2_2nn3nimFv"); + +/** + * Lists the title packages the system holds. + * + * \param outPackageIds + * Receives the IDs of the packages. These are package IDs, not title IDs; + * \link GetTitlePackageInfos \endlink turns them into title IDs. + * + * \param maxCount + * Number of entries \p outPackageIds has room for. + */ +nn::Result +ListTitlePackagesStatically(uint64_t *outPackageIds, + uint32_t maxCount) + asm("ListTitlePackagesStatically__Q2_2nn3nimFPULUi"); + +/** + * Reads the state of title packages. + * + * \param outInfos + * Receives one \link TitlePackageInfo \endlink per entry of \p packageIds. + * + * \param packageIds + * IDs as \link ListTitlePackagesStatically \endlink returns them. + * + * \param count + * Number of entries in \p packageIds. + */ +nn::Result +GetTitlePackageInfos(TitlePackageInfo *outInfos, + const uint64_t *packageIds, + uint32_t count) + asm("GetTitlePackageInfos__Q2_2nn3nimFPQ3_2nn3nim16TitlePackageInfoPCULUi"); + +} // namespace nim + +} // namespace nn + +#endif + +/** @} */ diff --git a/tests/test_compile_headers_common/test_compile_headers_list.h b/tests/test_compile_headers_common/test_compile_headers_list.h index 0e9f871d0..159ea61ea 100644 --- a/tests/test_compile_headers_common/test_compile_headers_list.h +++ b/tests/test_compile_headers_common/test_compile_headers_list.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include From d18999c4f44daf038d3e4b0f5ec93e27239c8f45 Mon Sep 17 00:00:00 2001 From: "Claude Opus 5.5" Date: Fri, 30 Oct 2026 12:02:11 +0000 Subject: [PATCH 2/3] nn_nim: Document the title types that install The doc of titleType named 1 and 2 from what a 5.5.5E console showed: an update package the system registered on its own, and a demo that was installed as additional content. Both are tried now. An update registered with 1 and a DLC registered with 2 downloaded and installed through the Download Management on the same console. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KUHQm9dhEc1v1icAZBZA4f --- include/nn/nim/nim_cpp.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/nn/nim/nim_cpp.h b/include/nn/nim/nim_cpp.h index 1036012b3..9485cbc28 100644 --- a/include/nn/nim/nim_cpp.h +++ b/include/nn/nim/nim_cpp.h @@ -41,10 +41,12 @@ struct WUT_PACKED TitlePackageTaskConfig //! The values are undocumented. Seen on a 5.5.5E console: //! - 0: application. Works for a game, and a demo installs as a demo //! with it. - //! - 1: update, carried by an update package the system registered on its - //! own. Cemu also marks 1 as an update. - //! - 2: additional content. A demo registered with 2 was listed as - //! additional content and installed without an entry on the Wii U Menu. + //! - 1: update. An update registered with it installs, and an update + //! package the system registered on its own carried it. Cemu also marks + //! 1 as an update. + //! - 2: additional content. A DLC registered with it installs. A demo + //! registered with it was installed as additional content, without an + //! entry on the Wii U Menu. //! //! This is not the category byte of the title ID: 0x0C and 0x0E are //! accepted, but left the Download Management blank. From 59a26d7992fcc36e8b366a64ac75426c104dd3d0 Mon Sep 17 00:00:00 2001 From: "Claude Opus 5.5" Date: Fri, 30 Oct 2026 12:53:47 +0000 Subject: [PATCH 3/3] nn_nim: Document the USB install device The doc of applicationBoxDevice1 named 2 only from an update package a 5.5.5E console registered on its own for a game on a USB drive. A title registered with 2 has since been installed to the USB drive of that console, so the doc says what 2 does. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KUHQm9dhEc1v1icAZBZA4f --- include/nn/nim/nim_cpp.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/nn/nim/nim_cpp.h b/include/nn/nim/nim_cpp.h index 9485cbc28..34b7e9c4e 100644 --- a/include/nn/nim/nim_cpp.h +++ b/include/nn/nim/nim_cpp.h @@ -51,9 +51,9 @@ struct WUT_PACKED TitlePackageTaskConfig //! This is not the category byte of the title ID: 0x0C and 0x0E are //! accepted, but left the Download Management blank. uint8_t titleType; - //! Device to install to. 1 installs to the internal memory. An update the - //! system registered on its own, for a game stored on a USB drive, - //! carried 2 here and in \link applicationBoxDevice2 \endlink. + //! Device to install to: 1 installs to the internal memory, 2 to the USB + //! drive. An update the system registered on its own, for a game stored on + //! a USB drive, carried 2 here and in \link applicationBoxDevice2 \endlink. uint8_t applicationBoxDevice1; WUT_UNKNOWN_BYTES(1); //! Device to install to, set like \link applicationBoxDevice1 \endlink.