mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Add option to configure to download system files from Nintendo Update Service (#6269)
Co-authored-by: B3n30 <benediktthomas@gmail.com>
This commit is contained in:
		
							parent
							
								
									691cb43871
								
							
						
					
					
						commit
						6bef34852c
					
				
					 16 changed files with 1076 additions and 10 deletions
				
			
		|  | @ -209,7 +209,7 @@ if (ENABLE_QT) | ||||||
|         set(QT_PREFIX_HINT) |         set(QT_PREFIX_HINT) | ||||||
|     endif() |     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) |     if (ENABLE_QT_TRANSLATION) | ||||||
|         find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) |         find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) | ||||||
|  |  | ||||||
|  | @ -266,7 +266,7 @@ endif() | ||||||
| create_target_directory_groups(citra-qt) | 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 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_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||||||
| 
 | 
 | ||||||
| target_compile_definitions(citra-qt PRIVATE | target_compile_definitions(citra-qt PRIVATE | ||||||
|  |  | ||||||
|  | @ -3,14 +3,22 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | #include <QFutureWatcher> | ||||||
| #include <QMessageBox> | #include <QMessageBox> | ||||||
|  | #include <QProgressDialog> | ||||||
|  | #include <QtConcurrent/QtConcurrentMap> | ||||||
| #include "citra_qt/configuration/configuration_shared.h" | #include "citra_qt/configuration/configuration_shared.h" | ||||||
| #include "citra_qt/configuration/configure_system.h" | #include "citra_qt/configuration/configure_system.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  | #include "core/hle/service/am/am.h" | ||||||
| #include "core/hle/service/cfg/cfg.h" | #include "core/hle/service/cfg/cfg.h" | ||||||
| #include "core/hle/service/ptm/ptm.h" | #include "core/hle/service/ptm/ptm.h" | ||||||
|  | #include "core/hw/aes/key.h" | ||||||
| #include "ui_configure_system.h" | #include "ui_configure_system.h" | ||||||
|  | #ifdef ENABLE_WEB_SERVICE | ||||||
|  | #include "web_service/nus_titles.h" | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| static const std::array<int, 12> days_in_month = {{ | static const std::array<int, 12> days_in_month = {{ | ||||||
|     31, |     31, | ||||||
|  | @ -239,6 +247,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) | ||||||
|             &ConfigureSystem::UpdateInitTime); |             &ConfigureSystem::UpdateInitTime); | ||||||
|     connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, |     connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, | ||||||
|             &ConfigureSystem::RefreshConsoleID); |             &ConfigureSystem::RefreshConsoleID); | ||||||
|  |     connect(ui->button_start_download, &QPushButton::clicked, this, | ||||||
|  |             &ConfigureSystem::DownloadFromNUS); | ||||||
|     for (u8 i = 0; i < country_names.size(); i++) { |     for (u8 i = 0; i < country_names.size(); i++) { | ||||||
|         if (std::strcmp(country_names.at(i), "") != 0) { |         if (std::strcmp(country_names.at(i), "") != 0) { | ||||||
|             ui->combo_country->addItem(tr(country_names.at(i)), i); |             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()); |     ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); | ||||||
| 
 | 
 | ||||||
|     SetupPerGameUI(); |     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. <br><a " | ||||||
|  |                "href='https://citra-emu.org/wiki/aes-keys/'><span style=\"text-decoration: " | ||||||
|  |                "underline; color:#039be5;\">How to get keys?</span></a>")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     ConfigureTime(); |     ConfigureTime(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -542,3 +576,44 @@ void ConfigureSystem::SetupPerGameUI() { | ||||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds, |     ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds, | ||||||
|                                             is_new_3ds); |                                             is_new_3ds); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void ConfigureSystem::DownloadFromNUS() { | ||||||
|  | #ifdef ENABLE_WEB_SERVICE | ||||||
|  |     ui->button_start_download->setEnabled(false); | ||||||
|  | 
 | ||||||
|  |     const auto mode = static_cast<Title::Mode>(ui->combo_download_mode->currentIndex()); | ||||||
|  |     const std::vector<u64> titles = BuildFirmwareTitleList(mode, cfg->GetRegionValue()); | ||||||
|  | 
 | ||||||
|  |     QProgressDialog progress(tr("Downloading files..."), tr("Cancel"), 0, | ||||||
|  |                              static_cast<int>(titles.size()), this); | ||||||
|  |     progress.setWindowModality(Qt::WindowModal); | ||||||
|  | 
 | ||||||
|  |     QFutureWatcher<void> future_watcher; | ||||||
|  |     QObject::connect(&future_watcher, &QFutureWatcher<void>::finished, &progress, | ||||||
|  |                      &QProgressDialog::reset); | ||||||
|  |     QObject::connect(&progress, &QProgressDialog::canceled, &future_watcher, | ||||||
|  |                      &QFutureWatcher<void>::cancel); | ||||||
|  |     QObject::connect(&future_watcher, &QFutureWatcher<void>::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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -43,6 +43,8 @@ private: | ||||||
| 
 | 
 | ||||||
|     void SetupPerGameUI(); |     void SetupPerGameUI(); | ||||||
| 
 | 
 | ||||||
|  |     void DownloadFromNUS(); | ||||||
|  | 
 | ||||||
|     ConfigurationShared::CheckState is_new_3ds; |     ConfigurationShared::CheckState is_new_3ds; | ||||||
|     std::unique_ptr<Ui::ConfigureSystem> ui; |     std::unique_ptr<Ui::ConfigureSystem> ui; | ||||||
|     bool enabled = false; |     bool enabled = false; | ||||||
|  |  | ||||||
|  | @ -361,6 +361,52 @@ | ||||||
|             </property> |             </property> | ||||||
|           </widget> |           </widget> | ||||||
|         </item> |         </item> | ||||||
|  |         <item row="15" column="0"> | ||||||
|  |          <widget class="QLabel" name="label_nus_download"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Download System Files from Nitendo servers</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item row="15" column="1"> | ||||||
|  |          <layout class="QHBoxLayout" name="horizontalLayout_nus_download"> | ||||||
|  |           <item> | ||||||
|  |            <widget class="QComboBox" name="combo_download_mode"> | ||||||
|  |             <item> | ||||||
|  |              <property name="text"> | ||||||
|  |               <string>All</string> | ||||||
|  |              </property> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |              <property name="text"> | ||||||
|  |               <string>Recommended</string> | ||||||
|  |              </property> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |              <property name="text"> | ||||||
|  |               <string>Minimal</string> | ||||||
|  |              </property> | ||||||
|  |             </item> | ||||||
|  |            </widget> | ||||||
|  |           </item> | ||||||
|  |           <item> | ||||||
|  |            <widget class="QPushButton" name="button_start_download"> | ||||||
|  |             <property name="sizePolicy"> | ||||||
|  |              <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||||
|  |               <horstretch>0</horstretch> | ||||||
|  |               <verstretch>0</verstretch> | ||||||
|  |              </sizepolicy> | ||||||
|  |             </property> | ||||||
|  |             <property name="layoutDirection"> | ||||||
|  |              <enum>Qt::RightToLeft</enum> | ||||||
|  |             </property> | ||||||
|  |             <property name="text"> | ||||||
|  |              <string>Download</string> | ||||||
|  |             </property> | ||||||
|  |            </widget> | ||||||
|  |           </item> | ||||||
|  |          </layout> | ||||||
|  |         </item> | ||||||
|        </layout> |        </layout> | ||||||
|       </widget> |       </widget> | ||||||
|      </item> |      </item> | ||||||
|  |  | ||||||
|  | @ -1920,6 +1920,7 @@ void GMainWindow::OnLoadState() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnConfigure() { | void GMainWindow::OnConfigure() { | ||||||
|  |     game_list->SetDirectoryWatcherEnabled(false); | ||||||
|     Settings::SetConfiguringGlobal(true); |     Settings::SetConfiguringGlobal(true); | ||||||
|     ConfigureDialog configureDialog(this, hotkey_registry, |     ConfigureDialog configureDialog(this, hotkey_registry, | ||||||
|                                     !multiplayer_state->IsHostingPublicRoom()); |                                     !multiplayer_state->IsHostingPublicRoom()); | ||||||
|  | @ -1931,6 +1932,7 @@ void GMainWindow::OnConfigure() { | ||||||
|     const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; |     const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; | ||||||
|     const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); |     const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); | ||||||
|     auto result = configureDialog.exec(); |     auto result = configureDialog.exec(); | ||||||
|  |     game_list->SetDirectoryWatcherEnabled(true); | ||||||
|     if (result == QDialog::Accepted) { |     if (result == QDialog::Accepted) { | ||||||
|         configureDialog.ApplyConfiguration(); |         configureDialog.ApplyConfiguration(); | ||||||
|         InitializeHotkeys(); |         InitializeHotkeys(); | ||||||
|  |  | ||||||
|  | @ -16,8 +16,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| 
 | 
 | ||||||
| constexpr u32 CIA_SECTION_ALIGNMENT = 0x40; |  | ||||||
| 
 |  | ||||||
| Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) { | Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) { | ||||||
|     std::vector<u8> header_data(sizeof(Header)); |     std::vector<u8> header_data(sizeof(Header)); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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_HEADER_SIZE = 0x2020; | ||||||
| constexpr std::size_t CIA_DEPENDENCY_SIZE = 0x300; | constexpr std::size_t CIA_DEPENDENCY_SIZE = 0x300; | ||||||
| constexpr std::size_t CIA_METADATA_SIZE = 0x400; | 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. |  * Helper which implements an interface to read and write CTR Installable Archive (CIA) files. | ||||||
|  | @ -69,7 +70,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     void Print() const; |     void Print() const; | ||||||
| 
 | 
 | ||||||
| private: |  | ||||||
|     struct Header { |     struct Header { | ||||||
|         u32_le header_size; |         u32_le header_size; | ||||||
|         u16_le type; |         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.
 |             // 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; |             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"); |     static_assert(sizeof(Header) == CIA_HEADER_SIZE, "CIA Header structure size is wrong"); | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|     struct Metadata { |     struct Metadata { | ||||||
|         std::array<u64_le, 0x30> dependencies; |         std::array<u64_le, 0x30> dependencies; | ||||||
|         std::array<u8, 0x180> reserved; |         std::array<u8, 0x180> reserved; | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <cryptopp/aes.h> | #include <cryptopp/aes.h> | ||||||
| #include <cryptopp/modes.h> | #include <cryptopp/modes.h> | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
|  | #include "common/alignment.h" | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  | @ -31,6 +32,9 @@ | ||||||
| #include "core/hle/service/fs/fs_user.h" | #include "core/hle/service/fs/fs_user.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/loader/smdh.h" | #include "core/loader/smdh.h" | ||||||
|  | #ifdef ENABLE_WEB_SERVICE | ||||||
|  | #include "web_service/nus_download.h" | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| namespace Service::AM { | namespace Service::AM { | ||||||
| 
 | 
 | ||||||
|  | @ -138,6 +142,8 @@ ResultCode CIAFile::WriteTitleMetadata() { | ||||||
|             decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(), |             decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(), | ||||||
|                                                       ctr.data()); |                                                       ctr.data()); | ||||||
|         } |         } | ||||||
|  |     } else { | ||||||
|  |         LOG_ERROR(Service_AM, "Can't get title key from ticket"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     install_state = CIAInstallState::TMDLoaded; |     install_state = CIAInstallState::TMDLoaded; | ||||||
|  | @ -180,6 +186,11 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length, | ||||||
|                                  buffer + (range_min - offset) + available_to_write); |                                  buffer + (range_min - offset) + available_to_write); | ||||||
| 
 | 
 | ||||||
|             if ((tmd.GetContentTypeByIndex(i) & FileSys::TMDContentTypeFlag::Encrypted) != 0) { |             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()); |                 decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size()); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -235,7 +246,7 @@ ResultVal<std::size_t> CIAFile::Write(u64 offset, std::size_t length, bool flush | ||||||
|         std::size_t buf_offset = buf_loaded - offset; |         std::size_t buf_offset = buf_loaded - offset; | ||||||
|         std::size_t buf_copy_size = |         std::size_t buf_copy_size = | ||||||
|             std::min(length, static_cast<std::size_t>(container.GetContentOffset() - offset)) - |             std::min(length, static_cast<std::size_t>(container.GetContentOffset() - offset)) - | ||||||
|             buf_loaded; |             buf_offset; | ||||||
|         std::size_t buf_max_size = std::min(offset + length, container.GetContentOffset()); |         std::size_t buf_max_size = std::min(offset + length, container.GetContentOffset()); | ||||||
|         data.resize(buf_max_size); |         data.resize(buf_max_size); | ||||||
|         memcpy(data.data() + copy_offset, buffer + buf_offset, buf_copy_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; |     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<u8> 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<u32_le>(cetk_response->size()), | ||||||
|  |         .tmd_size = static_cast<u32_le>(tmd_response->size()), | ||||||
|  |         .meta_size = 0, | ||||||
|  |     }; | ||||||
|  |     for (u16 i = 0; i < content_count; ++i) { | ||||||
|  |         fake_header.SetContentPresent(i); | ||||||
|  |     } | ||||||
|  |     std::vector<u8> 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<u8>& 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) { | Service::FS::MediaType GetTitleMediaType(u64 titleId) { | ||||||
|     u16 platform = static_cast<u16>(titleId >> 48); |     u16 platform = static_cast<u16>(titleId >> 48); | ||||||
|     u16 category = static_cast<u16>((titleId >> 32) & 0xFFFF); |     u16 category = static_cast<u16>((titleId >> 32) & 0xFFFF); | ||||||
|  |  | ||||||
|  | @ -110,6 +110,13 @@ private: | ||||||
| InstallStatus InstallCIA(const std::string& path, | InstallStatus InstallCIA(const std::string& path, | ||||||
|                          std::function<ProgressCallback>&& update_callback = nullptr); |                          std::function<ProgressCallback>&& 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 |  * Get the mediatype for an installed title | ||||||
|  * @param titleId the installed title ID |  * @param titleId the installed title ID | ||||||
|  |  | ||||||
|  | @ -88,7 +88,7 @@ struct KeySlot { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| std::array<KeySlot, KeySlotID::MaxKeySlotID> key_slots; | std::array<KeySlot, KeySlotID::MaxKeySlotID> key_slots; | ||||||
| std::array<std::optional<AESKey>, 6> common_key_y_slots; | std::array<std::optional<AESKey>, MaxCommonKeySlot> common_key_y_slots; | ||||||
| 
 | 
 | ||||||
| enum class FirmwareType : u32 { | enum class FirmwareType : u32 { | ||||||
|     ARM9 = 0,  // uses NDMA
 |     ARM9 = 0,  // uses NDMA
 | ||||||
|  | @ -494,9 +494,9 @@ void LoadPresetKeys() { | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
| void InitKeys() { | void InitKeys(bool force) { | ||||||
|     static bool initialized = false; |     static bool initialized = false; | ||||||
|     if (initialized) |     if (initialized && !force) | ||||||
|         return; |         return; | ||||||
|     initialized = true; |     initialized = true; | ||||||
|     HW::RSA::InitSlots(); |     HW::RSA::InitSlots(); | ||||||
|  |  | ||||||
|  | @ -48,11 +48,13 @@ enum KeySlotID : std::size_t { | ||||||
|     MaxKeySlotID = 0x40, |     MaxKeySlotID = 0x40, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | constexpr std::size_t MaxCommonKeySlot = 6; | ||||||
|  | 
 | ||||||
| constexpr std::size_t AES_BLOCK_SIZE = 16; | constexpr std::size_t AES_BLOCK_SIZE = 16; | ||||||
| 
 | 
 | ||||||
| using AESKey = std::array<u8, AES_BLOCK_SIZE>; | using AESKey = std::array<u8, AES_BLOCK_SIZE>; | ||||||
| 
 | 
 | ||||||
| void InitKeys(); | void InitKeys(bool force = false); | ||||||
| 
 | 
 | ||||||
| void SetGeneratorConstant(const AESKey& key); | void SetGeneratorConstant(const AESKey& key); | ||||||
| void SetKeyX(std::size_t slot_id, const AESKey& key); | void SetKeyX(std::size_t slot_id, const AESKey& key); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,9 @@ | ||||||
| add_library(web_service STATIC | add_library(web_service STATIC | ||||||
|     announce_room_json.cpp |     announce_room_json.cpp | ||||||
|     announce_room_json.h |     announce_room_json.h | ||||||
|  |     nus_download.cpp | ||||||
|  |     nus_download.h | ||||||
|  |     nus_titles.h | ||||||
|     precompiled_headers.h |     precompiled_headers.h | ||||||
|     telemetry_json.cpp |     telemetry_json.cpp | ||||||
|     telemetry_json.h |     telemetry_json.h | ||||||
|  |  | ||||||
							
								
								
									
										47
									
								
								src/web_service/nus_download.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/web_service/nus_download.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | // Copyright 2020 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <httplib.h> | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "web_service/nus_download.h" | ||||||
|  | 
 | ||||||
|  | namespace WebService::NUS { | ||||||
|  | 
 | ||||||
|  | std::optional<std::vector<u8>> Download(const std::string& path) { | ||||||
|  |     constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net"; | ||||||
|  | 
 | ||||||
|  |     std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(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<u8>(response.body.begin(), response.body.end()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace WebService::NUS
 | ||||||
							
								
								
									
										15
									
								
								src/web_service/nus_download.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/web_service/nus_download.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 <optional> | ||||||
|  | #include <vector> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | namespace WebService::NUS { | ||||||
|  | 
 | ||||||
|  | std::optional<std::vector<u8>> Download(const std::string& path); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										761
									
								
								src/web_service/nus_titles.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										761
									
								
								src/web_service/nus_titles.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<u32, 6> lower_title_id; | ||||||
|  |     Mode mode = Mode::All; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const std::array<Title, 9> 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<Title, 17> 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<Title, 7> 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<Title, 27> 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<Title, 25> 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<Title, 5> 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<Title, 100> 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<u64> 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<Title> 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; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue