From 6ad5c39fac80d53fd5325642ddb4c01ef832bb87 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Tue, 19 Jan 2021 23:04:18 +0100 Subject: [PATCH] [XAM/Content] Implemented Custom CON Header Handling --- src/xenia/kernel/xam/content_manager.cc | 82 ++++++++++++++++++++++--- src/xenia/kernel/xam/content_manager.h | 4 ++ src/xenia/kernel/xam/xam_content.cc | 3 + 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 883020ce9..7a7e14f3d 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -9,6 +9,7 @@ #include "xenia/kernel/xam/content_manager.h" +#include #include #include "third_party/fmt/include/fmt/format.h" @@ -26,6 +27,7 @@ namespace xam { static const char* kThumbnailFileName = "__thumbnail.png"; static const char* kGameUserContentDirName = "profile"; +static const char* kGameContentHeaderDirName = "Headers"; static int content_device_id_ = 0; @@ -95,15 +97,21 @@ std::vector ContentManager::ListContent( // Directories only. continue; } - XCONTENT_AGGREGATE_DATA content_data; - content_data.device_id = device_id; - content_data.content_type = content_type; - content_data.set_display_name(xe::path_to_utf16(file_info.name)); - content_data.set_file_name(xe::path_to_utf8(file_info.name)); - content_data.title_id = title_id; - result.emplace_back(std::move(content_data)); - } + XCONTENT_AGGREGATE_DATA content_data; + if (XSUCCEEDED( + ReadContentHeaderFile(xe::path_to_utf8(file_info.name) + ".header", + content_type, content_data))) { + result.emplace_back(std::move(content_data)); + } else { + content_data.device_id = device_id; + content_data.content_type = content_type; + content_data.set_display_name(xe::path_to_utf16(file_info.name)); + content_data.set_file_name(xe::path_to_utf8(file_info.name)); + content_data.title_id = title_id; + result.emplace_back(std::move(content_data)); + } + } return result; } @@ -126,6 +134,64 @@ bool ContentManager::ContentExists(const XCONTENT_AGGREGATE_DATA& data) { return std::filesystem::exists(path); } +X_RESULT ContentManager::WriteContentHeaderFile( + const XCONTENT_AGGREGATE_DATA* data) { + auto title_id = fmt::format("{:8X}", kernel_state_->title_id()); + auto content_type = + fmt::format("{:08X}", load_and_swap(&data->content_type)); + auto header_path = + root_path_ / title_id / kGameContentHeaderDirName / content_type; + + if (!std::filesystem::exists(header_path)) { + if (!std::filesystem::create_directories(header_path)) { + return X_STATUS_ACCESS_DENIED; + } + } + auto header_filename = data->file_name() + ".header"; + + xe::filesystem::CreateEmptyFile(header_path / header_filename); + + if (std::filesystem::exists(header_path / header_filename)) { + auto file = xe::filesystem::OpenFile(header_path / header_filename, "wb"); + fwrite(data, 1, sizeof(XCONTENT_AGGREGATE_DATA), file); + fclose(file); + return X_STATUS_SUCCESS; + } + return X_STATUS_NO_SUCH_FILE; +} + +X_RESULT ContentManager::ReadContentHeaderFile(const std::string_view file_name, + XContentType content_type, + XCONTENT_AGGREGATE_DATA& data) { + auto title_id = fmt::format("{:8X}", kernel_state_->title_id()); + auto content_type_directory = fmt::format("{:08X}", content_type); + auto header_file_path = root_path_ / title_id / kGameContentHeaderDirName / + content_type_directory / file_name; + constexpr uint32_t header_size = sizeof(XCONTENT_AGGREGATE_DATA); + + if (std::filesystem::exists(header_file_path)) { + auto file = xe::filesystem::OpenFile(header_file_path, "rb"); + + std::array buffer; + + auto file_size = std::filesystem::file_size(header_file_path); + if (file_size != header_size && file_size != sizeof(XCONTENT_DATA)) { + fclose(file); + return X_STATUS_END_OF_FILE; + } + + size_t result = fread(buffer.data(), 1, file_size, file); + if (result != file_size) { + fclose(file); + return X_STATUS_END_OF_FILE; + } + fclose(file); + std::memcpy(&data, buffer.data(), buffer.size()); + return X_STATUS_SUCCESS; + } + return X_STATUS_NO_SUCH_FILE; +} + X_RESULT ContentManager::CreateContent(const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data) { auto global_lock = global_critical_region_.Acquire(); diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 0db9f0cb5..039a95b4e 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -149,6 +149,10 @@ class ContentManager { const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data); bool ContentExists(const XCONTENT_AGGREGATE_DATA& data); + X_RESULT WriteContentHeaderFile(const XCONTENT_AGGREGATE_DATA* data_raw); + X_RESULT ReadContentHeaderFile(const std::string_view file_name, + XContentType content_type, + XCONTENT_AGGREGATE_DATA& data); X_RESULT CreateContent(const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data); X_RESULT OpenContent(const std::string_view root_name, diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 11a9cf52b..7ecb0cc22 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -204,6 +204,9 @@ dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name, if (create) { result = content_manager->CreateContent(root_name, content_data); + if (XSUCCEEDED(result)) { + content_manager->WriteContentHeaderFile(&content_data); + } } else if (open) { result = content_manager->OpenContent(root_name, content_data); }