Merge pull request #1894 from wwylele/set-config-block
Implement config savegame editing & clean up
This commit is contained in:
		| @@ -22,6 +22,7 @@ set(SRCS | |||||||
|             configure_debug.cpp |             configure_debug.cpp | ||||||
|             configure_dialog.cpp |             configure_dialog.cpp | ||||||
|             configure_general.cpp |             configure_general.cpp | ||||||
|  |             configure_system.cpp | ||||||
|             game_list.cpp |             game_list.cpp | ||||||
|             hotkeys.cpp |             hotkeys.cpp | ||||||
|             main.cpp |             main.cpp | ||||||
| @@ -52,6 +53,7 @@ set(HEADERS | |||||||
|             configure_debug.h |             configure_debug.h | ||||||
|             configure_dialog.h |             configure_dialog.h | ||||||
|             configure_general.h |             configure_general.h | ||||||
|  |             configure_system.h | ||||||
|             game_list.h |             game_list.h | ||||||
|             game_list_p.h |             game_list_p.h | ||||||
|             hotkeys.h |             hotkeys.h | ||||||
| @@ -69,6 +71,7 @@ set(UIS | |||||||
|             configure_audio.ui |             configure_audio.ui | ||||||
|             configure_debug.ui |             configure_debug.ui | ||||||
|             configure_general.ui |             configure_general.ui | ||||||
|  |             configure_system.ui | ||||||
|             hotkeys.ui |             hotkeys.ui | ||||||
|             main.ui |             main.ui | ||||||
|             ) |             ) | ||||||
|   | |||||||
| @@ -24,6 +24,11 @@ | |||||||
|        <string>General</string> |        <string>General</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|      </widget> |      </widget> | ||||||
|  |      <widget class="ConfigureSystem" name="systemTab"> | ||||||
|  |       <attribute name="title"> | ||||||
|  |        <string>System</string> | ||||||
|  |       </attribute> | ||||||
|  |      </widget> | ||||||
|      <widget class="QWidget" name="inputTab"> |      <widget class="QWidget" name="inputTab"> | ||||||
|       <attribute name="title"> |       <attribute name="title"> | ||||||
|        <string>Input</string> |        <string>Input</string> | ||||||
| @@ -57,6 +62,12 @@ | |||||||
|    <header>configure_general.h</header> |    <header>configure_general.h</header> | ||||||
|    <container>1</container> |    <container>1</container> | ||||||
|   </customwidget> |   </customwidget> | ||||||
|  |   <customwidget> | ||||||
|  |    <class>ConfigureSystem</class> | ||||||
|  |    <extends>QWidget</extends> | ||||||
|  |    <header>configure_system.h</header> | ||||||
|  |    <container>1</container> | ||||||
|  |   </customwidget> | ||||||
|   <customwidget> |   <customwidget> | ||||||
|    <class>ConfigureAudio</class> |    <class>ConfigureAudio</class> | ||||||
|    <extends>QWidget</extends> |    <extends>QWidget</extends> | ||||||
|   | |||||||
| @@ -9,9 +9,10 @@ | |||||||
|  |  | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
|  |  | ||||||
| ConfigureDialog::ConfigureDialog(QWidget *parent) : | ConfigureDialog::ConfigureDialog(QWidget *parent, bool running) : | ||||||
|     QDialog(parent), |     QDialog(parent), | ||||||
|     ui(new Ui::ConfigureDialog) |     ui(new Ui::ConfigureDialog), | ||||||
|  |     emulation_running(running) | ||||||
| { | { | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     this->setConfiguration(); |     this->setConfiguration(); | ||||||
| @@ -21,10 +22,14 @@ ConfigureDialog::~ConfigureDialog() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void ConfigureDialog::setConfiguration() { | void ConfigureDialog::setConfiguration() { | ||||||
|  |     // System tab needs set manually | ||||||
|  |     // depending on whether emulation is running | ||||||
|  |     ui->systemTab->setConfiguration(emulation_running); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ConfigureDialog::applyConfiguration() { | void ConfigureDialog::applyConfiguration() { | ||||||
|     ui->generalTab->applyConfiguration(); |     ui->generalTab->applyConfiguration(); | ||||||
|  |     ui->systemTab->applyConfiguration(); | ||||||
|     ui->audioTab->applyConfiguration(); |     ui->audioTab->applyConfiguration(); | ||||||
|     ui->debugTab->applyConfiguration(); |     ui->debugTab->applyConfiguration(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ class ConfigureDialog : public QDialog | |||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
|  |  | ||||||
| public: | public: | ||||||
|     explicit ConfigureDialog(QWidget *parent = nullptr); |     explicit ConfigureDialog(QWidget *parent, bool emulation_running); | ||||||
|     ~ConfigureDialog(); |     ~ConfigureDialog(); | ||||||
|  |  | ||||||
|     void applyConfiguration(); |     void applyConfiguration(); | ||||||
| @@ -26,4 +26,5 @@ private: | |||||||
|  |  | ||||||
| private: | private: | ||||||
|     std::unique_ptr<Ui::ConfigureDialog> ui; |     std::unique_ptr<Ui::ConfigureDialog> ui; | ||||||
|  |     bool emulation_running; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										136
									
								
								src/citra_qt/configure_system.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/citra_qt/configure_system.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | |||||||
|  | // Copyright 2016 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "citra_qt/configure_system.h" | ||||||
|  | #include "citra_qt/ui_settings.h" | ||||||
|  | #include "ui_configure_system.h" | ||||||
|  |  | ||||||
|  | #include "core/hle/service/fs/archive.h" | ||||||
|  | #include "core/hle/service/cfg/cfg.h" | ||||||
|  |  | ||||||
|  | static const std::array<int, 12> days_in_month = {{ | ||||||
|  |     31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||||||
|  | }}; | ||||||
|  |  | ||||||
|  | ConfigureSystem::ConfigureSystem(QWidget *parent) : | ||||||
|  |     QWidget(parent), | ||||||
|  |     ui(new Ui::ConfigureSystem) { | ||||||
|  |     ui->setupUi(this); | ||||||
|  |  | ||||||
|  |     connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ConfigureSystem::~ConfigureSystem() { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ConfigureSystem::setConfiguration(bool emulation_running) { | ||||||
|  |     enabled = !emulation_running; | ||||||
|  |  | ||||||
|  |     if (!enabled) { | ||||||
|  |         ReadSystemSettings(); | ||||||
|  |         ui->group_system_settings->setEnabled(false); | ||||||
|  |     } else { | ||||||
|  |         // This tab is enabled only when game is not running (i.e. all service are not initialized). | ||||||
|  |         // Temporarily register archive types and load the config savegame file to memory. | ||||||
|  |         Service::FS::RegisterArchiveTypes(); | ||||||
|  |         ResultCode result = Service::CFG::LoadConfigNANDSaveFile(); | ||||||
|  |         Service::FS::UnregisterArchiveTypes(); | ||||||
|  |  | ||||||
|  |         if (result.IsError()) { | ||||||
|  |             ui->label_disable_info->setText(tr("Failed to load system settings data.")); | ||||||
|  |             ui->group_system_settings->setEnabled(false); | ||||||
|  |             enabled = false; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ReadSystemSettings(); | ||||||
|  |         ui->label_disable_info->hide(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ConfigureSystem::ReadSystemSettings() { | ||||||
|  |     // set username | ||||||
|  |     username = Service::CFG::GetUsername(); | ||||||
|  |     // ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5 | ||||||
|  |     ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); | ||||||
|  |  | ||||||
|  |     // set birthday | ||||||
|  |     std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); | ||||||
|  |     ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); | ||||||
|  |     ui->combo_birthday->setCurrentIndex(birthday - 1); | ||||||
|  |  | ||||||
|  |     // set system language | ||||||
|  |     language_index = Service::CFG::GetSystemLanguage(); | ||||||
|  |     ui->combo_language->setCurrentIndex(language_index); | ||||||
|  |  | ||||||
|  |     // set sound output mode | ||||||
|  |     sound_index = Service::CFG::GetSoundOutputMode(); | ||||||
|  |     ui->combo_sound->setCurrentIndex(sound_index); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ConfigureSystem::applyConfiguration() { | ||||||
|  |     if (!enabled) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     bool modified = false; | ||||||
|  |  | ||||||
|  |     // apply username | ||||||
|  |     // std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5 | ||||||
|  |     std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); | ||||||
|  |     if (new_username != username) { | ||||||
|  |         Service::CFG::SetUsername(new_username); | ||||||
|  |         modified = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // apply birthday | ||||||
|  |     int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1; | ||||||
|  |     int new_birthday = ui->combo_birthday->currentIndex() + 1; | ||||||
|  |     if (birthmonth != new_birthmonth || birthday != new_birthday) { | ||||||
|  |         Service::CFG::SetBirthday(new_birthmonth, new_birthday); | ||||||
|  |         modified = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // apply language | ||||||
|  |     int new_language = ui->combo_language->currentIndex(); | ||||||
|  |     if (language_index != new_language) { | ||||||
|  |         Service::CFG::SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language)); | ||||||
|  |         modified = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // apply sound | ||||||
|  |     int new_sound = ui->combo_sound->currentIndex(); | ||||||
|  |     if (sound_index != new_sound) { | ||||||
|  |         Service::CFG::SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound)); | ||||||
|  |         modified = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // update the config savegame if any item is modified. | ||||||
|  |     if (modified) | ||||||
|  |         Service::CFG::UpdateConfigNANDSavegame(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | ||||||
|  |     if (birthmonth_index < 0 || birthmonth_index >= 12) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |     // store current day selection | ||||||
|  |     int birthday_index = ui->combo_birthday->currentIndex(); | ||||||
|  |  | ||||||
|  |     // get number of days in the new selected month | ||||||
|  |     int days = days_in_month[birthmonth_index]; | ||||||
|  |  | ||||||
|  |     // if the selected day is out of range, | ||||||
|  |     // reset it to 1st | ||||||
|  |     if (birthday_index < 0 || birthday_index >= days) | ||||||
|  |         birthday_index = 0; | ||||||
|  |  | ||||||
|  |     // update the day combo box | ||||||
|  |     ui->combo_birthday->clear(); | ||||||
|  |     for (int i = 1; i <= days; ++i) { | ||||||
|  |         ui->combo_birthday->addItem(QString::number(i)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // restore the day selection | ||||||
|  |     ui->combo_birthday->setCurrentIndex(birthday_index); | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								src/citra_qt/configure_system.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/citra_qt/configure_system.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | // Copyright 2016 Citra Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  | #include <QWidget> | ||||||
|  |  | ||||||
|  | namespace Ui { | ||||||
|  | class ConfigureSystem; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class ConfigureSystem : public QWidget | ||||||
|  | { | ||||||
|  |     Q_OBJECT | ||||||
|  |  | ||||||
|  | public: | ||||||
|  |     explicit ConfigureSystem(QWidget *parent = nullptr); | ||||||
|  |     ~ConfigureSystem(); | ||||||
|  |  | ||||||
|  |     void applyConfiguration(); | ||||||
|  |     void setConfiguration(bool emulation_running); | ||||||
|  |  | ||||||
|  | public slots: | ||||||
|  |     void updateBirthdayComboBox(int birthmonth_index); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     void ReadSystemSettings(); | ||||||
|  |  | ||||||
|  |     std::unique_ptr<Ui::ConfigureSystem> ui; | ||||||
|  |     bool enabled; | ||||||
|  |  | ||||||
|  |     std::u16string username; | ||||||
|  |     int birthmonth, birthday; | ||||||
|  |     int language_index; | ||||||
|  |     int sound_index; | ||||||
|  | }; | ||||||
							
								
								
									
										252
									
								
								src/citra_qt/configure_system.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								src/citra_qt/configure_system.ui
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | |||||||
|  | <?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>360</width> | ||||||
|  |     <height>377</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="0" column="0"> | ||||||
|  |          <widget class="QLabel" name="label_username"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Username</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item row="0" 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="1" column="0"> | ||||||
|  |          <widget class="QLabel" name="label_birthday"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Birthday</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item row="1" 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="2" column="0"> | ||||||
|  |          <widget class="QLabel" name="label_language"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Language</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item row="2" column="1"> | ||||||
|  |          <widget class="QComboBox" name="combo_language"> | ||||||
|  |           <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="3" column="0"> | ||||||
|  |          <widget class="QLabel" name="label_sound"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Sound output mode</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item row="3" 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> | ||||||
|  |        </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> | ||||||
|  |     </layout> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources/> | ||||||
|  |  <connections/> | ||||||
|  | </ui> | ||||||
| @@ -508,7 +508,7 @@ void GMainWindow::ToggleWindowMode() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void GMainWindow::OnConfigure() { | void GMainWindow::OnConfigure() { | ||||||
|     ConfigureDialog configureDialog(this); |     ConfigureDialog configureDialog(this, emulation_running); | ||||||
|     auto result = configureDialog.exec(); |     auto result = configureDialog.exec(); | ||||||
|     if (result == QDialog::Accepted) |     if (result == QDialog::Accepted) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -40,6 +40,20 @@ struct SaveFileConfig { | |||||||
| }; | }; | ||||||
| static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); | static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); | ||||||
|  |  | ||||||
|  | enum ConfigBlockID { | ||||||
|  |     StereoCameraSettingsBlockID = 0x00050005, | ||||||
|  |     SoundOutputModeBlockID      = 0x00070001, | ||||||
|  |     ConsoleUniqueIDBlockID      = 0x00090001, | ||||||
|  |     UsernameBlockID             = 0x000A0000, | ||||||
|  |     BirthdayBlockID             = 0x000A0001, | ||||||
|  |     LanguageBlockID             = 0x000A0002, | ||||||
|  |     CountryInfoBlockID          = 0x000B0000, | ||||||
|  |     CountryNameBlockID          = 0x000B0001, | ||||||
|  |     StateNameBlockID            = 0x000B0002, | ||||||
|  |     EULAVersionBlockID          = 0x000D0000, | ||||||
|  |     ConsoleModelBlockID         = 0x000F0004, | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct UsernameBlock { | struct UsernameBlock { | ||||||
|     char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary |     char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary | ||||||
|     u32 zero; |     u32 zero; | ||||||
| @@ -73,8 +87,7 @@ static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } }; | |||||||
| static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; | static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; | ||||||
| static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 }; | static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 }; | ||||||
| static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014 | static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014 | ||||||
| /// TODO(Subv): Find out what this actually is | static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND; | ||||||
| static const u8 SOUND_OUTPUT_MODE = 2; |  | ||||||
| static const u8 UNITED_STATES_COUNTRY_ID = 49; | static const u8 UNITED_STATES_COUNTRY_ID = 49; | ||||||
| /// TODO(Subv): Find what the other bytes are | /// TODO(Subv): Find what the other bytes are | ||||||
| static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; | static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; | ||||||
| @@ -224,6 +237,22 @@ void GetConfigInfoBlk8(Service::Interface* self) { | |||||||
|     Memory::WriteBlock(data_pointer, data.data(), data.size()); |     Memory::WriteBlock(data_pointer, data.data(), data.size()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void SetConfigInfoBlk4(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     u32 block_id = cmd_buff[1]; | ||||||
|  |     u32 size = cmd_buff[2]; | ||||||
|  |     VAddr data_pointer = cmd_buff[4]; | ||||||
|  |  | ||||||
|  |     if (!Memory::IsValidVirtualAddress(data_pointer)) { | ||||||
|  |         cmd_buff[1] = -1; // TODO(Subv): Find the right error code | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::vector<u8> data(size); | ||||||
|  |     Memory::ReadBlock(data_pointer, data.data(), data.size()); | ||||||
|  |     cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw; | ||||||
|  | } | ||||||
|  |  | ||||||
| void UpdateConfigNANDSavegame(Service::Interface* self) { | void UpdateConfigNANDSavegame(Service::Interface* self) { | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|     cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw; |     cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw; | ||||||
| @@ -234,13 +263,13 @@ void FormatConfig(Service::Interface* self) { | |||||||
|     cmd_buff[1] = Service::CFG::FormatConfig().raw; |     cmd_buff[1] = Service::CFG::FormatConfig().raw; | ||||||
| } | } | ||||||
|  |  | ||||||
| ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) { | ||||||
|     // Read the header |     // Read the header | ||||||
|     SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); |     SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||||||
|  |  | ||||||
|     auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), |     auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), | ||||||
|         [&](const SaveConfigBlockEntry& entry) { |         [&](const SaveConfigBlockEntry& entry) { | ||||||
|             return entry.block_id == block_id && (entry.flags & flag); |             return entry.block_id == block_id; | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     if (itr == std::end(config->block_entries)) { |     if (itr == std::end(config->block_entries)) { | ||||||
| @@ -248,17 +277,38 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | |||||||
|         return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); |         return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if ((itr->flags & flag) == 0) { | ||||||
|  |         LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id, size); | ||||||
|  |         return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (itr->size != size) { |     if (itr->size != size) { | ||||||
|         LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); |         LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); | ||||||
|         return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); |         return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void* pointer; | ||||||
|  |  | ||||||
|     // The data is located in the block header itself if the size is less than 4 bytes |     // The data is located in the block header itself if the size is less than 4 bytes | ||||||
|     if (itr->size <= 4) |     if (itr->size <= 4) | ||||||
|         memcpy(output, &itr->offset_or_data, itr->size); |         pointer = &itr->offset_or_data; | ||||||
|     else |     else | ||||||
|         memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size); |         pointer = &cfg_config_file_buffer[itr->offset_or_data]; | ||||||
|  |  | ||||||
|  |     return MakeResult<void*>(pointer); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) { | ||||||
|  |     void* pointer; | ||||||
|  |     CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); | ||||||
|  |     memcpy(output, pointer, size); | ||||||
|  |     return RESULT_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) { | ||||||
|  |     void* pointer; | ||||||
|  |     CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); | ||||||
|  |     memcpy(pointer, input, size); | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -336,25 +386,25 @@ ResultCode FormatConfig() { | |||||||
|     res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer); |     res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); |     res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); |     res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); |     res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK); |     res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY); |     res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE); |     res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO); |     res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     u16_le country_name_buffer[16][0x40] = {}; |     u16_le country_name_buffer[16][0x40] = {}; | ||||||
| @@ -363,10 +413,10 @@ ResultCode FormatConfig() { | |||||||
|         std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]); |         std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]); | ||||||
|     } |     } | ||||||
|     // 0x000B0001 - Localized names for the profile Country |     // 0x000B0001 - Localized names for the profile Country | ||||||
|     res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer); |     res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|     // 0x000B0002 - Localized names for the profile State/Province |     // 0x000B0002 - Localized names for the profile State/Province | ||||||
|     res = CreateConfigInfoBlk(0x000B0002, sizeof(country_name_buffer), 0xE, country_name_buffer); |     res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     // 0x000B0003 - Unknown, related to country/address (zip code?) |     // 0x000B0003 - Unknown, related to country/address (zip code?) | ||||||
| @@ -382,10 +432,10 @@ ResultCode FormatConfig() { | |||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     // 0x000D0000 - Accepted EULA version |     // 0x000D0000 - Accepted EULA version | ||||||
|     res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer); |     res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); |     res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); | ||||||
|     if (!res.IsSuccess()) return res; |     if (!res.IsSuccess()) return res; | ||||||
|  |  | ||||||
|     // 0x00170000 - Unknown |     // 0x00170000 - Unknown | ||||||
| @@ -399,11 +449,7 @@ ResultCode FormatConfig() { | |||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Init() { | ResultCode LoadConfigNANDSaveFile() { | ||||||
|     AddService(new CFG_I_Interface); |  | ||||||
|     AddService(new CFG_S_Interface); |  | ||||||
|     AddService(new CFG_U_Interface); |  | ||||||
|  |  | ||||||
|     // Open the SystemSaveData archive 0x00010017 |     // Open the SystemSaveData archive 0x00010017 | ||||||
|     FileSys::Path archive_path(cfg_system_savedata_id); |     FileSys::Path archive_path(cfg_system_savedata_id); | ||||||
|     auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); |     auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); | ||||||
| @@ -431,14 +477,75 @@ void Init() { | |||||||
|     if (config_result.Succeeded()) { |     if (config_result.Succeeded()) { | ||||||
|         auto config = config_result.MoveFrom(); |         auto config = config_result.MoveFrom(); | ||||||
|         config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); |         config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); | ||||||
|         return; |         return RESULT_SUCCESS; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     FormatConfig(); |     return FormatConfig(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Init() { | ||||||
|  |     AddService(new CFG_I_Interface); | ||||||
|  |     AddService(new CFG_S_Interface); | ||||||
|  |     AddService(new CFG_U_Interface); | ||||||
|  |  | ||||||
|  |     LoadConfigNANDSaveFile(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Shutdown() { | void Shutdown() { | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void SetUsername(const std::u16string& name) { | ||||||
|  |     ASSERT(name.size() <= 10); | ||||||
|  |     UsernameBlock block{}; | ||||||
|  |     name.copy(block.username, name.size()); | ||||||
|  |     SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::u16string GetUsername() { | ||||||
|  |     UsernameBlock block; | ||||||
|  |     GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block); | ||||||
|  |  | ||||||
|  |     // the username string in the block isn't null-terminated, | ||||||
|  |     // so we need to find the end manually. | ||||||
|  |     std::u16string username(block.username, ARRAY_SIZE(block.username)); | ||||||
|  |     const size_t pos = username.find(u'\0'); | ||||||
|  |     if (pos != std::u16string::npos) | ||||||
|  |         username.erase(pos); | ||||||
|  |     return username; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SetBirthday(u8 month, u8 day) { | ||||||
|  |     BirthdayBlock block = { month, day }; | ||||||
|  |     SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::tuple<u8, u8> GetBirthday() { | ||||||
|  |     BirthdayBlock block; | ||||||
|  |     GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block); | ||||||
|  |     return std::make_tuple(block.month, block.day); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SetSystemLanguage(SystemLanguage language) { | ||||||
|  |     u8 block = language; | ||||||
|  |     SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SystemLanguage GetSystemLanguage() { | ||||||
|  |     u8 block; | ||||||
|  |     GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block); | ||||||
|  |     return static_cast<SystemLanguage>(block); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SetSoundOutputMode(SoundOutputMode mode) { | ||||||
|  |     u8 block = mode; | ||||||
|  |     SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | SoundOutputMode GetSoundOutputMode() { | ||||||
|  |     u8 block; | ||||||
|  |     GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block); | ||||||
|  |     return static_cast<SoundOutputMode>(block); | ||||||
|  | } | ||||||
|  |  | ||||||
| } // namespace CFG | } // namespace CFG | ||||||
| } // namespace Service | } // namespace Service | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  |  | ||||||
| @@ -35,7 +36,14 @@ enum SystemLanguage { | |||||||
|     LANGUAGE_KO = 7, |     LANGUAGE_KO = 7, | ||||||
|     LANGUAGE_NL = 8, |     LANGUAGE_NL = 8, | ||||||
|     LANGUAGE_PT = 9, |     LANGUAGE_PT = 9, | ||||||
|     LANGUAGE_RU = 10 |     LANGUAGE_RU = 10, | ||||||
|  |     LANGUAGE_TW = 11 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum SoundOutputMode { | ||||||
|  |     SOUND_MONO = 0, | ||||||
|  |     SOUND_STEREO = 1, | ||||||
|  |     SOUND_SURROUND = 2 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Block header in the config savedata file | /// Block header in the config savedata file | ||||||
| @@ -177,6 +185,22 @@ void GetConfigInfoBlk2(Service::Interface* self); | |||||||
|  */ |  */ | ||||||
| void GetConfigInfoBlk8(Service::Interface* self); | void GetConfigInfoBlk8(Service::Interface* self); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * CFG::SetConfigInfoBlk4 service function | ||||||
|  |  *  Inputs: | ||||||
|  |  *      0 : 0x04020082 / 0x08020082 | ||||||
|  |  *      1 : Block ID | ||||||
|  |  *      2 : Size | ||||||
|  |  *      3 : Descriptor for the output buffer | ||||||
|  |  *      4 : Output buffer pointer | ||||||
|  |  *  Outputs: | ||||||
|  |  *      1 : Result of function, 0 on success, otherwise error code | ||||||
|  |  *  Note: | ||||||
|  |  *      The parameters order is different from GetConfigInfoBlk2/8's, | ||||||
|  |  *      where Block ID and Size are switched. | ||||||
|  |  */ | ||||||
|  | void SetConfigInfoBlk4(Service::Interface* self); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * CFG::UpdateConfigNANDSavegame service function |  * CFG::UpdateConfigNANDSavegame service function | ||||||
|  *  Inputs: |  *  Inputs: | ||||||
| @@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self); | |||||||
|  * @param output A pointer where we will write the read data |  * @param output A pointer where we will write the read data | ||||||
|  * @returns ResultCode indicating the result of the operation, 0 on success |  * @returns ResultCode indicating the result of the operation, 0 on success | ||||||
|  */ |  */ | ||||||
| ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Reads data from input and writes to a block with the specified id and flag | ||||||
|  |  * in the Config savegame buffer. | ||||||
|  |  * The input size must match exactly the size of the target block | ||||||
|  |  * @param block_id The id of the block we want to write | ||||||
|  |  * @param size The size of the block we want to write | ||||||
|  |  * @param flag The target block must have this flag set | ||||||
|  |  * @param input A pointer where we will read data and write to Config savegame buffer | ||||||
|  |  * @returns ResultCode indicating the result of the operation, 0 on success | ||||||
|  |  */ | ||||||
|  | ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. |  * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. | ||||||
| @@ -236,11 +272,70 @@ ResultCode UpdateConfigNANDSavegame(); | |||||||
|  */ |  */ | ||||||
| ResultCode FormatConfig(); | ResultCode FormatConfig(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Open the config savegame file and load it to the memory buffer | ||||||
|  |  * @returns ResultCode indicating the result of the operation, 0 on success | ||||||
|  |  */ | ||||||
|  | ResultCode LoadConfigNANDSaveFile(); | ||||||
|  |  | ||||||
| /// Initialize the config service | /// Initialize the config service | ||||||
| void Init(); | void Init(); | ||||||
|  |  | ||||||
| /// Shutdown the config service | /// Shutdown the config service | ||||||
| void Shutdown(); | void Shutdown(); | ||||||
|  |  | ||||||
|  | // Utilities for frontend to set config data. | ||||||
|  | // Note: before calling these functions, LoadConfigNANDSaveFile should be called, | ||||||
|  | // and UpdateConfigNANDSavegame should be called after making changes to config data. | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Sets the username in config savegame. | ||||||
|  |  * @param name the username to set. The maximum size is 10 in char16_t. | ||||||
|  |  */ | ||||||
|  | void SetUsername(const std::u16string& name); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the username from config savegame. | ||||||
|  |  * @returns the username | ||||||
|  |  */ | ||||||
|  | std::u16string GetUsername(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Sets the profile birthday in config savegame. | ||||||
|  |  * @param month the month of birthday. | ||||||
|  |  * @param day the day of the birthday. | ||||||
|  |  */ | ||||||
|  | void SetBirthday(u8 month, u8 day); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the profile birthday from the config savegame. | ||||||
|  |  * @returns a tuple of (month, day) of birthday | ||||||
|  |  */ | ||||||
|  | std::tuple<u8, u8> GetBirthday(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Sets the system language in config savegame. | ||||||
|  |  * @param language the system language to set. | ||||||
|  |  */ | ||||||
|  | void SetSystemLanguage(SystemLanguage language); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the system language from config savegame. | ||||||
|  |  * @returns the system language | ||||||
|  |  */ | ||||||
|  | SystemLanguage GetSystemLanguage(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Sets the sound output mode in config savegame. | ||||||
|  |  * @param mode the sound output mode to set | ||||||
|  |  */ | ||||||
|  | void SetSoundOutputMode(SoundOutputMode mode); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Gets the sound output mode from config savegame. | ||||||
|  |  * @returns the sound output mode | ||||||
|  |  */ | ||||||
|  | SoundOutputMode GetSoundOutputMode(); | ||||||
|  |  | ||||||
| } // namespace CFG | } // namespace CFG | ||||||
| } // namespace Service | } // namespace Service | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||||||
|     {0x000A0040, GetCountryCodeID,                     "GetCountryCodeID"}, |     {0x000A0040, GetCountryCodeID,                     "GetCountryCodeID"}, | ||||||
|     // cfg:i |     // cfg:i | ||||||
|     {0x04010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"}, |     {0x04010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"}, | ||||||
|     {0x04020082, nullptr,                              "SetConfigInfoBlk4"}, |     {0x04020082, SetConfigInfoBlk4,                    "SetConfigInfoBlk4"}, | ||||||
|     {0x04030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"}, |     {0x04030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"}, | ||||||
|     {0x04040042, nullptr,                              "GetLocalFriendCodeSeedData"}, |     {0x04040042, nullptr,                              "GetLocalFriendCodeSeedData"}, | ||||||
|     {0x04050000, nullptr,                              "GetLocalFriendCodeSeed"}, |     {0x04050000, nullptr,                              "GetLocalFriendCodeSeed"}, | ||||||
| @@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||||||
|     {0x04080042, nullptr,                              "SecureInfoGetSerialNo"}, |     {0x04080042, nullptr,                              "SecureInfoGetSerialNo"}, | ||||||
|     {0x04090000, nullptr,                              "UpdateConfigBlk00040003"}, |     {0x04090000, nullptr,                              "UpdateConfigBlk00040003"}, | ||||||
|     {0x08010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"}, |     {0x08010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"}, | ||||||
|     {0x08020082, nullptr,                              "SetConfigInfoBlk4"}, |     {0x08020082, SetConfigInfoBlk4,                    "SetConfigInfoBlk4"}, | ||||||
|     {0x08030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"}, |     {0x08030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"}, | ||||||
|     {0x080400C2, nullptr,                              "CreateConfigInfoBlk"}, |     {0x080400C2, nullptr,                              "CreateConfigInfoBlk"}, | ||||||
|     {0x08050000, nullptr,                              "DeleteConfigNANDSavefile"}, |     {0x08050000, nullptr,                              "DeleteConfigNANDSavefile"}, | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||||||
|     {0x000A0040, GetCountryCodeID,                     "GetCountryCodeID"}, |     {0x000A0040, GetCountryCodeID,                     "GetCountryCodeID"}, | ||||||
|     // cfg:s |     // cfg:s | ||||||
|     {0x04010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"}, |     {0x04010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"}, | ||||||
|     {0x04020082, nullptr,                              "SetConfigInfoBlk4"}, |     {0x04020082, SetConfigInfoBlk4,                    "SetConfigInfoBlk4"}, | ||||||
|     {0x04030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"}, |     {0x04030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"}, | ||||||
|     {0x04040042, nullptr,                              "GetLocalFriendCodeSeedData"}, |     {0x04040042, nullptr,                              "GetLocalFriendCodeSeedData"}, | ||||||
|     {0x04050000, nullptr,                              "GetLocalFriendCodeSeed"}, |     {0x04050000, nullptr,                              "GetLocalFriendCodeSeed"}, | ||||||
|   | |||||||
| @@ -259,7 +259,7 @@ using FileSys::ArchiveFactory; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Map of registered archives, identified by id code. Once an archive is registered here, it is |  * Map of registered archives, identified by id code. Once an archive is registered here, it is | ||||||
|  * never removed until the FS service is shut down. |  * never removed until UnregisterArchiveTypes is called. | ||||||
|  */ |  */ | ||||||
| static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; | static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; | ||||||
|  |  | ||||||
| @@ -520,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) { | |||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Initialize archives | void RegisterArchiveTypes() { | ||||||
| void ArchiveInit() { |  | ||||||
|     next_handle = 1; |  | ||||||
|  |  | ||||||
|     AddService(new FS::Interface); |  | ||||||
|  |  | ||||||
|     // TODO(Subv): Add the other archive types (see here for the known types: |     // TODO(Subv): Add the other archive types (see here for the known types: | ||||||
|     // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). |     // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). | ||||||
|  |  | ||||||
| @@ -562,10 +557,23 @@ void ArchiveInit() { | |||||||
|     RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); |     RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void UnregisterArchiveTypes() { | ||||||
|  |     id_code_map.clear(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Initialize archives | ||||||
|  | void ArchiveInit() { | ||||||
|  |     next_handle = 1; | ||||||
|  |  | ||||||
|  |     AddService(new FS::Interface); | ||||||
|  |  | ||||||
|  |     RegisterArchiveTypes(); | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Shutdown archives | /// Shutdown archives | ||||||
| void ArchiveShutdown() { | void ArchiveShutdown() { | ||||||
|     handle_map.clear(); |     handle_map.clear(); | ||||||
|     id_code_map.clear(); |     UnregisterArchiveTypes(); | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace FS | } // namespace FS | ||||||
|   | |||||||
| @@ -235,5 +235,11 @@ void ArchiveInit(); | |||||||
| /// Shutdown archives | /// Shutdown archives | ||||||
| void ArchiveShutdown(); | void ArchiveShutdown(); | ||||||
|  |  | ||||||
|  | /// Register all archive types | ||||||
|  | void RegisterArchiveTypes(); | ||||||
|  |  | ||||||
|  | /// Unregister all archive types | ||||||
|  | void UnregisterArchiveTypes(); | ||||||
|  |  | ||||||
| } // namespace FS | } // namespace FS | ||||||
| } // namespace Service | } // namespace Service | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user