Merge remote-tracking branch 'GliniakRepo/xam_swap_disc' into canary_experimental
This commit is contained in:
commit
5ce75a1479
|
@ -40,6 +40,7 @@
|
||||||
#include "xenia/kernel/xbdm/xbdm_module.h"
|
#include "xenia/kernel/xbdm/xbdm_module.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.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"
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
|
@ -48,7 +49,6 @@
|
||||||
#include "xenia/vfs/devices/host_path_device.h"
|
#include "xenia/vfs/devices/host_path_device.h"
|
||||||
#include "xenia/vfs/devices/null_device.h"
|
#include "xenia/vfs/devices/null_device.h"
|
||||||
#include "xenia/vfs/devices/stfs_container_device.h"
|
#include "xenia/vfs/devices/stfs_container_device.h"
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
|
||||||
|
|
||||||
DEFINE_double(time_scalar, 1.0,
|
DEFINE_double(time_scalar, 1.0,
|
||||||
"Scalar used to speed or slow time (1x, 2x, 1/2x, etc).",
|
"Scalar used to speed or slow time (1x, 2x, 1/2x, etc).",
|
||||||
|
@ -267,19 +267,56 @@ X_STATUS Emulator::TerminateTitle() {
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::unique_ptr<vfs::Device> Emulator::CreateVfsDeviceBasedOnPath(
|
||||||
|
const std::filesystem::path& path, const std::string_view mount_path) {
|
||||||
|
if (!path.has_extension()) {
|
||||||
|
return std::make_unique<vfs::StfsContainerDevice>(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<vfs::HostPathDevice>(mount_path, parent_path, true);
|
||||||
|
} else {
|
||||||
|
return std::make_unique<vfs::DiscImageDevice>(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) {
|
X_STATUS Emulator::LaunchPath(const std::filesystem::path& path) {
|
||||||
// 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.
|
||||||
if (!path.has_extension()) {
|
if (!path.has_extension()) {
|
||||||
// Likely an STFS container.
|
// Likely an STFS container.
|
||||||
|
MountPath(path, "\\Device\\Cdrom0");
|
||||||
return LaunchStfsContainer(path);
|
return LaunchStfsContainer(path);
|
||||||
};
|
};
|
||||||
auto extension = xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
auto extension = xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
||||||
if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
|
if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
|
||||||
// Treat as a naked xex file.
|
// Treat as a naked xex file.
|
||||||
|
MountPath(path, "\\Device\\Harddisk0\\Partition1");
|
||||||
return LaunchXexFile(path);
|
return LaunchXexFile(path);
|
||||||
} else {
|
} else {
|
||||||
// Assume a disc image.
|
// Assume a disc image.
|
||||||
|
MountPath(path, "\\Device\\Cdrom0");
|
||||||
return LaunchDiscImage(path);
|
return LaunchDiscImage(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,26 +328,6 @@ X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) {
|
||||||
// \\Device\\Harddisk0\\Partition1
|
// \\Device\\Harddisk0\\Partition1
|
||||||
// and then get that symlinked to game:\, so
|
// and then get that symlinked to game:\, so
|
||||||
// -> game:\foo.xex
|
// -> 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<vfs::HostPathDevice>(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).
|
// Get just the filename (foo.xex).
|
||||||
auto file_name = path.filename();
|
auto file_name = path.filename();
|
||||||
|
|
||||||
|
@ -320,47 +337,11 @@ X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::LaunchDiscImage(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<vfs::DiscImageDevice>(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());
|
auto module_path(FindLaunchModule());
|
||||||
return CompleteLaunch(path, module_path);
|
return CompleteLaunch(path, module_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::LaunchStfsContainer(const std::filesystem::path& 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<vfs::StfsContainerDevice>(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());
|
auto module_path(FindLaunchModule());
|
||||||
return CompleteLaunch(path, module_path);
|
return CompleteLaunch(path, module_path);
|
||||||
}
|
}
|
||||||
|
@ -532,9 +513,38 @@ void Emulator::LaunchNextTitle() {
|
||||||
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
||||||
auto next_title = xam->loader_data().launch_path;
|
auto next_title = xam->loader_data().launch_path;
|
||||||
|
|
||||||
|
// Swap disk doesn't require reloading
|
||||||
|
// This function should be purged?
|
||||||
CompleteLaunch("", next_title);
|
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) {
|
bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) {
|
||||||
return reinterpret_cast<Emulator*>(data)->ExceptionCallback(ex);
|
return reinterpret_cast<Emulator*>(data)->ExceptionCallback(ex);
|
||||||
}
|
}
|
||||||
|
@ -592,7 +602,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[0], context->v[i].u32[1], context->v[i].u32[2],
|
||||||
context->v[i].u32[3]);
|
context->v[i].u32[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display a dialog telling the user the guest has crashed.
|
// Display a dialog telling the user the guest has crashed.
|
||||||
if (display_window_ && imgui_drawer_) {
|
if (display_window_ && imgui_drawer_) {
|
||||||
display_window_->app_context().CallInUIThreadSynchronous([this]() {
|
display_window_->app_context().CallInUIThreadSynchronous([this]() {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
#include "xenia/patcher/patcher.h"
|
#include "xenia/patcher/patcher.h"
|
||||||
|
#include "xenia/vfs/device.h"
|
||||||
#include "xenia/vfs/virtual_file_system.h"
|
#include "xenia/vfs/virtual_file_system.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
|
@ -174,6 +175,11 @@ class Emulator {
|
||||||
// Terminates the currently running title.
|
// Terminates the currently running title.
|
||||||
X_STATUS TerminateTitle();
|
X_STATUS TerminateTitle();
|
||||||
|
|
||||||
|
const std::unique_ptr<vfs::Device> 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.
|
// 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.
|
||||||
|
@ -199,6 +205,7 @@ class Emulator {
|
||||||
// The game can request another title to be loaded.
|
// The game can request another title to be loaded.
|
||||||
bool TitleRequested();
|
bool TitleRequested();
|
||||||
void LaunchNextTitle();
|
void LaunchNextTitle();
|
||||||
|
const std::filesystem::path GetNewDiscPath(std::string window_message = "");
|
||||||
|
|
||||||
void WaitUntilExit();
|
void WaitUntilExit();
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,12 @@
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/string_util.h"
|
#include "xenia/base/string_util.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
#include "xenia/kernel/user_module.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xam/xam_content_device.h"
|
#include "xenia/kernel/xam/xam_content_device.h"
|
||||||
#include "xenia/kernel/xam/xam_private.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/kernel/xenumerator.h"
|
||||||
#include "xenia/xbox.h"
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
|
@ -433,6 +436,63 @@ dword_result_t XamContentDeleteInternal_entry(lpvoid_t content_data_ptr,
|
||||||
}
|
}
|
||||||
DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented);
|
DECLARE_XAM_EXPORT1(XamContentDeleteInternal, kContent, kImplemented);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
xe::be<uint32_t> stringTitlePtr;
|
||||||
|
xe::be<uint32_t> stringTextPtr;
|
||||||
|
xe::be<uint32_t> stringBtnMsgPtr;
|
||||||
|
} X_SWAPDISC_ERROR_MESSAGE;
|
||||||
|
static_assert_size(X_SWAPDISC_ERROR_MESSAGE, 12);
|
||||||
|
|
||||||
|
dword_result_t XamSwapDisc(dword_t disc_number,
|
||||||
|
pointer_t<X_KEVENT> completion_handle,
|
||||||
|
pointer_t<X_SWAPDISC_ERROR_MESSAGE> 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<XObject>(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<std::u16string>(
|
||||||
|
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 xam
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
Loading…
Reference in New Issue