diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index f0ae442d6..01129b7ae 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -3,12 +3,14 @@ // Refer to the license.txt file included. #include <cstring> +#include <QFileDialog> #include <QFutureWatcher> #include <QMessageBox> #include <QProgressDialog> #include <QtConcurrent/QtConcurrentMap> #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_system.h" +#include "common/file_util.h" #include "common/settings.h" #include "core/core.h" #include "core/hle/service/am/am.h" @@ -234,6 +236,24 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent) &ConfigureSystem::RefreshConsoleID); connect(ui->button_start_download, &QPushButton::clicked, this, &ConfigureSystem::DownloadFromNUS); + + connect(ui->button_secure_info, &QPushButton::clicked, this, [this] { + ui->button_secure_info->setEnabled(false); + const QString file_path_qtstr = + QFileDialog::getOpenFileName(this, tr("Select SecureInfo_A"), QString(), + tr("SecureInfo_A (SecureInfo_A);;All Files (*.*)")); + ui->button_secure_info->setEnabled(true); + InstallSecureData(file_path_qtstr.toStdString(), cfg->GetSecureInfoAPath()); + }); + connect(ui->button_friend_code_seed, &QPushButton::clicked, this, [this] { + ui->button_friend_code_seed->setEnabled(false); + const QString file_path_qtstr = QFileDialog::getOpenFileName( + this, tr("Select LocalFriendCodeSeed_B"), QString(), + tr("LocalFriendCodeSeed_B (LocalFriendCodeSeed_B);;All Files (*.*)")); + ui->button_friend_code_seed->setEnabled(true); + InstallSecureData(file_path_qtstr.toStdString(), cfg->GetLocalFriendCodeSeedBPath()); + }); + 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); @@ -298,6 +318,7 @@ void ConfigureSystem::SetConfiguration() { ASSERT_MSG(cfg, "CFG Module missing!"); ReadSystemSettings(); ui->group_system_settings->setEnabled(false); + ui->group_real_console_unique_data->setEnabled(false); } else { // This tab is enabled only when game is not running (i.e. all service are not initialized). cfg = std::make_shared<Service::CFG::Module>(); @@ -351,6 +372,9 @@ void ConfigureSystem::ReadSystemSettings() { // set firmware download region ui->combo_download_region->setCurrentIndex(static_cast<int>(cfg->GetRegionValue())); + + // Refresh secure data status + RefreshSecureDataStatus(); } void ConfigureSystem::ApplyConfiguration() { @@ -501,6 +525,41 @@ void ConfigureSystem::RefreshConsoleID() { tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); } +void ConfigureSystem::InstallSecureData(const std::string& from_path, const std::string& to_path) { + std::string from = + FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault); + std::string to = FileUtil::SanitizePath(to_path, FileUtil::DirectorySeparator::PlatformDefault); + if (from.empty() || from == to) { + return; + } + FileUtil::CreateFullPath(to_path); + FileUtil::Copy(from, to); + cfg->InvalidateSecureData(); + RefreshSecureDataStatus(); +} + +void ConfigureSystem::RefreshSecureDataStatus() { + auto status_to_str = [](Service::CFG::SecureDataLoadStatus status) { + switch (status) { + case Service::CFG::SecureDataLoadStatus::Loaded: + return "Loaded"; + case Service::CFG::SecureDataLoadStatus::NotFound: + return "Not Found"; + case Service::CFG::SecureDataLoadStatus::Invalid: + return "Invalid"; + case Service::CFG::SecureDataLoadStatus::IOError: + return "IO Error"; + default: + return ""; + } + }; + + ui->label_secure_info_status->setText( + tr((std::string("Status: ") + status_to_str(cfg->LoadSecureInfoAFile())).c_str())); + ui->label_friend_code_seed_status->setText( + tr((std::string("Status: ") + status_to_str(cfg->LoadLocalFriendCodeSeedBFile())).c_str())); +} + void ConfigureSystem::RetranslateUI() { ui->retranslateUi(this); } diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index 86b6b7016..9f107cb4b 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -45,6 +45,9 @@ private: void UpdateInitTime(int init_clock); void RefreshConsoleID(); + void InstallSecureData(const std::string& from_path, const std::string& to_path); + void RefreshSecureDataStatus(); + void SetupPerGameUI(); void DownloadFromNUS(); diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui index 728db6c76..1f5197d2b 100644 --- a/src/citra_qt/configuration/configure_system.ui +++ b/src/citra_qt/configuration/configure_system.ui @@ -1,503 +1,584 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureSystem</class> - <widget class="QWidget" name="ConfigureSystem"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>535</width> - <height>619</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QGroupBox" name="group_system_settings"> - <property name="title"> - <string>System Settings</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0"> - <widget class="QCheckBox" name="toggle_new_3ds"> - <property name="text"> - <string>Enable New 3DS mode</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="edit_username"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="maxLength"> - <number>10</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_username"> - <property name="text"> - <string>Username</string> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_birthday"> - <property name="text"> - <string>Birthday</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> + <class>ConfigureSystem</class> + <widget class="QWidget" name="ConfigureSystem"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>535</width> + <height>619</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QComboBox" name="combo_birthmonth"> - <item> - <property name="text"> - <string>January</string> - </property> - </item> - <item> - <property name="text"> - <string>February</string> - </property> - </item> - <item> - <property name="text"> - <string>March</string> - </property> - </item> - <item> - <property name="text"> - <string>April</string> - </property> - </item> - <item> - <property name="text"> - <string>May</string> - </property> - </item> - <item> - <property name="text"> - <string>June</string> - </property> - </item> - <item> - <property name="text"> - <string>July</string> - </property> - </item> - <item> - <property name="text"> - <string>August</string> - </property> - </item> - <item> - <property name="text"> - <string>September</string> - </property> - </item> - <item> - <property name="text"> - <string>October</string> - </property> - </item> - <item> - <property name="text"> - <string>November</string> - </property> - </item> - <item> - <property name="text"> - <string>December</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QComboBox" name="combo_birthday"/> - </item> - </layout> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_language"> - <property name="text"> - <string>Language</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QComboBox" name="combo_language"> - <property name="toolTip"> - <string>Note: this can be overridden when region setting is auto-select</string> - </property> - <item> - <property name="text"> - <string>Japanese (日本語)</string> - </property> - </item> - <item> - <property name="text"> - <string>English</string> - </property> - </item> - <item> - <property name="text"> - <string>French (français)</string> - </property> - </item> - <item> - <property name="text"> - <string>German (Deutsch)</string> - </property> - </item> - <item> - <property name="text"> - <string>Italian (italiano)</string> - </property> - </item> - <item> - <property name="text"> - <string>Spanish (español)</string> - </property> - </item> - <item> - <property name="text"> - <string>Simplified Chinese (简体中文)</string> - </property> - </item> - <item> - <property name="text"> - <string>Korean (한국어)</string> - </property> - </item> - <item> - <property name="text"> - <string>Dutch (Nederlands)</string> - </property> - </item> - <item> - <property name="text"> - <string>Portuguese (português)</string> - </property> - </item> - <item> - <property name="text"> - <string>Russian (Русский)</string> - </property> - </item> - <item> - <property name="text"> - <string>Traditional Chinese (正體中文)</string> - </property> - </item> - </widget> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_sound"> - <property name="text"> - <string>Sound output mode</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QComboBox" name="combo_sound"> - <item> - <property name="text"> - <string>Mono</string> - </property> - </item> - <item> - <property name="text"> - <string>Stereo</string> - </property> - </item> - <item> - <property name="text"> - <string>Surround</string> - </property> - </item> - </widget> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_country"> - <property name="text"> - <string>Country</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QComboBox" name="combo_country"/> - </item> - <item row="7" column="0"> - <widget class="QLabel" name="label_init_clock"> - <property name="text"> - <string>Clock</string> - </property> - </widget> - </item> - <item row="7" column="1"> - <widget class="QComboBox" name="combo_init_clock"> - <item> - <property name="text"> - <string>System Clock</string> - </property> - </item> - <item> - <property name="text"> - <string>Fixed Time</string> - </property> - </item> - </widget> - </item> - <item row="8" column="0"> - <widget class="QLabel" name="label_init_time"> - <property name="text"> - <string>Startup time</string> - </property> - </widget> - </item> - <item row="8" column="1"> - <widget class="QDateTimeEdit" name="edit_init_time"> - <property name="displayFormat"> - <string>yyyy-MM-ddTHH:mm:ss</string> - </property> - </widget> - </item> - <item row="8" column="0"> - <widget class="QLabel" name="label_init_time_offset"> - <property name="text"> - <string>Offset time</string> - </property> - </widget> - </item> - <item row="8" column="1"> - <layout class="QGridLayout" name="edit_init_time_offset_grid"> - <item row="0" column="0"> - <widget class="QSpinBox" name="edit_init_time_offset_days"> - <property name="suffix"> - <string> days</string> - </property> - <property name="minimum"> - <number>-2147483648</number> - </property> - <property name="maximum"> - <number>2147483647</number> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QTimeEdit" name="edit_init_time_offset_time"> - <property name="displayFormat"> - <string>HH:mm:ss</string> - </property> - </widget> - </item> - </layout> - </item> - <item row="9" column="0"> - <widget class="QLabel" name="label_play_coins"> - <property name="text"> - <string>Play Coins:</string> - </property> - </widget> - </item> - <item row="9" column="1"> - <widget class="QSpinBox" name="spinBox_play_coins"> - <property name="maximum"> - <number>300</number> - </property> - </widget> - </item> - <item row="10" column="1"> - <widget class="QCheckBox" name="toggle_system_setup"> - <property name="text"> - <string>Run System Setup when Home Menu is launched</string> - </property> - </widget> - </item> - <item row="11" column="0"> - <widget class="QLabel" name="label_console_id"> - <property name="text"> - <string>Console ID:</string> - </property> - </widget> - </item> - <item row="11" column="1"> - <widget class="QPushButton" name="button_regenerate_console_id"> - <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>Regenerate</string> - </property> - </widget> - </item> - <item row="12" column="0"> - <widget class="QLabel" name="label_plugin_loader"> - <property name="text"> - <string>3GX Plugin Loader:</string> - </property> - </widget> - </item> - <item row="12" column="1"> - <widget class="QCheckBox" name="plugin_loader"> - <property name="text"> - <string>Enable 3GX plugin loader</string> - </property> - </widget> - </item> - <item row="13" column="1"> - <widget class="QCheckBox" name="allow_plugin_loader"> - <property name="text"> - <string>Allow games to change plugin loader state</string> - </property> - </widget> - </item> - <item row="14" 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="14" column="1"> - <widget class="QWidget" name="body_nus_download"> - <layout class="QHBoxLayout" name="horizontalLayout_nus_download"> - <item> - <widget class="QComboBox" name="combo_download_set"> - <item> - <property name="text"> - <string>Minimal</string> + <widget class="QGroupBox" name="group_system_settings"> + <property name="title"> + <string>System Settings</string> </property> - </item> - <item> - <property name="text"> - <string>Old 3DS</string> - </property> - </item> - <item> - <property name="text"> - <string>New 3DS</string> - </property> - </item> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0"> + <widget class="QCheckBox" name="toggle_new_3ds"> + <property name="text"> + <string>Enable New 3DS mode</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="edit_username"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxLength"> + <number>10</number> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_username"> + <property name="text"> + <string>Username</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_birthday"> + <property name="text"> + <string>Birthday</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> + <item> + <widget class="QComboBox" name="combo_birthmonth"> + <item> + <property name="text"> + <string>January</string> + </property> + </item> + <item> + <property name="text"> + <string>February</string> + </property> + </item> + <item> + <property name="text"> + <string>March</string> + </property> + </item> + <item> + <property name="text"> + <string>April</string> + </property> + </item> + <item> + <property name="text"> + <string>May</string> + </property> + </item> + <item> + <property name="text"> + <string>June</string> + </property> + </item> + <item> + <property name="text"> + <string>July</string> + </property> + </item> + <item> + <property name="text"> + <string>August</string> + </property> + </item> + <item> + <property name="text"> + <string>September</string> + </property> + </item> + <item> + <property name="text"> + <string>October</string> + </property> + </item> + <item> + <property name="text"> + <string>November</string> + </property> + </item> + <item> + <property name="text"> + <string>December</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_birthday"/> + </item> + </layout> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_language"> + <property name="text"> + <string>Language</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="combo_language"> + <property name="toolTip"> + <string>Note: this can be overridden when region setting is auto-select</string> + </property> + <item> + <property name="text"> + <string>Japanese (日本語)</string> + </property> + </item> + <item> + <property name="text"> + <string>English</string> + </property> + </item> + <item> + <property name="text"> + <string>French (français)</string> + </property> + </item> + <item> + <property name="text"> + <string>German (Deutsch)</string> + </property> + </item> + <item> + <property name="text"> + <string>Italian (italiano)</string> + </property> + </item> + <item> + <property name="text"> + <string>Spanish (español)</string> + </property> + </item> + <item> + <property name="text"> + <string>Simplified Chinese (简体中文)</string> + </property> + </item> + <item> + <property name="text"> + <string>Korean (한국어)</string> + </property> + </item> + <item> + <property name="text"> + <string>Dutch (Nederlands)</string> + </property> + </item> + <item> + <property name="text"> + <string>Portuguese (português)</string> + </property> + </item> + <item> + <property name="text"> + <string>Russian (Русский)</string> + </property> + </item> + <item> + <property name="text"> + <string>Traditional Chinese (正體中文)</string> + </property> + </item> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_sound"> + <property name="text"> + <string>Sound output mode</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QComboBox" name="combo_sound"> + <item> + <property name="text"> + <string>Mono</string> + </property> + </item> + <item> + <property name="text"> + <string>Stereo</string> + </property> + </item> + <item> + <property name="text"> + <string>Surround</string> + </property> + </item> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_country"> + <property name="text"> + <string>Country</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QComboBox" name="combo_country"/> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="label_init_clock"> + <property name="text"> + <string>Clock</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QComboBox" name="combo_init_clock"> + <item> + <property name="text"> + <string>System Clock</string> + </property> + </item> + <item> + <property name="text"> + <string>Fixed Time</string> + </property> + </item> + </widget> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="label_init_time"> + <property name="text"> + <string>Startup time</string> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QDateTimeEdit" name="edit_init_time"> + <property name="displayFormat"> + <string>yyyy-MM-ddTHH:mm:ss</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="label_init_time_offset"> + <property name="text"> + <string>Offset time</string> + </property> + </widget> + </item> + <item row="8" column="1"> + <layout class="QGridLayout" name="edit_init_time_offset_grid"> + <item row="0" column="0"> + <widget class="QSpinBox" name="edit_init_time_offset_days"> + <property name="suffix"> + <string> days</string> + </property> + <property name="minimum"> + <number>-2147483648</number> + </property> + <property name="maximum"> + <number>2147483647</number> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QTimeEdit" name="edit_init_time_offset_time"> + <property name="displayFormat"> + <string>HH:mm:ss</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="9" column="0"> + <widget class="QLabel" name="label_play_coins"> + <property name="text"> + <string>Play Coins:</string> + </property> + </widget> + </item> + <item row="9" column="1"> + <widget class="QSpinBox" name="spinBox_play_coins"> + <property name="maximum"> + <number>300</number> + </property> + </widget> + </item> + <item row="10" column="1"> + <widget class="QCheckBox" name="toggle_system_setup"> + <property name="text"> + <string>Run System Setup when Home Menu is launched</string> + </property> + </widget> + </item> + <item row="11" column="0"> + <widget class="QLabel" name="label_console_id"> + <property name="text"> + <string>Console ID:</string> + </property> + </widget> + </item> + <item row="11" column="1"> + <widget class="QPushButton" name="button_regenerate_console_id"> + <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>Regenerate</string> + </property> + </widget> + </item> + <item row="12" column="0"> + <widget class="QLabel" name="label_plugin_loader"> + <property name="text"> + <string>3GX Plugin Loader:</string> + </property> + </widget> + </item> + <item row="12" column="1"> + <widget class="QCheckBox" name="plugin_loader"> + <property name="text"> + <string>Enable 3GX plugin loader</string> + </property> + </widget> + </item> + <item row="13" column="1"> + <widget class="QCheckBox" name="allow_plugin_loader"> + <property name="text"> + <string>Allow games to change plugin loader state</string> + </property> + </widget> + </item> + <item row="14" 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="14" column="1"> + <widget class="QWidget" name="body_nus_download"> + <layout class="QHBoxLayout" name="horizontalLayout_nus_download"> + <item> + <widget class="QComboBox" name="combo_download_set"> + <item> + <property name="text"> + <string>Minimal</string> + </property> + </item> + <item> + <property name="text"> + <string>Old 3DS</string> + </property> + </item> + <item> + <property name="text"> + <string>New 3DS</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_download_region"> + <item> + <property name="text"> + <string>JPN</string> + </property> + </item> + <item> + <property name="text"> + <string>USA</string> + </property> + </item> + <item> + <property name="text"> + <string>EUR</string> + </property> + </item> + <item> + <property name="text"> + <string>AUS</string> + </property> + </item> + <item> + <property name="text"> + <string>CHN</string> + </property> + </item> + <item> + <property name="text"> + <string>KOR</string> + </property> + </item> + <item> + <property name="text"> + <string>TWN</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> + </widget> + </item> + </layout> </widget> - </item> - <item> - <widget class="QComboBox" name="combo_download_region"> - <item> - <property name="text"> - <string>JPN</string> + </item> + <item> + <widget class="QGroupBox" name="group_real_console_unique_data"> + <property name="title"> + <string>Real Console Unique Data</string> </property> - </item> - <item> - <property name="text"> - <string>USA</string> - </property> - </item> - <item> - <property name="text"> - <string>EUR</string> - </property> - </item> - <item> - <property name="text"> - <string>AUS</string> - </property> - </item> - <item> - <property name="text"> - <string>CHN</string> - </property> - </item> - <item> - <property name="text"> - <string>KOR</string> - </property> - </item> - <item> - <property name="text"> - <string>TWN</string> - </property> - </item> + <layout class="QGridLayout" name="gridLayout1"> + <item row="1" column="0"> + <widget class="QLabel" name="label_secure_info"> + <property name="text"> + <string>SecureInfo_A</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QWidget" name="secure_info"> + <layout class="QHBoxLayout" name="horizontalLayout_secure_info"> + <item> + <widget class="QLabel" name="label_secure_info_status"> + <property name="text"> + <string></string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_secure_info"> + <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>Choose</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_friend_code_seed"> + <property name="text"> + <string>LocalFriendCodeSeed_B</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QWidget" name="friend_code_seed"> + <layout class="QHBoxLayout" name="horizontalLayout_friend_code_seed"> + <item> + <widget class="QLabel" name="label_friend_code_seed_status"> + <property name="text"> + <string></string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_friend_code_seed"> + <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>Choose</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> </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> + </item> + <item> + <widget class="QLabel" name="label_disable_info"> + <property name="text"> + <string>System settings are available only when game is not running.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QLabel" name="label_disable_info"> - <property name="text"> - <string>System settings are available only when game is not running.</string> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> </layout> - </item> - </layout> - </widget> - <tabstops> - <tabstop>toggle_new_3ds</tabstop> - <tabstop>edit_username</tabstop> - <tabstop>combo_birthmonth</tabstop> - <tabstop>combo_birthday</tabstop> - <tabstop>combo_language</tabstop> - <tabstop>combo_sound</tabstop> - <tabstop>combo_country</tabstop> - <tabstop>combo_init_clock</tabstop> - <tabstop>edit_init_time</tabstop> - <tabstop>spinBox_play_coins</tabstop> - <tabstop>button_regenerate_console_id</tabstop> - </tabstops> - <resources/> - <connections/> + </widget> + <tabstops> + <tabstop>toggle_new_3ds</tabstop> + <tabstop>edit_username</tabstop> + <tabstop>combo_birthmonth</tabstop> + <tabstop>combo_birthday</tabstop> + <tabstop>combo_language</tabstop> + <tabstop>combo_sound</tabstop> + <tabstop>combo_country</tabstop> + <tabstop>combo_init_clock</tabstop> + <tabstop>edit_init_time</tabstop> + <tabstop>spinBox_play_coins</tabstop> + <tabstop>button_regenerate_console_id</tabstop> + </tabstops> + <resources/> + <connections/> </ui> diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 8fbbca114..4ac067d1a 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -212,12 +212,39 @@ void Module::Interface::GetRegion(Kernel::HLERequestContext& ctx) { void Module::Interface::SecureInfoGetByte101(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - LOG_DEBUG(Service_CFG, "(STUBBED) called"); + u8 ret = 0; + if (cfg->secure_info_a_loaded) { + ret = cfg->secure_info_a.unknown; + } IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); - // According to 3dbrew this is normally 0. - rb.Push<u8>(0); + rb.Push<u8>(ret); +} + +void Module::Interface::SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + [[maybe_unused]] u32 out_size = rp.Pop<u32>(); + auto out_buffer = rp.PopMappedBuffer(); + + if (out_buffer.GetSize() < sizeof(SecureInfoA::serial_number)) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, + ErrorSummary::WrongArgument, ErrorLevel::Permanent)); + } + // Never happens on real hardware, but may happen if user didn't supply a dump. + // Always make sure to have available both secure data kinds or error otherwise. + if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Config, + ErrorSummary::InvalidState, ErrorLevel::Permanent)); + } + + out_buffer.Write(&cfg->secure_info_a.serial_number, 0, sizeof(SecureInfoA::serial_number)); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(out_buffer); } void Module::Interface::GetTransferableId(Kernel::HLERequestContext& ctx) { @@ -342,6 +369,43 @@ void Module::Interface::UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx) rb.Push(cfg->UpdateConfigNANDSavegame()); } +void Module::Interface::GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + [[maybe_unused]] u32 out_size = rp.Pop<u32>(); + auto out_buffer = rp.PopMappedBuffer(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + if (out_buffer.GetSize() < sizeof(LocalFriendCodeSeedB)) { + rb.Push(ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, + ErrorSummary::WrongArgument, ErrorLevel::Permanent)); + } + // Never happens on real hardware, but may happen if user didn't supply a dump. + // Always make sure to have available both secure data kinds or error otherwise. + if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) { + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Config, + ErrorSummary::InvalidState, ErrorLevel::Permanent)); + } + + out_buffer.Write(&cfg->local_friend_code_seed_b, 0, sizeof(LocalFriendCodeSeedB)); + rb.Push(RESULT_SUCCESS); +} + +void Module::Interface::GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + + // Never happens on real hardware, but may happen if user didn't supply a dump. + // Always make sure to have available both secure data kinds or error otherwise. + if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Config, + ErrorSummary::InvalidState, ErrorLevel::Permanent)); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(cfg->local_friend_code_seed_b.friend_code_seed); +} + void Module::Interface::FormatConfig(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -484,6 +548,14 @@ ResultCode Module::UpdateConfigNANDSavegame() { return RESULT_SUCCESS; } +std::string Module::GetLocalFriendCodeSeedBPath() { + return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/LocalFriendCodeSeed_B"; +} + +std::string Module::GetSecureInfoAPath() { + return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/SecureInfo_A"; +} + ResultCode Module::FormatConfig() { ResultCode res = DeleteConfigNANDSaveFile(); // The delete command fails if the file doesn't exist, so we have to check that too @@ -557,6 +629,55 @@ ResultCode Module::LoadConfigNANDSaveFile() { return FormatConfig(); } +void Module::InvalidateSecureData() { + secure_info_a_loaded = local_friend_code_seed_b_loaded = false; +} + +SecureDataLoadStatus Module::LoadSecureInfoAFile() { + if (secure_info_a_loaded) { + return SecureDataLoadStatus::Loaded; + } + std::string file_path = GetSecureInfoAPath(); + if (!FileUtil::Exists(file_path)) { + return SecureDataLoadStatus::NotFound; + } + FileUtil::IOFile file(file_path, "rb"); + if (!file.IsOpen()) { + return SecureDataLoadStatus::IOError; + } + if (file.GetSize() != sizeof(SecureInfoA)) { + return SecureDataLoadStatus::Invalid; + } + if (file.ReadBytes(&secure_info_a, sizeof(SecureInfoA)) != sizeof(SecureInfoA)) { + return SecureDataLoadStatus::IOError; + } + secure_info_a_loaded = true; + return SecureDataLoadStatus::Loaded; +} + +SecureDataLoadStatus Module::LoadLocalFriendCodeSeedBFile() { + if (local_friend_code_seed_b_loaded) { + return SecureDataLoadStatus::Loaded; + } + std::string file_path = GetLocalFriendCodeSeedBPath(); + if (!FileUtil::Exists(file_path)) { + return SecureDataLoadStatus::NotFound; + } + FileUtil::IOFile file(file_path, "rb"); + if (!file.IsOpen()) { + return SecureDataLoadStatus::IOError; + } + if (file.GetSize() != sizeof(LocalFriendCodeSeedB)) { + return SecureDataLoadStatus::Invalid; + } + if (file.ReadBytes(&local_friend_code_seed_b, sizeof(LocalFriendCodeSeedB)) != + sizeof(LocalFriendCodeSeedB)) { + return SecureDataLoadStatus::IOError; + } + local_friend_code_seed_b_loaded = true; + return SecureDataLoadStatus::Loaded; +} + Module::Module() { LoadConfigNANDSaveFile(); // Check the config savegame EULA Version and update it to 0x7F7F if necessary @@ -569,6 +690,8 @@ Module::Module() { SetEULAVersion(default_version); UpdateConfigNANDSavegame(); } + LoadSecureInfoAFile(); + LoadLocalFriendCodeSeedBFile(); } Module::~Module() = default; diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 738709829..2d0b69e81 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -176,6 +176,28 @@ enum class AccessFlag : u16 { }; DECLARE_ENUM_FLAG_OPERATORS(AccessFlag); +struct SecureInfoA { + std::array<u8, 0x100> signature; + u8 region; + u8 unknown; + std::array<u8, 0xF> serial_number; +}; +static_assert(sizeof(SecureInfoA) == 0x111); + +struct LocalFriendCodeSeedB { + std::array<u8, 0x100> signature; + u64 unknown; + u64 friend_code_seed; +}; +static_assert(sizeof(LocalFriendCodeSeedB) == 0x110); + +enum class SecureDataLoadStatus { + Loaded, + NotFound, + Invalid, + IOError, +}; + class Module final { public: Module(); @@ -230,6 +252,18 @@ public: */ void SecureInfoGetByte101(Kernel::HLERequestContext& ctx); + /** + * CFG::SecureInfoGetSerialNo service function + * Inputs: + * 1 : Buffer Size + * 2-3: Output mapped buffer + * Outputs: + * 0 : Result Header code + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Output mapped buffer + */ + void SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx); + /** * CFG::GetTransferableId service function * Inputs: @@ -324,6 +358,27 @@ public: */ void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx); + /** + * CFG::GetLocalFriendCodeSeedData service function + * Inputs: + * 1 : Buffer Size + * 2-3: Output mapped buffer + * Outputs: + * 0 : Result Header code + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx); + + /** + * CFG::GetLocalFriendCodeSeed service function + * Inputs: + * Outputs: + * 0 : Result Header code + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Friend code seed + */ + void GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx); + /** * CFG::FormatConfig service function * Inputs: @@ -538,11 +593,43 @@ public: */ ResultCode UpdateConfigNANDSavegame(); + /** + * Invalidates the loaded secure data so that it is loaded again. + */ + void InvalidateSecureData(); + /** + * Loads the LocalFriendCodeSeed_B file from NAND. + * @returns LocalFriendCodeSeedBLoadStatus indicating the file load status. + */ + SecureDataLoadStatus LoadSecureInfoAFile(); + + /** + * Loads the LocalFriendCodeSeed_B file from NAND. + * @returns LocalFriendCodeSeedBLoadStatus indicating the file load status. + */ + SecureDataLoadStatus LoadLocalFriendCodeSeedBFile(); + + /** + * Gets the SecureInfo_A path in the host filesystem + * @returns std::string SecureInfo_A path in the host filesystem + */ + std::string GetSecureInfoAPath(); + + /** + * Gets the LocalFriendCodeSeed_B path in the host filesystem + * @returns std::string LocalFriendCodeSeed_B path in the host filesystem + */ + std::string GetLocalFriendCodeSeedBPath(); + private: static constexpr u32 CONFIG_SAVEFILE_SIZE = 0x8000; std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer; std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive; u32 preferred_region_code = 0; + bool secure_info_a_loaded = false; + SecureInfoA secure_info_a; + bool local_friend_code_seed_b_loaded = false; + LocalFriendCodeSeedB local_friend_code_seed_b; template <class Archive> void serialize(Archive& ar, const unsigned int); diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 5223f8d08..aa7c17c3b 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -28,11 +28,11 @@ CFG_I::CFG_I(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c {0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"}, {0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"}, {0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, - {0x0404, nullptr, "GetLocalFriendCodeSeedData"}, - {0x0405, nullptr, "GetLocalFriendCodeSeed"}, + {0x0404, &CFG_I::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"}, + {0x0405, &CFG_I::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"}, {0x0406, &CFG_I::GetRegion, "GetRegion"}, {0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"}, - {0x0408, nullptr, "SecureInfoGetSerialNo"}, + {0x0408, &CFG_I::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"}, {0x0409, nullptr, "UpdateConfigBlk00040003"}, // cfg:i {0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index e75ca8548..4ad653f61 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -28,11 +28,11 @@ CFG_S::CFG_S(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c {0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"}, {0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"}, {0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, - {0x0404, nullptr, "GetLocalFriendCodeSeedData"}, - {0x0405, nullptr, "GetLocalFriendCodeSeed"}, + {0x0404, &CFG_S::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"}, + {0x0405, &CFG_S::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"}, {0x0406, &CFG_S::GetRegion, "GetRegion"}, {0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"}, - {0x0408, nullptr, "SecureInfoGetSerialNo"}, + {0x0408, &CFG_S::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"}, {0x0409, nullptr, "UpdateConfigBlk00040003"}, // clang-format on };