From 010b59e81cee3459dc03ed40f814537c2f682b6c Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sat, 20 Aug 2022 20:41:08 +0200 Subject: [PATCH] [Emulator] Install Content: Create header for installed packages This fixes support for certain DLCs --- src/xenia/emulator.cc | 10 ++++++- src/xenia/kernel/xam/content_manager.cc | 2 +- .../vfs/devices/stfs_container_device.cc | 26 +++++++++++++++++ src/xenia/vfs/devices/stfs_container_device.h | 3 ++ src/xenia/vfs/vfs_dump.cc | 2 +- src/xenia/vfs/virtual_file_system.cc | 29 +++++++++++++++++-- src/xenia/vfs/virtual_file_system.h | 6 +++- 7 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 5d010c0d0..8aacd7f70 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -370,6 +370,10 @@ X_STATUS Emulator::InstallContentPackage(const std::filesystem::path& path) { content_root() / fmt::format("{:08X}", device->title_id()) / fmt::format("{:08X}", device->content_type()) / path.filename(); + std::filesystem::path header_path = + content_root() / fmt::format("{:08X}", device->title_id()) / "Headers" / + fmt::format("{:08X}", device->content_type()) / path.filename(); + if (std::filesystem::exists(installation_path)) { // TODO(Gliniak): Popup // Do you want to overwrite already existing data? @@ -380,7 +384,11 @@ X_STATUS Emulator::InstallContentPackage(const std::filesystem::path& path) { return error_code.value(); } } - return vfs::VirtualFileSystem::ExtractFiles(device.get(), installation_path); + + vfs::VirtualFileSystem::ExtractContentHeader(device.get(), header_path); + + return vfs::VirtualFileSystem::ExtractContentFiles(device.get(), + installation_path); } void Emulator::Pause() { diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index f70067e8a..6eabb3c8b 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -144,7 +144,7 @@ bool ContentManager::ContentExists(const XCONTENT_AGGREGATE_DATA& data) { X_RESULT ContentManager::WriteContentHeaderFile( const XCONTENT_AGGREGATE_DATA* data) { - auto title_id = fmt::format("{:8X}", kernel_state_->title_id()); + auto title_id = fmt::format("{:08X}", kernel_state_->title_id()); auto content_type = fmt::format("{:08X}", load_and_swap(&data->content_type)); auto header_path = diff --git a/src/xenia/vfs/devices/stfs_container_device.cc b/src/xenia/vfs/devices/stfs_container_device.cc index ad7e07ce2..702374855 100644 --- a/src/xenia/vfs/devices/stfs_container_device.cc +++ b/src/xenia/vfs/devices/stfs_container_device.cc @@ -856,5 +856,31 @@ bool StfsContainerDevice::ResolveFromFolder(const std::filesystem::path& path) { return true; } +kernel::xam::XCONTENT_AGGREGATE_DATA StfsContainerDevice::content_header() const { + kernel::xam::XCONTENT_AGGREGATE_DATA data; + + std::memset(&data, 0, sizeof(kernel::xam::XCONTENT_AGGREGATE_DATA)); + + data.device_id = 1; + data.title_id = header_.metadata.execution_info.title_id; + data.content_type = header_.metadata.content_type; + + auto name = header_.metadata.display_name(XLanguage::kEnglish); + if (name.empty()) { + // Find first filled language and use it. It might be incorrect, but meh + // until stfs support is done. + for (uint8_t i = 0; i < header_.metadata.kNumLanguagesV2; i++) { + name = header_.metadata.display_name((XLanguage)i); + if (!name.empty()) { + break; + } + } + } + + data.set_display_name(name); + + return data; +} + } // namespace vfs } // namespace xe diff --git a/src/xenia/vfs/devices/stfs_container_device.h b/src/xenia/vfs/devices/stfs_container_device.h index 65bd6d7d3..8c8577a96 100644 --- a/src/xenia/vfs/devices/stfs_container_device.h +++ b/src/xenia/vfs/devices/stfs_container_device.h @@ -18,6 +18,7 @@ #include "xenia/base/math.h" #include "xenia/base/string_util.h" #include "xenia/kernel/util/xex2_info.h" +#include "xenia/kernel/xam/content_manager.h" #include "xenia/vfs/device.h" #include "xenia/vfs/devices/stfs_xbox.h" @@ -86,6 +87,8 @@ class StfsContainerDevice : public Device { uint32_t title_id() const { return header_.metadata.execution_info.title_id; } XContentType content_type() const { return header_.metadata.content_type; } + kernel::xam::XCONTENT_AGGREGATE_DATA content_header() const; + private: const uint32_t kBlocksPerHashLevel[3] = {170, 28900, 4913000}; const uint32_t kEndOfChain = 0xFFFFFF; diff --git a/src/xenia/vfs/vfs_dump.cc b/src/xenia/vfs/vfs_dump.cc index 20994256d..b33a498bd 100644 --- a/src/xenia/vfs/vfs_dump.cc +++ b/src/xenia/vfs/vfs_dump.cc @@ -45,7 +45,7 @@ int vfs_dump_main(const std::vector& args) { XELOGE("Failed to initialize device"); return 1; } - return VirtualFileSystem::ExtractFiles(device.get(), base_path); + return VirtualFileSystem::ExtractContentFiles(device.get(), base_path); } } // namespace vfs diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index 7af2d3e6a..ba25260a6 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -8,6 +8,8 @@ */ #include "xenia/vfs/virtual_file_system.h" +#include "xenia/kernel/xam/content_manager.h" +#include "xenia/vfs/devices/stfs_container_device.h" #include "xenia/base/literals.h" #include "xenia/base/logging.h" @@ -310,8 +312,8 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, return result; } -X_STATUS VirtualFileSystem::ExtractFiles(Device* device, - std::filesystem::path base_path) { +X_STATUS VirtualFileSystem::ExtractContentFiles( + Device* device, std::filesystem::path base_path) { // Run through all the files, breadth-first style. std::queue queue; auto root = device->ResolvePath("/"); @@ -382,5 +384,28 @@ X_STATUS VirtualFileSystem::ExtractFiles(Device* device, return X_STATUS_SUCCESS; } + +void VirtualFileSystem::ExtractContentHeader(Device* device, + std::filesystem::path base_path) { + auto stfs_device = ((StfsContainerDevice*)device); + + if (!std::filesystem::exists(base_path.parent_path())) { + if (!std::filesystem::create_directories(base_path.parent_path())) { + return; + } + } + auto header_filename = base_path.filename().string() + ".header"; + auto header_path = base_path.parent_path() / header_filename; + xe::filesystem::CreateEmptyFile(header_path); + + if (std::filesystem::exists(header_path)) { + auto file = xe::filesystem::OpenFile(header_path, "wb"); + kernel::xam::XCONTENT_AGGREGATE_DATA data = stfs_device->content_header(); + data.set_file_name(base_path.filename().string()); + fwrite(&data, 1, sizeof(kernel::xam::XCONTENT_AGGREGATE_DATA), file); + fclose(file); + } + return; +} } // namespace vfs } // namespace xe diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h index 47ca5d947..360a23bd5 100644 --- a/src/xenia/vfs/virtual_file_system.h +++ b/src/xenia/vfs/virtual_file_system.h @@ -47,7 +47,11 @@ class VirtualFileSystem { bool is_non_directory, File** out_file, FileAction* out_action); - static X_STATUS ExtractFiles(Device* device, std::filesystem::path base_path); + static X_STATUS ExtractContentFiles(Device* device, + std::filesystem::path base_path); + static void ExtractContentHeader(Device* device, + std::filesystem::path base_path); + private: xe::global_critical_region global_critical_region_; std::vector> devices_;