From 10af7060034b37e45a37cf2c76d7db112bef9cc1 Mon Sep 17 00:00:00 2001 From: emoose Date: Thu, 1 Nov 2018 18:26:24 +0000 Subject: [PATCH 1/4] [Kernel] ContentManager: add support for STFS packages, title_id override Normally ContentManager resolves package paths by using kernel_state_->title_id() as the title ID, but if we want to use ContentManager for TU's that won't work, since the XEX won't be fully loaded into kernel_state_->executable_module_ yet. We can get around this by allowing the title ID used by ContentManager to be overridden, this should also help us support games that load packages from other title IDs. --- src/xenia/kernel/xam/content_manager.cc | 72 +++++++++++++------------ src/xenia/kernel/xam/content_manager.h | 7 +++ 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 4b5306e39..f6b157eaa 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -14,8 +14,10 @@ #include "xenia/base/filesystem.h" #include "xenia/base/string.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/user_module.h" #include "xenia/kernel/xobject.h" #include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/stfs_container_device.h" namespace xe { namespace kernel { @@ -33,8 +35,19 @@ ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name, std::to_string(++content_device_id_) + "\\"; auto fs = kernel_state_->file_system(); - auto device = - std::make_unique(device_path_, package_path, false); + + std::unique_ptr device; + + // If this isn't a folder try mounting as STFS package + // Otherwise mount as a local host path + if (!filesystem::IsFolder(package_path)) { + device = + std::make_unique(device_path_, package_path); + } else { + device = std::make_unique(device_path_, package_path, + false); + } + device->Initialize(); fs->RegisterDevice(std::move(device)); fs->RegisterSymbolicLink(root_name_ + ":", device_path_); @@ -52,37 +65,27 @@ ContentManager::ContentManager(KernelState* kernel_state, ContentManager::~ContentManager() = default; -std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { - wchar_t title_id[9] = L"00000000"; - std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id()); - - std::wstring type_name; - switch (content_type) { - case 1: - // Save games. - type_name = L"00000001"; - break; - case 2: - // DLC from the marketplace. - type_name = L"00000002"; - break; - case 3: - // Publisher content? - type_name = L"00000003"; - break; - case 0x000D0000: - // ??? - type_name = L"000D0000"; - break; - default: - assert_unhandled_case(data.content_type); - return nullptr; +uint32_t ContentManager::title_id() { + if (title_id_override_) { + return title_id_override_; } + if (!kernel_state_->GetExecutableModule()) { + return -1; + } + return kernel_state_->title_id(); +} + +std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { + wchar_t title_id_str[9] = L"00000000"; + std::swprintf(title_id_str, 9, L"%.8X", title_id()); + + wchar_t content_type_str[9] = L"00000000"; + std::swprintf(content_type_str, 9, L"%.8X", content_type); // Package root path: // content_root/title_id/type_name/ - auto package_root = - xe::join_paths(root_path_, xe::join_paths(title_id, type_name)); + auto package_root = xe::join_paths( + root_path_, xe::join_paths(title_id_str, content_type_str)); return package_root + xe::kWPathSeparator; } @@ -92,7 +95,6 @@ std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { auto package_root = ResolvePackageRoot(data.content_type); auto package_path = xe::join_paths(package_root, xe::to_wstring(data.file_name)); - package_path += xe::kPathSeparator; return package_path; } @@ -105,10 +107,6 @@ std::vector ContentManager::ListContent(uint32_t device_id, auto package_root = ResolvePackageRoot(content_type); auto file_infos = xe::filesystem::ListFiles(package_root); for (const auto& file_info : file_infos) { - if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) { - // Directories only. - continue; - } XCONTENT_DATA content_data; content_data.device_id = device_id; content_data.content_type = content_type; @@ -245,7 +243,11 @@ X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) { auto package_path = ResolvePackagePath(data); if (xe::filesystem::PathExists(package_path)) { - xe::filesystem::DeleteFolder(package_path); + if (xe::filesystem::IsFolder(package_path)) { + xe::filesystem::DeleteFolder(package_path); + } else { + // TODO: delete STFS package? + } return X_ERROR_SUCCESS; } else { return X_ERROR_FILE_NOT_FOUND; diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 9a0bd1948..437d88588 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -85,7 +85,11 @@ class ContentManager { std::vector buffer); X_RESULT DeleteContent(const XCONTENT_DATA& data); + void SetTitleIdOverride(uint32_t title_id) { title_id_override_ = title_id; } + private: + uint32_t title_id(); + std::wstring ResolvePackageRoot(uint32_t content_type); std::wstring ResolvePackagePath(const XCONTENT_DATA& data); @@ -95,6 +99,9 @@ class ContentManager { // TODO(benvanik): remove use of global lock, it's bad here! xe::global_critical_region global_critical_region_; std::unordered_map open_packages_; + + uint32_t title_id_override_; // can be used for games/apps that request + // content for other IDs }; } // namespace xam From 708bee2b7df9536dea9f16565ceac7015725dc2f Mon Sep 17 00:00:00 2001 From: emoose Date: Thu, 1 Nov 2018 18:30:34 +0000 Subject: [PATCH 2/4] [Kernel] UserModule: try searching for & mounting TU packages to load XEXP patches from. This uses ContentManager to look for title updates, updates should be inside Content\[TitleID]\000B0000\ The updates can either be made up of an STFS package, or a folder containing extracted update files. The update will then be mounted under update:\, so games that rely on loading updated assets from the TU should also work with it. --- src/xenia/kernel/user_module.cc | 47 ++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 644b8c315..d630ab669 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -19,6 +19,7 @@ #include "xenia/emulator.h" #include "xenia/kernel/xfile.h" #include "xenia/kernel/xthread.h" +#include "xenia/vfs/devices/stfs_container_device.h" namespace xe { namespace kernel { @@ -49,9 +50,11 @@ uint32_t UserModule::title_id() const { X_STATUS UserModule::LoadFromFile(std::string path) { X_STATUS result = X_STATUS_UNSUCCESSFUL; + auto file_system = kernel_state()->file_system(); + // Resolve the file to open. // TODO(benvanik): make this code shared? - auto fs_entry = kernel_state()->file_system()->ResolvePath(path); + auto fs_entry = file_system->ResolvePath(path); if (!fs_entry) { XELOGE("File not found: %s", path.c_str()); return X_STATUS_NO_SUCH_FILE; @@ -100,8 +103,46 @@ X_STATUS UserModule::LoadFromFile(std::string path) { return result; } - // Search for xexp patch file - auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p"); + auto module_path = fs_entry->path(); + + auto content_manager = kernel_state()->content_manager(); + + if (!file_system->IsSymbolicLink("update:")) { + // update:\\ path isn't symlinked, try searching for an update package + + xex2_opt_execution_info* exec_info = 0; + xex_module()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &exec_info); + + if (exec_info) { + content_manager->SetTitleIdOverride(exec_info->title_id); + + auto update_packages = content_manager->ListContent( + 0, (uint32_t)vfs::StfsContentType::kInstaller); + + for (auto& update : update_packages) { + auto result = content_manager->OpenContent("update", update); + + if (!file_system->ResolvePath("update:\\" + module_path + "p")) { + // XEXP/DLLP doesn't exist in this package, lets just close it + content_manager->CloseContent("update"); + continue; + } else { + // XEXP/DLLP found, break out of package loop + // TODO: verify XEXP/DLLP works first? + break; + } + } + } + } + + // Unset content_manager title ID override + content_manager->SetTitleIdOverride(0); + + // First try checking update:\ root for patch, otherwise try same path as XEX + auto patch_entry = file_system->ResolvePath("update:\\" + module_path + "p"); + if (!patch_entry) { + patch_entry = file_system->ResolvePath(path_ + "p"); + } if (patch_entry) { auto patch_path = patch_entry->absolute_path(); From 49966a36c1475548fe18dfb503e74a13e2c69e77 Mon Sep 17 00:00:00 2001 From: emoose Date: Fri, 2 Nov 2018 01:08:34 +0000 Subject: [PATCH 3/4] [Kernel] ContentManager: add more checks for folder-packages Fixes problems with folder packages not being created --- src/xenia/kernel/xam/content_manager.cc | 10 +++++++++- src/xenia/kernel/xam/content_manager.h | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index f6b157eaa..546c116cb 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -40,7 +40,8 @@ ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name, // If this isn't a folder try mounting as STFS package // Otherwise mount as a local host path - if (!filesystem::IsFolder(package_path)) { + if (filesystem::PathExists(package_path) && + !filesystem::IsFolder(package_path)) { device = std::make_unique(device_path_, package_path); } else { @@ -95,6 +96,13 @@ std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { auto package_root = ResolvePackageRoot(data.content_type); auto package_path = xe::join_paths(package_root, xe::to_wstring(data.file_name)); + + // Add slash to end of path if this is a folder + // (or package doesn't exist, meaning we're creating a new folder) + if (!xe::filesystem::PathExists(package_path) || + xe::filesystem::IsFolder(package_path)) { + package_path += xe::kPathSeparator; + } return package_path; } diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 437d88588..bfedf9b44 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -100,8 +100,8 @@ class ContentManager { xe::global_critical_region global_critical_region_; std::unordered_map open_packages_; - uint32_t title_id_override_; // can be used for games/apps that request - // content for other IDs + uint32_t title_id_override_ = + 0; // can be used for games/apps that request content for other IDs }; } // namespace xam From a5d5f635e1afb4c0b81059ae87eb53c39689db83 Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 5 Mar 2019 18:11:33 +0000 Subject: [PATCH 4/4] [Kernel] UserModule: minor cleanup --- src/xenia/kernel/user_module.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 18835d956..477b61fae 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -108,9 +108,9 @@ X_STATUS UserModule::LoadFromFile(std::string path) { if (!FLAGS_xex_apply_patches) { return LoadXexContinue(); } - + auto module_path = fs_entry->path(); - + auto content_manager = kernel_state()->content_manager(); if (!file_system->IsSymbolicLink("update:")) {