diff options
| author | 2014-12-19 15:30:25 -0500 | |
|---|---|---|
| committer | 2014-12-21 16:38:59 -0500 | |
| commit | 4cd21b43c1e28933898c4a2e829555efe22ff12e (patch) | |
| tree | bff4da5013682afa238f56efdc6ffd0ff9e827bf | |
| parent | CFG:U: Add some data to the 0x00050005 config block. (diff) | |
| download | yuzu-4cd21b43c1e28933898c4a2e829555efe22ff12e.tar.gz yuzu-4cd21b43c1e28933898c4a2e829555efe22ff12e.tar.xz yuzu-4cd21b43c1e28933898c4a2e829555efe22ff12e.zip | |
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.
| -rw-r--r-- | src/core/file_sys/archive_systemsavedata.cpp | 2 | ||||
| -rw-r--r-- | 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 392c3cd39..b942864b2 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp | |||
| @@ -18,7 +18,7 @@ namespace FileSys { | |||
| 18 | 18 | ||
| 19 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) | 19 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) |
| 20 | : DiskArchive(Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), | 20 | : DiskArchive(Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), |
| 21 | static_cast<u32>(save_id & 0xFFFFFFFF), static_cast<u32>((save_id >> 31) & 0xFFFFFFFF))) { | 21 | static_cast<u32>(save_id & 0xFFFFFFFF), static_cast<u32>((save_id >> 32) & 0xFFFFFFFF))) { |
| 22 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); | 22 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); |
| 23 | } | 23 | } |
| 24 | 24 | ||
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp index 771575e29..827399cf9 100644 --- a/src/core/hle/service/cfg_u.cpp +++ b/src/core/hle/service/cfg_u.cpp | |||
| @@ -13,9 +13,20 @@ | |||
| 13 | 13 | ||
| 14 | namespace CFG_U { | 14 | namespace CFG_U { |
| 15 | 15 | ||
| 16 | enum SystemModel { | ||
| 17 | NINTENDO_3DS, | ||
| 18 | NINTENDO_3DS_XL, | ||
| 19 | NEW_NINTENDO_3DS, | ||
| 20 | NINTENDO_2DS, | ||
| 21 | NEW_NINTENDO_3DS_XL | ||
| 22 | }; | ||
| 23 | |||
| 16 | static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data; | 24 | static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data; |
| 17 | static const u64 CFG_SAVE_ID = 0x00010017; | 25 | static const u64 CFG_SAVE_ID = 0x00010017; |
| 18 | static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; | 26 | static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; |
| 27 | static const u32 CONSOLE_MODEL = NINTENDO_3DS_XL; | ||
| 28 | static const u32 CONFIG_SAVEFILE_SIZE = 0x8000; | ||
| 29 | static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer = { }; | ||
| 19 | 30 | ||
| 20 | /// TODO(Subv): Find out what this actually is | 31 | /// TODO(Subv): Find out what this actually is |
| 21 | /// Thanks Normmatt for providing this information | 32 | /// Thanks Normmatt for providing this information |
| @@ -24,14 +35,6 @@ static const u8 STEREO_CAMERA_SETTINGS[32] = { | |||
| 24 | 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0xA0, 0x40, 0xEC, 0x51, 0x5E, 0x42, 0x5C, 0x8F, 0xAC, 0x41 | 35 | 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0xA0, 0x40, 0xEC, 0x51, 0x5E, 0x42, 0x5C, 0x8F, 0xAC, 0x41 |
| 25 | }; | 36 | }; |
| 26 | 37 | ||
| 27 | enum SystemModel { | ||
| 28 | NINTENDO_3DS, | ||
| 29 | NINTENDO_3DS_XL, | ||
| 30 | NEW_NINTENDO_3DS, | ||
| 31 | NINTENDO_2DS, | ||
| 32 | NEW_NINTENDO_3DS_XL | ||
| 33 | }; | ||
| 34 | |||
| 35 | // TODO(Link Mauve): use a constexpr once MSVC starts supporting it. | 38 | // TODO(Link Mauve): use a constexpr once MSVC starts supporting it. |
| 36 | #define C(code) ((code)[0] | ((code)[1] << 8)) | 39 | #define C(code) ((code)[0] | ((code)[1] << 8)) |
| 37 | 40 | ||
| @@ -134,9 +137,11 @@ struct SaveFileConfig { | |||
| 134 | u16 total_entries; | 137 | u16 total_entries; |
| 135 | u16 data_entries_offset; | 138 | u16 data_entries_offset; |
| 136 | SaveConfigBlockEntry block_entries[1479]; | 139 | SaveConfigBlockEntry block_entries[1479]; |
| 140 | u32 unknown; | ||
| 137 | }; | 141 | }; |
| 138 | 142 | ||
| 139 | /* Reads a block with the specified id and flag from the Config savegame file | 143 | /** |
| 144 | * Reads a block with the specified id and flag from the Config savegame buffer | ||
| 140 | * and writes the output to output. | 145 | * and writes the output to output. |
| 141 | * The input size must match exactly the size of the requested block | 146 | * The input size must match exactly the size of the requested block |
| 142 | * TODO(Subv): This should actually be in some file common to the CFG process | 147 | * TODO(Subv): This should actually be in some file common to the CFG process |
| @@ -147,41 +152,128 @@ struct SaveFileConfig { | |||
| 147 | * @returns ResultCode indicating the result of the operation, 0 on success | 152 | * @returns ResultCode indicating the result of the operation, 0 on success |
| 148 | */ | 153 | */ |
| 149 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { | 154 | ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) { |
| 150 | FileSys::Mode mode; | 155 | // Read the header |
| 151 | mode.hex = 0; | 156 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); |
| 152 | mode.read_flag = 1; | ||
| 153 | FileSys::Path path("config"); | ||
| 154 | auto file = cfg_system_save_data->OpenFile(path, mode); | ||
| 155 | _dbg_assert_msg_(Service_CFG, file != nullptr, "Could not open the CFG service config file"); | ||
| 156 | SaveFileConfig config; | ||
| 157 | size_t read = file->Read(0, sizeof(SaveFileConfig), reinterpret_cast<u8*>(&config)); | ||
| 158 | 157 | ||
| 159 | if (read != sizeof(SaveFileConfig)) { | 158 | auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries), |
| 160 | LOG_CRITICAL(Service_CFG, "The config savefile is corrupted"); | ||
| 161 | return ResultCode(-1); // TODO(Subv): Find the correct error code | ||
| 162 | } | ||
| 163 | |||
| 164 | auto itr = std::find_if(std::begin(config.block_entries), std::end(config.block_entries), | ||
| 165 | [&](SaveConfigBlockEntry const& entry) { | 159 | [&](SaveConfigBlockEntry const& entry) { |
| 166 | return entry.block_id == block_id && entry.size == size && (entry.flags & flag); | 160 | return entry.block_id == block_id && entry.size == size && (entry.flags & flag); |
| 167 | }); | 161 | }); |
| 168 | 162 | ||
| 169 | if (itr == std::end(config.block_entries)) { | 163 | if (itr == std::end(config->block_entries)) { |
| 170 | LOG_TRACE(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag); | 164 | LOG_ERROR(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag); |
| 171 | return ResultCode(-1); // TODO(Subv): Find the correct error code | 165 | return ResultCode(-1); // TODO(Subv): Find the correct error code |
| 172 | } | 166 | } |
| 173 | 167 | ||
| 174 | // The data is located in the block header itself if the size is less than 4 bytes | 168 | // The data is located in the block header itself if the size is less than 4 bytes |
| 175 | if (itr->size <= 4) { | 169 | if (itr->size <= 4) |
| 176 | memcpy(output, &itr->offset_or_data, itr->size); | 170 | memcpy(output, &itr->offset_or_data, itr->size); |
| 177 | } else { | 171 | else |
| 178 | size_t data_read = file->Read(itr->offset_or_data, itr->size, output); | 172 | memcpy(output, &cfg_config_file_buffer[config->data_entries_offset + itr->offset_or_data], itr->size); |
| 179 | if (data_read != itr->size) { | 173 | |
| 180 | LOG_CRITICAL(Service_CFG, "The config savefile is corrupted"); | 174 | return RESULT_SUCCESS; |
| 181 | return ResultCode(-1); // TODO(Subv): Find the correct error code | 175 | } |
| 176 | |||
| 177 | /** | ||
| 178 | * Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory. | ||
| 179 | * The config savegame file in the filesystem is not updated. | ||
| 180 | * TODO(Subv): This should actually be in some file common to the CFG process | ||
| 181 | * @param block_id The id of the block we want to create | ||
| 182 | * @param size The size of the block we want to create | ||
| 183 | * @param flag The flags of the new block | ||
| 184 | * @param data A pointer containing the data we will write to the new block | ||
| 185 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 186 | */ | ||
| 187 | ResultCode CreateConfigInfoBlk(u32 block_id, u32 size, u32 flags, u8 const* data) { | ||
| 188 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 189 | // Insert the block header with offset 0 for now | ||
| 190 | config->block_entries[config->total_entries] = { block_id, 0, size, flags }; | ||
| 191 | if (size > 4) { | ||
| 192 | s32 total_entries = config->total_entries - 1; | ||
| 193 | u32 offset = 0; | ||
| 194 | // Perform a search to locate the next offset for the new data | ||
| 195 | while (total_entries >= 0) { | ||
| 196 | // Ignore the blocks that don't have a separate data offset | ||
| 197 | if (config->block_entries[total_entries].size <= 4) { | ||
| 198 | --total_entries; | ||
| 199 | continue; | ||
| 200 | } | ||
| 201 | |||
| 202 | offset = config->block_entries[total_entries].offset_or_data + | ||
| 203 | config->block_entries[total_entries].size; | ||
| 204 | break; | ||
| 182 | } | 205 | } |
| 206 | |||
| 207 | config->block_entries[config->total_entries].offset_or_data = offset; | ||
| 208 | |||
| 209 | // Write the data at the new offset | ||
| 210 | memcpy(&cfg_config_file_buffer[config->data_entries_offset + offset], data, size); | ||
| 211 | } else { | ||
| 212 | // The offset_or_data field in the header contains the data itself if it's 4 bytes or less | ||
| 213 | memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size); | ||
| 183 | } | 214 | } |
| 184 | 215 | ||
| 216 | ++config->total_entries; | ||
| 217 | return RESULT_SUCCESS; | ||
| 218 | } | ||
| 219 | |||
| 220 | /** | ||
| 221 | * Deletes the config savegame file from the filesystem, the buffer in memory is not affected | ||
| 222 | * TODO(Subv): This should actually be in some file common to the CFG process | ||
| 223 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 224 | */ | ||
| 225 | ResultCode DeleteConfigNANDSaveFile() { | ||
| 226 | FileSys::Path path("config"); | ||
| 227 | if (cfg_system_save_data->DeleteFile(path)) | ||
| 228 | return RESULT_SUCCESS; | ||
| 229 | return ResultCode(-1); // TODO(Subv): Find the right error code | ||
| 230 | } | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Writes the config savegame memory buffer to the config savegame file in the filesystem | ||
| 234 | * TODO(Subv): This should actually be in some file common to the CFG process | ||
| 235 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 236 | */ | ||
| 237 | ResultCode UpdateConfigNANDSavegame() { | ||
| 238 | FileSys::Mode mode; | ||
| 239 | mode.hex = 0; | ||
| 240 | mode.write_flag = 1; | ||
| 241 | mode.create_flag = 1; | ||
| 242 | FileSys::Path path("config"); | ||
| 243 | auto file = cfg_system_save_data->OpenFile(path, mode); | ||
| 244 | _dbg_assert_msg_(Service_CFG, file != nullptr, "could not open file"); | ||
| 245 | file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); | ||
| 246 | return RESULT_SUCCESS; | ||
| 247 | } | ||
| 248 | |||
| 249 | /** | ||
| 250 | * Re-creates the config savegame file in memory and the filesystem with the default blocks | ||
| 251 | * TODO(Subv): This should actually be in some file common to the CFG process | ||
| 252 | * @returns ResultCode indicating the result of the operation, 0 on success | ||
| 253 | */ | ||
| 254 | ResultCode FormatConfig() { | ||
| 255 | ResultCode res = DeleteConfigNANDSaveFile(); | ||
| 256 | if (!res.IsSuccess()) | ||
| 257 | return res; | ||
| 258 | // Delete the old data | ||
| 259 | std::fill(cfg_config_file_buffer.begin(), cfg_config_file_buffer.end(), 0); | ||
| 260 | // Create the header | ||
| 261 | SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data()); | ||
| 262 | config->data_entries_offset = 0x455C; | ||
| 263 | // Insert the default blocks | ||
| 264 | res = CreateConfigInfoBlk(0x00050005, 0x20, 0xE, STEREO_CAMERA_SETTINGS); | ||
| 265 | if (!res.IsSuccess()) | ||
| 266 | return res; | ||
| 267 | res = CreateConfigInfoBlk(0x00090001, 0x8, 0xE, reinterpret_cast<u8 const*>(&CONSOLE_UNIQUE_ID)); | ||
| 268 | if (!res.IsSuccess()) | ||
| 269 | return res; | ||
| 270 | res = CreateConfigInfoBlk(0x000F0004, 0x4, 0x8, reinterpret_cast<u8 const*>(&CONSOLE_MODEL)); | ||
| 271 | if (!res.IsSuccess()) | ||
| 272 | return res; | ||
| 273 | // Save the buffer to the file | ||
| 274 | res = UpdateConfigNANDSavegame(); | ||
| 275 | if (!res.IsSuccess()) | ||
| 276 | return res; | ||
| 185 | return RESULT_SUCCESS; | 277 | return RESULT_SUCCESS; |
| 186 | } | 278 | } |
| 187 | 279 | ||
| @@ -271,6 +363,8 @@ Interface::Interface() { | |||
| 271 | return; | 363 | return; |
| 272 | } | 364 | } |
| 273 | 365 | ||
| 366 | // TODO(Subv): All this code should be moved to cfg:i, | ||
| 367 | // it's only here because we do not currently emulate the lower level code that uses that service | ||
| 274 | // Try to open the file in read-only mode to check its existence | 368 | // Try to open the file in read-only mode to check its existence |
| 275 | FileSys::Mode mode; | 369 | FileSys::Mode mode; |
| 276 | mode.hex = 0; | 370 | mode.hex = 0; |
| @@ -282,30 +376,7 @@ Interface::Interface() { | |||
| 282 | if (file != nullptr) | 376 | if (file != nullptr) |
| 283 | return; | 377 | return; |
| 284 | 378 | ||
| 285 | mode.create_flag = 1; | 379 | FormatConfig(); |
| 286 | mode.write_flag = 1; | ||
| 287 | mode.read_flag = 0; | ||
| 288 | // Re-open the file in write-create mode | ||
| 289 | file = cfg_system_save_data->OpenFile(path, mode); | ||
| 290 | |||
| 291 | // Setup the default config file data header | ||
| 292 | SaveFileConfig config = { 3, 0, {} }; | ||
| 293 | u32 offset = sizeof(SaveFileConfig); | ||
| 294 | // Console-unique ID | ||
| 295 | config.block_entries[0] = { 0x00090001, offset, 0x8, 0xE }; | ||
| 296 | offset += 0x8; | ||
| 297 | // Stereo Camera Settings? | ||
| 298 | config.block_entries[1] = { 0x00050005, offset, 0x20, 0xE }; | ||
| 299 | offset += 0x20; | ||
| 300 | // System Model (Nintendo 3DS XL) | ||
| 301 | config.block_entries[2] = { 0x000F0004, NINTENDO_3DS_XL, 0x4, 0x8 }; | ||
| 302 | |||
| 303 | // Write the config file data header to the config file | ||
| 304 | file->Write(0, sizeof(SaveFileConfig), 1, reinterpret_cast<u8*>(&config)); | ||
| 305 | // Write the data itself | ||
| 306 | file->Write(config.block_entries[0].offset_or_data, 0x8, 1, | ||
| 307 | reinterpret_cast<u8 const*>(&CONSOLE_UNIQUE_ID)); | ||
| 308 | file->Write(config.block_entries[1].offset_or_data, 0x20, 1, STEREO_CAMERA_SETTINGS); | ||
| 309 | } | 380 | } |
| 310 | 381 | ||
| 311 | Interface::~Interface() { | 382 | Interface::~Interface() { |