diff options
| author | 2016-07-09 20:39:19 -0700 | |
|---|---|---|
| committer | 2016-07-09 20:39:19 -0700 | |
| commit | ffda82eea546830d15284810e91e58c5b6627a0c (patch) | |
| tree | 5d0cb7b7f17ef73bd382b08cce863f1b05ed10df | |
| parent | Merge pull request #1940 from JamePeng/fix-archive-error-code (diff) | |
| parent | Qt: add system settings config tab (diff) | |
| download | yuzu-ffda82eea546830d15284810e91e58c5b6627a0c.tar.gz yuzu-ffda82eea546830d15284810e91e58c5b6627a0c.tar.xz yuzu-ffda82eea546830d15284810e91e58c5b6627a0c.zip | |
Merge pull request #1894 from wwylele/set-config-block
Implement config savegame editing & clean up
| -rw-r--r-- | src/citra_qt/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/citra_qt/configure.ui | 11 | ||||
| -rw-r--r-- | src/citra_qt/configure_dialog.cpp | 9 | ||||
| -rw-r--r-- | src/citra_qt/configure_dialog.h | 3 | ||||
| -rw-r--r-- | src/citra_qt/configure_system.cpp | 136 | ||||
| -rw-r--r-- | src/citra_qt/configure_system.h | 38 | ||||
| -rw-r--r-- | src/citra_qt/configure_system.ui | 252 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/service/cfg/cfg.cpp | 155 | ||||
| -rw-r--r-- | src/core/hle/service/cfg/cfg.h | 99 | ||||
| -rw-r--r-- | src/core/hle/service/cfg/cfg_i.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/cfg/cfg_s.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/service/fs/archive.cpp | 24 | ||||
| -rw-r--r-- | src/core/hle/service/fs/archive.h | 6 |
14 files changed, 703 insertions, 41 deletions
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 43a766053..017b43871 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -22,6 +22,7 @@ set(SRCS | |||
| 22 | configure_debug.cpp | 22 | configure_debug.cpp |
| 23 | configure_dialog.cpp | 23 | configure_dialog.cpp |
| 24 | configure_general.cpp | 24 | configure_general.cpp |
| 25 | configure_system.cpp | ||
| 25 | game_list.cpp | 26 | game_list.cpp |
| 26 | hotkeys.cpp | 27 | hotkeys.cpp |
| 27 | main.cpp | 28 | main.cpp |
| @@ -52,6 +53,7 @@ set(HEADERS | |||
| 52 | configure_debug.h | 53 | configure_debug.h |
| 53 | configure_dialog.h | 54 | configure_dialog.h |
| 54 | configure_general.h | 55 | configure_general.h |
| 56 | configure_system.h | ||
| 55 | game_list.h | 57 | game_list.h |
| 56 | game_list_p.h | 58 | game_list_p.h |
| 57 | hotkeys.h | 59 | hotkeys.h |
| @@ -69,6 +71,7 @@ set(UIS | |||
| 69 | configure_audio.ui | 71 | configure_audio.ui |
| 70 | configure_debug.ui | 72 | configure_debug.ui |
| 71 | configure_general.ui | 73 | configure_general.ui |
| 74 | configure_system.ui | ||
| 72 | hotkeys.ui | 75 | hotkeys.ui |
| 73 | main.ui | 76 | main.ui |
| 74 | ) | 77 | ) |
diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configure.ui index e1624bbef..4a9c52650 100644 --- a/src/citra_qt/configure.ui +++ b/src/citra_qt/configure.ui | |||
| @@ -24,6 +24,11 @@ | |||
| 24 | <string>General</string> | 24 | <string>General</string> |
| 25 | </attribute> | 25 | </attribute> |
| 26 | </widget> | 26 | </widget> |
| 27 | <widget class="ConfigureSystem" name="systemTab"> | ||
| 28 | <attribute name="title"> | ||
| 29 | <string>System</string> | ||
| 30 | </attribute> | ||
| 31 | </widget> | ||
| 27 | <widget class="QWidget" name="inputTab"> | 32 | <widget class="QWidget" name="inputTab"> |
| 28 | <attribute name="title"> | 33 | <attribute name="title"> |
| 29 | <string>Input</string> | 34 | <string>Input</string> |
| @@ -58,6 +63,12 @@ | |||
| 58 | <container>1</container> | 63 | <container>1</container> |
| 59 | </customwidget> | 64 | </customwidget> |
| 60 | <customwidget> | 65 | <customwidget> |
| 66 | <class>ConfigureSystem</class> | ||
| 67 | <extends>QWidget</extends> | ||
| 68 | <header>configure_system.h</header> | ||
| 69 | <container>1</container> | ||
| 70 | </customwidget> | ||
| 71 | <customwidget> | ||
| 61 | <class>ConfigureAudio</class> | 72 | <class>ConfigureAudio</class> |
| 62 | <extends>QWidget</extends> | 73 | <extends>QWidget</extends> |
| 63 | <header>configure_audio.h</header> | 74 | <header>configure_audio.h</header> |
diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp index 2f0317fe0..77c266d01 100644 --- a/src/citra_qt/configure_dialog.cpp +++ b/src/citra_qt/configure_dialog.cpp | |||
| @@ -9,9 +9,10 @@ | |||
| 9 | 9 | ||
| 10 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| 11 | 11 | ||
| 12 | ConfigureDialog::ConfigureDialog(QWidget *parent) : | 12 | ConfigureDialog::ConfigureDialog(QWidget *parent, bool running) : |
| 13 | QDialog(parent), | 13 | QDialog(parent), |
| 14 | ui(new Ui::ConfigureDialog) | 14 | ui(new Ui::ConfigureDialog), |
| 15 | emulation_running(running) | ||
| 15 | { | 16 | { |
| 16 | ui->setupUi(this); | 17 | ui->setupUi(this); |
| 17 | this->setConfiguration(); | 18 | this->setConfiguration(); |
| @@ -21,10 +22,14 @@ ConfigureDialog::~ConfigureDialog() { | |||
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | void ConfigureDialog::setConfiguration() { | 24 | void ConfigureDialog::setConfiguration() { |
| 25 | // System tab needs set manually | ||
| 26 | // depending on whether emulation is running | ||
| 27 | ui->systemTab->setConfiguration(emulation_running); | ||
| 24 | } | 28 | } |
| 25 | 29 | ||
| 26 | void ConfigureDialog::applyConfiguration() { | 30 | void ConfigureDialog::applyConfiguration() { |
| 27 | ui->generalTab->applyConfiguration(); | 31 | ui->generalTab->applyConfiguration(); |
| 32 | ui->systemTab->applyConfiguration(); | ||
| 28 | ui->audioTab->applyConfiguration(); | 33 | ui->audioTab->applyConfiguration(); |
| 29 | ui->debugTab->applyConfiguration(); | 34 | ui->debugTab->applyConfiguration(); |
| 30 | } | 35 | } |
diff --git a/src/citra_qt/configure_dialog.h b/src/citra_qt/configure_dialog.h index 89020eeb4..305b33bdf 100644 --- a/src/citra_qt/configure_dialog.h +++ b/src/citra_qt/configure_dialog.h | |||
| @@ -16,7 +16,7 @@ class ConfigureDialog : public QDialog | |||
| 16 | Q_OBJECT | 16 | Q_OBJECT |
| 17 | 17 | ||
| 18 | public: | 18 | public: |
| 19 | explicit ConfigureDialog(QWidget *parent = nullptr); | 19 | explicit ConfigureDialog(QWidget *parent, bool emulation_running); |
| 20 | ~ConfigureDialog(); | 20 | ~ConfigureDialog(); |
| 21 | 21 | ||
| 22 | void applyConfiguration(); | 22 | void applyConfiguration(); |
| @@ -26,4 +26,5 @@ private: | |||
| 26 | 26 | ||
| 27 | private: | 27 | private: |
| 28 | std::unique_ptr<Ui::ConfigureDialog> ui; | 28 | std::unique_ptr<Ui::ConfigureDialog> ui; |
| 29 | bool emulation_running; | ||
| 29 | }; | 30 | }; |
diff --git a/src/citra_qt/configure_system.cpp b/src/citra_qt/configure_system.cpp new file mode 100644 index 000000000..4f0d4dbfe --- /dev/null +++ b/src/citra_qt/configure_system.cpp | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "citra_qt/configure_system.h" | ||
| 6 | #include "citra_qt/ui_settings.h" | ||
| 7 | #include "ui_configure_system.h" | ||
| 8 | |||
| 9 | #include "core/hle/service/fs/archive.h" | ||
| 10 | #include "core/hle/service/cfg/cfg.h" | ||
| 11 | |||
| 12 | static const std::array<int, 12> days_in_month = {{ | ||
| 13 | 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
| 14 | }}; | ||
| 15 | |||
| 16 | ConfigureSystem::ConfigureSystem(QWidget *parent) : | ||
| 17 | QWidget(parent), | ||
| 18 | ui(new Ui::ConfigureSystem) { | ||
| 19 | ui->setupUi(this); | ||
| 20 | |||
| 21 | connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int))); | ||
| 22 | } | ||
| 23 | |||
| 24 | ConfigureSystem::~ConfigureSystem() { | ||
| 25 | } | ||
| 26 | |||
| 27 | void ConfigureSystem::setConfiguration(bool emulation_running) { | ||
| 28 | enabled = !emulation_running; | ||
| 29 | |||
| 30 | if (!enabled) { | ||
| 31 | ReadSystemSettings(); | ||
| 32 | ui->group_system_settings->setEnabled(false); | ||
| 33 | } else { | ||
| 34 | // This tab is enabled only when game is not running (i.e. all service are not initialized). | ||
| 35 | // Temporarily register archive types and load the config savegame file to memory. | ||
| 36 | Service::FS::RegisterArchiveTypes(); | ||
| 37 | ResultCode result = Service::CFG::LoadConfigNANDSaveFile(); | ||
| 38 | Service::FS::UnregisterArchiveTypes(); | ||
| 39 | |||
| 40 | if (result.IsError()) { | ||
| 41 | ui->label_disable_info->setText(tr("Failed to load system settings data.")); | ||
| 42 | ui->group_system_settings->setEnabled(false); | ||
| 43 | enabled = false; | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | |||
| 47 | ReadSystemSettings(); | ||
| 48 | ui->label_disable_info->hide(); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | void ConfigureSystem::ReadSystemSettings() { | ||
| 53 | // set username | ||
| 54 | username = Service::CFG::GetUsername(); | ||
| 55 | // ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5 | ||
| 56 | ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data()))); | ||
| 57 | |||
| 58 | // set birthday | ||
| 59 | std::tie(birthmonth, birthday) = Service::CFG::GetBirthday(); | ||
| 60 | ui->combo_birthmonth->setCurrentIndex(birthmonth - 1); | ||
| 61 | ui->combo_birthday->setCurrentIndex(birthday - 1); | ||
| 62 | |||
| 63 | // set system language | ||
| 64 | language_index = Service::CFG::GetSystemLanguage(); | ||
| 65 | ui->combo_language->setCurrentIndex(language_index); | ||
| 66 | |||
| 67 | // set sound output mode | ||
| 68 | sound_index = Service::CFG::GetSoundOutputMode(); | ||
| 69 | ui->combo_sound->setCurrentIndex(sound_index); | ||
| 70 | } | ||
| 71 | |||
| 72 | void ConfigureSystem::applyConfiguration() { | ||
| 73 | if (!enabled) | ||
| 74 | return; | ||
| 75 | |||
| 76 | bool modified = false; | ||
| 77 | |||
| 78 | // apply username | ||
| 79 | // std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5 | ||
| 80 | std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16())); | ||
| 81 | if (new_username != username) { | ||
| 82 | Service::CFG::SetUsername(new_username); | ||
| 83 | modified = true; | ||
| 84 | } | ||
| 85 | |||
| 86 | // apply birthday | ||
| 87 | int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1; | ||
| 88 | int new_birthday = ui->combo_birthday->currentIndex() + 1; | ||
| 89 | if (birthmonth != new_birthmonth || birthday != new_birthday) { | ||
| 90 | Service::CFG::SetBirthday(new_birthmonth, new_birthday); | ||
| 91 | modified = true; | ||
| 92 | } | ||
| 93 | |||
| 94 | // apply language | ||
| 95 | int new_language = ui->combo_language->currentIndex(); | ||
| 96 | if (language_index != new_language) { | ||
| 97 | Service::CFG::SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language)); | ||
| 98 | modified = true; | ||
| 99 | } | ||
| 100 | |||
| 101 | // apply sound | ||
| 102 | int new_sound = ui->combo_sound->currentIndex(); | ||
| 103 | if (sound_index != new_sound) { | ||
| 104 | Service::CFG::SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound)); | ||
| 105 | modified = true; | ||
| 106 | } | ||
| 107 | |||
| 108 | // update the config savegame if any item is modified. | ||
| 109 | if (modified) | ||
| 110 | Service::CFG::UpdateConfigNANDSavegame(); | ||
| 111 | } | ||
| 112 | |||
| 113 | void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | ||
| 114 | if (birthmonth_index < 0 || birthmonth_index >= 12) | ||
| 115 | return; | ||
| 116 | |||
| 117 | // store current day selection | ||
| 118 | int birthday_index = ui->combo_birthday->currentIndex(); | ||
| 119 | |||
| 120 | // get number of days in the new selected month | ||
| 121 | int days = days_in_month[birthmonth_index]; | ||
| 122 | |||
| 123 | // if the selected day is out of range, | ||
| 124 | // reset it to 1st | ||
| 125 | if (birthday_index < 0 || birthday_index >= days) | ||
| 126 | birthday_index = 0; | ||
| 127 | |||
| 128 | // update the day combo box | ||
| 129 | ui->combo_birthday->clear(); | ||
| 130 | for (int i = 1; i <= days; ++i) { | ||
| 131 | ui->combo_birthday->addItem(QString::number(i)); | ||
| 132 | } | ||
| 133 | |||
| 134 | // restore the day selection | ||
| 135 | ui->combo_birthday->setCurrentIndex(birthday_index); | ||
| 136 | } | ||
diff --git a/src/citra_qt/configure_system.h b/src/citra_qt/configure_system.h new file mode 100644 index 000000000..1f5577070 --- /dev/null +++ b/src/citra_qt/configure_system.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <QWidget> | ||
| 9 | |||
| 10 | namespace Ui { | ||
| 11 | class ConfigureSystem; | ||
| 12 | } | ||
| 13 | |||
| 14 | class ConfigureSystem : public QWidget | ||
| 15 | { | ||
| 16 | Q_OBJECT | ||
| 17 | |||
| 18 | public: | ||
| 19 | explicit ConfigureSystem(QWidget *parent = nullptr); | ||
| 20 | ~ConfigureSystem(); | ||
| 21 | |||
| 22 | void applyConfiguration(); | ||
| 23 | void setConfiguration(bool emulation_running); | ||
| 24 | |||
| 25 | public slots: | ||
| 26 | void updateBirthdayComboBox(int birthmonth_index); | ||
| 27 | |||
| 28 | private: | ||
| 29 | void ReadSystemSettings(); | ||
| 30 | |||
| 31 | std::unique_ptr<Ui::ConfigureSystem> ui; | ||
| 32 | bool enabled; | ||
| 33 | |||
| 34 | std::u16string username; | ||
| 35 | int birthmonth, birthday; | ||
| 36 | int language_index; | ||
| 37 | int sound_index; | ||
| 38 | }; | ||
diff --git a/src/citra_qt/configure_system.ui b/src/citra_qt/configure_system.ui new file mode 100644 index 000000000..6a906b61b --- /dev/null +++ b/src/citra_qt/configure_system.ui | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureSystem</class> | ||
| 4 | <widget class="QWidget" name="ConfigureSystem"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>360</width> | ||
| 10 | <height>377</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Form</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="group_system_settings"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>System Settings</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QGridLayout" name="gridLayout"> | ||
| 25 | <item row="0" column="0"> | ||
| 26 | <widget class="QLabel" name="label_username"> | ||
| 27 | <property name="text"> | ||
| 28 | <string>Username</string> | ||
| 29 | </property> | ||
| 30 | </widget> | ||
| 31 | </item> | ||
| 32 | <item row="0" column="1"> | ||
| 33 | <widget class="QLineEdit" name="edit_username"> | ||
| 34 | <property name="sizePolicy"> | ||
| 35 | <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||
| 36 | <horstretch>0</horstretch> | ||
| 37 | <verstretch>0</verstretch> | ||
| 38 | </sizepolicy> | ||
| 39 | </property> | ||
| 40 | <property name="maxLength"> | ||
| 41 | <number>10</number> | ||
| 42 | </property> | ||
| 43 | </widget> | ||
| 44 | </item> | ||
| 45 | <item row="1" column="0"> | ||
| 46 | <widget class="QLabel" name="label_birthday"> | ||
| 47 | <property name="text"> | ||
| 48 | <string>Birthday</string> | ||
| 49 | </property> | ||
| 50 | </widget> | ||
| 51 | </item> | ||
| 52 | <item row="1" column="1"> | ||
| 53 | <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> | ||
| 54 | <item> | ||
| 55 | <widget class="QComboBox" name="combo_birthmonth"> | ||
| 56 | <item> | ||
| 57 | <property name="text"> | ||
| 58 | <string>January</string> | ||
| 59 | </property> | ||
| 60 | </item> | ||
| 61 | <item> | ||
| 62 | <property name="text"> | ||
| 63 | <string>February</string> | ||
| 64 | </property> | ||
| 65 | </item> | ||
| 66 | <item> | ||
| 67 | <property name="text"> | ||
| 68 | <string>March</string> | ||
| 69 | </property> | ||
| 70 | </item> | ||
| 71 | <item> | ||
| 72 | <property name="text"> | ||
| 73 | <string>April</string> | ||
| 74 | </property> | ||
| 75 | </item> | ||
| 76 | <item> | ||
| 77 | <property name="text"> | ||
| 78 | <string>May</string> | ||
| 79 | </property> | ||
| 80 | </item> | ||
| 81 | <item> | ||
| 82 | <property name="text"> | ||
| 83 | <string>June</string> | ||
| 84 | </property> | ||
| 85 | </item> | ||
| 86 | <item> | ||
| 87 | <property name="text"> | ||
| 88 | <string>July</string> | ||
| 89 | </property> | ||
| 90 | </item> | ||
| 91 | <item> | ||
| 92 | <property name="text"> | ||
| 93 | <string>August</string> | ||
| 94 | </property> | ||
| 95 | </item> | ||
| 96 | <item> | ||
| 97 | <property name="text"> | ||
| 98 | <string>September</string> | ||
| 99 | </property> | ||
| 100 | </item> | ||
| 101 | <item> | ||
| 102 | <property name="text"> | ||
| 103 | <string>October</string> | ||
| 104 | </property> | ||
| 105 | </item> | ||
| 106 | <item> | ||
| 107 | <property name="text"> | ||
| 108 | <string>November</string> | ||
| 109 | </property> | ||
| 110 | </item> | ||
| 111 | <item> | ||
| 112 | <property name="text"> | ||
| 113 | <string>December</string> | ||
| 114 | </property> | ||
| 115 | </item> | ||
| 116 | </widget> | ||
| 117 | </item> | ||
| 118 | <item> | ||
| 119 | <widget class="QComboBox" name="combo_birthday"/> | ||
| 120 | </item> | ||
| 121 | </layout> | ||
| 122 | </item> | ||
| 123 | <item row="2" column="0"> | ||
| 124 | <widget class="QLabel" name="label_language"> | ||
| 125 | <property name="text"> | ||
| 126 | <string>Language</string> | ||
| 127 | </property> | ||
| 128 | </widget> | ||
| 129 | </item> | ||
| 130 | <item row="2" column="1"> | ||
| 131 | <widget class="QComboBox" name="combo_language"> | ||
| 132 | <item> | ||
| 133 | <property name="text"> | ||
| 134 | <string>Japanese (日本語)</string> | ||
| 135 | </property> | ||
| 136 | </item> | ||
| 137 | <item> | ||
| 138 | <property name="text"> | ||
| 139 | <string>English</string> | ||
| 140 | </property> | ||
| 141 | </item> | ||
| 142 | <item> | ||
| 143 | <property name="text"> | ||
| 144 | <string>French (français)</string> | ||
| 145 | </property> | ||
| 146 | </item> | ||
| 147 | <item> | ||
| 148 | <property name="text"> | ||
| 149 | <string>German (Deutsch)</string> | ||
| 150 | </property> | ||
| 151 | </item> | ||
| 152 | <item> | ||
| 153 | <property name="text"> | ||
| 154 | <string>Italian (italiano)</string> | ||
| 155 | </property> | ||
| 156 | </item> | ||
| 157 | <item> | ||
| 158 | <property name="text"> | ||
| 159 | <string>Spanish (español)</string> | ||
| 160 | </property> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <property name="text"> | ||
| 164 | <string>Simplified Chinese (简体中文)</string> | ||
| 165 | </property> | ||
| 166 | </item> | ||
| 167 | <item> | ||
| 168 | <property name="text"> | ||
| 169 | <string>Korean (한국어)</string> | ||
| 170 | </property> | ||
| 171 | </item> | ||
| 172 | <item> | ||
| 173 | <property name="text"> | ||
| 174 | <string>Dutch (Nederlands)</string> | ||
| 175 | </property> | ||
| 176 | </item> | ||
| 177 | <item> | ||
| 178 | <property name="text"> | ||
| 179 | <string>Portuguese (português)</string> | ||
| 180 | </property> | ||
| 181 | </item> | ||
| 182 | <item> | ||
| 183 | <property name="text"> | ||
| 184 | <string>Russian (Русский)</string> | ||
| 185 | </property> | ||
| 186 | </item> | ||
| 187 | <item> | ||
| 188 | <property name="text"> | ||
| 189 | <string>Traditional Chinese (正體中文)</string> | ||
| 190 | </property> | ||
| 191 | </item> | ||
| 192 | </widget> | ||
| 193 | </item> | ||
| 194 | <item row="3" column="0"> | ||
| 195 | <widget class="QLabel" name="label_sound"> | ||
| 196 | <property name="text"> | ||
| 197 | <string>Sound output mode</string> | ||
| 198 | </property> | ||
| 199 | </widget> | ||
| 200 | </item> | ||
| 201 | <item row="3" column="1"> | ||
| 202 | <widget class="QComboBox" name="combo_sound"> | ||
| 203 | <item> | ||
| 204 | <property name="text"> | ||
| 205 | <string>Mono</string> | ||
| 206 | </property> | ||
| 207 | </item> | ||
| 208 | <item> | ||
| 209 | <property name="text"> | ||
| 210 | <string>Stereo</string> | ||
| 211 | </property> | ||
| 212 | </item> | ||
| 213 | <item> | ||
| 214 | <property name="text"> | ||
| 215 | <string>Surround</string> | ||
| 216 | </property> | ||
| 217 | </item> | ||
| 218 | </widget> | ||
| 219 | </item> | ||
| 220 | </layout> | ||
| 221 | </widget> | ||
| 222 | </item> | ||
| 223 | <item> | ||
| 224 | <widget class="QLabel" name="label_disable_info"> | ||
| 225 | <property name="text"> | ||
| 226 | <string>System settings are available only when game is not running.</string> | ||
| 227 | </property> | ||
| 228 | <property name="wordWrap"> | ||
| 229 | <bool>true</bool> | ||
| 230 | </property> | ||
| 231 | </widget> | ||
| 232 | </item> | ||
| 233 | <item> | ||
| 234 | <spacer name="verticalSpacer"> | ||
| 235 | <property name="orientation"> | ||
| 236 | <enum>Qt::Vertical</enum> | ||
| 237 | </property> | ||
| 238 | <property name="sizeHint" stdset="0"> | ||
| 239 | <size> | ||
| 240 | <width>20</width> | ||
| 241 | <height>40</height> | ||
| 242 | </size> | ||
| 243 | </property> | ||
| 244 | </spacer> | ||
| 245 | </item> | ||
| 246 | </layout> | ||
| 247 | </item> | ||
| 248 | </layout> | ||
| 249 | </widget> | ||
| 250 | <resources/> | ||
| 251 | <connections/> | ||
| 252 | </ui> | ||
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 0ed1ffa5a..6fe5d7a3f 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -508,7 +508,7 @@ void GMainWindow::ToggleWindowMode() { | |||
| 508 | } | 508 | } |
| 509 | 509 | ||
| 510 | void GMainWindow::OnConfigure() { | 510 | void GMainWindow::OnConfigure() { |
| 511 | ConfigureDialog configureDialog(this); | 511 | ConfigureDialog configureDialog(this, emulation_running); |
| 512 | auto result = configureDialog.exec(); | 512 | auto result = configureDialog.exec(); |
| 513 | if (result == QDialog::Accepted) | 513 | if (result == QDialog::Accepted) |
| 514 | { | 514 | { |
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index e067db645..a5dc47322 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -40,6 +40,20 @@ struct SaveFileConfig { | |||
| 40 | }; | 40 | }; |
| 41 | static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); | 41 | static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); |
| 42 | 42 | ||
| 43 | enum ConfigBlockID { | ||
| 44 | StereoCameraSettingsBlockID = 0x00050005, | ||
| 45 | SoundOutputModeBlockID = 0x00070001, | ||
| 46 | ConsoleUniqueIDBlockID = 0x00090001, | ||
| 47 | UsernameBlockID = 0x000A0000, | ||
| 48 | BirthdayBlockID = 0x000A0001, | ||
| 49 | LanguageBlockID = 0x000A0002, | ||
| 50 | CountryInfoBlockID = 0x000B0000, | ||
| 51 | CountryNameBlockID = 0x000B0001, | ||
| 52 | StateNameBlockID = 0x000B0002, | ||
| 53 | EULAVersionBlockID = 0x000D0000, | ||
| 54 | ConsoleModelBlockID = 0x000F0004, | ||
| 55 | }; | ||
| 56 | |||
| 43 | struct UsernameBlock { | 57 | struct UsernameBlock { |
| 44 | char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary | 58 | char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary |
| 45 | u32 zero; | 59 | u32 zero; |
| @@ -73,8 +87,7 @@ static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } }; | |||
| 73 | static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; | 87 | static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; |
| 74 | static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 }; | 88 | static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 }; |
| 75 | static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014 | 89 | static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014 |
| 76 | /// TODO(Subv): Find out what this actually is | 90 | static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND; |
| 77 | static const u8 SOUND_OUTPUT_MODE = 2; | ||
| 78 | static const u8 UNITED_STATES_COUNTRY_ID = 49; | 91 | static const u8 UNITED_STATES_COUNTRY_ID = 49; |
| 79 | /// TODO(Subv): Find what the other bytes are | 92 | /// TODO(Subv): Find what the other bytes are |
| 80 | static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; | 93 | static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; |
| @@ -224,6 +237,22 @@ void GetConfigInfoBlk8(Service::Interface* self) { | |||
| 224 | Memory::WriteBlock(data_pointer, data.data(), data.size()); | 237 | Memory::WriteBlock(data_pointer, data.data(), data.size()); |
| 225 | } | 238 | } |
| 226 | 239 | ||
| 240 | void SetConfigInfoBlk4(Service::Interface* self) { | ||
| 241 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 242 | u32 block_id = cmd_buff[1]; | ||
| 243 | u32 size = cmd_buff[2]; | ||
| 244 | VAddr data_pointer = cmd_buff[4]; | ||
| 245 | |||
| 246 | if (!Memory::IsValidVirtualAddress(data_pointer)) { | ||
| 247 | cmd_buff[1] = -1; // TODO(Subv): Find the right error code | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | |||
| 251 | std::vector<u8> data(size); | ||
| 252 | Memory::ReadBlock(data_pointer, data.data(), data.size()); | ||
| 253 | cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw; | ||
| 254 | } | ||
| 255 | |||
| 227 | void UpdateConfigNANDSavegame(Service::Interface* self) { | 256 | void UpdateConfigNANDSavegame(Service::Interface* self) { |
| 228 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 257 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 229 | cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw; | 258 | cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw; |
| @@ -234,13 +263,13 @@ void FormatConfig(Service::Interface* self) { | |||
| 234 | cmd_buff[1] = Service::CFG::FormatConfig().raw; | 263 | cmd_buff[1] = Service::CFG::FormatConfig().raw; |
| 235 | } | 264 | } |
| 236 | 265 | ||
| 237 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | 266 | static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) { |
| 238 | // Read the header | 267 | // Read the header |
| 239 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | 268 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); |
| 240 | 269 | ||
| 241 | auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), | 270 | auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), |
| 242 | [&](const SaveConfigBlockEntry& entry) { | 271 | [&](const SaveConfigBlockEntry& entry) { |
| 243 | return entry.block_id == block_id && (entry.flags & flag); | 272 | return entry.block_id == block_id; |
| 244 | }); | 273 | }); |
| 245 | 274 | ||
| 246 | if (itr == std::end(config->block_entries)) { | 275 | if (itr == std::end(config->block_entries)) { |
| @@ -248,17 +277,38 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | |||
| 248 | return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | 277 | return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); |
| 249 | } | 278 | } |
| 250 | 279 | ||
| 280 | if ((itr->flags & flag) == 0) { | ||
| 281 | LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id, size); | ||
| 282 | return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 283 | } | ||
| 284 | |||
| 251 | if (itr->size != size) { | 285 | if (itr->size != size) { |
| 252 | LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); | 286 | LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag); |
| 253 | return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); | 287 | return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent); |
| 254 | } | 288 | } |
| 255 | 289 | ||
| 290 | void* pointer; | ||
| 291 | |||
| 256 | // The data is located in the block header itself if the size is less than 4 bytes | 292 | // The data is located in the block header itself if the size is less than 4 bytes |
| 257 | if (itr->size <= 4) | 293 | if (itr->size <= 4) |
| 258 | memcpy(output, &itr->offset_or_data, itr->size); | 294 | pointer = &itr->offset_or_data; |
| 259 | else | 295 | else |
| 260 | memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size); | 296 | pointer = &cfg_config_file_buffer[itr->offset_or_data]; |
| 297 | |||
| 298 | return MakeResult<void*>(pointer); | ||
| 299 | } | ||
| 300 | |||
| 301 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) { | ||
| 302 | void* pointer; | ||
| 303 | CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); | ||
| 304 | memcpy(output, pointer, size); | ||
| 305 | return RESULT_SUCCESS; | ||
| 306 | } | ||
| 261 | 307 | ||
| 308 | ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) { | ||
| 309 | void* pointer; | ||
| 310 | CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); | ||
| 311 | memcpy(pointer, input, size); | ||
| 262 | return RESULT_SUCCESS; | 312 | return RESULT_SUCCESS; |
| 263 | } | 313 | } |
| 264 | 314 | ||
| @@ -336,25 +386,25 @@ ResultCode FormatConfig() { | |||
| 336 | res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer); | 386 | res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer); |
| 337 | if (!res.IsSuccess()) return res; | 387 | if (!res.IsSuccess()) return res; |
| 338 | 388 | ||
| 339 | res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); | 389 | res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data()); |
| 340 | if (!res.IsSuccess()) return res; | 390 | if (!res.IsSuccess()) return res; |
| 341 | 391 | ||
| 342 | res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); | 392 | res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE); |
| 343 | if (!res.IsSuccess()) return res; | 393 | if (!res.IsSuccess()) return res; |
| 344 | 394 | ||
| 345 | res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); | 395 | res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); |
| 346 | if (!res.IsSuccess()) return res; | 396 | if (!res.IsSuccess()) return res; |
| 347 | 397 | ||
| 348 | res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK); | 398 | res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK); |
| 349 | if (!res.IsSuccess()) return res; | 399 | if (!res.IsSuccess()) return res; |
| 350 | 400 | ||
| 351 | res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY); | 401 | res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY); |
| 352 | if (!res.IsSuccess()) return res; | 402 | if (!res.IsSuccess()) return res; |
| 353 | 403 | ||
| 354 | res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE); | 404 | res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE); |
| 355 | if (!res.IsSuccess()) return res; | 405 | if (!res.IsSuccess()) return res; |
| 356 | 406 | ||
| 357 | res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO); | 407 | res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO); |
| 358 | if (!res.IsSuccess()) return res; | 408 | if (!res.IsSuccess()) return res; |
| 359 | 409 | ||
| 360 | u16_le country_name_buffer[16][0x40] = {}; | 410 | u16_le country_name_buffer[16][0x40] = {}; |
| @@ -363,10 +413,10 @@ ResultCode FormatConfig() { | |||
| 363 | std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]); | 413 | std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]); |
| 364 | } | 414 | } |
| 365 | // 0x000B0001 - Localized names for the profile Country | 415 | // 0x000B0001 - Localized names for the profile Country |
| 366 | res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer); | 416 | res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer); |
| 367 | if (!res.IsSuccess()) return res; | 417 | if (!res.IsSuccess()) return res; |
| 368 | // 0x000B0002 - Localized names for the profile State/Province | 418 | // 0x000B0002 - Localized names for the profile State/Province |
| 369 | res = CreateConfigInfoBlk(0x000B0002, sizeof(country_name_buffer), 0xE, country_name_buffer); | 419 | res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer); |
| 370 | if (!res.IsSuccess()) return res; | 420 | if (!res.IsSuccess()) return res; |
| 371 | 421 | ||
| 372 | // 0x000B0003 - Unknown, related to country/address (zip code?) | 422 | // 0x000B0003 - Unknown, related to country/address (zip code?) |
| @@ -382,10 +432,10 @@ ResultCode FormatConfig() { | |||
| 382 | if (!res.IsSuccess()) return res; | 432 | if (!res.IsSuccess()) return res; |
| 383 | 433 | ||
| 384 | // 0x000D0000 - Accepted EULA version | 434 | // 0x000D0000 - Accepted EULA version |
| 385 | res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer); | 435 | res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer); |
| 386 | if (!res.IsSuccess()) return res; | 436 | if (!res.IsSuccess()) return res; |
| 387 | 437 | ||
| 388 | res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); | 438 | res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL); |
| 389 | if (!res.IsSuccess()) return res; | 439 | if (!res.IsSuccess()) return res; |
| 390 | 440 | ||
| 391 | // 0x00170000 - Unknown | 441 | // 0x00170000 - Unknown |
| @@ -399,11 +449,7 @@ ResultCode FormatConfig() { | |||
| 399 | return RESULT_SUCCESS; | 449 | return RESULT_SUCCESS; |
| 400 | } | 450 | } |
| 401 | 451 | ||
| 402 | void Init() { | 452 | ResultCode LoadConfigNANDSaveFile() { |
| 403 | AddService(new CFG_I_Interface); | ||
| 404 | AddService(new CFG_S_Interface); | ||
| 405 | AddService(new CFG_U_Interface); | ||
| 406 | |||
| 407 | // Open the SystemSaveData archive 0x00010017 | 453 | // Open the SystemSaveData archive 0x00010017 |
| 408 | FileSys::Path archive_path(cfg_system_savedata_id); | 454 | FileSys::Path archive_path(cfg_system_savedata_id); |
| 409 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); | 455 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); |
| @@ -431,14 +477,75 @@ void Init() { | |||
| 431 | if (config_result.Succeeded()) { | 477 | if (config_result.Succeeded()) { |
| 432 | auto config = config_result.MoveFrom(); | 478 | auto config = config_result.MoveFrom(); |
| 433 | config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); | 479 | config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); |
| 434 | return; | 480 | return RESULT_SUCCESS; |
| 435 | } | 481 | } |
| 436 | 482 | ||
| 437 | FormatConfig(); | 483 | return FormatConfig(); |
| 484 | } | ||
| 485 | |||
| 486 | void Init() { | ||
| 487 | AddService(new CFG_I_Interface); | ||
| 488 | AddService(new CFG_S_Interface); | ||
| 489 | AddService(new CFG_U_Interface); | ||
| 490 | |||
| 491 | LoadConfigNANDSaveFile(); | ||
| 438 | } | 492 | } |
| 439 | 493 | ||
| 440 | void Shutdown() { | 494 | void Shutdown() { |
| 441 | } | 495 | } |
| 442 | 496 | ||
| 497 | void SetUsername(const std::u16string& name) { | ||
| 498 | ASSERT(name.size() <= 10); | ||
| 499 | UsernameBlock block{}; | ||
| 500 | name.copy(block.username, name.size()); | ||
| 501 | SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block); | ||
| 502 | } | ||
| 503 | |||
| 504 | std::u16string GetUsername() { | ||
| 505 | UsernameBlock block; | ||
| 506 | GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block); | ||
| 507 | |||
| 508 | // the username string in the block isn't null-terminated, | ||
| 509 | // so we need to find the end manually. | ||
| 510 | std::u16string username(block.username, ARRAY_SIZE(block.username)); | ||
| 511 | const size_t pos = username.find(u'\0'); | ||
| 512 | if (pos != std::u16string::npos) | ||
| 513 | username.erase(pos); | ||
| 514 | return username; | ||
| 515 | } | ||
| 516 | |||
| 517 | void SetBirthday(u8 month, u8 day) { | ||
| 518 | BirthdayBlock block = { month, day }; | ||
| 519 | SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block); | ||
| 520 | } | ||
| 521 | |||
| 522 | std::tuple<u8, u8> GetBirthday() { | ||
| 523 | BirthdayBlock block; | ||
| 524 | GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block); | ||
| 525 | return std::make_tuple(block.month, block.day); | ||
| 526 | } | ||
| 527 | |||
| 528 | void SetSystemLanguage(SystemLanguage language) { | ||
| 529 | u8 block = language; | ||
| 530 | SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block); | ||
| 531 | } | ||
| 532 | |||
| 533 | SystemLanguage GetSystemLanguage() { | ||
| 534 | u8 block; | ||
| 535 | GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block); | ||
| 536 | return static_cast<SystemLanguage>(block); | ||
| 537 | } | ||
| 538 | |||
| 539 | void SetSoundOutputMode(SoundOutputMode mode) { | ||
| 540 | u8 block = mode; | ||
| 541 | SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block); | ||
| 542 | } | ||
| 543 | |||
| 544 | SoundOutputMode GetSoundOutputMode() { | ||
| 545 | u8 block; | ||
| 546 | GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block); | ||
| 547 | return static_cast<SoundOutputMode>(block); | ||
| 548 | } | ||
| 549 | |||
| 443 | } // namespace CFG | 550 | } // namespace CFG |
| 444 | } // namespace Service | 551 | } // namespace Service |
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index c01806836..18f60f4ca 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | ||
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | 11 | ||
| @@ -35,7 +36,14 @@ enum SystemLanguage { | |||
| 35 | LANGUAGE_KO = 7, | 36 | LANGUAGE_KO = 7, |
| 36 | LANGUAGE_NL = 8, | 37 | LANGUAGE_NL = 8, |
| 37 | LANGUAGE_PT = 9, | 38 | LANGUAGE_PT = 9, |
| 38 | LANGUAGE_RU = 10 | 39 | LANGUAGE_RU = 10, |
| 40 | LANGUAGE_TW = 11 | ||
| 41 | }; | ||
| 42 | |||
| 43 | enum SoundOutputMode { | ||
| 44 | SOUND_MONO = 0, | ||
| 45 | SOUND_STEREO = 1, | ||
| 46 | SOUND_SURROUND = 2 | ||
| 39 | }; | 47 | }; |
| 40 | 48 | ||
| 41 | /// Block header in the config savedata file | 49 | /// Block header in the config savedata file |
| @@ -178,6 +186,22 @@ void GetConfigInfoBlk2(Service::Interface* self); | |||
| 178 | void GetConfigInfoBlk8(Service::Interface* self); | 186 | void GetConfigInfoBlk8(Service::Interface* self); |
| 179 | 187 | ||
| 180 | /** | 188 | /** |
| 189 | * CFG::SetConfigInfoBlk4 service function | ||
| 190 | * Inputs: | ||
| 191 | * 0 : 0x04020082 / 0x08020082 | ||
| 192 | * 1 : Block ID | ||
| 193 | * 2 : Size | ||
| 194 | * 3 : Descriptor for the output buffer | ||
| 195 | * 4 : Output buffer pointer | ||
| 196 | * Outputs: | ||
| 197 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 198 | * Note: | ||
| 199 | * The parameters order is different from GetConfigInfoBlk2/8's, | ||
| 200 | * where Block ID and Size are switched. | ||
| 201 | */ | ||
| 202 | void SetConfigInfoBlk4(Service::Interface* self); | ||
| 203 | |||
| 204 | /** | ||
| 181 | * CFG::UpdateConfigNANDSavegame service function | 205 | * CFG::UpdateConfigNANDSavegame service function |
| 182 | * Inputs: | 206 | * Inputs: |
| 183 | * 0 : 0x04030000 / 0x08030000 | 207 | * 0 : 0x04030000 / 0x08030000 |
| @@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self); | |||
| 205 | * @param output A pointer where we will write the read data | 229 | * @param output A pointer where we will write the read data |
| 206 | * @returns ResultCode indicating the result of the operation, 0 on success | 230 | * @returns ResultCode indicating the result of the operation, 0 on success |
| 207 | */ | 231 | */ |
| 208 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); | 232 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output); |
| 233 | |||
| 234 | /** | ||
| 235 | * Reads data from input and writes to a block with the specified id and flag | ||
| 236 | * in the Config savegame buffer. | ||
| 237 | * The input size must match exactly the size of the target block | ||
| 238 | * @param block_id The id of the block we want to write | ||
| 239 | * @param size The size of the block we want to write | ||
| 240 | * @param flag The target block must have this flag set | ||
| 241 | * @param input A pointer where we will read data and write to Config savegame buffer | ||
| 242 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 243 | */ | ||
| 244 | ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input); | ||
| 209 | 245 | ||
| 210 | /** | 246 | /** |
| 211 | * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. | 247 | * 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(); | |||
| 236 | */ | 272 | */ |
| 237 | ResultCode FormatConfig(); | 273 | ResultCode FormatConfig(); |
| 238 | 274 | ||
| 275 | /** | ||
| 276 | * Open the config savegame file and load it to the memory buffer | ||
| 277 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 278 | */ | ||
| 279 | ResultCode LoadConfigNANDSaveFile(); | ||
| 280 | |||
| 239 | /// Initialize the config service | 281 | /// Initialize the config service |
| 240 | void Init(); | 282 | void Init(); |
| 241 | 283 | ||
| 242 | /// Shutdown the config service | 284 | /// Shutdown the config service |
| 243 | void Shutdown(); | 285 | void Shutdown(); |
| 244 | 286 | ||
| 287 | // Utilities for frontend to set config data. | ||
| 288 | // Note: before calling these functions, LoadConfigNANDSaveFile should be called, | ||
| 289 | // and UpdateConfigNANDSavegame should be called after making changes to config data. | ||
| 290 | |||
| 291 | /** | ||
| 292 | * Sets the username in config savegame. | ||
| 293 | * @param name the username to set. The maximum size is 10 in char16_t. | ||
| 294 | */ | ||
| 295 | void SetUsername(const std::u16string& name); | ||
| 296 | |||
| 297 | /** | ||
| 298 | * Gets the username from config savegame. | ||
| 299 | * @returns the username | ||
| 300 | */ | ||
| 301 | std::u16string GetUsername(); | ||
| 302 | |||
| 303 | /** | ||
| 304 | * Sets the profile birthday in config savegame. | ||
| 305 | * @param month the month of birthday. | ||
| 306 | * @param day the day of the birthday. | ||
| 307 | */ | ||
| 308 | void SetBirthday(u8 month, u8 day); | ||
| 309 | |||
| 310 | /** | ||
| 311 | * Gets the profile birthday from the config savegame. | ||
| 312 | * @returns a tuple of (month, day) of birthday | ||
| 313 | */ | ||
| 314 | std::tuple<u8, u8> GetBirthday(); | ||
| 315 | |||
| 316 | /** | ||
| 317 | * Sets the system language in config savegame. | ||
| 318 | * @param language the system language to set. | ||
| 319 | */ | ||
| 320 | void SetSystemLanguage(SystemLanguage language); | ||
| 321 | |||
| 322 | /** | ||
| 323 | * Gets the system language from config savegame. | ||
| 324 | * @returns the system language | ||
| 325 | */ | ||
| 326 | SystemLanguage GetSystemLanguage(); | ||
| 327 | |||
| 328 | /** | ||
| 329 | * Sets the sound output mode in config savegame. | ||
| 330 | * @param mode the sound output mode to set | ||
| 331 | */ | ||
| 332 | void SetSoundOutputMode(SoundOutputMode mode); | ||
| 333 | |||
| 334 | /** | ||
| 335 | * Gets the sound output mode from config savegame. | ||
| 336 | * @returns the sound output mode | ||
| 337 | */ | ||
| 338 | SoundOutputMode GetSoundOutputMode(); | ||
| 339 | |||
| 245 | } // namespace CFG | 340 | } // namespace CFG |
| 246 | } // namespace Service | 341 | } // namespace Service |
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index b18060f6d..8b0db785f 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp | |||
| @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 22 | {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, | 22 | {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, |
| 23 | // cfg:i | 23 | // cfg:i |
| 24 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, | 24 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, |
| 25 | {0x04020082, nullptr, "SetConfigInfoBlk4"}, | 25 | {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, |
| 26 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | 26 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, |
| 27 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, | 27 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, |
| 28 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, | 28 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, |
| @@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 31 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, | 31 | {0x04080042, nullptr, "SecureInfoGetSerialNo"}, |
| 32 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, | 32 | {0x04090000, nullptr, "UpdateConfigBlk00040003"}, |
| 33 | {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, | 33 | {0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, |
| 34 | {0x08020082, nullptr, "SetConfigInfoBlk4"}, | 34 | {0x08020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, |
| 35 | {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | 35 | {0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, |
| 36 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, | 36 | {0x080400C2, nullptr, "CreateConfigInfoBlk"}, |
| 37 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, | 37 | {0x08050000, nullptr, "DeleteConfigNANDSavefile"}, |
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index e001f7687..12b458783 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp | |||
| @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 22 | {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, | 22 | {0x000A0040, GetCountryCodeID, "GetCountryCodeID"}, |
| 23 | // cfg:s | 23 | // cfg:s |
| 24 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, | 24 | {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, |
| 25 | {0x04020082, nullptr, "SetConfigInfoBlk4"}, | 25 | {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"}, |
| 26 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, | 26 | {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, |
| 27 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, | 27 | {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, |
| 28 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, | 28 | {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index f4acc4895..4c7aaa7f2 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -259,7 +259,7 @@ using FileSys::ArchiveFactory; | |||
| 259 | 259 | ||
| 260 | /** | 260 | /** |
| 261 | * Map of registered archives, identified by id code. Once an archive is registered here, it is | 261 | * Map of registered archives, identified by id code. Once an archive is registered here, it is |
| 262 | * never removed until the FS service is shut down. | 262 | * never removed until UnregisterArchiveTypes is called. |
| 263 | */ | 263 | */ |
| 264 | static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; | 264 | static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; |
| 265 | 265 | ||
| @@ -520,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) { | |||
| 520 | return RESULT_SUCCESS; | 520 | return RESULT_SUCCESS; |
| 521 | } | 521 | } |
| 522 | 522 | ||
| 523 | /// Initialize archives | 523 | void RegisterArchiveTypes() { |
| 524 | void ArchiveInit() { | ||
| 525 | next_handle = 1; | ||
| 526 | |||
| 527 | AddService(new FS::Interface); | ||
| 528 | |||
| 529 | // TODO(Subv): Add the other archive types (see here for the known types: | 524 | // TODO(Subv): Add the other archive types (see here for the known types: |
| 530 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). | 525 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). |
| 531 | 526 | ||
| @@ -562,10 +557,23 @@ void ArchiveInit() { | |||
| 562 | RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); | 557 | RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); |
| 563 | } | 558 | } |
| 564 | 559 | ||
| 560 | void UnregisterArchiveTypes() { | ||
| 561 | id_code_map.clear(); | ||
| 562 | } | ||
| 563 | |||
| 564 | /// Initialize archives | ||
| 565 | void ArchiveInit() { | ||
| 566 | next_handle = 1; | ||
| 567 | |||
| 568 | AddService(new FS::Interface); | ||
| 569 | |||
| 570 | RegisterArchiveTypes(); | ||
| 571 | } | ||
| 572 | |||
| 565 | /// Shutdown archives | 573 | /// Shutdown archives |
| 566 | void ArchiveShutdown() { | 574 | void ArchiveShutdown() { |
| 567 | handle_map.clear(); | 575 | handle_map.clear(); |
| 568 | id_code_map.clear(); | 576 | UnregisterArchiveTypes(); |
| 569 | } | 577 | } |
| 570 | 578 | ||
| 571 | } // namespace FS | 579 | } // namespace FS |
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 006606740..f7a50a3a7 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -235,5 +235,11 @@ void ArchiveInit(); | |||
| 235 | /// Shutdown archives | 235 | /// Shutdown archives |
| 236 | void ArchiveShutdown(); | 236 | void ArchiveShutdown(); |
| 237 | 237 | ||
| 238 | /// Register all archive types | ||
| 239 | void RegisterArchiveTypes(); | ||
| 240 | |||
| 241 | /// Unregister all archive types | ||
| 242 | void UnregisterArchiveTypes(); | ||
| 243 | |||
| 238 | } // namespace FS | 244 | } // namespace FS |
| 239 | } // namespace Service | 245 | } // namespace Service |