Core: Add a file open dialog and refactor logic around loading new games
This commit is contained in:
parent
dcd71c1613
commit
b82f6a990a
|
@ -20,6 +20,8 @@
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/gpu/graphics_system.h"
|
#include "xenia/gpu/graphics_system.h"
|
||||||
|
|
||||||
|
#include "xenia/ui/file_picker.h"
|
||||||
#include "xenia/ui/imgui_dialog.h"
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
#include "xenia/ui/imgui_drawer.h"
|
#include "xenia/ui/imgui_drawer.h"
|
||||||
|
|
||||||
|
@ -114,7 +116,6 @@ bool EmulatorWindow::Initialize() {
|
||||||
// TODO: Spawn a new thread to do this.
|
// TODO: Spawn a new thread to do this.
|
||||||
emulator()->RestoreFromFile(L"test.sav");
|
emulator()->RestoreFromFile(L"test.sav");
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 0x7A: { // VK_F11
|
case 0x7A: { // VK_F11
|
||||||
ToggleFullscreen();
|
ToggleFullscreen();
|
||||||
} break;
|
} break;
|
||||||
|
@ -145,6 +146,9 @@ bool EmulatorWindow::Initialize() {
|
||||||
auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
|
auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
|
||||||
auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&File");
|
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",
|
file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, L"E&xit",
|
||||||
L"Alt+F4",
|
L"Alt+F4",
|
||||||
[this]() { window_->Close(); }));
|
[this]() { window_->Close(); }));
|
||||||
|
@ -239,6 +243,40 @@ bool EmulatorWindow::Initialize() {
|
||||||
return true;
|
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() {
|
void EmulatorWindow::CpuTimeScalarReset() {
|
||||||
Clock::set_guest_time_scalar(1.0);
|
Clock::set_guest_time_scalar(1.0);
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
|
|
|
@ -42,6 +42,7 @@ class EmulatorWindow {
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
|
void FileOpen();
|
||||||
void CpuTimeScalarReset();
|
void CpuTimeScalarReset();
|
||||||
void CpuTimeScalarSetHalf();
|
void CpuTimeScalarSetHalf();
|
||||||
void CpuTimeScalarSetDouble();
|
void CpuTimeScalarSetDouble();
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include "xenia/app/emulator_window.h"
|
#include "xenia/app/emulator_window.h"
|
||||||
|
#include "xenia/base/debugging.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/main.h"
|
#include "xenia/base/main.h"
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/debug/ui/debug_window.h"
|
#include "xenia/debug/ui/debug_window.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/ui/file_picker.h"
|
#include "xenia/ui/file_picker.h"
|
||||||
|
@ -159,6 +161,18 @@ int xenia_main(const std::vector<std::wstring>& 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.
|
// Grab path from the flag or unnamed argument.
|
||||||
std::wstring path;
|
std::wstring path;
|
||||||
if (!FLAGS_target.empty() || args.size() >= 2) {
|
if (!FLAGS_target.empty() || args.size() >= 2) {
|
||||||
|
@ -173,43 +187,23 @@ int xenia_main(const std::vector<std::wstring>& 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()) {
|
if (!path.empty()) {
|
||||||
// Normalize the path and make absolute.
|
// Normalize the path and make absolute.
|
||||||
std::wstring abs_path = xe::to_absolute_path(path);
|
std::wstring abs_path = xe::to_absolute_path(path);
|
||||||
|
result = emulator->LaunchPath(abs_path);
|
||||||
result = emulator->LaunchPath(abs_path,
|
|
||||||
[&]() { emulator_window->UpdateTitle(); });
|
|
||||||
if (XFAILED(result)) {
|
if (XFAILED(result)) {
|
||||||
XELOGE("Failed to launch target: %.8X", result);
|
XELOGE("Failed to launch target: %.8X", result);
|
||||||
emulator.reset();
|
emulator.reset();
|
||||||
emulator_window.reset();
|
emulator_window.reset();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until we are exited.
|
// Now, we're going to use the main thread to drive events related to
|
||||||
emulator->display_window()->loop()->AwaitQuit();
|
// emulation.
|
||||||
|
while (!exiting) {
|
||||||
|
xe::threading::Wait(evt.get(), false);
|
||||||
|
emulator->WaitUntilExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_window.reset();
|
debug_window.reset();
|
||||||
|
|
|
@ -177,8 +177,7 @@ X_STATUS Emulator::Setup(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::LaunchPath(std::wstring path,
|
X_STATUS Emulator::LaunchPath(std::wstring path) {
|
||||||
std::function<void()> on_launch) {
|
|
||||||
// Launch based on file type.
|
// Launch based on file type.
|
||||||
// This is a silly guess based on file extension.
|
// This is a silly guess based on file extension.
|
||||||
auto last_slash = path.find_last_of(xe::kPathSeparator);
|
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) {
|
if (last_dot == std::wstring::npos) {
|
||||||
// Likely an STFS container.
|
// Likely an STFS container.
|
||||||
return LaunchStfsContainer(path, on_launch);
|
return LaunchStfsContainer(path);
|
||||||
} else if (path.substr(last_dot) == L".xex" ||
|
} else if (path.substr(last_dot) == L".xex" ||
|
||||||
path.substr(last_dot) == L".elf") {
|
path.substr(last_dot) == L".elf") {
|
||||||
// Treat as a naked xex file.
|
// Treat as a naked xex file.
|
||||||
return LaunchXexFile(path, on_launch);
|
return LaunchXexFile(path);
|
||||||
} else {
|
} else {
|
||||||
// Assume a disc image.
|
// Assume a disc image.
|
||||||
return LaunchDiscImage(path, on_launch);
|
return LaunchDiscImage(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::LaunchXexFile(std::wstring path,
|
X_STATUS Emulator::LaunchXexFile(std::wstring path) {
|
||||||
std::function<void()> on_launch) {
|
|
||||||
// We create a virtual filesystem pointing to its directory and symlink
|
// We create a virtual filesystem pointing to its directory and symlink
|
||||||
// that to the game filesystem.
|
// that to the game filesystem.
|
||||||
// e.g., /my/files/foo.xex will get a local fs at:
|
// 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.
|
// Launch the game.
|
||||||
std::string fs_path = "game:\\" + xe::to_string(file_name);
|
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,
|
X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
|
||||||
std::function<void()> on_launch) {
|
|
||||||
auto mount_path = "\\Device\\Cdrom0";
|
auto mount_path = "\\Device\\Cdrom0";
|
||||||
|
|
||||||
// Register the disc image in the virtual filesystem.
|
// 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);
|
file_system_->RegisterSymbolicLink("d:", mount_path);
|
||||||
|
|
||||||
// Launch the game.
|
// Launch the game.
|
||||||
return CompleteLaunch(path, "game:\\default.xex", on_launch);
|
return CompleteLaunch(path, "game:\\default.xex");
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::LaunchStfsContainer(std::wstring path,
|
X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
|
||||||
std::function<void()> on_launch) {
|
|
||||||
auto mount_path = "\\Device\\Cdrom0";
|
auto mount_path = "\\Device\\Cdrom0";
|
||||||
|
|
||||||
// Register the container in the virtual filesystem.
|
// Register the container in the virtual filesystem.
|
||||||
|
@ -278,7 +274,7 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path,
|
||||||
file_system_->RegisterSymbolicLink("d:", mount_path);
|
file_system_->RegisterSymbolicLink("d:", mount_path);
|
||||||
|
|
||||||
// Launch the game.
|
// Launch the game.
|
||||||
return CompleteLaunch(path, "game:\\default.xex", on_launch);
|
return CompleteLaunch(path, "game:\\default.xex");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::Pause() {
|
void Emulator::Pause() {
|
||||||
|
@ -301,8 +297,10 @@ void Emulator::Pause() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thread->is_running()) {
|
||||||
thread->thread()->Suspend(nullptr);
|
thread->thread()->Suspend(nullptr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
XELOGD("! EMULATOR PAUSED !");
|
XELOGD("! EMULATOR PAUSED !");
|
||||||
}
|
}
|
||||||
|
@ -326,9 +324,11 @@ void Emulator::Resume() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thread->is_running()) {
|
||||||
thread->thread()->Resume(nullptr);
|
thread->thread()->Resume(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Emulator::SaveToFile(const std::wstring& path) {
|
bool Emulator::SaveToFile(const std::wstring& path) {
|
||||||
Pause();
|
Pause();
|
||||||
|
@ -485,24 +485,29 @@ void Emulator::WaitUntilExit() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
||||||
const std::string& module_path,
|
const std::string& module_path) {
|
||||||
std::function<void()> on_launch) {
|
|
||||||
// Allow xam to request module loads.
|
// Allow xam to request module loads.
|
||||||
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
||||||
|
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
XELOGI("Launching module %s", module_path.c_str());
|
||||||
auto next_module = module_path;
|
auto module = kernel_state_->LoadUserModule(module_path.c_str());
|
||||||
while (next_module != "") {
|
|
||||||
XELOGI("Launching module %s", next_module.c_str());
|
|
||||||
auto module = kernel_state_->LoadUserModule(next_module.c_str());
|
|
||||||
if (!module) {
|
if (!module) {
|
||||||
XELOGE("Failed to load user module %S", path.c_str());
|
XELOGE("Failed to load user module %S", path.c_str());
|
||||||
return X_STATUS_NOT_FOUND;
|
return X_STATUS_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
kernel_state_->SetExecutableModule(module);
|
kernel_state_->SetExecutableModule(module);
|
||||||
|
|
||||||
// Try and load the resource database (xex only).
|
// Try and load the resource database (xex only).
|
||||||
|
@ -532,21 +537,8 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
||||||
|
|
||||||
on_launch();
|
on_launch();
|
||||||
main_thread_ = main_xthread->thread();
|
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;
|
return X_STATUS_SUCCESS;
|
||||||
} else {
|
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "xenia/base/delegate.h"
|
||||||
#include "xenia/base/exception_handler.h"
|
#include "xenia/base/exception_handler.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
@ -105,18 +106,17 @@ class Emulator {
|
||||||
// Launches a game from the given file 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)
|
// This will attempt to infer the type of the given file (such as an iso, etc)
|
||||||
// using heuristics.
|
// using heuristics.
|
||||||
X_STATUS LaunchPath(std::wstring path, std::function<void()> on_launch);
|
X_STATUS LaunchPath(std::wstring path);
|
||||||
|
|
||||||
// Launches a game from a .xex file by mounting the containing folder as if it
|
// Launches a game from a .xex file by mounting the containing folder as if it
|
||||||
// was an extracted STFS container.
|
// was an extracted STFS container.
|
||||||
X_STATUS LaunchXexFile(std::wstring path, std::function<void()> on_launch);
|
X_STATUS LaunchXexFile(std::wstring path);
|
||||||
|
|
||||||
// Launches a game from a disc image file (.iso, etc).
|
// Launches a game from a disc image file (.iso, etc).
|
||||||
X_STATUS LaunchDiscImage(std::wstring path, std::function<void()> on_launch);
|
X_STATUS LaunchDiscImage(std::wstring path);
|
||||||
|
|
||||||
// Launches a game from an STFS container file.
|
// Launches a game from an STFS container file.
|
||||||
X_STATUS LaunchStfsContainer(std::wstring path,
|
X_STATUS LaunchStfsContainer(std::wstring path);
|
||||||
std::function<void()> on_launch);
|
|
||||||
|
|
||||||
void Pause();
|
void Pause();
|
||||||
void Resume();
|
void Resume();
|
||||||
|
@ -127,13 +127,16 @@ class Emulator {
|
||||||
|
|
||||||
void WaitUntilExit();
|
void WaitUntilExit();
|
||||||
|
|
||||||
|
public:
|
||||||
|
xe::Delegate<> on_launch;
|
||||||
|
xe::Delegate<> on_exit;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||||
bool ExceptionCallback(Exception* ex);
|
bool ExceptionCallback(Exception* ex);
|
||||||
|
|
||||||
X_STATUS CompleteLaunch(const std::wstring& path,
|
X_STATUS CompleteLaunch(const std::wstring& path,
|
||||||
const std::string& module_path,
|
const std::string& module_path);
|
||||||
std::function<void()> on_launch);
|
|
||||||
|
|
||||||
std::wstring command_line_;
|
std::wstring command_line_;
|
||||||
std::wstring game_title_;
|
std::wstring game_title_;
|
||||||
|
@ -152,6 +155,7 @@ class Emulator {
|
||||||
|
|
||||||
std::unique_ptr<kernel::KernelState> kernel_state_;
|
std::unique_ptr<kernel::KernelState> kernel_state_;
|
||||||
threading::Thread* main_thread_ = nullptr;
|
threading::Thread* main_thread_ = nullptr;
|
||||||
|
uint32_t title_id_ = 0; // Currently running title ID
|
||||||
|
|
||||||
bool paused_ = false;
|
bool paused_ = false;
|
||||||
bool restoring_ = false;
|
bool restoring_ = false;
|
||||||
|
|
Loading…
Reference in New Issue