From 6bef34852c9225802cb62459b39bf3d40f82594b Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Thu, 9 Feb 2023 11:58:08 -0800 Subject: [PATCH] Add option to configure to download system files from Nintendo Update Service (#6269) Co-authored-by: B3n30 --- CMakeLists.txt | 2 +- src/citra_qt/CMakeLists.txt | 2 +- .../configuration/configure_system.cpp | 75 ++ src/citra_qt/configuration/configure_system.h | 2 + .../configuration/configure_system.ui | 46 ++ src/citra_qt/main.cpp | 2 + src/core/file_sys/cia_container.cpp | 2 - src/core/file_sys/cia_container.h | 6 +- src/core/hle/service/am/am.cpp | 106 ++- src/core/hle/service/am/am.h | 7 + src/core/hw/aes/key.cpp | 6 +- src/core/hw/aes/key.h | 4 +- src/web_service/CMakeLists.txt | 3 + src/web_service/nus_download.cpp | 47 ++ src/web_service/nus_download.h | 15 + src/web_service/nus_titles.h | 761 ++++++++++++++++++ 16 files changed, 1076 insertions(+), 10 deletions(-) create mode 100644 src/web_service/nus_download.cpp create mode 100644 src/web_service/nus_download.h create mode 100644 src/web_service/nus_titles.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c98cc3a09..cd85c0b8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,7 +209,7 @@ if (ENABLE_QT) set(QT_PREFIX_HINT) endif() - find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia ${QT_PREFIX_HINT}) + find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia Concurrent ${QT_PREFIX_HINT}) if (ENABLE_QT_TRANSLATION) find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index b95d6998a..4634c9603 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -266,7 +266,7 @@ endif() create_target_directory_groups(citra-qt) target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) -target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia) +target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent) target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) target_compile_definitions(citra-qt PRIVATE diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index e47b0db96..8c4fbe5c5 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -3,14 +3,22 @@ // Refer to the license.txt file included. #include +#include #include +#include +#include #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_system.h" #include "common/settings.h" #include "core/core.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/ptm/ptm.h" +#include "core/hw/aes/key.h" #include "ui_configure_system.h" +#ifdef ENABLE_WEB_SERVICE +#include "web_service/nus_titles.h" +#endif static const std::array days_in_month = {{ 31, @@ -239,6 +247,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) &ConfigureSystem::UpdateInitTime); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, &ConfigureSystem::RefreshConsoleID); + connect(ui->button_start_download, &QPushButton::clicked, this, + &ConfigureSystem::DownloadFromNUS); for (u8 i = 0; i < country_names.size(); i++) { if (std::strcmp(country_names.at(i), "") != 0) { ui->combo_country->addItem(tr(country_names.at(i)), i); @@ -257,6 +267,30 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); SetupPerGameUI(); + + ui->combo_download_mode->setCurrentIndex(1); // set to Recommended + bool keys_available = true; + HW::AES::InitKeys(true); + for (u8 i = 0; i < HW::AES::MaxCommonKeySlot; i++) { + HW::AES::SelectCommonKeyIndex(i); + if (!HW::AES::IsNormalKeyAvailable(HW::AES::KeySlotID::TicketCommonKey)) { + keys_available = false; + break; + } + } + if (keys_available) { + ui->button_start_download->setEnabled(true); + ui->combo_download_mode->setEnabled(true); + ui->label_nus_download->setText(tr("Download System Files from Nintendo servers")); + } else { + ui->button_start_download->setEnabled(false); + ui->combo_download_mode->setEnabled(false); + ui->label_nus_download->setText( + tr("Citra is missing keys to download system files.
How to get keys?")); + } + ConfigureTime(); } @@ -542,3 +576,44 @@ void ConfigureSystem::SetupPerGameUI() { ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds, is_new_3ds); } + +void ConfigureSystem::DownloadFromNUS() { +#ifdef ENABLE_WEB_SERVICE + ui->button_start_download->setEnabled(false); + + const auto mode = static_cast(ui->combo_download_mode->currentIndex()); + const std::vector titles = BuildFirmwareTitleList(mode, cfg->GetRegionValue()); + + QProgressDialog progress(tr("Downloading files..."), tr("Cancel"), 0, + static_cast(titles.size()), this); + progress.setWindowModality(Qt::WindowModal); + + QFutureWatcher future_watcher; + QObject::connect(&future_watcher, &QFutureWatcher::finished, &progress, + &QProgressDialog::reset); + QObject::connect(&progress, &QProgressDialog::canceled, &future_watcher, + &QFutureWatcher::cancel); + QObject::connect(&future_watcher, &QFutureWatcher::progressValueChanged, &progress, + &QProgressDialog::setValue); + + auto failed = false; + const auto download_title = [&future_watcher, &failed](const u64& title_id) { + if (Service::AM::InstallFromNus(title_id) != Service::AM::InstallStatus::Success) { + failed = true; + future_watcher.cancel(); + } + }; + + future_watcher.setFuture(QtConcurrent::map(titles, download_title)); + progress.exec(); + future_watcher.waitForFinished(); + + if (failed) { + QMessageBox::critical(this, tr("Citra"), tr("Downloading system files failed.")); + } else if (!future_watcher.isCanceled()) { + QMessageBox::information(this, tr("Citra"), tr("Successfully downloaded system files.")); + } + + ui->button_start_download->setEnabled(true); +#endif +} diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index 007bed199..d8b0e25fb 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -43,6 +43,8 @@ private: void SetupPerGameUI(); + void DownloadFromNUS(); + ConfigurationShared::CheckState is_new_3ds; std::unique_ptr ui; bool enabled = false; diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui index 0dae06b70..02acf35c5 100644 --- a/src/citra_qt/configuration/configure_system.ui +++ b/src/citra_qt/configuration/configure_system.ui @@ -361,6 +361,52 @@ + + + + Download System Files from Nitendo servers + + + + + + + + + + All + + + + + Recommended + + + + + Minimal + + + + + + + + + 0 + 0 + + + + Qt::RightToLeft + + + Download + + + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index d11e71997..f9c67b027 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1920,6 +1920,7 @@ void GMainWindow::OnLoadState() { } void GMainWindow::OnConfigure() { + game_list->SetDirectoryWatcherEnabled(false); Settings::SetConfiguringGlobal(true); ConfigureDialog configureDialog(this, hotkey_registry, !multiplayer_state->IsHostingPublicRoom()); @@ -1931,6 +1932,7 @@ void GMainWindow::OnConfigure() { const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); auto result = configureDialog.exec(); + game_list->SetDirectoryWatcherEnabled(true); if (result == QDialog::Accepted) { configureDialog.ApplyConfiguration(); InitializeHotkeys(); diff --git a/src/core/file_sys/cia_container.cpp b/src/core/file_sys/cia_container.cpp index 64ed8bbb0..5764a7632 100644 --- a/src/core/file_sys/cia_container.cpp +++ b/src/core/file_sys/cia_container.cpp @@ -16,8 +16,6 @@ namespace FileSys { -constexpr u32 CIA_SECTION_ALIGNMENT = 0x40; - Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) { std::vector header_data(sizeof(Header)); diff --git a/src/core/file_sys/cia_container.h b/src/core/file_sys/cia_container.h index 332f2715f..021e8b7cd 100644 --- a/src/core/file_sys/cia_container.h +++ b/src/core/file_sys/cia_container.h @@ -29,6 +29,7 @@ constexpr std::size_t CIA_CONTENT_BITS_SIZE = (CIA_CONTENT_MAX_COUNT / 8); constexpr std::size_t CIA_HEADER_SIZE = 0x2020; constexpr std::size_t CIA_DEPENDENCY_SIZE = 0x300; constexpr std::size_t CIA_METADATA_SIZE = 0x400; +constexpr u32 CIA_SECTION_ALIGNMENT = 0x40; /** * Helper which implements an interface to read and write CTR Installable Archive (CIA) files. @@ -69,7 +70,6 @@ public: void Print() const; -private: struct Header { u32_le header_size; u16_le type; @@ -87,10 +87,14 @@ private: // The bits in the content index are arranged w/ index 0 as the MSB, 7 as the LSB, etc. return (content_present[index >> 3] & (0x80 >> (index & 7))) != 0; } + void SetContentPresent(u16 index) { + content_present[index >> 3] |= (0x80 >> (index & 7)); + } }; static_assert(sizeof(Header) == CIA_HEADER_SIZE, "CIA Header structure size is wrong"); +private: struct Metadata { std::array dependencies; std::array reserved; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 71a09e9e5..4578ffc23 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -9,6 +9,7 @@ #include #include #include +#include "common/alignment.h" #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -31,6 +32,9 @@ #include "core/hle/service/fs/fs_user.h" #include "core/loader/loader.h" #include "core/loader/smdh.h" +#ifdef ENABLE_WEB_SERVICE +#include "web_service/nus_download.h" +#endif namespace Service::AM { @@ -138,6 +142,8 @@ ResultCode CIAFile::WriteTitleMetadata() { decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(), ctr.data()); } + } else { + LOG_ERROR(Service_AM, "Can't get title key from ticket"); } install_state = CIAInstallState::TMDLoaded; @@ -180,6 +186,11 @@ ResultVal CIAFile::WriteContentData(u64 offset, std::size_t length, buffer + (range_min - offset) + available_to_write); if ((tmd.GetContentTypeByIndex(i) & FileSys::TMDContentTypeFlag::Encrypted) != 0) { + if (decryption_state->content.size() <= i) { + // TODO: There is probably no correct error to return here. What error should be + // returned? + return FileSys::ERROR_INSUFFICIENT_SPACE; + } decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size()); } @@ -235,7 +246,7 @@ ResultVal CIAFile::Write(u64 offset, std::size_t length, bool flush std::size_t buf_offset = buf_loaded - offset; std::size_t buf_copy_size = std::min(length, static_cast(container.GetContentOffset() - offset)) - - buf_loaded; + buf_offset; std::size_t buf_max_size = std::min(offset + length, container.GetContentOffset()); data.resize(buf_max_size); memcpy(data.data() + copy_offset, buffer + buf_offset, buf_copy_size); @@ -418,6 +429,99 @@ InstallStatus InstallCIA(const std::string& path, return InstallStatus::ErrorInvalid; } +InstallStatus InstallFromNus(u64 title_id, int version) { +#ifdef ENABLE_WEB_SERVICE + LOG_DEBUG(Service_AM, "Downloading {:X}", title_id); + + CIAFile install_file{GetTitleMediaType(title_id)}; + + std::string path = fmt::format("/ccs/download/{:016X}/tmd", title_id); + if (version != -1) { + path += fmt::format(".{}", version); + } + auto tmd_response = WebService::NUS::Download(path); + if (!tmd_response) { + LOG_ERROR(Service_AM, "Failed to download tmd for {:016X}", title_id); + return InstallStatus::ErrorFileNotFound; + } + FileSys::TitleMetadata tmd; + tmd.Load(*tmd_response); + + path = fmt::format("/ccs/download/{:016X}/cetk", title_id); + auto cetk_response = WebService::NUS::Download(path); + if (!cetk_response) { + LOG_ERROR(Service_AM, "Failed to download cetk for {:016X}", title_id); + return InstallStatus::ErrorFileNotFound; + } + + std::vector content; + const auto content_count = tmd.GetContentCount(); + for (std::size_t i = 0; i < content_count; ++i) { + const std::string filename = fmt::format("{:08x}", tmd.GetContentIDByIndex(i)); + path = fmt::format("/ccs/download/{:016X}/{}", title_id, filename); + const auto temp_response = WebService::NUS::Download(path); + if (!temp_response) { + LOG_ERROR(Service_AM, "Failed to download content for {:016X}", title_id); + return InstallStatus::ErrorFileNotFound; + } + content.insert(content.end(), temp_response->begin(), temp_response->end()); + } + + FileSys::CIAContainer::Header fake_header{ + .header_size = sizeof(FileSys::CIAContainer::Header), + .type = 0, + .version = 0, + .cert_size = 0, + .tik_size = static_cast(cetk_response->size()), + .tmd_size = static_cast(tmd_response->size()), + .meta_size = 0, + }; + for (u16 i = 0; i < content_count; ++i) { + fake_header.SetContentPresent(i); + } + std::vector header_data(sizeof(fake_header)); + std::memcpy(header_data.data(), &fake_header, sizeof(fake_header)); + + std::size_t current_offset = 0; + const auto write_to_cia_file_aligned = [&install_file, ¤t_offset](std::vector& data) { + const u64 offset = + Common::AlignUp(current_offset + data.size(), FileSys::CIA_SECTION_ALIGNMENT); + data.resize(offset - current_offset, 0); + const auto result = install_file.Write(current_offset, data.size(), true, data.data()); + if (result.Failed()) { + LOG_ERROR(Service_AM, "CIA file installation aborted with error code {:08x}", + result.Code().raw); + return InstallStatus::ErrorAborted; + } + current_offset += data.size(); + return InstallStatus::Success; + }; + + auto result = write_to_cia_file_aligned(header_data); + if (result != InstallStatus::Success) { + return result; + } + + result = write_to_cia_file_aligned(*cetk_response); + if (result != InstallStatus::Success) { + return result; + } + + result = write_to_cia_file_aligned(*tmd_response); + if (result != InstallStatus::Success) { + return result; + } + + result = write_to_cia_file_aligned(content); + if (result != InstallStatus::Success) { + return result; + } + return InstallStatus::Success; +#else + return InstallStatus::ErrorFileNotFound; +#endif +} + Service::FS::MediaType GetTitleMediaType(u64 titleId) { u16 platform = static_cast(titleId >> 48); u16 category = static_cast((titleId >> 32) & 0xFFFF); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 6e2dae6fc..cd8f4dcf7 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -110,6 +110,13 @@ private: InstallStatus InstallCIA(const std::string& path, std::function&& update_callback = nullptr); +/** + * Downloads and installs title form the Nintendo Update Service. + * @param title_id the title_id to download + * @returns whether the install was successful or error code + */ +InstallStatus InstallFromNus(u64 title_id, int version = -1); + /** * Get the mediatype for an installed title * @param titleId the installed title ID diff --git a/src/core/hw/aes/key.cpp b/src/core/hw/aes/key.cpp index e406aed71..8fdb7f514 100644 --- a/src/core/hw/aes/key.cpp +++ b/src/core/hw/aes/key.cpp @@ -88,7 +88,7 @@ struct KeySlot { }; std::array key_slots; -std::array, 6> common_key_y_slots; +std::array, MaxCommonKeySlot> common_key_y_slots; enum class FirmwareType : u32 { ARM9 = 0, // uses NDMA @@ -494,9 +494,9 @@ void LoadPresetKeys() { } // namespace -void InitKeys() { +void InitKeys(bool force) { static bool initialized = false; - if (initialized) + if (initialized && !force) return; initialized = true; HW::RSA::InitSlots(); diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h index 0e1530f0c..fa21ea783 100644 --- a/src/core/hw/aes/key.h +++ b/src/core/hw/aes/key.h @@ -48,11 +48,13 @@ enum KeySlotID : std::size_t { MaxKeySlotID = 0x40, }; +constexpr std::size_t MaxCommonKeySlot = 6; + constexpr std::size_t AES_BLOCK_SIZE = 16; using AESKey = std::array; -void InitKeys(); +void InitKeys(bool force = false); void SetGeneratorConstant(const AESKey& key); void SetKeyX(std::size_t slot_id, const AESKey& key); diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt index 1fd61ffa0..553b36298 100644 --- a/src/web_service/CMakeLists.txt +++ b/src/web_service/CMakeLists.txt @@ -1,6 +1,9 @@ add_library(web_service STATIC announce_room_json.cpp announce_room_json.h + nus_download.cpp + nus_download.h + nus_titles.h precompiled_headers.h telemetry_json.cpp telemetry_json.h diff --git a/src/web_service/nus_download.cpp b/src/web_service/nus_download.cpp new file mode 100644 index 000000000..6811f5c23 --- /dev/null +++ b/src/web_service/nus_download.cpp @@ -0,0 +1,47 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/logging/log.h" +#include "web_service/nus_download.h" + +namespace WebService::NUS { + +std::optional> Download(const std::string& path) { + constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net"; + + std::unique_ptr client = std::make_unique(HOST); + if (client == nullptr) { + LOG_ERROR(WebService, "Invalid URL {}{}", HOST, path); + return {}; + } + + httplib::Request request{ + .method = "GET", + .path = path, + }; + + client->set_follow_location(true); + const auto result = client->send(request); + if (!result) { + LOG_ERROR(WebService, "GET to {}{} returned null", HOST, path); + return {}; + } + + const auto& response = result.value(); + if (response.status >= 400) { + LOG_ERROR(WebService, "GET to {}{} returned error status code: {}", HOST, path, + response.status); + return {}; + } + if (!response.headers.contains("content-type")) { + LOG_ERROR(WebService, "GET to {}{} returned no content", HOST, path); + return {}; + } + + return std::vector(response.body.begin(), response.body.end()); +} + +} // namespace WebService::NUS diff --git a/src/web_service/nus_download.h b/src/web_service/nus_download.h new file mode 100644 index 000000000..34633b767 --- /dev/null +++ b/src/web_service/nus_download.h @@ -0,0 +1,15 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/common_types.h" + +namespace WebService::NUS { + +std::optional> Download(const std::string& path); + +} diff --git a/src/web_service/nus_titles.h b/src/web_service/nus_titles.h new file mode 100644 index 000000000..fe7b6d183 --- /dev/null +++ b/src/web_service/nus_titles.h @@ -0,0 +1,761 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +constexpr u32 SYSTEM_FIRMWARE_UPPER_TITLE_ID = 0x00040138; +constexpr u32 SYSTEM_APPLICATION_UPPER_TITLE_ID = 0x00040010; +constexpr u32 SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID = 0x0004001B; +constexpr u32 SYSTEM_APPLET_UPPER_TITLE_ID = 0x00040030; +constexpr u32 SHARED_DATA_ARCHIVE_UPPER_TITLE_ID = 0x0004009B; +constexpr u32 SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID = 0x000400DB; +constexpr u32 SYSTEM_MODULE_UPPER_TITLE_ID = 0x00040130; + +struct Title { + enum Mode { All, Recommended, Minimal }; + std::string name; + u32 upper_title_id; + std::array lower_title_id; + Mode mode = Mode::All; +}; + +static const std::array SYSTEM_FIRMWARE = { + {{"Safe Mode Native Firmware", + SYSTEM_FIRMWARE_UPPER_TITLE_ID, + {{0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003}}, + Title::Mode::Minimal}, + {"New_3DS Safe Mode Native Firmware", + SYSTEM_FIRMWARE_UPPER_TITLE_ID, + {{0x20000003, 0x20000003, 0x20000003, 0x20000003, 0x20000003, 0x20000003}}, + Title::Mode::Minimal}, + {"Native Firmware", + SYSTEM_FIRMWARE_UPPER_TITLE_ID, + {{0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002}}, + Title::Mode::Minimal}, + {"New_3DS Native Firmware", + SYSTEM_FIRMWARE_UPPER_TITLE_ID, + {{0x20000002, 0x20000002, 0x20000002, 0x20000002, 0x20000002, 0x20000002}}, + Title::Mode::Minimal}}}; +static const std::array SYSTEM_APPLICATIONS = { + {{"System Settings", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020000, 0x00021000, 0x00022000, 0x00026000, 0x00027000, 0x00028000}}, + Title::Mode::All}, + {"Download Play", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020100, 0x00021100, 0x00022100, 0x00026100, 0x00027100, 0x00028100}}, + Title::Mode::Recommended}, + {"Activity Log", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020200, 0x00021200, 0x00022200, 0x00026200, 0x00027200, 0x00028200}}, + Title::Mode::All}, + {"Health and Safety Information", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020300, 0x00021300, 0x00022300, 0x00026300, 0x00027300, 0x00028300}}, + Title::Mode::All}, + {"New_3DS Health and Safety Information", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x20020300, 0x20021300, 0x20022300, 0x0, 0x20027300, 0x0}}, + Title::Mode::All}, + {"Nintendo 3DS Camera", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020400, 0x00021400, 0x00022400, 0x00026400, 0x00027400, 0x00028400}}, + Title::Mode::All}, + {"Nintendo 3DS Sound", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020500, 0x00021500, 0x00022500, 0x00026500, 0x00027500, 0x00028500}}, + Title::Mode::All}, + {"Mii Maker", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020700, 0x00021700, 0x00022700, 0x00026700, 0x00027700, 0x00028700}}, + Title::Mode::Recommended}, + {"StreetPass Mii Plaza", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020800, 0x00021800, 0x00022800, 0x00026800, 0x00027800, 0x00028800}}, + Title::Mode::All}, + {"eShop", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020900, 0x00021900, 0x00022900, 0x0, 0x00027900, 0x00028900}}, + Title::Mode::Recommended}, + {"System Transfer", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020A00, 0x00021A00, 0x00022A00, 0x0, 0x00027A00, 0x00028A00}}, + Title::Mode::All}, + {"Nintendo Zone", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020B00, 0x00021B00, 0x00022B00, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"Face Raiders", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020D00, 0x00021D00, 0x00022D00, 0x00026D00, 0x00027D00, 0x00028D00}}, + Title::Mode::All}, + {"New_3DS Face Raiders", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x20020D00, 0x20021D00, 0x20022D00, 0x0, 0x20027D00, 0x0}}, + Title::Mode::All}, + {"AR Games", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x00020E00, 0x00021E00, 0x00022E00, 0x00026E00, 0x00027E00, 0x00028E00}}, + Title::Mode::All}, + {"Nintendo Network ID Settings", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x0002BF00, 0x0002C000, 0x0002C100, 0x0, 0x0, 0x0}}, + Title::Mode::Recommended}, + {"microSD Management", + SYSTEM_APPLICATION_UPPER_TITLE_ID, + {{0x20023100, 0x20024100, 0x20025100, 0x0, 0x0, 0x0}}, + Title::Mode::All}}}; + +static const std::array SYSTEM_DATA_ARCHIVES = { + {{"ClCertA", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00010002, 0x00010002, 0x00010002, 0x00010002, 0x00010002, 0x00010002}}, + Title::Mode::Recommended}, + {"NS CFA", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00010702, 0x00010702, 0x00010702, 0x00010702, 0x00010702, 0x00010702}}, + Title::Mode::All}, + {"dummy.txt", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00010802, 0x00010802, 0x00010802, 0x00010802, 0x00010802, 0x00010802}}, + Title::Mode::All}, + {"CFA web-browser data", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00018002, 0x00018002, 0x00018002, 0x00018002, 0x00018002, 0x00018002}}, + Title::Mode::All}, + {"local web-browser data", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00018102, 0x00018102, 0x00018102, 0x00018102, 0x00018102, 0x00018102}}, + Title::Mode::All}, + {"webkit/OSS CROs", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00018202, 0x00018202, 0x00018202, 0x00018202, 0x00018202, 0x00018202}}, + Title::Mode::All}, + {"Fangate_updater", + SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00019002, 0x00019002, 0x00019002, 0x00019002, 0x00019002, 0x00019002}}, + Title::Mode::All}}}; + +static const std::array SYSTEM_APPLETS = { + {{"Home Menu", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008202, 0x00008F02, 0x00009802, 0x0000A102, 0x0000A902, 0x0000B102}}, + Title::Mode::All}, + {"Camera applet", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008402, 0x00009002, 0x00009902, 0x0000A202, 0x0000AA02, 0x0000B202}}, + Title::Mode::All}, + {"Instruction Manual", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008602, 0x00009202, 0x00009B02, 0x0000A402, 0x0000AC02, 0x0000B402}}, + Title::Mode::Recommended}, + {"Game Notes", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008702, 0x00009302, 0x00009C02, 0x0000A502, 0x0000AD02, 0x0000B502}}, + Title::Mode::All}, + {"Internet Browser", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008802, 0x00009402, 0x00009D02, 0x0000A602, 0x0000AE02, 0x0000B602}}, + Title::Mode::All}, + {"New 3DS Internet Browser", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x20008802, 0x20009402, 0x20009D02, 0x0, 0x2000AE02, 0x0}}, + Title::Mode::All}, + {"Fatal error viewer", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008A02, 0x00008A02, 0x00008A02, 0x00008A02, 0x00008A02, 0x00008A02}}, + Title::Mode::All}, + {"Safe Mode Fatal error viewer", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008A03, 0x00008A03, 0x00008A03, 0x00008A03, 0x00008A03, 0x00008A03}}, + Title::Mode::All}, + {"New 3DS Safe Mode Fatal error viewer", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x20008A03, 0x20008A03, 0x20008A03, 0x0, 0x20008A03, 0x0}}, + Title::Mode::All}, + {"Friend List", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008D02, 0x00009602, 0x00009F02, 0x0000A702, 0x0000AF02, 0x0000B702}}, + Title::Mode::Recommended}, + {"Notifications", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008E02, 0x000009702, 0x0000A002, 0x0000A802, 0x0000B002, 0x0000B802}}, + Title::Mode::All}, + {"Software Keyboard", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00000C002, 0x0000C802, 0x0000D002, 0x0000D802, 0x0000DE02, 0x0000E402}}, + Title::Mode::Recommended}, + {"Safe Mode Software Keyboard", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00000C003, 0x0000C803, 0x0000D003, 0x0000D803, 0x0000DE03, 0x0000E403}}, + Title::Mode::All}, + {"New 3DS Safe Mode Software Keyboard", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x2000C003, 0x2000C803, 0x2000D003, 0x0, 0x2000DE03, 0x0}}, + Title::Mode::All}, + {"Mii picker", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000C102, 0x0000C902, 0x0000D102, 0x0000D902, 0x0000DF02, 0x0000E502}}, + Title::Mode::Recommended}, + {"Picture picker", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000C302, 0x0000CB02, 0x0000D302, 0x0000DB02, 0x0000E102, 0x0000E702}}, + Title::Mode::All}, + {"Voice memo picker", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000C402, 0x0000CC02, 0x0000D402, 0x0000DC02, 0x0000E202, 0x0000E802}}, + Title::Mode::All}, + {"Error display", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000C502, 0x0000C502, 0x0000C502, 0x0000CF02, 0x0000CF02, 0x0000CF02}}, + Title::Mode::All}, + {"Safe mode error display", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000C503, 0x0000C503, 0x0000C503, 0x0000CF03, 0x0000CF03, 0x0000CF03}}, + Title::Mode::All}, + {"New 3DS safe mode error display", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x2000C503, 0x2000C503, 0x2000C503, 0x0, 0x2000CF03, 0x0}}, + Title::Mode::All}, + {"Circle Pad Pro test/calibration applet", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000CD02, 0x0000CD02, 0x0000CD02, 0x0000D502, 0x0000D502, 0x0000D502}}, + Title::Mode::All}, + {"eShop applet", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000C602, 0x0000CE02, 0x0000D602, 0x0, 0x0000E302, 0x0000E902}}, + Title::Mode::Recommended}, + {"Miiverse", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000BC02, 0x0000BC02, 0x0000BC02, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"Miiverse system library", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x0000F602, 0x0000F602, 0x0000F602, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"Miiverse-posting applet", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00008302, 0x00008B02, 0x0000BA02, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"Amiibo Settings", + SYSTEM_APPLET_UPPER_TITLE_ID, + {{0x00009502, 0x00009E02, 0x0000B902, 0x0, 0x00008C02, 0x0000BF02}}, + Title::Mode::All}}}; + +static const std::array SHARED_DATA_ARCHIVES = { + {{"CFL_Res.dat", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00010202, 0x00010202, 0x00010202, 0x00010202, 0x00010202, 0x00010202}}, + Title::Mode::All}, + {"Region Manifest", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00010402, 0x00010402, 0x00010402, 0x00010402, 0x00010402, 0x00010402}}, + Title::Mode::All}, + {"Non-Nintendo TLS Root-CA Certificates", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00010602, 0x00010602, 0x00010602, 0x00010602, 0x00010602, 0x00010602}}, + Title::Mode::Recommended}, + {"CHN/CN Dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x0, 0x00011002, 0x0, 0x0}}, + Title::Mode::All}, + {"TWN/TN dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x0, 0x0, 0x0, 0x00011102}}, + Title::Mode::All}, + {"NL/NL dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011202, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"EN/GB dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011302, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"EN/US dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x00011402, 0x0, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"FR/FR/regular dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011502, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"FR/CA/regular dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x00011602, 0x0, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"DE/regular dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011702, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"IT/IT dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011802, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"JA_small/32 dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00011902, 0x0, 0x0, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"KO/KO dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x0, 0x0, 0x00011A02, 0x0}}, + Title::Mode::All}, + {"PT/PT/regular dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011B02, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"RU/regular dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x0, 0x00011C02, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"ES/ES dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x00011D02, 0x00011D02, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"PT/BR/regular dictionary", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x0, 0x00011E02, 0x0, 0x0, 0x0, 0x0}}, + Title::Mode::All}, + {"error strings", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00012202, 0x00012302, 0x00012102, 0x00012402, 0x00012502, 0x00012602}}, + Title::Mode::All}, + {"eula", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00013202, 0x00013302, 0x00013102, 0x00013502, 0x0, 0x0}}, + Title::Mode::All}, + {"JPN/EUR/USA System Font", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00014002, 0x00014002, 0x00014002, 0x00014002, 0x00014002, 0x00014002}}, + Title::Mode::Recommended}, + {"CHN System Font", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00014102, 0x00014102, 0x00014102, 0x00014102, 0x00014102, 0x00014102}}, + Title::Mode::Recommended}, + {"KOR System Font", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00014202, 0x00014202, 0x00014202, 0x00014202, 0x00014202, 0x00014202}}, + Title::Mode::Recommended}, + {"TWN System Font", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00014302, 0x00014302, 0x00014302, 0x00014302, 0x00014302, 0x00014302}}, + Title::Mode::Recommended}, + {"rate", + SHARED_DATA_ARCHIVE_UPPER_TITLE_ID, + {{0x00015202, 0x00015302, 0x00015102, 0x0, 0x0015502, 0x00015602}}, + Title::Mode::All}}}; + +static const std::array SYSTEM_DATA_ARCHIVES_2 = { + {{"bad word list", + SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID, + {{0x00010302, 0x00010302, 0x00010302, 0x00010302, 0x00010302, 0x00010302}}, + Title::Mode::All}, + {"Nintendo Zone hotspot list", + SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID, + {{0x00010502, 0x00010502, 0x00010502, 0x00010502, 0x00010502, 0x00010502}}, + Title::Mode::All}, + {"NVer", + SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID, + {{0x00016102, 0x00016202, 0x00016302, 0x00016402, 0x00016502, 0x00016602}}, + Title::Mode::All}, + {"New_3DS NVer", + SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID, + {{0x20016102, 0x20016202, 0x20016302, 0x0, 0x20016502, 0x0}}, + Title::Mode::All}, + {"CVer", + SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID, + {{0x00017102, 0x00017202, 0x00017302, 0x00017402, 0x00017502, 0x00017602}}, + Title::Mode::All}}}; + +static const std::array SYSTEM_MODULES = { + {{"AM ( Application Manager )", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001502, 0x00001502, 0x00001502, 0x00001502, 0x00001502, 0x00001502}}, + Title::Mode::All}, + {"Safe Mode AM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001503, 0x00001503, 0x00001503, 0x00001503, 0x00001503, 0x00001503}}, + Title::Mode::All}, + {"New_3DS Safe Mode AM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001503, 0x20001503, 0x20001503, 0x20001503, 0x20001503, 0x20001503}}, + Title::Mode::All}, + {"Camera", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001602, 0x00001602, 0x00001602, 0x00001602, 0x00001602, 0x00001602}}, + Title::Mode::All}, + {"New_3DS Camera", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001602, 0x20001602, 0x20001602, 0x20001602, 0x20001602, 0x20001602}}, + Title::Mode::All}, + {"Config (cfg)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001702, 0x00001702, 0x00001702, 0x00001702, 0x00001702, 0x00001702}}, + Title::Mode::All}, + {"Safe Mode Config (cfg)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001703, 0x00001703, 0x00001703, 0x00001703, 0x00001703, 0x00001703}}, + Title::Mode::All}, + {"New_3DS Safe Mode Config (cfg)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001703, 0x20001703, 0x20001703, 0x20001703, 0x20001703, 0x20001703}}, + Title::Mode::All}, + {"Codec", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001802, 0x00001802, 0x00001802, 0x00001802, 0x00001802, 0x00001802}}, + Title::Mode::All}, + {"Safe Mode Codec", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001803, 0x00001803, 0x00001803, 0x00001803, 0x00001803, 0x00001803}}, + Title::Mode::All}, + {"New_3DS Safe Mode Codec", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001803, 0x20001803, 0x20001803, 0x20001803, 0x20001803, 0x20001803}}, + Title::Mode::All}, + {"DSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001A02, 0x00001A02, 0x00001A02, 0x00001A02, 0x00001A02, 0x00001A02}}, + Title::Mode::All}, + {"Safe Mode DSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001A03, 0x00001A03, 0x00001A03, 0x00001A03, 0x00001A03, 0x00001A03}}, + Title::Mode::All}, + {"New_3DS Safe Mode DSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001A03, 0x20001A03, 0x20001A03, 0x20001A03, 0x20001A03, 0x20001A03}}, + Title::Mode::All}, + {"GPIO", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001B02, 0x00001B02, 0x00001B02, 0x00001B02, 0x00001B02, 0x00001B02}}, + Title::Mode::All}, + {"Safe Mode GPIO", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001B03, 0x00001B03, 0x00001B03, 0x00001B03, 0x00001B03, 0x00001B03}}, + Title::Mode::All}, + {"New_3DS Safe Mode GPIO", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001B03, 0x20001B03, 0x20001B03, 0x20001B03, 0x20001B03, 0x20001B03}}, + Title::Mode::All}, + {"GSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001C02, 0x00001C02, 0x00001C02, 0x00001C02, 0x00001C02, 0x00001C02}}, + Title::Mode::All}, + {"New_3DS GSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001C02, 0x20001C02, 0x20001C02, 0x20001C02, 0x20001C02, 0x20001C02}}, + Title::Mode::All}, + {"Safe Mode GSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001C03, 0x00001C03, 0x00001C03, 0x00001C03, 0x00001C03, 0x00001C03}}, + Title::Mode::All}, + {"New_3DS Safe Mode GSP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001C03, 0x20001C03, 0x20001C03, 0x20001C03, 0x20001C03, 0x20001C03}}, + Title::Mode::All}, + {"HID (Human Interface Devices)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001D02, 0x00001D02, 0x00001D02, 0x00001D02, 0x00001D02, 0x00001D02}}, + Title::Mode::All}, + {"Safe Mode HID", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001D03, 0x00001D03, 0x00001D03, 0x00001D03, 0x00001D03, 0x00001D03}}, + Title::Mode::All}, + {"New_3DS Safe Mode HID", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001D03, 0x20001D03, 0x20001D03, 0x20001D03, 0x20001D03, 0x20001D03}}, + Title::Mode::All}, + {"i2c", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001E02, 0x00001E02, 0x00001E02, 0x00001E02, 0x00001E02, 0x00001E02}}, + Title::Mode::All}, + {"New_3DS i2c", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001E02, 0x20001E02, 0x20001E02, 0x20001E02, 0x20001E02, 0x20001E02}}, + Title::Mode::All}, + {"Safe Mode i2c", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001E03, 0x00001E03, 0x00001E03, 0x00001E03, 0x00001E03, 0x00001E03}}, + Title::Mode::All}, + {"New_3DS Safe Mode i2c", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001E03, 0x20001E03, 0x20001E03, 0x20001E03, 0x20001E03, 0x20001E03}}, + Title::Mode::All}, + {"MCU", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001F02, 0x00001F02, 0x00001F02, 0x00001F02, 0x00001F02, 0x00001F02}}, + Title::Mode::All}, + {"New_3DS MCU", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001F02, 0x20001F02, 0x20001F02, 0x20001F02, 0x20001F02, 0x20001F02}}, + Title::Mode::All}, + {"Safe Mode MCU", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00001F03, 0x00001F03, 0x00001F03, 0x00001F03, 0x00001F03, 0x00001F03}}, + Title::Mode::All}, + {"New_3DS Safe Mode MCU", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20001F03, 0x20001F03, 0x20001F03, 0x20001F03, 0x20001F03, 0x20001F03}}, + Title::Mode::All}, + {"MIC (Microphone)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002}}, + Title::Mode::All}, + {"PDN", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002102, 0x00002102, 0x00002102, 0x00002102, 0x00002102, 0x00002102}}, + Title::Mode::All}, + {"Safe Mode PDN", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002103, 0x00002103, 0x00002103, 0x00002103, 0x00002103, 0x00002103}}, + Title::Mode::All}, + {"New_3DS Safe Mode PDN", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002103, 0x20002103, 0x20002103, 0x20002103, 0x20002103, 0x20002103}}, + Title::Mode::All}, + {"PTM (Play time, pedometer, and battery manager)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002202, 0x00002202, 0x00002202, 0x00002202, 0x00002202, 0x00002202}}, + Title::Mode::All}, + {"New_3DS PTM (Play time, pedometer, and battery manager)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002202, 0x20002202, 0x20002202, 0x20002202, 0x20002202, 0x20002202}}, + Title::Mode::All}, + {"Safe Mode PTM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002203, 0x00002203, 0x00002203, 0x00002203, 0x00002203, 0x00002203}}, + Title::Mode::All}, + {"New_3DS Safe Mode PTM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002203, 0x20002203, 0x20002203, 0x20002203, 0x20002203, 0x20002203}}, + Title::Mode::All}, + {"spi", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002302, 0x00002302, 0x00002302, 0x00002302, 0x00002302, 0x00002302}}, + Title::Mode::All}, + {"New_3DS spi", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002302, 0x20002302, 0x20002302, 0x20002302, 0x20002302, 0x20002302}}, + Title::Mode::All}, + {"Safe Mode spi", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002303, 0x00002303, 0x00002303, 0x00002303, 0x00002303, 0x00002303}}, + Title::Mode::All}, + {"New_3DS Safe Mode spi", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002303, 0x20002303, 0x20002303, 0x20002303, 0x20002303, 0x20002303}}, + Title::Mode::All}, + {"AC (Network manager)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002402, 0x00002402, 0x00002402, 0x00002402, 0x00002402, 0x00002402}}, + Title::Mode::All}, + {"Safe Mode AC", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002403, 0x00002403, 0x00002403, 0x00002403, 0x00002403, 0x00002403}}, + Title::Mode::All}, + {"New_3DS Safe Mode AC", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002403, 0x20002403, 0x20002403, 0x20002403, 0x20002403, 0x20002403}}, + Title::Mode::All}, + {"Cecd (StreetPass)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002602, 0x00002602, 0x00002602, 0x00002602, 0x00002602, 0x00002602}}, + Title::Mode::All}, + {"CSND", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002702, 0x00002702, 0x00002702, 0x00002702, 0x00002702, 0x00002702}}, + Title::Mode::All}, + {"Safe Mode CSND", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002703, 0x00002703, 0x00002703, 0x00002703, 0x00002703, 0x00002703}}, + Title::Mode::All}, + {"New_3DS Safe Mode CSND", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002703, 0x20002703, 0x20002703, 0x20002703, 0x20002703, 0x20002703}}, + Title::Mode::All}, + {"DLP (Download Play)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002802, 0x00002802, 0x00002802, 0x00002802, 0x00002802, 0x00002802}}, + Title::Mode::Recommended}, + {"HTTP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002902, 0x00002902, 0x00002902, 0x00002902, 0x00002902, 0x00002902}}, + Title::Mode::All}, + {"Safe Mode HTTP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002903, 0x00002903, 0x00002903, 0x00002903, 0x00002903, 0x00002903}}, + Title::Mode::All}, + {"New_3DS Safe Mode HTTP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002903, 0x20002903, 0x20002903, 0x20002903, 0x20002903, 0x20002903}}, + Title::Mode::All}, + {"MP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002A02, 0x00002A02, 0x00002A02, 0x00002A02, 0x00002A02, 0x00002A02}}, + Title::Mode::All}, + {"Safe Mode MP", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002A03, 0x00002A03, 0x00002A03, 0x00002A03, 0x00002A03, 0x00002A03}}, + Title::Mode::All}, + {"NDM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002B02, 0x00002B02, 0x00002B02, 0x00002B02, 0x00002B02, 0x00002B02}}, + Title::Mode::All}, + {"NIM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002C02, 0x00002C02, 0x00002C02, 0x00002C02, 0x00002C02, 0x00002C02}}, + Title::Mode::All}, + {"Safe Mode NIM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002C03, 0x00002C03, 0x00002C03, 0x00002C03, 0x00002C03, 0x00002C03}}, + Title::Mode::All}, + {"New_3DS Safe Mode NIM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002C03, 0x20002C03, 0x20002C03, 0x20002C03, 0x20002C03, 0x20002C03}}, + Title::Mode::All}, + {"NWM ( Low-level wifi manager )", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002D02, 0x00002D02, 0x00002D02, 0x00002D02, 0x00002D02, 0x00002D02}}, + Title::Mode::All}, + {"Safe Mode NWM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002D03, 0x00002D03, 0x00002D03, 0x00002D03, 0x00002D03, 0x00002D03}}, + Title::Mode::All}, + {"New_3DS Safe Mode NWM", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002D03, 0x20002D03, 0x20002D03, 0x20002D03, 0x20002D03, 0x20002D03}}, + Title::Mode::All}, + {"Sockets", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002E02, 0x00002E02, 0x00002E02, 0x00002E02, 0x00002E02, 0x00002E02}}, + Title::Mode::All}, + {"Safe Mode Sockets", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002E03, 0x00002E03, 0x00002E03, 0x00002E03, 0x00002E03, 0x00002E03}}, + Title::Mode::All}, + {"New_3DS Safe Mode Sockets", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002E03, 0x20002E03, 0x20002E03, 0x20002E03, 0x20002E03, 0x20002E03}}, + Title::Mode::All}, + {"SSL", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002F02, 0x00002F02, 0x00002F02, 0x00002F02, 0x00002F02, 0x00002F02}}, + Title::Mode::All}, + {"Safe Mode SSL", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00002F03, 0x00002F03, 0x00002F03, 0x00002F03, 0x00002F03, 0x00002F03}}, + Title::Mode::All}, + {"New_3DS Safe Mode SSL", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20002F03, 0x20002F03, 0x20002F03, 0x20002F03, 0x20002F03, 0x20002F03}}, + Title::Mode::All}, + {"PS ( Process Manager )", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003102, 0x00003102, 0x00003102, 0x00003102, 0x00003102, 0x00003102}}, + Title::Mode::All}, + {"Safe Mode PS", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003103, 0x00003103, 0x00003103, 0x00003103, 0x00003103, 0x00003103}}, + Title::Mode::All}, + {"New_3DS Safe Mode PS", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20003103, 0x20003103, 0x20003103, 0x20003103, 0x20003103, 0x20003103}}, + Title::Mode::All}, + {"friends (Friends list)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003202, 0x00003202, 0x00003202, 0x00003202, 0x00003202, 0x00003202}}, + Title::Mode::All}, + {"Safe Mode friends (Friends list)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003203, 0x00003203, 0x00003203, 0x00003203, 0x00003203, 0x00003203}}, + Title::Mode::All}, + {"New_3DS Safe Mode friends (Friends list)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20003203, 0x20003203, 0x20003203, 0x20003203, 0x20003203, 0x20003203}}, + Title::Mode::All}, + {"IR (Infrared)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003302, 0x00003302, 0x00003302, 0x00003302, 0x00003302, 0x00003302}}, + Title::Mode::All}, + {"Safe Mode IR", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003303, 0x00003303, 0x00003303, 0x00003303, 0x00003303, 0x00003303}}, + Title::Mode::All}, + {"New_3DS Safe Mode IR", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20003303, 0x20003303, 0x20003303, 0x20003303, 0x20003303, 0x20003303}}, + Title::Mode::All}, + {"BOSS (SpotPass)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003402, 0x00003402, 0x00003402, 0x00003402, 0x00003402, 0x00003402}}, + Title::Mode::All}, + {"News (Notifications)", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003502, 0x00003502, 0x00003502, 0x00003502, 0x00003502, 0x00003502}}, + Title::Mode::All}, + {"RO", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003702, 0x00003702, 0x00003702, 0x00003702, 0x00003702, 0x00003702}}, + Title::Mode::All}, + {"act", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00003802, 0x00003802, 0x00003802, 0x00003802, 0x00003802, 0x00003802}}, + Title::Mode::All}, + {"nfc", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00004002, 0x00004002, 0x00004002, 0x00004002, 0x00004002, 0x00004002}}, + Title::Mode::All}, + {"New_3DS mvd", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20004102, 0x20004102, 0x20004102, 0x20004102, 0x20004102, 0x20004102}}, + Title::Mode::All}, + {"New_3DS qtm", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20004202, 0x20004202, 0x20004202, 0x20004202, 0x20004202, 0x20004202}}, + Title::Mode::All}, + {"NS", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00008002, 0x00008002, 0x00008002, 0x00008002, 0x00008002, 0x00008002}}, + Title::Mode::All}, + {"Safe Mode NS", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x00008003, 0x00008003, 0x00008003, 0x00008003, 0x00008003, 0x00008003}}, + Title::Mode::All}, + {"New_3DS Safe Mode NS", + SYSTEM_MODULE_UPPER_TITLE_ID, + {{0x20008003, 0x20008003, 0x20008003, 0x20008003, 0x20008003, 0x20008003}}, + Title::Mode::All}}}; + +std::vector BuildFirmwareTitleList(const Title::Mode& mode, u32 region) { + // Since Australia and Europe share the same title, + // offset down by one for Australia and above. + const u32 region_index = region >= 3 ? region - 1 : region; + + const auto titles_with_mode = [mode, region_index](const Title& title) { + return mode <= title.mode && title.lower_title_id[region_index] != 0; + }; + + std::vector titles; + const auto inserter = std::back_inserter(titles); + std::copy_if(SYSTEM_FIRMWARE.begin(), SYSTEM_FIRMWARE.end(), inserter, titles_with_mode); + std::copy_if(SYSTEM_APPLICATIONS.begin(), SYSTEM_APPLICATIONS.end(), inserter, + titles_with_mode); + std::copy_if(SYSTEM_DATA_ARCHIVES.begin(), SYSTEM_DATA_ARCHIVES.end(), inserter, + titles_with_mode); + std::copy_if(SYSTEM_APPLETS.begin(), SYSTEM_APPLETS.end(), inserter, titles_with_mode); + std::copy_if(SHARED_DATA_ARCHIVES.begin(), SHARED_DATA_ARCHIVES.end(), inserter, + titles_with_mode); + std::copy_if(SYSTEM_DATA_ARCHIVES_2.begin(), SYSTEM_DATA_ARCHIVES_2.end(), inserter, + titles_with_mode); + std::copy_if(SYSTEM_MODULES.begin(), SYSTEM_MODULES.end(), inserter, titles_with_mode); + + const auto get_title_id = [region_index](const Title& title) { + return (static_cast<u64>(title.upper_title_id) << 32) + + static_cast<u64>(title.lower_title_id[region_index]); + }; + + std::vector<u64> title_ids; + std::transform(titles.begin(), titles.end(), std::back_inserter(title_ids), get_title_id); + return title_ids; +}