summaryrefslogtreecommitdiff
path: root/src/core
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 /src/core
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
Diffstat (limited to 'src/core')
-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
6 files changed, 253 insertions, 37 deletions
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