summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2016-07-09 20:39:19 -0700
committerGravatar GitHub2016-07-09 20:39:19 -0700
commitffda82eea546830d15284810e91e58c5b6627a0c (patch)
tree5d0cb7b7f17ef73bd382b08cce863f1b05ed10df
parentMerge pull request #1940 from JamePeng/fix-archive-error-code (diff)
parentQt: add system settings config tab (diff)
downloadyuzu-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.txt3
-rw-r--r--src/citra_qt/configure.ui11
-rw-r--r--src/citra_qt/configure_dialog.cpp9
-rw-r--r--src/citra_qt/configure_dialog.h3
-rw-r--r--src/citra_qt/configure_system.cpp136
-rw-r--r--src/citra_qt/configure_system.h38
-rw-r--r--src/citra_qt/configure_system.ui252
-rw-r--r--src/citra_qt/main.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp155
-rw-r--r--src/core/hle/service/cfg/cfg.h99
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp2
-rw-r--r--src/core/hle/service/fs/archive.cpp24
-rw-r--r--src/core/hle/service/fs/archive.h6
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
12ConfigureDialog::ConfigureDialog(QWidget *parent) : 12ConfigureDialog::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
23void ConfigureDialog::setConfiguration() { 24void 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
26void ConfigureDialog::applyConfiguration() { 30void 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
18public: 18public:
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
27private: 27private:
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
12static const std::array<int, 12> days_in_month = {{
13 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
14}};
15
16ConfigureSystem::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
24ConfigureSystem::~ConfigureSystem() {
25}
26
27void 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
52void 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
72void 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
113void 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
10namespace Ui {
11class ConfigureSystem;
12}
13
14class ConfigureSystem : public QWidget
15{
16 Q_OBJECT
17
18public:
19 explicit ConfigureSystem(QWidget *parent = nullptr);
20 ~ConfigureSystem();
21
22 void applyConfiguration();
23 void setConfiguration(bool emulation_running);
24
25public slots:
26 void updateBirthdayComboBox(int birthmonth_index);
27
28private:
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
510void GMainWindow::OnConfigure() { 510void 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};
41static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes"); 41static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes");
42 42
43enum 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
43struct UsernameBlock { 57struct 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 } };
73static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; 87static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
74static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 }; 88static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
75static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014 89static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
76/// TODO(Subv): Find out what this actually is 90static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
77static const u8 SOUND_OUTPUT_MODE = 2;
78static const u8 UNITED_STATES_COUNTRY_ID = 49; 91static 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
80static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID }; 93static 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
240void 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
227void UpdateConfigNANDSavegame(Service::Interface* self) { 256void 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
237ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { 266static 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
301ResultCode 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
308ResultCode 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
402void Init() { 452ResultCode 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
486void 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
440void Shutdown() { 494void Shutdown() {
441} 495}
442 496
497void 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
504std::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
517void SetBirthday(u8 month, u8 day) {
518 BirthdayBlock block = { month, day };
519 SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block);
520}
521
522std::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
528void SetSystemLanguage(SystemLanguage language) {
529 u8 block = language;
530 SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block);
531}
532
533SystemLanguage GetSystemLanguage() {
534 u8 block;
535 GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block);
536 return static_cast<SystemLanguage>(block);
537}
538
539void SetSoundOutputMode(SoundOutputMode mode) {
540 u8 block = mode;
541 SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block);
542}
543
544SoundOutputMode 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
43enum 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);
178void GetConfigInfoBlk8(Service::Interface* self); 186void 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 */
202void 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 */
208ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); 232ResultCode 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 */
244ResultCode 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 */
237ResultCode FormatConfig(); 273ResultCode 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 */
279ResultCode LoadConfigNANDSaveFile();
280
239/// Initialize the config service 281/// Initialize the config service
240void Init(); 282void Init();
241 283
242/// Shutdown the config service 284/// Shutdown the config service
243void Shutdown(); 285void 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 */
295void SetUsername(const std::u16string& name);
296
297/**
298 * Gets the username from config savegame.
299 * @returns the username
300 */
301std::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 */
308void 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 */
314std::tuple<u8, u8> GetBirthday();
315
316/**
317 * Sets the system language in config savegame.
318 * @param language the system language to set.
319 */
320void SetSystemLanguage(SystemLanguage language);
321
322/**
323 * Gets the system language from config savegame.
324 * @returns the system language
325 */
326SystemLanguage GetSystemLanguage();
327
328/**
329 * Sets the sound output mode in config savegame.
330 * @param mode the sound output mode to set
331 */
332void SetSoundOutputMode(SoundOutputMode mode);
333
334/**
335 * Gets the sound output mode from config savegame.
336 * @returns the sound output mode
337 */
338SoundOutputMode 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 */
264static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map; 264static 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 523void RegisterArchiveTypes() {
524void 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
560void UnregisterArchiveTypes() {
561 id_code_map.clear();
562}
563
564/// Initialize archives
565void ArchiveInit() {
566 next_handle = 1;
567
568 AddService(new FS::Interface);
569
570 RegisterArchiveTypes();
571}
572
565/// Shutdown archives 573/// Shutdown archives
566void ArchiveShutdown() { 574void 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
236void ArchiveShutdown(); 236void ArchiveShutdown();
237 237
238/// Register all archive types
239void RegisterArchiveTypes();
240
241/// Unregister all archive types
242void UnregisterArchiveTypes();
243
238} // namespace FS 244} // namespace FS
239} // namespace Service 245} // namespace Service