diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 0fbf74d18..4f6f5823a 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -751,13 +751,25 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, // Allow xam to request module loads. auto xam = kernel_state()->GetKernelModule("xam.xex"); - XELOGI("Launching module {}", module_path); + XELOGI("Loading module {}", module_path); auto module = kernel_state_->LoadUserModule(module_path); if (!module) { XELOGE("Failed to load user module {}", xe::path_to_utf8(path)); return X_STATUS_NOT_FOUND; } + X_RESULT result = kernel_state_->ApplyTitleUpdate(module); + if (XFAILED(result)) { + XELOGE("Failed to apply title update! Cannot run module {}", + xe::path_to_utf8(path)); + return result; + } + + result = kernel_state_->FinishLoadingUserModule(module); + if (XFAILED(result)) { + XELOGE("Failed to initialize user module {}", xe::path_to_utf8(path)); + return result; + } // Grab the current title ID. xex2_opt_execution_info* info = nullptr; module->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &info); diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 7d60aac64..538daf345 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -28,6 +28,8 @@ #include "xenia/kernel/xobject.h" #include "xenia/kernel/xthread.h" +DEFINE_bool(apply_title_update, true, "Apply title updates.", "Kernel"); + namespace xe { namespace kernel { @@ -404,7 +406,16 @@ object_ref KernelState::LoadUserModule( // Putting into the listing automatically retains. user_modules_.push_back(module); } + return module; +} +X_RESULT KernelState::FinishLoadingUserModule( + const object_ref module, bool call_entry) { + // TODO(Gliniak): Apply custom patches here + X_RESULT result = module->LoadContinue(); + if (XFAILED(result)) { + return result; + } module->Dump(); if (module->is_dll_module() && module->entry_point() && call_entry) { @@ -419,8 +430,55 @@ object_ref KernelState::LoadUserModule( processor()->Execute(thread_state, module->entry_point(), args, xe::countof(args)); } + return result; +} - return module; +X_RESULT KernelState::ApplyTitleUpdate(const object_ref module) { + X_RESULT result = X_STATUS_SUCCESS; + if (!cvars::apply_title_update) { + return result; + } + + std::vector tu_list = + content_manager()->ListContent(1, xe::XContentType::kInstaller, + module->title_id()); + + if (tu_list.empty()) { + return result; + } + + uint32_t disc_number = -1; + if (module->is_multi_disc_title()) { + disc_number = module->disc_number(); + } + // TODO(Gliniak): Support for selecting from multiple TUs + const xam::XCONTENT_AGGREGATE_DATA& title_update = tu_list.front(); + X_RESULT open_status = + content_manager()->OpenContent("UPDATE", title_update, disc_number); + + std::string resolved_path = ""; + file_system()->FindSymbolicLink("UPDATE:", resolved_path); + xe::vfs::Entry* patch_entry = kernel_state()->file_system()->ResolvePath( + resolved_path + "default.xexp"); + + if (patch_entry) { + const std::string patch_path = patch_entry->absolute_path(); + XELOGI("Loading XEX patch from {}", patch_path); + auto patch_module = object_ref(new UserModule(this)); + + result = patch_module->LoadFromFile(patch_path); + if (result != X_STATUS_SUCCESS) { + XELOGE("Failed to load XEX patch, code: {}", result); + return X_STATUS_UNSUCCESSFUL; + } + + result = patch_module->xex_module()->ApplyPatch(module->xex_module()); + if (result != X_STATUS_SUCCESS) { + XELOGE("Failed to apply XEX patch, code: {}", result); + return X_STATUS_UNSUCCESSFUL; + } + } + return result; } void KernelState::UnloadUserModule(const object_ref& module, diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 796d2dada..7fdc74ce1 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -134,6 +134,8 @@ class KernelState { void SetExecutableModule(object_ref module); object_ref LoadUserModule(const std::string_view name, bool call_entry = true); + X_RESULT FinishLoadingUserModule(const object_ref module, + bool call_entry = true); void UnloadUserModule(const object_ref& module, bool call_entry = true); @@ -150,6 +152,7 @@ class KernelState { return object_ref(reinterpret_cast(module.release())); } + X_RESULT ApplyTitleUpdate(const object_ref module); // Terminates a title: Unloads all modules, and kills all guest threads. // This DOES NOT RETURN if called from a guest thread! void TerminateTitle(); diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index a85a919c6..b59410032 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -20,8 +20,6 @@ #include "xenia/kernel/xfile.h" #include "xenia/kernel/xthread.h" -DEFINE_bool(xex_apply_patches, true, "Apply XEX patches.", "Kernel"); - namespace xe { namespace kernel { @@ -34,20 +32,38 @@ uint32_t UserModule::title_id() const { if (module_format_ != kModuleFormatXex) { return 0; } - auto header = xex_header(); - for (uint32_t i = 0; i < header->header_count; i++) { - auto& opt_header = header->headers[i]; - if (opt_header.key == XEX_HEADER_EXECUTION_INFO) { - auto opt_header_ptr = - reinterpret_cast(header) + opt_header.offset; - auto opt_exec_info = - reinterpret_cast(opt_header_ptr); - return static_cast(opt_exec_info->title_id); - } + + xex2_opt_execution_info* opt_exec_info = nullptr; + if (xex_module()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &opt_exec_info)) { + return static_cast(opt_exec_info->title_id); } return 0; } +uint32_t UserModule::disc_number() const { + if (module_format_ != kModuleFormatXex) { + return 1; + } + + xex2_opt_execution_info* opt_exec_info = nullptr; + if (xex_module()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &opt_exec_info)) { + return static_cast(opt_exec_info->disc_number); + } + return 1; +} + +bool UserModule::is_multi_disc_title() const { + if (module_format_ != kModuleFormatXex) { + return false; + } + + xex2_opt_execution_info* opt_exec_info = nullptr; + if (xex_module()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &opt_exec_info)) { + return opt_exec_info->disc_count > 1; + } + return false; +} + X_STATUS UserModule::LoadFromFile(const std::string_view path) { X_STATUS result = X_STATUS_UNSUCCESSFUL; @@ -97,38 +113,7 @@ X_STATUS UserModule::LoadFromFile(const std::string_view path) { file->Destroy(); } - // Only XEX returns X_STATUS_PENDING - if (result != X_STATUS_PENDING) { - return result; - } - - if (cvars::xex_apply_patches) { - // Search for xexp patch file - auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p"); - - if (patch_entry) { - auto patch_path = patch_entry->absolute_path(); - - XELOGI("Loading XEX patch from {}", patch_path); - - 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: {}", result); - } - } else { - XELOGE("Failed to load XEX patch, code: {}", result); - } - - if (result) { - return X_STATUS_UNSUCCESSFUL; - } - } - } - - return LoadXexContinue(); + return result; } X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { @@ -194,7 +179,7 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { return X_STATUS_SUCCESS; } -X_STATUS UserModule::LoadXexContinue() { +X_STATUS UserModule::LoadContinue() { // LoadXexContinue: finishes loading XEX after a patch has been applied (or // patch wasn't found) diff --git a/src/xenia/kernel/user_module.h b/src/xenia/kernel/user_module.h index f904f8a4f..c1b8d3716 100644 --- a/src/xenia/kernel/user_module.h +++ b/src/xenia/kernel/user_module.h @@ -58,6 +58,9 @@ class UserModule : public XModule { uint32_t guest_xex_header() const { return guest_xex_header_; } // The title ID in the xex header or 0 if this is not a xex. uint32_t title_id() const; + uint32_t disc_number() const; + bool is_multi_disc_title() const; + bool is_executable() const { return processor_module_->is_executable(); } bool is_dll_module() const { return is_dll_module_; } @@ -66,6 +69,7 @@ class UserModule : public XModule { X_STATUS LoadFromFile(const std::string_view path); X_STATUS LoadFromMemory(const void* addr, const size_t length); + X_STATUS LoadContinue(); X_STATUS Unload(); uint32_t GetProcAddressByOrdinal(uint16_t ordinal) override; @@ -96,8 +100,6 @@ class UserModule : public XModule { const std::string_view path); private: - X_STATUS LoadXexContinue(); - std::string name_; std::string path_; diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 883020ce9..5d9ac81e3 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -71,11 +71,18 @@ std::filesystem::path ContentManager::ResolvePackageRoot( } std::filesystem::path ContentManager::ResolvePackagePath( - const XCONTENT_AGGREGATE_DATA& data) { + const XCONTENT_AGGREGATE_DATA& data, const uint32_t disc_number) { // Content path: // content_root/title_id/content_type/data_file_name/ auto package_root = ResolvePackageRoot(data.content_type, data.title_id); - return package_root / xe::to_path(data.file_name()); + std::string disc_directory = ""; + std::filesystem::path package_path = + package_root / xe::to_path(data.file_name()); + + if (disc_number != -1) { + package_path /= fmt::format("disc{:03}", disc_number); + } + return package_path; } std::vector ContentManager::ListContent( @@ -108,8 +115,9 @@ std::vector ContentManager::ListContent( } std::unique_ptr ContentManager::ResolvePackage( - const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data) { - auto package_path = ResolvePackagePath(data); + const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data, + const uint32_t disc_number) { + auto package_path = ResolvePackagePath(data, disc_number); if (!std::filesystem::exists(package_path)) { return nullptr; } @@ -154,7 +162,8 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name, } X_RESULT ContentManager::OpenContent(const std::string_view root_name, - const XCONTENT_AGGREGATE_DATA& data) { + const XCONTENT_AGGREGATE_DATA& data, + const uint32_t disc_number) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -162,14 +171,14 @@ X_RESULT ContentManager::OpenContent(const std::string_view root_name, return X_ERROR_ALREADY_EXISTS; } - auto package_path = ResolvePackagePath(data); + auto package_path = ResolvePackagePath(data, disc_number); if (!std::filesystem::exists(package_path)) { // Does not exist, must be created. return X_ERROR_FILE_NOT_FOUND; } // Open package. - auto package = ResolvePackage(root_name, data); + auto package = ResolvePackage(root_name, data, disc_number); assert_not_null(package); open_packages_.insert({string_key::create(root_name), package.release()}); diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 0db9f0cb5..94c6911fc 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -146,13 +146,15 @@ class ContentManager { uint32_t title_id = -1); std::unique_ptr ResolvePackage( - const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data); + const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data, + const uint32_t disc_number = -1); bool ContentExists(const 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, - const XCONTENT_AGGREGATE_DATA& data); + const XCONTENT_AGGREGATE_DATA& data, + const uint32_t disc_number = -1); X_RESULT CloseContent(const std::string_view root_name); X_RESULT GetContentThumbnail(const XCONTENT_AGGREGATE_DATA& data, std::vector* buffer); @@ -166,7 +168,8 @@ class ContentManager { private: std::filesystem::path ResolvePackageRoot(XContentType content_type, uint32_t title_id = -1); - std::filesystem::path ResolvePackagePath(const XCONTENT_AGGREGATE_DATA& data); + std::filesystem::path ResolvePackagePath(const XCONTENT_AGGREGATE_DATA& data, + const uint32_t disc_number = -1); KernelState* kernel_state_; std::filesystem::path root_path_; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index b17e4b2a2..42d2584f6 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -97,6 +97,7 @@ dword_result_t XexLoadImage_entry(lpstring_t module_name, dword_t module_flags, // Not found; attempt to load as a user module. auto user_module = kernel_state()->LoadUserModule(module_name.value()); if (user_module) { + kernel_state()->FinishLoadingUserModule(user_module); // Give up object ownership, this reference will be released by the last // XexUnloadImage call auto user_module_raw = user_module.release();