From b82f6a990aa491432a77bb657a7b2a1b989444bf Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 18 Jun 2016 20:42:28 -0500 Subject: [PATCH] Core: Add a file open dialog and refactor logic around loading new games --- src/xenia/app/emulator_window.cc | 40 ++++++++++- src/xenia/app/emulator_window.h | 1 + src/xenia/app/xenia_main.cc | 48 ++++++------- src/xenia/emulator.cc | 118 ++++++++++++++----------------- src/xenia/emulator.h | 18 +++-- 5 files changed, 127 insertions(+), 98 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index e4030ca9e..5d2ec6970 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -20,6 +20,8 @@ #include "xenia/base/threading.h" #include "xenia/emulator.h" #include "xenia/gpu/graphics_system.h" + +#include "xenia/ui/file_picker.h" #include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_drawer.h" @@ -114,7 +116,6 @@ bool EmulatorWindow::Initialize() { // TODO: Spawn a new thread to do this. emulator()->RestoreFromFile(L"test.sav"); } break; - case 0x7A: { // VK_F11 ToggleFullscreen(); } break; @@ -145,6 +146,9 @@ bool EmulatorWindow::Initialize() { auto main_menu = MenuItem::Create(MenuItem::Type::kNormal); auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&File"); { + file_menu->AddChild( + MenuItem::Create(MenuItem::Type::kString, L"&Open", L"Ctrl+O", + std::bind(&EmulatorWindow::FileOpen, this))); file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, L"E&xit", L"Alt+F4", [this]() { window_->Close(); })); @@ -239,6 +243,40 @@ bool EmulatorWindow::Initialize() { return true; } +void EmulatorWindow::FileOpen() { + std::wstring 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(L"Select Content Package"); + file_picker->set_extensions({ + {L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"}, + {L"Disc Image (*.iso)", L"*.iso"}, + {L"Xbox Executable (*.xex)", L"*.xex"}, + //{ L"Content Package (*.xcp)", L"*.xcp" }, + {L"All Files (*.*)", L"*.*"}, + }); + if (file_picker->Show(window_->native_handle())) { + auto selected_files = file_picker->selected_files(); + if (!selected_files.empty()) { + path = selected_files[0]; + } + } + + if (!path.empty()) { + // Normalize the path and make absolute. + std::wstring abs_path = xe::to_absolute_path(path); + + auto result = emulator_->LaunchPath(abs_path); + if (XFAILED(result)) { + // TODO: Display a message box. + XELOGE("Failed to launch target: %.8X", result); + } + } +} + void EmulatorWindow::CpuTimeScalarReset() { Clock::set_guest_time_scalar(1.0); UpdateTitle(); diff --git a/src/xenia/app/emulator_window.h b/src/xenia/app/emulator_window.h index fa8db88b3..c493ef414 100644 --- a/src/xenia/app/emulator_window.h +++ b/src/xenia/app/emulator_window.h @@ -42,6 +42,7 @@ class EmulatorWindow { bool Initialize(); + void FileOpen(); void CpuTimeScalarReset(); void CpuTimeScalarSetHalf(); void CpuTimeScalarSetDouble(); diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 3bfd11cb4..be963f06d 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -10,9 +10,11 @@ #include #include "xenia/app/emulator_window.h" +#include "xenia/base/debugging.h" #include "xenia/base/logging.h" #include "xenia/base/main.h" #include "xenia/base/profiling.h" +#include "xenia/base/threading.h" #include "xenia/debug/ui/debug_window.h" #include "xenia/emulator.h" #include "xenia/ui/file_picker.h" @@ -159,6 +161,18 @@ int xenia_main(const std::vector& args) { }); } + auto evt = xe::threading::Event::CreateAutoResetEvent(false); + emulator->on_launch.AddListener([&]() { + emulator_window->UpdateTitle(); + evt->Set(); + }); + + bool exiting = false; + emulator_window->loop()->on_quit.AddListener([&](ui::UIEvent* e) { + exiting = true; + evt->Set(); + }); + // Grab path from the flag or unnamed argument. std::wstring path; if (!FLAGS_target.empty() || args.size() >= 2) { @@ -173,43 +187,23 @@ int xenia_main(const std::vector& args) { } } - // If no path passed, ask the user. - if (path.empty()) { - 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(L"Select Content Package"); - file_picker->set_extensions({ - {L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"}, - {L"Disc Image (*.iso)", L"*.iso"}, - {L"Xbox Executable (*.xex)", L"*.xex"}, - //{ L"Content Package (*.xcp)", L"*.xcp" }, - {L"All Files (*.*)", L"*.*"}, - }); - if (file_picker->Show(emulator->display_window()->native_handle())) { - auto selected_files = file_picker->selected_files(); - if (!selected_files.empty()) { - path = selected_files[0]; - } - } - } - if (!path.empty()) { // Normalize the path and make absolute. std::wstring abs_path = xe::to_absolute_path(path); - - result = emulator->LaunchPath(abs_path, - [&]() { emulator_window->UpdateTitle(); }); + result = emulator->LaunchPath(abs_path); if (XFAILED(result)) { XELOGE("Failed to launch target: %.8X", result); emulator.reset(); emulator_window.reset(); return 1; } + } - // Wait until we are exited. - emulator->display_window()->loop()->AwaitQuit(); + // Now, we're going to use the main thread to drive events related to + // emulation. + while (!exiting) { + xe::threading::Wait(evt.get(), false); + emulator->WaitUntilExit(); } debug_window.reset(); diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index a12569dcc..98e1d2a14 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -177,8 +177,7 @@ X_STATUS Emulator::Setup( return result; } -X_STATUS Emulator::LaunchPath(std::wstring path, - std::function on_launch) { +X_STATUS Emulator::LaunchPath(std::wstring path) { // Launch based on file type. // This is a silly guess based on file extension. auto last_slash = path.find_last_of(xe::kPathSeparator); @@ -188,19 +187,18 @@ X_STATUS Emulator::LaunchPath(std::wstring path, } if (last_dot == std::wstring::npos) { // Likely an STFS container. - return LaunchStfsContainer(path, on_launch); + return LaunchStfsContainer(path); } else if (path.substr(last_dot) == L".xex" || path.substr(last_dot) == L".elf") { // Treat as a naked xex file. - return LaunchXexFile(path, on_launch); + return LaunchXexFile(path); } else { // Assume a disc image. - return LaunchDiscImage(path, on_launch); + return LaunchDiscImage(path); } } -X_STATUS Emulator::LaunchXexFile(std::wstring path, - std::function on_launch) { +X_STATUS Emulator::LaunchXexFile(std::wstring path) { // We create a virtual filesystem pointing to its directory and symlink // that to the game filesystem. // e.g., /my/files/foo.xex will get a local fs at: @@ -232,11 +230,10 @@ X_STATUS Emulator::LaunchXexFile(std::wstring path, // Launch the game. std::string fs_path = "game:\\" + xe::to_string(file_name); - return CompleteLaunch(path, fs_path, on_launch); + return CompleteLaunch(path, fs_path); } -X_STATUS Emulator::LaunchDiscImage(std::wstring path, - std::function on_launch) { +X_STATUS Emulator::LaunchDiscImage(std::wstring path) { auto mount_path = "\\Device\\Cdrom0"; // Register the disc image in the virtual filesystem. @@ -255,11 +252,10 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path, file_system_->RegisterSymbolicLink("d:", mount_path); // Launch the game. - return CompleteLaunch(path, "game:\\default.xex", on_launch); + return CompleteLaunch(path, "game:\\default.xex"); } -X_STATUS Emulator::LaunchStfsContainer(std::wstring path, - std::function on_launch) { +X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { auto mount_path = "\\Device\\Cdrom0"; // Register the container in the virtual filesystem. @@ -278,7 +274,7 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path, file_system_->RegisterSymbolicLink("d:", mount_path); // Launch the game. - return CompleteLaunch(path, "game:\\default.xex", on_launch); + return CompleteLaunch(path, "game:\\default.xex"); } void Emulator::Pause() { @@ -301,7 +297,9 @@ void Emulator::Pause() { continue; } - thread->thread()->Suspend(nullptr); + if (thread->is_running()) { + thread->thread()->Suspend(nullptr); + } } XELOGD("! EMULATOR PAUSED !"); @@ -326,7 +324,9 @@ void Emulator::Resume() { continue; } - thread->thread()->Resume(nullptr); + if (thread->is_running()) { + thread->thread()->Resume(nullptr); + } } } @@ -485,68 +485,60 @@ void Emulator::WaitUntilExit() { break; } } + + on_exit(); } X_STATUS Emulator::CompleteLaunch(const std::wstring& path, - const std::string& module_path, - std::function on_launch) { + const std::string& module_path) { // Allow xam to request module loads. auto xam = kernel_state()->GetKernelModule("xam.xex"); - X_STATUS result = X_STATUS_SUCCESS; - auto next_module = module_path; - while (next_module != "") { - XELOGI("Launching module %s", next_module.c_str()); - auto module = kernel_state_->LoadUserModule(next_module.c_str()); - if (!module) { - XELOGE("Failed to load user module %S", path.c_str()); - return X_STATUS_NOT_FOUND; - } + XELOGI("Launching module %s", module_path.c_str()); + auto module = kernel_state_->LoadUserModule(module_path.c_str()); + if (!module) { + XELOGE("Failed to load user module %S", path.c_str()); + return X_STATUS_NOT_FOUND; + } - kernel_state_->SetExecutableModule(module); + // Grab the current title ID. + xex2_opt_execution_info* info = nullptr; + module->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &info); + if (info) { + title_id_ = info->title_id; + } - // Try and load the resource database (xex only). - if (module->title_id()) { - char title_id[9] = {0}; - std::sprintf(title_id, "%08X", module->title_id()); - uint32_t resource_data = 0; - uint32_t resource_size = 0; - if (XSUCCEEDED( - module->GetSection(title_id, &resource_data, &resource_size))) { - kernel::util::XdbfGameData db( - module->memory()->TranslateVirtual(resource_data), resource_size); - if (db.is_valid()) { - game_title_ = xe::to_wstring(db.title()); - auto icon_block = db.icon(); - if (icon_block) { - display_window_->SetIcon(icon_block.buffer, icon_block.size); - } + kernel_state_->SetExecutableModule(module); + + // Try and load the resource database (xex only). + if (module->title_id()) { + char title_id[9] = {0}; + std::sprintf(title_id, "%08X", module->title_id()); + uint32_t resource_data = 0; + uint32_t resource_size = 0; + if (XSUCCEEDED( + module->GetSection(title_id, &resource_data, &resource_size))) { + kernel::util::XdbfGameData db( + module->memory()->TranslateVirtual(resource_data), resource_size); + if (db.is_valid()) { + game_title_ = xe::to_wstring(db.title()); + auto icon_block = db.icon(); + if (icon_block) { + display_window_->SetIcon(icon_block.buffer, icon_block.size); } } } - - auto main_xthread = module->Launch(); - if (!main_xthread) { - return X_STATUS_UNSUCCESSFUL; - } - - on_launch(); - main_thread_ = main_xthread->thread(); - WaitUntilExit(); - - // Check xam and see if they want us to load another module. - auto& loader_data = xam->loader_data(); - next_module = loader_data.launch_path; - - // And blank out the launch path to avoid an infinite loop. - loader_data.launch_path = ""; } - if (result == 0) { - return X_STATUS_SUCCESS; - } else { + auto main_xthread = module->Launch(); + if (!main_xthread) { return X_STATUS_UNSUCCESSFUL; } + + on_launch(); + main_thread_ = main_xthread->thread(); + + return X_STATUS_SUCCESS; } } // namespace xe diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 05780d690..ab4ae5400 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -13,6 +13,7 @@ #include #include +#include "xenia/base/delegate.h" #include "xenia/base/exception_handler.h" #include "xenia/kernel/kernel_state.h" #include "xenia/memory.h" @@ -105,18 +106,17 @@ class Emulator { // 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. - X_STATUS LaunchPath(std::wstring path, std::function on_launch); + X_STATUS LaunchPath(std::wstring path); // Launches a game from a .xex file by mounting the containing folder as if it // was an extracted STFS container. - X_STATUS LaunchXexFile(std::wstring path, std::function on_launch); + X_STATUS LaunchXexFile(std::wstring path); // Launches a game from a disc image file (.iso, etc). - X_STATUS LaunchDiscImage(std::wstring path, std::function on_launch); + X_STATUS LaunchDiscImage(std::wstring path); // Launches a game from an STFS container file. - X_STATUS LaunchStfsContainer(std::wstring path, - std::function on_launch); + X_STATUS LaunchStfsContainer(std::wstring path); void Pause(); void Resume(); @@ -127,13 +127,16 @@ class Emulator { void WaitUntilExit(); + public: + xe::Delegate<> on_launch; + xe::Delegate<> on_exit; + private: static bool ExceptionCallbackThunk(Exception* ex, void* data); bool ExceptionCallback(Exception* ex); X_STATUS CompleteLaunch(const std::wstring& path, - const std::string& module_path, - std::function on_launch); + const std::string& module_path); std::wstring command_line_; std::wstring game_title_; @@ -152,6 +155,7 @@ class Emulator { std::unique_ptr kernel_state_; threading::Thread* main_thread_ = nullptr; + uint32_t title_id_ = 0; // Currently running title ID bool paused_ = false; bool restoring_ = false;