mirror of
https://github.com/xenia-project/xenia.git
synced 2025-12-06 07:12:03 +01:00
[Emulator] Launch file based on signature instead of file extension
This commit is contained in:
parent
5eecb4e65d
commit
5efcc6a71b
|
|
@ -1534,6 +1534,11 @@ void EmulatorWindow::DisplayHotKeysConfig() {
|
||||||
msg);
|
msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string EmulatorWindow::CanonicalizeFileExtension(
|
||||||
|
const std::filesystem::path& path) {
|
||||||
|
return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
||||||
|
}
|
||||||
|
|
||||||
xe::X_STATUS EmulatorWindow::RunTitle(std::filesystem::path path_to_file) {
|
xe::X_STATUS EmulatorWindow::RunTitle(std::filesystem::path path_to_file) {
|
||||||
bool titleExists = !std::filesystem::exists(path_to_file);
|
bool titleExists = !std::filesystem::exists(path_to_file);
|
||||||
|
|
||||||
|
|
@ -1564,6 +1569,20 @@ xe::X_STATUS EmulatorWindow::RunTitle(std::filesystem::path path_to_file) {
|
||||||
// Prevent crashing the emulator by not loading a game if a game is already
|
// Prevent crashing the emulator by not loading a game if a game is already
|
||||||
// loaded.
|
// loaded.
|
||||||
auto abs_path = std::filesystem::absolute(path_to_file);
|
auto abs_path = std::filesystem::absolute(path_to_file);
|
||||||
|
|
||||||
|
auto extension = CanonicalizeFileExtension(abs_path);
|
||||||
|
|
||||||
|
if (extension == ".7z" || extension == ".zip" || extension == ".rar" ||
|
||||||
|
extension == ".tar" || extension == ".gz") {
|
||||||
|
xe::ShowSimpleMessageBox(
|
||||||
|
xe::SimpleMessageBoxType::Error,
|
||||||
|
fmt::format(
|
||||||
|
"Unsupported format!\n"
|
||||||
|
"Xenia does not support running software in an archived format."));
|
||||||
|
|
||||||
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
auto result = emulator_->LaunchPath(abs_path);
|
auto result = emulator_->LaunchPath(abs_path);
|
||||||
|
|
||||||
imgui_drawer_.get()->ClearDialogs();
|
imgui_drawer_.get()->ClearDialogs();
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,9 @@ class EmulatorWindow {
|
||||||
bool IsUseNexusForGameBarEnabled();
|
bool IsUseNexusForGameBarEnabled();
|
||||||
void DisplayHotKeysConfig();
|
void DisplayHotKeysConfig();
|
||||||
|
|
||||||
|
static std::string CanonicalizeFileExtension(
|
||||||
|
const std::filesystem::path& path);
|
||||||
|
|
||||||
void RunPreviouslyPlayedTitle();
|
void RunPreviouslyPlayedTitle();
|
||||||
void FillRecentlyLaunchedTitlesMenu(xe::ui::MenuItem* recent_menu);
|
void FillRecentlyLaunchedTitlesMenu(xe::ui::MenuItem* recent_menu);
|
||||||
void LoadRecentlyLaunchedTitles();
|
void LoadRecentlyLaunchedTitles();
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
#include "third_party/tabulate/single_include/tabulate/tabulate.hpp"
|
#include "third_party/tabulate/single_include/tabulate/tabulate.hpp"
|
||||||
|
#include "third_party/zarchive/include/zarchive/zarchivecommon.h"
|
||||||
#include "xenia/apu/audio_system.h"
|
#include "xenia/apu/audio_system.h"
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/byte_stream.h"
|
#include "xenia/base/byte_stream.h"
|
||||||
|
|
@ -314,32 +315,35 @@ X_STATUS Emulator::TerminateTitle() {
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Emulator::CanonicalizeFileExtension(
|
const std::unique_ptr<vfs::Device> Emulator::CreateVfsDevice(
|
||||||
const std::filesystem::path& path) {
|
|
||||||
return xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::unique_ptr<vfs::Device> Emulator::CreateVfsDeviceBasedOnPath(
|
|
||||||
const std::filesystem::path& path, const std::string_view mount_path) {
|
const std::filesystem::path& path, const std::string_view mount_path) {
|
||||||
if (!path.has_extension()) {
|
// Must check if the type has changed e.g. XamSwapDisc
|
||||||
return vfs::XContentContainerDevice::CreateContentDevice(mount_path, path);
|
switch (GetFileSignature(path)) {
|
||||||
|
case FileSignatureType::XEX1:
|
||||||
|
case FileSignatureType::XEX2:
|
||||||
|
case FileSignatureType::ELF: {
|
||||||
|
auto parent_path = path.parent_path();
|
||||||
|
return std::make_unique<vfs::HostPathDevice>(
|
||||||
|
mount_path, parent_path, !cvars::allow_game_relative_writes);
|
||||||
|
} break;
|
||||||
|
case FileSignatureType::LIVE:
|
||||||
|
case FileSignatureType::CON:
|
||||||
|
case FileSignatureType::PIRS: {
|
||||||
|
return vfs::XContentContainerDevice::CreateContentDevice(mount_path,
|
||||||
|
path);
|
||||||
|
} break;
|
||||||
|
case FileSignatureType::XISO: {
|
||||||
|
return std::make_unique<vfs::DiscImageDevice>(mount_path, path);
|
||||||
|
} break;
|
||||||
|
case FileSignatureType::ZAR: {
|
||||||
|
return std::make_unique<vfs::DiscZarchiveDevice>(mount_path, path);
|
||||||
|
} break;
|
||||||
|
case FileSignatureType::EXE:
|
||||||
|
case FileSignatureType::Unknown:
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
auto extension = CanonicalizeFileExtension(path);
|
|
||||||
if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
|
|
||||||
auto parent_path = path.parent_path();
|
|
||||||
return std::make_unique<vfs::HostPathDevice>(
|
|
||||||
mount_path, parent_path, !cvars::allow_game_relative_writes);
|
|
||||||
} else if (extension == ".zar") {
|
|
||||||
return std::make_unique<vfs::DiscZarchiveDevice>(mount_path, path);
|
|
||||||
} else if (extension == ".7z" || extension == ".zip" || extension == ".rar" ||
|
|
||||||
extension == ".tar" || extension == ".gz") {
|
|
||||||
xe::ShowSimpleMessageBox(
|
|
||||||
xe::SimpleMessageBoxType::Error,
|
|
||||||
fmt::format(
|
|
||||||
"Unsupported format!\n"
|
|
||||||
"Xenia does not support running software in an archived format."));
|
|
||||||
}
|
|
||||||
return std::make_unique<vfs::DiscImageDevice>(mount_path, path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t Emulator::GetPersistentEmulatorFlags() {
|
uint64_t Emulator::GetPersistentEmulatorFlags() {
|
||||||
|
|
@ -386,7 +390,7 @@ void Emulator::SetPersistentEmulatorFlags(uint64_t new_flags) {
|
||||||
|
|
||||||
X_STATUS Emulator::MountPath(const std::filesystem::path& path,
|
X_STATUS Emulator::MountPath(const std::filesystem::path& path,
|
||||||
const std::string_view mount_path) {
|
const std::string_view mount_path) {
|
||||||
auto device = CreateVfsDeviceBasedOnPath(path, mount_path);
|
auto device = CreateVfsDevice(path, mount_path);
|
||||||
if (!device || !device->Initialize()) {
|
if (!device || !device->Initialize()) {
|
||||||
XELOGE(
|
XELOGE(
|
||||||
"Unable to mount the selected file, it is an unsupported format or "
|
"Unable to mount the selected file, it is an unsupported format or "
|
||||||
|
|
@ -410,30 +414,108 @@ X_STATUS Emulator::MountPath(const std::filesystem::path& path,
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::LaunchPath(const std::filesystem::path& path) {
|
Emulator::FileSignatureType Emulator::GetFileSignature(
|
||||||
// Launch based on file type.
|
const std::filesystem::path& path) {
|
||||||
// This is a silly guess based on file extension.
|
FILE* file = xe::filesystem::OpenFile(path, "rb");
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return FileSignatureType::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t file_size = std::filesystem::file_size(path);
|
||||||
|
const uint64_t header_size = 4;
|
||||||
|
|
||||||
|
if (file_size < header_size) {
|
||||||
|
return FileSignatureType::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
char file_magic[header_size];
|
||||||
|
fread_s(file_magic, sizeof(file_magic), 1, header_size, file);
|
||||||
|
|
||||||
|
fourcc_t magic_value =
|
||||||
|
make_fourcc(file_magic[0], file_magic[1], file_magic[2], file_magic[3]);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
switch (magic_value) {
|
||||||
|
case xe::cpu::kXEX1Signature:
|
||||||
|
return FileSignatureType::XEX1;
|
||||||
|
case xe::cpu::kXEX2Signature:
|
||||||
|
return FileSignatureType::XEX2;
|
||||||
|
case xe::vfs::kCONSignature:
|
||||||
|
return FileSignatureType::CON;
|
||||||
|
case xe::vfs::kLIVESignature:
|
||||||
|
return FileSignatureType::LIVE;
|
||||||
|
case xe::vfs::kPIRSSignature:
|
||||||
|
return FileSignatureType::PIRS;
|
||||||
|
case xe::vfs::kXSFSignature:
|
||||||
|
return FileSignatureType::XISO;
|
||||||
|
case xe::cpu::kElfSignature:
|
||||||
|
return FileSignatureType::ELF;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
magic_value = make_fourcc(file_magic[0], file_magic[1], 0, 0);
|
||||||
|
|
||||||
|
if (xe::kernel::kEXESignature == magic_value) {
|
||||||
|
return FileSignatureType::EXE;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = xe::filesystem::OpenFile(path, "rb");
|
||||||
|
xe::filesystem::Seek(file, header_size, SEEK_END);
|
||||||
|
fread_s(file_magic, sizeof(file_magic), 1, header_size, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
magic_value =
|
||||||
|
make_fourcc(file_magic[0], file_magic[1], file_magic[2], file_magic[3]);
|
||||||
|
|
||||||
|
if (xe::vfs::kZarMagic == magic_value) {
|
||||||
|
return FileSignatureType::ZAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if XISO
|
||||||
|
std::unique_ptr<vfs::Device> device =
|
||||||
|
std::make_unique<vfs::DiscImageDevice>("", path);
|
||||||
|
|
||||||
|
XELOGI("Checking for XISO");
|
||||||
|
|
||||||
|
if (device->Initialize()) {
|
||||||
|
return FileSignatureType::XISO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileSignatureType::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_STATUS Emulator::LaunchPath(const std::filesystem::path& path) {
|
||||||
X_STATUS mount_result = X_STATUS_SUCCESS;
|
X_STATUS mount_result = X_STATUS_SUCCESS;
|
||||||
|
|
||||||
if (!path.has_extension()) {
|
switch (GetFileSignature(path)) {
|
||||||
// Likely an STFS container.
|
case FileSignatureType::XEX1:
|
||||||
mount_result = MountPath(path, "\\Device\\Cdrom0");
|
case FileSignatureType::XEX2:
|
||||||
return mount_result ? mount_result : LaunchStfsContainer(path);
|
case FileSignatureType::ELF: {
|
||||||
};
|
mount_result = MountPath(path, "\\Device\\Harddisk0\\Partition1");
|
||||||
auto extension = xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
|
return mount_result ? mount_result : LaunchXexFile(path);
|
||||||
if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
|
} break;
|
||||||
// Treat as a naked xex file.
|
case FileSignatureType::LIVE:
|
||||||
mount_result = MountPath(path, "\\Device\\Harddisk0\\Partition1");
|
case FileSignatureType::CON:
|
||||||
return mount_result ? mount_result : LaunchXexFile(path);
|
case FileSignatureType::PIRS: {
|
||||||
} else if (extension == ".zar") {
|
mount_result = MountPath(path, "\\Device\\Cdrom0");
|
||||||
// Assume a disc image.
|
return mount_result ? mount_result : LaunchStfsContainer(path);
|
||||||
mount_result = MountPath(path, "\\Device\\Cdrom0");
|
} break;
|
||||||
return mount_result ? mount_result : LaunchDiscArchive(path);
|
case FileSignatureType::XISO: {
|
||||||
} else {
|
mount_result = MountPath(path, "\\Device\\Cdrom0");
|
||||||
// Assume a disc image.
|
return mount_result ? mount_result : LaunchDiscImage(path);
|
||||||
mount_result = MountPath(path, "\\Device\\Cdrom0");
|
} break;
|
||||||
return mount_result ? mount_result : LaunchDiscImage(path);
|
case FileSignatureType::ZAR: {
|
||||||
|
mount_result = MountPath(path, "\\Device\\Cdrom0");
|
||||||
|
return mount_result ? mount_result : LaunchDiscArchive(path);
|
||||||
|
} break;
|
||||||
|
case FileSignatureType::EXE:
|
||||||
|
case FileSignatureType::Unknown:
|
||||||
|
default:
|
||||||
|
return X_STATUS_NOT_SUPPORTED;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,11 +181,28 @@ 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::unique_ptr<vfs::Device> CreateVfsDevice(
|
||||||
const std::filesystem::path& path, const std::string_view mount_path);
|
const std::filesystem::path& path, const std::string_view mount_path);
|
||||||
|
|
||||||
X_STATUS MountPath(const std::filesystem::path& path,
|
X_STATUS MountPath(const std::filesystem::path& path,
|
||||||
const std::string_view mount_path);
|
const std::string_view mount_path);
|
||||||
|
|
||||||
|
enum class FileSignatureType {
|
||||||
|
XEX1,
|
||||||
|
XEX2,
|
||||||
|
ELF,
|
||||||
|
CON,
|
||||||
|
LIVE,
|
||||||
|
PIRS,
|
||||||
|
XISO,
|
||||||
|
ZAR,
|
||||||
|
EXE,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine the executable signature
|
||||||
|
FileSignatureType GetFileSignature(const std::filesystem::path& 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.
|
||||||
|
|
@ -233,8 +250,6 @@ class Emulator {
|
||||||
enum : uint64_t { EmulatorFlagDisclaimerAcknowledged = 1ULL << 0 };
|
enum : uint64_t { EmulatorFlagDisclaimerAcknowledged = 1ULL << 0 };
|
||||||
static uint64_t GetPersistentEmulatorFlags();
|
static uint64_t GetPersistentEmulatorFlags();
|
||||||
static void SetPersistentEmulatorFlags(uint64_t new_flags);
|
static void SetPersistentEmulatorFlags(uint64_t new_flags);
|
||||||
static std::string CanonicalizeFileExtension(
|
|
||||||
const std::filesystem::path& path);
|
|
||||||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||||
bool ExceptionCallback(Exception* ex);
|
bool ExceptionCallback(Exception* ex);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,9 +126,12 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
} else if (magic == xe::cpu::kElfSignature) {
|
} else if (magic == xe::cpu::kElfSignature) {
|
||||||
module_format_ = kModuleFormatElf;
|
module_format_ = kModuleFormatElf;
|
||||||
} else {
|
} else {
|
||||||
be<uint16_t> magic16;
|
uint8_t M = xe::load<uint8_t>(addr);
|
||||||
magic16.value = xe::load<uint16_t>(addr);
|
uint8_t Z = xe::load<uint8_t>(reinterpret_cast<void*>(
|
||||||
if (magic16 == 0x4D5A) {
|
reinterpret_cast<uint64_t>(addr) + sizeof(uint8_t)));
|
||||||
|
|
||||||
|
magic = make_fourcc(M, Z, 0, 0);
|
||||||
|
if (magic == kEXESignature) {
|
||||||
XELOGE("XNA executables are not yet implemented");
|
XELOGE("XNA executables are not yet implemented");
|
||||||
return X_STATUS_NOT_IMPLEMENTED;
|
return X_STATUS_NOT_IMPLEMENTED;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ class XThread;
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
constexpr fourcc_t kEXESignature = make_fourcc('M', 'Z', 0, 0);
|
||||||
|
|
||||||
class UserModule : public XModule {
|
class UserModule : public XModule {
|
||||||
public:
|
public:
|
||||||
UserModule(KernelState* kernel_state);
|
UserModule(KernelState* kernel_state);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ namespace vfs {
|
||||||
|
|
||||||
class DiscImageEntry;
|
class DiscImageEntry;
|
||||||
|
|
||||||
|
constexpr fourcc_t kXSFSignature = make_fourcc(0x58, 0x53, 0x46, 0x1A);
|
||||||
|
|
||||||
class DiscImageDevice : public Device {
|
class DiscImageDevice : public Device {
|
||||||
public:
|
public:
|
||||||
DiscImageDevice(const std::string_view mount_path,
|
DiscImageDevice(const std::string_view mount_path,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace vfs {
|
namespace vfs {
|
||||||
|
|
||||||
|
const fourcc_t kZarMagic = make_fourcc((_ZARCHIVE::Footer::kMagic >> 24 & 0xFF),
|
||||||
|
(_ZARCHIVE::Footer::kMagic >> 16 & 0xFF),
|
||||||
|
(_ZARCHIVE::Footer::kMagic >> 8 & 0xFF),
|
||||||
|
(_ZARCHIVE::Footer::kMagic & 0xFF));
|
||||||
|
|
||||||
class DiscZarchiveEntry;
|
class DiscZarchiveEntry;
|
||||||
|
|
||||||
class DiscZarchiveDevice : public Device {
|
class DiscZarchiveDevice : public Device {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace vfs {
|
namespace vfs {
|
||||||
|
|
||||||
|
constexpr fourcc_t kLIVESignature = make_fourcc("LIVE");
|
||||||
|
constexpr fourcc_t kCONSignature = make_fourcc("CON ");
|
||||||
|
constexpr fourcc_t kPIRSSignature = make_fourcc("PIRS");
|
||||||
|
|
||||||
class XContentContainerDevice : public Device {
|
class XContentContainerDevice : public Device {
|
||||||
public:
|
public:
|
||||||
const static uint32_t kBlockSize = 0x1000;
|
const static uint32_t kBlockSize = 0x1000;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue