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.
This commit is contained in:
Subv 2014-12-19 15:30:25 -05:00
parent 462740278d
commit 4cd21b43c1
2 changed files with 130 additions and 59 deletions

View File

@ -18,7 +18,7 @@ namespace FileSys {
Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id)
: DiskArchive(Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), : DiskArchive(Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(),
static_cast<u32>(save_id & 0xFFFFFFFF), static_cast<u32>((save_id >> 31) & 0xFFFFFFFF))) { static_cast<u32>(save_id & 0xFFFFFFFF), static_cast<u32>((save_id >> 32) & 0xFFFFFFFF))) {
LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str());
} }

View File

@ -13,17 +13,6 @@
namespace CFG_U { namespace CFG_U {
static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data;
static const u64 CFG_SAVE_ID = 0x00010017;
static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
/// TODO(Subv): Find out what this actually is
/// Thanks Normmatt for providing this information
static const u8 STEREO_CAMERA_SETTINGS[32] = {
0x00, 0x00, 0x78, 0x42, 0x00, 0x80, 0x90, 0x43, 0x9A, 0x99, 0x99, 0x42, 0xEC, 0x51, 0x38, 0x42,
0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0xA0, 0x40, 0xEC, 0x51, 0x5E, 0x42, 0x5C, 0x8F, 0xAC, 0x41
};
enum SystemModel { enum SystemModel {
NINTENDO_3DS, NINTENDO_3DS,
NINTENDO_3DS_XL, NINTENDO_3DS_XL,
@ -32,6 +21,20 @@ enum SystemModel {
NEW_NINTENDO_3DS_XL NEW_NINTENDO_3DS_XL
}; };
static std::unique_ptr<FileSys::Archive_SystemSaveData> 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<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer = { };
/// TODO(Subv): Find out what this actually is
/// Thanks Normmatt for providing this information
static const u8 STEREO_CAMERA_SETTINGS[32] = {
0x00, 0x00, 0x78, 0x42, 0x00, 0x80, 0x90, 0x43, 0x9A, 0x99, 0x99, 0x42, 0xEC, 0x51, 0x38, 0x42,
0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0xA0, 0x40, 0xEC, 0x51, 0x5E, 0x42, 0x5C, 0x8F, 0xAC, 0x41
};
// TODO(Link Mauve): use a constexpr once MSVC starts supporting it. // TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
#define C(code) ((code)[0] | ((code)[1] << 8)) #define C(code) ((code)[0] | ((code)[1] << 8))
@ -134,9 +137,11 @@ struct SaveFileConfig {
u16 total_entries; u16 total_entries;
u16 data_entries_offset; u16 data_entries_offset;
SaveConfigBlockEntry block_entries[1479]; 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. * and writes the output to output.
* The input size must match exactly the size of the requested block * 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 * 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 * @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, u8* output) {
FileSys::Mode mode; // Read the header
mode.hex = 0; SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
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<u8*>(&config));
if (read != sizeof(SaveFileConfig)) { auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
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),
[&](SaveConfigBlockEntry const& entry) { [&](SaveConfigBlockEntry const& entry) {
return entry.block_id == block_id && entry.size == size && (entry.flags & flag); return entry.block_id == block_id && entry.size == size && (entry.flags & flag);
}); });
if (itr == std::end(config.block_entries)) { 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); 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 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 // 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); memcpy(output, &itr->offset_or_data, itr->size);
} else { else
size_t data_read = file->Read(itr->offset_or_data, itr->size, output); memcpy(output, &cfg_config_file_buffer[config->data_entries_offset + itr->offset_or_data], itr->size);
if (data_read != itr->size) {
LOG_CRITICAL(Service_CFG, "The config savefile is corrupted"); return RESULT_SUCCESS;
return ResultCode(-1); // TODO(Subv): Find the correct error code }
/**
* 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<SaveFileConfig*>(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<SaveFileConfig*>(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<u8 const*>(&CONSOLE_UNIQUE_ID));
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x000F0004, 0x4, 0x8, reinterpret_cast<u8 const*>(&CONSOLE_MODEL));
if (!res.IsSuccess())
return res;
// Save the buffer to the file
res = UpdateConfigNANDSavegame();
if (!res.IsSuccess())
return res;
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -271,6 +363,8 @@ Interface::Interface() {
return; 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 // Try to open the file in read-only mode to check its existence
FileSys::Mode mode; FileSys::Mode mode;
mode.hex = 0; mode.hex = 0;
@ -282,30 +376,7 @@ Interface::Interface() {
if (file != nullptr) if (file != nullptr)
return; return;
mode.create_flag = 1; FormatConfig();
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<u8*>(&config));
// Write the data itself
file->Write(config.block_entries[0].offset_or_data, 0x8, 1,
reinterpret_cast<u8 const*>(&CONSOLE_UNIQUE_ID));
file->Write(config.block_entries[1].offset_or_data, 0x20, 1, STEREO_CAMERA_SETTINGS);
} }
Interface::~Interface() { Interface::~Interface() {