Merge remote-tracking branch 'GliniakRepo/xam_swap_disc' into canary_experimental

This commit is contained in:
Gliniak 2022-05-19 12:07:05 +02:00
commit 5ce75a1479
3 changed files with 134 additions and 58 deletions

View File

@ -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]() {

View File

@ -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();

View File

@ -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