From f21dbc66ba53ddfbd1da961a7fcc251c28e57942 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Thu, 19 May 2022 12:04:32 +0200 Subject: [PATCH] Implemented XamSwapDisc --- src/xenia/emulator.cc | 125 +++++++++++++++------------- src/xenia/emulator.h | 7 ++ src/xenia/kernel/xam/xam_content.cc | 60 +++++++++++++ 3 files changed, 134 insertions(+), 58 deletions(-) diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 0fbf74d18..8a7a129b6 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -40,6 +40,7 @@ #include "xenia/kernel/xbdm/xbdm_module.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_module.h" #include "xenia/memory.h" +#include "xenia/ui/file_picker.h" #include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_drawer.h" #include "xenia/ui/window.h" @@ -48,7 +49,6 @@ #include "xenia/vfs/devices/host_path_device.h" #include "xenia/vfs/devices/null_device.h" #include "xenia/vfs/devices/stfs_container_device.h" -#include "xenia/vfs/virtual_file_system.h" DEFINE_double(time_scalar, 1.0, "Scalar used to speed or slow time (1x, 2x, 1/2x, etc).", @@ -265,19 +265,56 @@ X_STATUS Emulator::TerminateTitle() { return X_STATUS_SUCCESS; } +const std::unique_ptr Emulator::CreateVfsDeviceBasedOnPath( + const std::filesystem::path& path, const std::string_view mount_path) { + if (!path.has_extension()) { + return std::make_unique(mount_path, path); + } + auto extension = xe::utf8::lower_ascii(xe::path_to_utf8(path.extension())); + if (extension == ".xex" || extension == ".elf" || extension == ".exe") { + auto parent_path = path.parent_path(); + return std::make_unique(mount_path, parent_path, true); + } else { + return std::make_unique(mount_path, path); + } +} + +X_STATUS Emulator::MountPath(const std::filesystem::path& path, + const std::string_view mount_path) { + auto device = CreateVfsDeviceBasedOnPath(path, mount_path); + if (!device->Initialize()) { + xe::FatalError("Unable to mount {}; file not found or corrupt."); + return X_STATUS_NO_SUCH_FILE; + } + if (!file_system_->RegisterDevice(std::move(device))) { + xe::FatalError("Unable to register {}."); + return X_STATUS_NO_SUCH_FILE; + } + + file_system_->UnregisterSymbolicLink("d:"); + file_system_->UnregisterSymbolicLink("game:"); + // Create symlinks to the device. + file_system_->RegisterSymbolicLink("game:", mount_path); + file_system_->RegisterSymbolicLink("d:", mount_path); + return X_STATUS_SUCCESS; +} + X_STATUS Emulator::LaunchPath(const std::filesystem::path& path) { // Launch based on file type. // This is a silly guess based on file extension. if (!path.has_extension()) { // Likely an STFS container. + MountPath(path, "\\Device\\Cdrom0"); return LaunchStfsContainer(path); }; auto extension = xe::utf8::lower_ascii(xe::path_to_utf8(path.extension())); if (extension == ".xex" || extension == ".elf" || extension == ".exe") { // Treat as a naked xex file. + MountPath(path, "\\Device\\Harddisk0\\Partition1"); return LaunchXexFile(path); } else { // Assume a disc image. + MountPath(path, "\\Device\\Cdrom0"); return LaunchDiscImage(path); } } @@ -289,26 +326,6 @@ X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) { // \\Device\\Harddisk0\\Partition1 // and then get that symlinked to game:\, so // -> game:\foo.xex - - auto mount_path = "\\Device\\Harddisk0\\Partition1"; - - // Register the local directory in the virtual filesystem. - auto parent_path = path.parent_path(); - auto device = - std::make_unique(mount_path, parent_path, true); - if (!device->Initialize()) { - XELOGE("Unable to scan host path"); - return X_STATUS_NO_SUCH_FILE; - } - if (!file_system_->RegisterDevice(std::move(device))) { - XELOGE("Unable to register host path"); - return X_STATUS_NO_SUCH_FILE; - } - - // Create symlinks to the device. - file_system_->RegisterSymbolicLink("game:", mount_path); - file_system_->RegisterSymbolicLink("d:", mount_path); - // Get just the filename (foo.xex). auto file_name = path.filename(); @@ -318,47 +335,11 @@ X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) { } X_STATUS Emulator::LaunchDiscImage(const std::filesystem::path& path) { - auto mount_path = "\\Device\\Cdrom0"; - - // Register the disc image in the virtual filesystem. - auto device = std::make_unique(mount_path, path); - if (!device->Initialize()) { - xe::FatalError("Unable to mount disc image; file not found or corrupt."); - return X_STATUS_NO_SUCH_FILE; - } - if (!file_system_->RegisterDevice(std::move(device))) { - xe::FatalError("Unable to register disc image."); - return X_STATUS_NO_SUCH_FILE; - } - - // Create symlinks to the device. - file_system_->RegisterSymbolicLink("game:", mount_path); - file_system_->RegisterSymbolicLink("d:", mount_path); - - // Launch the game. auto module_path(FindLaunchModule()); return CompleteLaunch(path, module_path); } X_STATUS Emulator::LaunchStfsContainer(const std::filesystem::path& path) { - auto mount_path = "\\Device\\Cdrom0"; - - // Register the container in the virtual filesystem. - auto device = std::make_unique(mount_path, path); - if (!device->Initialize()) { - xe::FatalError( - "Unable to mount STFS container; file not found or corrupt."); - return X_STATUS_NO_SUCH_FILE; - } - if (!file_system_->RegisterDevice(std::move(device))) { - xe::FatalError("Unable to register STFS container."); - return X_STATUS_NO_SUCH_FILE; - } - - file_system_->RegisterSymbolicLink("game:", mount_path); - file_system_->RegisterSymbolicLink("d:", mount_path); - - // Launch the game. auto module_path(FindLaunchModule()); return CompleteLaunch(path, module_path); } @@ -530,9 +511,38 @@ void Emulator::LaunchNextTitle() { auto xam = kernel_state()->GetKernelModule("xam.xex"); auto next_title = xam->loader_data().launch_path; + // Swap disk doesn't require reloading + // This function should be purged? CompleteLaunch("", next_title); } +const std::filesystem::path Emulator::GetNewDiscPath( + std::string window_message) { + std::filesystem::path path = ""; + + auto file_picker = xe::ui::FilePicker::Create(); + file_picker->set_mode(ui::FilePicker::Mode::kOpen); + file_picker->set_type(ui::FilePicker::Type::kFile); + file_picker->set_multi_selection(false); + file_picker->set_title(!window_message.empty() ? window_message + : "Select Content Package"); + file_picker->set_extensions({ + {"Supported Files", "*.iso;*.xex;*.xcp;*.*"}, + {"Disc Image (*.iso)", "*.iso"}, + {"Xbox Executable (*.xex)", "*.xex"}, + {"All Files (*.*)", "*.*"}, + }); + + if (file_picker->Show( + kernel_state()->emulator()->display_window()->native_handle())) { + auto selected_files = file_picker->selected_files(); + if (!selected_files.empty()) { + path = selected_files[0]; + } + } + return path; +} + bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) { return reinterpret_cast(data)->ExceptionCallback(ex); } @@ -590,7 +600,6 @@ bool Emulator::ExceptionCallback(Exception* ex) { context->v[i].u32[0], context->v[i].u32[1], context->v[i].u32[2], context->v[i].u32[3]); } - // Display a dialog telling the user the guest has crashed. if (display_window_ && imgui_drawer_) { display_window_->app_context().CallInUIThreadSynchronous([this]() { diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 96d81d00a..fe2efc6a2 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -21,6 +21,7 @@ #include "xenia/base/exception_handler.h" #include "xenia/kernel/kernel_state.h" #include "xenia/memory.h" +#include "xenia/vfs/device.h" #include "xenia/vfs/virtual_file_system.h" #include "xenia/xbox.h" @@ -171,6 +172,11 @@ class Emulator { // Terminates the currently running title. X_STATUS TerminateTitle(); +const std::unique_ptr CreateVfsDeviceBasedOnPath( + const std::filesystem::path& path, const std::string_view mount_path); + + X_STATUS MountPath(const std::filesystem::path& path, + const std::string_view mount_path); // Launches a game from the given file path. // This will attempt to infer the type of the given file (such as an iso, etc) // using heuristics. @@ -196,6 +202,7 @@ class Emulator { // The game can request another title to be loaded. bool TitleRequested(); void LaunchNextTitle(); + const std::filesystem::path GetNewDiscPath(std::string window_message = ""); void WaitUntilExit(); diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 11a9cf52b..0df27e9e5 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -11,9 +11,12 @@ #include "xenia/base/math.h" #include "xenia/base/string_util.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_content_device.h" #include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h" +#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" #include "xenia/kernel/xenumerator.h" #include "xenia/xbox.h" @@ -420,6 +423,63 @@ dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr, } DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented); +typedef struct { + xe::be stringTitlePtr; + xe::be stringTextPtr; + xe::be stringBtnMsgPtr; +} X_SWAPDISC_ERROR_MESSAGE; +static_assert_size(X_SWAPDISC_ERROR_MESSAGE, 12); + +dword_result_t XamSwapDisc(dword_t disc_number, + pointer_t completion_handle, + pointer_t error_message) { + + xex2_opt_execution_info* info = nullptr; + kernel_state()->GetExecutableModule()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, + &info); + + if (info->disc_number > info->disc_count) { + return X_ERROR_INVALID_PARAMETER; + } + + auto completion_event = [completion_handle]() -> void { + auto kevent = xboxkrnl::xeKeSetEvent(completion_handle, 1, 0); + + // Release the completion handle + auto object = + XObject::GetNativeObject(kernel_state(), completion_handle); + if (object) { + object->Retain(); + } + }; + + if (info->disc_number == disc_number) { + completion_event(); + return X_ERROR_SUCCESS; + } + + auto filesystem = kernel_state()->file_system(); + auto mount_path = "\\Device\\LauncherData"; + + if (filesystem->ResolvePath(mount_path) != NULL) { + filesystem->UnregisterDevice(mount_path); + } + + std::u16string text_message = xe::load_and_swap( + kernel_state()->memory()->TranslateVirtual(error_message->stringTextPtr)); + + const std::filesystem::path new_disc_path = + kernel_state()->emulator()->GetNewDiscPath(xe::to_utf8(text_message)); + XELOGI("GetNewDiscPath returned path {}.", new_disc_path.string().c_str()); + + // TODO(Gliniak): Implement checking if inserted file is requested one + kernel_state()->emulator()->MountPath(new_disc_path, mount_path); + completion_event(); + + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamSwapDisc, kContent, kSketchy); + } // namespace xam } // namespace kernel } // namespace xe