mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-11-03 23:28:48 +00:00 
			
		
		
		
	Merge pull request #1894 from wwylele/set-config-block
Implement config savegame editing & clean up
This commit is contained in:
		
						commit
						ffda82eea5
					
				
					 14 changed files with 703 additions and 41 deletions
				
			
		| 
						 | 
				
			
			@ -40,6 +40,20 @@ struct SaveFileConfig {
 | 
			
		|||
};
 | 
			
		||||
static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes");
 | 
			
		||||
 | 
			
		||||
enum ConfigBlockID {
 | 
			
		||||
    StereoCameraSettingsBlockID = 0x00050005,
 | 
			
		||||
    SoundOutputModeBlockID      = 0x00070001,
 | 
			
		||||
    ConsoleUniqueIDBlockID      = 0x00090001,
 | 
			
		||||
    UsernameBlockID             = 0x000A0000,
 | 
			
		||||
    BirthdayBlockID             = 0x000A0001,
 | 
			
		||||
    LanguageBlockID             = 0x000A0002,
 | 
			
		||||
    CountryInfoBlockID          = 0x000B0000,
 | 
			
		||||
    CountryNameBlockID          = 0x000B0001,
 | 
			
		||||
    StateNameBlockID            = 0x000B0002,
 | 
			
		||||
    EULAVersionBlockID          = 0x000D0000,
 | 
			
		||||
    ConsoleModelBlockID         = 0x000F0004,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct UsernameBlock {
 | 
			
		||||
    char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
 | 
			
		||||
    u32 zero;
 | 
			
		||||
| 
						 | 
				
			
			@ -73,8 +87,7 @@ static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
 | 
			
		|||
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
 | 
			
		||||
static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
 | 
			
		||||
static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
 | 
			
		||||
/// TODO(Subv): Find out what this actually is
 | 
			
		||||
static const u8 SOUND_OUTPUT_MODE = 2;
 | 
			
		||||
static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
 | 
			
		||||
static const u8 UNITED_STATES_COUNTRY_ID = 49;
 | 
			
		||||
/// TODO(Subv): Find what the other bytes are
 | 
			
		||||
static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID };
 | 
			
		||||
| 
						 | 
				
			
			@ -224,6 +237,22 @@ void GetConfigInfoBlk8(Service::Interface* self) {
 | 
			
		|||
    Memory::WriteBlock(data_pointer, data.data(), data.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetConfigInfoBlk4(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    u32 block_id = cmd_buff[1];
 | 
			
		||||
    u32 size = cmd_buff[2];
 | 
			
		||||
    VAddr data_pointer = cmd_buff[4];
 | 
			
		||||
 | 
			
		||||
    if (!Memory::IsValidVirtualAddress(data_pointer)) {
 | 
			
		||||
        cmd_buff[1] = -1; // TODO(Subv): Find the right error code
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> data(size);
 | 
			
		||||
    Memory::ReadBlock(data_pointer, data.data(), data.size());
 | 
			
		||||
    cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UpdateConfigNANDSavegame(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
 | 
			
		||||
| 
						 | 
				
			
			@ -234,13 +263,13 @@ void FormatConfig(Service::Interface* self) {
 | 
			
		|||
    cmd_buff[1] = Service::CFG::FormatConfig().raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
 | 
			
		||||
static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) {
 | 
			
		||||
    // Read the header
 | 
			
		||||
    SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
 | 
			
		||||
 | 
			
		||||
    auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
 | 
			
		||||
        [&](const SaveConfigBlockEntry& entry) {
 | 
			
		||||
            return entry.block_id == block_id && (entry.flags & flag);
 | 
			
		||||
            return entry.block_id == block_id;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    if (itr == std::end(config->block_entries)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -248,17 +277,38 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
 | 
			
		|||
        return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((itr->flags & flag) == 0) {
 | 
			
		||||
        LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id, size);
 | 
			
		||||
        return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (itr->size != size) {
 | 
			
		||||
        LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag);
 | 
			
		||||
        return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void* pointer;
 | 
			
		||||
 | 
			
		||||
    // The data is located in the block header itself if the size is less than 4 bytes
 | 
			
		||||
    if (itr->size <= 4)
 | 
			
		||||
        memcpy(output, &itr->offset_or_data, itr->size);
 | 
			
		||||
        pointer = &itr->offset_or_data;
 | 
			
		||||
    else
 | 
			
		||||
        memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size);
 | 
			
		||||
        pointer = &cfg_config_file_buffer[itr->offset_or_data];
 | 
			
		||||
 | 
			
		||||
    return MakeResult<void*>(pointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
 | 
			
		||||
    void* pointer;
 | 
			
		||||
    CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
 | 
			
		||||
    memcpy(output, pointer, size);
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) {
 | 
			
		||||
    void* pointer;
 | 
			
		||||
    CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
 | 
			
		||||
    memcpy(pointer, input, size);
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -336,25 +386,25 @@ ResultCode FormatConfig() {
 | 
			
		|||
    res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
 | 
			
		||||
    res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
 | 
			
		||||
    res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
 | 
			
		||||
    res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
 | 
			
		||||
    res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
 | 
			
		||||
    res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
 | 
			
		||||
    res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
 | 
			
		||||
    res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    u16_le country_name_buffer[16][0x40] = {};
 | 
			
		||||
| 
						 | 
				
			
			@ -363,10 +413,10 @@ ResultCode FormatConfig() {
 | 
			
		|||
        std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
 | 
			
		||||
    }
 | 
			
		||||
    // 0x000B0001 - Localized names for the profile Country
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer);
 | 
			
		||||
    res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
    // 0x000B0002 - Localized names for the profile State/Province
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000B0002, sizeof(country_name_buffer), 0xE, country_name_buffer);
 | 
			
		||||
    res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    // 0x000B0003 - Unknown, related to country/address (zip code?)
 | 
			
		||||
| 
						 | 
				
			
			@ -382,10 +432,10 @@ ResultCode FormatConfig() {
 | 
			
		|||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    // 0x000D0000 - Accepted EULA version
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer);
 | 
			
		||||
    res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
 | 
			
		||||
    res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
 | 
			
		||||
    if (!res.IsSuccess()) return res;
 | 
			
		||||
 | 
			
		||||
    // 0x00170000 - Unknown
 | 
			
		||||
| 
						 | 
				
			
			@ -399,11 +449,7 @@ ResultCode FormatConfig() {
 | 
			
		|||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Init() {
 | 
			
		||||
    AddService(new CFG_I_Interface);
 | 
			
		||||
    AddService(new CFG_S_Interface);
 | 
			
		||||
    AddService(new CFG_U_Interface);
 | 
			
		||||
 | 
			
		||||
ResultCode LoadConfigNANDSaveFile() {
 | 
			
		||||
    // Open the SystemSaveData archive 0x00010017
 | 
			
		||||
    FileSys::Path archive_path(cfg_system_savedata_id);
 | 
			
		||||
    auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
 | 
			
		||||
| 
						 | 
				
			
			@ -431,14 +477,75 @@ void Init() {
 | 
			
		|||
    if (config_result.Succeeded()) {
 | 
			
		||||
        auto config = config_result.MoveFrom();
 | 
			
		||||
        config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
 | 
			
		||||
        return;
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FormatConfig();
 | 
			
		||||
    return FormatConfig();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Init() {
 | 
			
		||||
    AddService(new CFG_I_Interface);
 | 
			
		||||
    AddService(new CFG_S_Interface);
 | 
			
		||||
    AddService(new CFG_U_Interface);
 | 
			
		||||
 | 
			
		||||
    LoadConfigNANDSaveFile();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Shutdown() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetUsername(const std::u16string& name) {
 | 
			
		||||
    ASSERT(name.size() <= 10);
 | 
			
		||||
    UsernameBlock block{};
 | 
			
		||||
    name.copy(block.username, name.size());
 | 
			
		||||
    SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::u16string GetUsername() {
 | 
			
		||||
    UsernameBlock block;
 | 
			
		||||
    GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block);
 | 
			
		||||
 | 
			
		||||
    // the username string in the block isn't null-terminated,
 | 
			
		||||
    // so we need to find the end manually.
 | 
			
		||||
    std::u16string username(block.username, ARRAY_SIZE(block.username));
 | 
			
		||||
    const size_t pos = username.find(u'\0');
 | 
			
		||||
    if (pos != std::u16string::npos)
 | 
			
		||||
        username.erase(pos);
 | 
			
		||||
    return username;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetBirthday(u8 month, u8 day) {
 | 
			
		||||
    BirthdayBlock block = { month, day };
 | 
			
		||||
    SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::tuple<u8, u8> GetBirthday() {
 | 
			
		||||
    BirthdayBlock block;
 | 
			
		||||
    GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block);
 | 
			
		||||
    return std::make_tuple(block.month, block.day);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetSystemLanguage(SystemLanguage language) {
 | 
			
		||||
    u8 block = language;
 | 
			
		||||
    SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SystemLanguage GetSystemLanguage() {
 | 
			
		||||
    u8 block;
 | 
			
		||||
    GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block);
 | 
			
		||||
    return static_cast<SystemLanguage>(block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SetSoundOutputMode(SoundOutputMode mode) {
 | 
			
		||||
    u8 block = mode;
 | 
			
		||||
    SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SoundOutputMode GetSoundOutputMode() {
 | 
			
		||||
    u8 block;
 | 
			
		||||
    GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block);
 | 
			
		||||
    return static_cast<SoundOutputMode>(block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace CFG
 | 
			
		||||
} // namespace Service
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +36,14 @@ enum SystemLanguage {
 | 
			
		|||
    LANGUAGE_KO = 7,
 | 
			
		||||
    LANGUAGE_NL = 8,
 | 
			
		||||
    LANGUAGE_PT = 9,
 | 
			
		||||
    LANGUAGE_RU = 10
 | 
			
		||||
    LANGUAGE_RU = 10,
 | 
			
		||||
    LANGUAGE_TW = 11
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum SoundOutputMode {
 | 
			
		||||
    SOUND_MONO = 0,
 | 
			
		||||
    SOUND_STEREO = 1,
 | 
			
		||||
    SOUND_SURROUND = 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Block header in the config savedata file
 | 
			
		||||
| 
						 | 
				
			
			@ -177,6 +185,22 @@ void GetConfigInfoBlk2(Service::Interface* self);
 | 
			
		|||
 */
 | 
			
		||||
void GetConfigInfoBlk8(Service::Interface* self);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * CFG::SetConfigInfoBlk4 service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0 : 0x04020082 / 0x08020082
 | 
			
		||||
 *      1 : Block ID
 | 
			
		||||
 *      2 : Size
 | 
			
		||||
 *      3 : Descriptor for the output buffer
 | 
			
		||||
 *      4 : Output buffer pointer
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
 *  Note:
 | 
			
		||||
 *      The parameters order is different from GetConfigInfoBlk2/8's,
 | 
			
		||||
 *      where Block ID and Size are switched.
 | 
			
		||||
 */
 | 
			
		||||
void SetConfigInfoBlk4(Service::Interface* self);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * CFG::UpdateConfigNANDSavegame service function
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self);
 | 
			
		|||
 * @param output A pointer where we will write the read data
 | 
			
		||||
 * @returns ResultCode indicating the result of the operation, 0 on success
 | 
			
		||||
 */
 | 
			
		||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
 | 
			
		||||
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reads data from input and writes to a block with the specified id and flag
 | 
			
		||||
 * in the Config savegame buffer.
 | 
			
		||||
 * The input size must match exactly the size of the target block
 | 
			
		||||
 * @param block_id The id of the block we want to write
 | 
			
		||||
 * @param size The size of the block we want to write
 | 
			
		||||
 * @param flag The target block must have this flag set
 | 
			
		||||
 * @param input A pointer where we will read data and write to Config savegame buffer
 | 
			
		||||
 * @returns ResultCode indicating the result of the operation, 0 on success
 | 
			
		||||
 */
 | 
			
		||||
ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory.
 | 
			
		||||
| 
						 | 
				
			
			@ -236,11 +272,70 @@ ResultCode UpdateConfigNANDSavegame();
 | 
			
		|||
 */
 | 
			
		||||
ResultCode FormatConfig();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Open the config savegame file and load it to the memory buffer
 | 
			
		||||
 * @returns ResultCode indicating the result of the operation, 0 on success
 | 
			
		||||
 */
 | 
			
		||||
ResultCode LoadConfigNANDSaveFile();
 | 
			
		||||
 | 
			
		||||
/// Initialize the config service
 | 
			
		||||
void Init();
 | 
			
		||||
 | 
			
		||||
/// Shutdown the config service
 | 
			
		||||
void Shutdown();
 | 
			
		||||
 | 
			
		||||
// Utilities for frontend to set config data.
 | 
			
		||||
// Note: before calling these functions, LoadConfigNANDSaveFile should be called,
 | 
			
		||||
// and UpdateConfigNANDSavegame should be called after making changes to config data.
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the username in config savegame.
 | 
			
		||||
 * @param name the username to set. The maximum size is 10 in char16_t.
 | 
			
		||||
 */
 | 
			
		||||
void SetUsername(const std::u16string& name);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the username from config savegame.
 | 
			
		||||
 * @returns the username
 | 
			
		||||
 */
 | 
			
		||||
std::u16string GetUsername();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the profile birthday in config savegame.
 | 
			
		||||
 * @param month the month of birthday.
 | 
			
		||||
 * @param day the day of the birthday.
 | 
			
		||||
 */
 | 
			
		||||
void SetBirthday(u8 month, u8 day);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the profile birthday from the config savegame.
 | 
			
		||||
 * @returns a tuple of (month, day) of birthday
 | 
			
		||||
 */
 | 
			
		||||
std::tuple<u8, u8> GetBirthday();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the system language in config savegame.
 | 
			
		||||
 * @param language the system language to set.
 | 
			
		||||
 */
 | 
			
		||||
void SetSystemLanguage(SystemLanguage language);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the system language from config savegame.
 | 
			
		||||
 * @returns the system language
 | 
			
		||||
 */
 | 
			
		||||
SystemLanguage GetSystemLanguage();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sets the sound output mode in config savegame.
 | 
			
		||||
 * @param mode the sound output mode to set
 | 
			
		||||
 */
 | 
			
		||||
void SetSoundOutputMode(SoundOutputMode mode);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets the sound output mode from config savegame.
 | 
			
		||||
 * @returns the sound output mode
 | 
			
		||||
 */
 | 
			
		||||
SoundOutputMode GetSoundOutputMode();
 | 
			
		||||
 | 
			
		||||
} // namespace CFG
 | 
			
		||||
} // namespace Service
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		|||
    {0x000A0040, GetCountryCodeID,                     "GetCountryCodeID"},
 | 
			
		||||
    // cfg:i
 | 
			
		||||
    {0x04010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"},
 | 
			
		||||
    {0x04020082, nullptr,                              "SetConfigInfoBlk4"},
 | 
			
		||||
    {0x04020082, SetConfigInfoBlk4,                    "SetConfigInfoBlk4"},
 | 
			
		||||
    {0x04030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"},
 | 
			
		||||
    {0x04040042, nullptr,                              "GetLocalFriendCodeSeedData"},
 | 
			
		||||
    {0x04050000, nullptr,                              "GetLocalFriendCodeSeed"},
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		|||
    {0x04080042, nullptr,                              "SecureInfoGetSerialNo"},
 | 
			
		||||
    {0x04090000, nullptr,                              "UpdateConfigBlk00040003"},
 | 
			
		||||
    {0x08010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"},
 | 
			
		||||
    {0x08020082, nullptr,                              "SetConfigInfoBlk4"},
 | 
			
		||||
    {0x08020082, SetConfigInfoBlk4,                    "SetConfigInfoBlk4"},
 | 
			
		||||
    {0x08030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"},
 | 
			
		||||
    {0x080400C2, nullptr,                              "CreateConfigInfoBlk"},
 | 
			
		||||
    {0x08050000, nullptr,                              "DeleteConfigNANDSavefile"},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		|||
    {0x000A0040, GetCountryCodeID,                     "GetCountryCodeID"},
 | 
			
		||||
    // cfg:s
 | 
			
		||||
    {0x04010082, GetConfigInfoBlk8,                    "GetConfigInfoBlk8"},
 | 
			
		||||
    {0x04020082, nullptr,                              "SetConfigInfoBlk4"},
 | 
			
		||||
    {0x04020082, SetConfigInfoBlk4,                    "SetConfigInfoBlk4"},
 | 
			
		||||
    {0x04030000, UpdateConfigNANDSavegame,             "UpdateConfigNANDSavegame"},
 | 
			
		||||
    {0x04040042, nullptr,                              "GetLocalFriendCodeSeedData"},
 | 
			
		||||
    {0x04050000, nullptr,                              "GetLocalFriendCodeSeed"},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -259,7 +259,7 @@ using FileSys::ArchiveFactory;
 | 
			
		|||
 | 
			
		||||
/**
 | 
			
		||||
 * Map of registered archives, identified by id code. Once an archive is registered here, it is
 | 
			
		||||
 * never removed until the FS service is shut down.
 | 
			
		||||
 * never removed until UnregisterArchiveTypes is called.
 | 
			
		||||
 */
 | 
			
		||||
static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -520,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) {
 | 
			
		|||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Initialize archives
 | 
			
		||||
void ArchiveInit() {
 | 
			
		||||
    next_handle = 1;
 | 
			
		||||
 | 
			
		||||
    AddService(new FS::Interface);
 | 
			
		||||
 | 
			
		||||
void RegisterArchiveTypes() {
 | 
			
		||||
    // TODO(Subv): Add the other archive types (see here for the known types:
 | 
			
		||||
    // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -562,10 +557,23 @@ void ArchiveInit() {
 | 
			
		|||
    RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UnregisterArchiveTypes() {
 | 
			
		||||
    id_code_map.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Initialize archives
 | 
			
		||||
void ArchiveInit() {
 | 
			
		||||
    next_handle = 1;
 | 
			
		||||
 | 
			
		||||
    AddService(new FS::Interface);
 | 
			
		||||
 | 
			
		||||
    RegisterArchiveTypes();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Shutdown archives
 | 
			
		||||
void ArchiveShutdown() {
 | 
			
		||||
    handle_map.clear();
 | 
			
		||||
    id_code_map.clear();
 | 
			
		||||
    UnregisterArchiveTypes();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FS
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -235,5 +235,11 @@ void ArchiveInit();
 | 
			
		|||
/// Shutdown archives
 | 
			
		||||
void ArchiveShutdown();
 | 
			
		||||
 | 
			
		||||
/// Register all archive types
 | 
			
		||||
void RegisterArchiveTypes();
 | 
			
		||||
 | 
			
		||||
/// Unregister all archive types
 | 
			
		||||
void UnregisterArchiveTypes();
 | 
			
		||||
 | 
			
		||||
} // namespace FS
 | 
			
		||||
} // namespace Service
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue