diff options
| author | 2016-07-09 20:39:19 -0700 | |
|---|---|---|
| committer | 2016-07-09 20:39:19 -0700 | |
| commit | ffda82eea546830d15284810e91e58c5b6627a0c (patch) | |
| tree | 5d0cb7b7f17ef73bd382b08cce863f1b05ed10df /src/core | |
| 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
Diffstat (limited to 'src/core')
| -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 |
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 | }; |
| 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 |