From 4cd21b43c1e28933898c4a2e829555efe22ff12e Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 19 Dec 2014 15:30:25 -0500 Subject: CFG: Refactored how the config file works. It is now kept in memory as per 3dbrew, all updates happen on memory, then they can be saved using UpdateConfigNANDSavegame. --- src/core/file_sys/archive_systemsavedata.cpp | 2 +- src/core/hle/service/cfg_u.cpp | 181 +++++++++++++++++++-------- 2 files changed, 127 insertions(+), 56 deletions(-) diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 392c3cd3..b942864b 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -18,7 +18,7 @@ namespace FileSys { Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) : DiskArchive(Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), - static_cast(save_id & 0xFFFFFFFF), static_cast((save_id >> 31) & 0xFFFFFFFF))) { + static_cast(save_id & 0xFFFFFFFF), static_cast((save_id >> 32) & 0xFFFFFFFF))) { LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); } diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp index 771575e2..827399cf 100644 --- a/src/core/hle/service/cfg_u.cpp +++ b/src/core/hle/service/cfg_u.cpp @@ -13,9 +13,20 @@ namespace CFG_U { +enum SystemModel { + NINTENDO_3DS, + NINTENDO_3DS_XL, + NEW_NINTENDO_3DS, + NINTENDO_2DS, + NEW_NINTENDO_3DS_XL +}; + static std::unique_ptr cfg_system_save_data; static const u64 CFG_SAVE_ID = 0x00010017; static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; +static const u32 CONSOLE_MODEL = NINTENDO_3DS_XL; +static const u32 CONFIG_SAVEFILE_SIZE = 0x8000; +static std::array cfg_config_file_buffer = { }; /// TODO(Subv): Find out what this actually is /// Thanks Normmatt for providing this information @@ -24,14 +35,6 @@ static const u8 STEREO_CAMERA_SETTINGS[32] = { 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0xA0, 0x40, 0xEC, 0x51, 0x5E, 0x42, 0x5C, 0x8F, 0xAC, 0x41 }; -enum SystemModel { - NINTENDO_3DS, - NINTENDO_3DS_XL, - NEW_NINTENDO_3DS, - NINTENDO_2DS, - NEW_NINTENDO_3DS_XL -}; - // TODO(Link Mauve): use a constexpr once MSVC starts supporting it. #define C(code) ((code)[0] | ((code)[1] << 8)) @@ -134,9 +137,11 @@ struct SaveFileConfig { u16 total_entries; u16 data_entries_offset; SaveConfigBlockEntry block_entries[1479]; + u32 unknown; }; -/* Reads a block with the specified id and flag from the Config savegame file +/** + * Reads a block with the specified id and flag from the Config savegame buffer * and writes the output to output. * The input size must match exactly the size of the requested block * TODO(Subv): This should actually be in some file common to the CFG process @@ -147,41 +152,128 @@ struct SaveFileConfig { * @returns ResultCode indicating the result of the operation, 0 on success */ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { - FileSys::Mode mode; - mode.hex = 0; - mode.read_flag = 1; - FileSys::Path path("config"); - auto file = cfg_system_save_data->OpenFile(path, mode); - _dbg_assert_msg_(Service_CFG, file != nullptr, "Could not open the CFG service config file"); - SaveFileConfig config; - size_t read = file->Read(0, sizeof(SaveFileConfig), reinterpret_cast(&config)); + // Read the header + SaveFileConfig* config = reinterpret_cast(cfg_config_file_buffer.data()); - if (read != sizeof(SaveFileConfig)) { - LOG_CRITICAL(Service_CFG, "The config savefile is corrupted"); - return ResultCode(-1); // TODO(Subv): Find the correct error code - } - - auto itr = std::find_if(std::begin(config.block_entries), std::end(config.block_entries), + auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), [&](SaveConfigBlockEntry const& entry) { return entry.block_id == block_id && entry.size == size && (entry.flags & flag); }); - if (itr == std::end(config.block_entries)) { - LOG_TRACE(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag); + if (itr == std::end(config->block_entries)) { + LOG_ERROR(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag); return ResultCode(-1); // TODO(Subv): Find the correct error code } // The data is located in the block header itself if the size is less than 4 bytes - if (itr->size <= 4) { + if (itr->size <= 4) memcpy(output, &itr->offset_or_data, itr->size); - } else { - size_t data_read = file->Read(itr->offset_or_data, itr->size, output); - if (data_read != itr->size) { - LOG_CRITICAL(Service_CFG, "The config savefile is corrupted"); - return ResultCode(-1); // TODO(Subv): Find the correct error code + else + memcpy(output, &cfg_config_file_buffer[config->data_entries_offset + itr->offset_or_data], itr->size); + + return RESULT_SUCCESS; +} + +/** + * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. + * The config savegame file in the filesystem is not updated. + * TODO(Subv): This should actually be in some file common to the CFG process + * @param block_id The id of the block we want to create + * @param size The size of the block we want to create + * @param flag The flags of the new block + * @param data A pointer containing the data we will write to the new block + * @returns ResultCode indicating the result of the operation, 0 on success + */ +ResultCode CreateConfigInfoBlk(u32 block_id, u32 size, u32 flags, u8 const* data) { + SaveFileConfig* config = reinterpret_cast(cfg_config_file_buffer.data()); + // Insert the block header with offset 0 for now + config->block_entries[config->total_entries] = { block_id, 0, size, flags }; + if (size > 4) { + s32 total_entries = config->total_entries - 1; + u32 offset = 0; + // Perform a search to locate the next offset for the new data + while (total_entries >= 0) { + // Ignore the blocks that don't have a separate data offset + if (config->block_entries[total_entries].size <= 4) { + --total_entries; + continue; + } + + offset = config->block_entries[total_entries].offset_or_data + + config->block_entries[total_entries].size; + break; } + + config->block_entries[config->total_entries].offset_or_data = offset; + + // Write the data at the new offset + memcpy(&cfg_config_file_buffer[config->data_entries_offset + offset], data, size); + } else { + // The offset_or_data field in the header contains the data itself if it's 4 bytes or less + memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size); } + ++config->total_entries; + return RESULT_SUCCESS; +} + +/** + * Deletes the config savegame file from the filesystem, the buffer in memory is not affected + * TODO(Subv): This should actually be in some file common to the CFG process + * @returns ResultCode indicating the result of the operation, 0 on success + */ +ResultCode DeleteConfigNANDSaveFile() { + FileSys::Path path("config"); + if (cfg_system_save_data->DeleteFile(path)) + return RESULT_SUCCESS; + return ResultCode(-1); // TODO(Subv): Find the right error code +} + +/** + * Writes the config savegame memory buffer to the config savegame file in the filesystem + * TODO(Subv): This should actually be in some file common to the CFG process + * @returns ResultCode indicating the result of the operation, 0 on success + */ +ResultCode UpdateConfigNANDSavegame() { + FileSys::Mode mode; + mode.hex = 0; + mode.write_flag = 1; + mode.create_flag = 1; + FileSys::Path path("config"); + auto file = cfg_system_save_data->OpenFile(path, mode); + _dbg_assert_msg_(Service_CFG, file != nullptr, "could not open file"); + file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); + return RESULT_SUCCESS; +} + +/** + * Re-creates the config savegame file in memory and the filesystem with the default blocks + * TODO(Subv): This should actually be in some file common to the CFG process + * @returns ResultCode indicating the result of the operation, 0 on success + */ +ResultCode FormatConfig() { + ResultCode res = DeleteConfigNANDSaveFile(); + if (!res.IsSuccess()) + return res; + // Delete the old data + std::fill(cfg_config_file_buffer.begin(), cfg_config_file_buffer.end(), 0); + // Create the header + SaveFileConfig* config = reinterpret_cast(cfg_config_file_buffer.data()); + config->data_entries_offset = 0x455C; + // Insert the default blocks + res = CreateConfigInfoBlk(0x00050005, 0x20, 0xE, STEREO_CAMERA_SETTINGS); + if (!res.IsSuccess()) + return res; + res = CreateConfigInfoBlk(0x00090001, 0x8, 0xE, reinterpret_cast(&CONSOLE_UNIQUE_ID)); + if (!res.IsSuccess()) + return res; + res = CreateConfigInfoBlk(0x000F0004, 0x4, 0x8, reinterpret_cast(&CONSOLE_MODEL)); + if (!res.IsSuccess()) + return res; + // Save the buffer to the file + res = UpdateConfigNANDSavegame(); + if (!res.IsSuccess()) + return res; return RESULT_SUCCESS; } @@ -271,6 +363,8 @@ Interface::Interface() { return; } + // TODO(Subv): All this code should be moved to cfg:i, + // it's only here because we do not currently emulate the lower level code that uses that service // Try to open the file in read-only mode to check its existence FileSys::Mode mode; mode.hex = 0; @@ -282,30 +376,7 @@ Interface::Interface() { if (file != nullptr) return; - mode.create_flag = 1; - mode.write_flag = 1; - mode.read_flag = 0; - // Re-open the file in write-create mode - file = cfg_system_save_data->OpenFile(path, mode); - - // Setup the default config file data header - SaveFileConfig config = { 3, 0, {} }; - u32 offset = sizeof(SaveFileConfig); - // Console-unique ID - config.block_entries[0] = { 0x00090001, offset, 0x8, 0xE }; - offset += 0x8; - // Stereo Camera Settings? - config.block_entries[1] = { 0x00050005, offset, 0x20, 0xE }; - offset += 0x20; - // System Model (Nintendo 3DS XL) - config.block_entries[2] = { 0x000F0004, NINTENDO_3DS_XL, 0x4, 0x8 }; - - // Write the config file data header to the config file - file->Write(0, sizeof(SaveFileConfig), 1, reinterpret_cast(&config)); - // Write the data itself - file->Write(config.block_entries[0].offset_or_data, 0x8, 1, - reinterpret_cast(&CONSOLE_UNIQUE_ID)); - file->Write(config.block_entries[1].offset_or_data, 0x20, 1, STEREO_CAMERA_SETTINGS); + FormatConfig(); } Interface::~Interface() { -- cgit v1.2.3