diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 2d070a65a..597ebbfe3 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" DEFINE_bool(xex_apply_patches, true, "Apply XEX patches.", "Kernel"); @@ -51,9 +52,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; @@ -105,26 +108,66 @@ X_STATUS UserModule::LoadFromFile(std::string path) { if (cvars::xex_apply_patches) { // Search for xexp patch file auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p"); + } + + auto module_path = fs_entry->path(); - if (patch_entry) { - auto patch_path = patch_entry->absolute_path(); + auto content_manager = kernel_state()->content_manager(); - XELOGI("Loading XEX patch from %s", patch_path.c_str()); + if (!file_system->IsSymbolicLink("update:")) { + // update:\\ path isn't symlinked, try searching for an update package - auto patch_module = object_ref(new UserModule(kernel_state_)); - result = patch_module->LoadFromFile(patch_path); - if (!result) { - result = patch_module->xex_module()->ApplyPatch(xex_module()); - if (result) { - XELOGE("Failed to apply XEX patch, code: %d", result); + 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; } - } else { - XELOGE("Failed to load XEX patch, code: %d", result); } + } + } + // 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(); + + XELOGI("Loading XEX patch from %s", patch_path.c_str()); + + auto patch_module = object_ref(new UserModule(kernel_state_)); + result = patch_module->LoadFromFile(patch_path); + if (!result) { + result = patch_module->xex_module()->ApplyPatch(xex_module()); if (result) { - return X_STATUS_UNSUCCESSFUL; + XELOGE("Failed to apply XEX patch, code: %d", result); } + } else { + XELOGE("Failed to load XEX patch, code: %d", result); + } + + if (result) { + return X_STATUS_UNSUCCESSFUL; } } diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 2ca34f716..e09defeda 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 { @@ -35,8 +37,20 @@ 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::PathExists(package_path) && + !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_); @@ -54,37 +68,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; } @@ -94,7 +98,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)); - package_path += xe::kPathSeparator; + + // 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; } @@ -107,10 +117,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; @@ -247,7 +253,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 179939b80..a8c652644 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -86,7 +86,11 @@ class ContentManager { X_RESULT DeleteContent(const XCONTENT_DATA& data); std::wstring ResolveGameUserContentPath(); + 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); @@ -96,6 +100,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_ = + 0; // can be used for games/apps that request content for other IDs }; } // namespace xam