Skip to content

Commit

Permalink
Merge branch 'xenia-canary:canary_experimental' into Custom
Browse files Browse the repository at this point in the history
  • Loading branch information
backgamon authored Mar 22, 2024
2 parents 8bed174 + 5efcc6a commit b05600f
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 69 deletions.
22 changes: 6 additions & 16 deletions .github/workflows/Windows_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,48 +47,38 @@ jobs:
lint:
name: Lint
runs-on: windows-2022

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Check Clang-Format Version
run: clang-format --version

- name: Lint
run: .\xb lint --all
- uses: actions/checkout@v4
- name: Check Clang-Format Version
run: clang-format --version
- name: Lint
run: .\xb lint --all

build-windows:
name: Build (Windows) # runner.os can't be used here
runs-on: windows-2022
needs: lint
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
needs: lint

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup
run: .\xb setup

- name: Build
run: .\xb build --config=Release --target=src\xenia-app

- name: Prepare artifacts
run: |
robocopy . build\bin\${{ runner.os }}\Release LICENSE /r:0 /w:0
robocopy build\bin\${{ runner.os }}\Release artifacts\xenia_canary xenia_canary.exe xenia_canary.pdb LICENSE /r:0 /w:0
If ($LastExitCode -le 7) { echo "LastExitCode = $LastExitCode";$LastExitCode = 0 }
- name: Upload xenia canary artifacts
uses: actions/upload-artifact@v4
with:
name: xenia_canary
path: artifacts\xenia_canary
if-no-files-found: error

- name: Create release
if: |
github.repository == 'xenia-canary/xenia-canary' &&
Expand Down
19 changes: 19 additions & 0 deletions src/xenia/app/emulator_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1534,6 +1534,11 @@ void EmulatorWindow::DisplayHotKeysConfig() {
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) {
bool titleExists = !std::filesystem::exists(path_to_file);

Expand Down Expand Up @@ -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
// loaded.
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);

imgui_drawer_.get()->ClearDialogs();
Expand Down
3 changes: 3 additions & 0 deletions src/xenia/app/emulator_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ class EmulatorWindow {
bool IsUseNexusForGameBarEnabled();
void DisplayHotKeysConfig();

static std::string CanonicalizeFileExtension(
const std::filesystem::path& path);

void RunPreviouslyPlayedTitle();
void FillRecentlyLaunchedTitlesMenu(xe::ui::MenuItem* recent_menu);
void LoadRecentlyLaunchedTitles();
Expand Down
176 changes: 129 additions & 47 deletions src/xenia/emulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "config.h"
#include "third_party/fmt/include/fmt/format.h"
#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/base/assert.h"
#include "xenia/base/byte_stream.h"
Expand Down Expand Up @@ -314,32 +315,35 @@ X_STATUS Emulator::TerminateTitle() {
return X_STATUS_SUCCESS;
}

std::string Emulator::CanonicalizeFileExtension(
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::unique_ptr<vfs::Device> Emulator::CreateVfsDevice(
const std::filesystem::path& path, const std::string_view mount_path) {
if (!path.has_extension()) {
return vfs::XContentContainerDevice::CreateContentDevice(mount_path, path);
}
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);
// Must check if the type has changed e.g. XamSwapDisc
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;
}
}

uint64_t Emulator::GetPersistentEmulatorFlags() {
Expand Down Expand Up @@ -386,7 +390,7 @@ void Emulator::SetPersistentEmulatorFlags(uint64_t new_flags) {

X_STATUS Emulator::MountPath(const std::filesystem::path& path,
const std::string_view mount_path) {
auto device = CreateVfsDeviceBasedOnPath(path, mount_path);
auto device = CreateVfsDevice(path, mount_path);
if (!device || !device->Initialize()) {
XELOGE(
"Unable to mount the selected file, it is an unsupported format or "
Expand All @@ -410,30 +414,108 @@ X_STATUS Emulator::MountPath(const std::filesystem::path& path,
return X_STATUS_SUCCESS;
}

X_STATUS Emulator::LaunchPath(const std::filesystem::path& path) {
// Launch based on file type.
// This is a silly guess based on file extension.
Emulator::FileSignatureType Emulator::GetFileSignature(
const std::filesystem::path& path) {
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;

if (!path.has_extension()) {
// Likely an STFS container.
mount_result = MountPath(path, "\\Device\\Cdrom0");
return mount_result ? mount_result : LaunchStfsContainer(path);
};
auto extension = xe::utf8::lower_ascii(xe::path_to_utf8(path.extension()));
if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
// Treat as a naked xex file.
mount_result = MountPath(path, "\\Device\\Harddisk0\\Partition1");
return mount_result ? mount_result : LaunchXexFile(path);
} else if (extension == ".zar") {
// Assume a disc image.
mount_result = MountPath(path, "\\Device\\Cdrom0");
return mount_result ? mount_result : LaunchDiscArchive(path);
} else {
// Assume a disc image.
mount_result = MountPath(path, "\\Device\\Cdrom0");
return mount_result ? mount_result : LaunchDiscImage(path);
switch (GetFileSignature(path)) {
case FileSignatureType::XEX1:
case FileSignatureType::XEX2:
case FileSignatureType::ELF: {
mount_result = MountPath(path, "\\Device\\Harddisk0\\Partition1");
return mount_result ? mount_result : LaunchXexFile(path);
} break;
case FileSignatureType::LIVE:
case FileSignatureType::CON:
case FileSignatureType::PIRS: {
mount_result = MountPath(path, "\\Device\\Cdrom0");
return mount_result ? mount_result : LaunchStfsContainer(path);
} break;
case FileSignatureType::XISO: {
mount_result = MountPath(path, "\\Device\\Cdrom0");
return mount_result ? mount_result : LaunchDiscImage(path);
} break;
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;
}
}

Expand Down
21 changes: 18 additions & 3 deletions src/xenia/emulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,28 @@ class Emulator {
// Terminates the currently running title.
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);

X_STATUS MountPath(const std::filesystem::path& 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.
// This will attempt to infer the type of the given file (such as an iso, etc)
// using heuristics.
Expand Down Expand Up @@ -233,8 +250,6 @@ class Emulator {
enum : uint64_t { EmulatorFlagDisclaimerAcknowledged = 1ULL << 0 };
static uint64_t GetPersistentEmulatorFlags();
static void SetPersistentEmulatorFlags(uint64_t new_flags);
static std::string CanonicalizeFileExtension(
const std::filesystem::path& path);
static bool ExceptionCallbackThunk(Exception* ex, void* data);
bool ExceptionCallback(Exception* ex);

Expand Down
9 changes: 6 additions & 3 deletions src/xenia/kernel/user_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,12 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
} else if (magic == xe::cpu::kElfSignature) {
module_format_ = kModuleFormatElf;
} else {
be<uint16_t> magic16;
magic16.value = xe::load<uint16_t>(addr);
if (magic16 == 0x4D5A) {
uint8_t M = xe::load<uint8_t>(addr);
uint8_t Z = xe::load<uint8_t>(reinterpret_cast<void*>(
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");
return X_STATUS_NOT_IMPLEMENTED;
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/xenia/kernel/user_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class XThread;
namespace xe {
namespace kernel {

constexpr fourcc_t kEXESignature = make_fourcc('M', 'Z', 0, 0);

class UserModule : public XModule {
public:
UserModule(KernelState* kernel_state);
Expand Down
2 changes: 2 additions & 0 deletions src/xenia/vfs/devices/disc_image_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace vfs {

class DiscImageEntry;

constexpr fourcc_t kXSFSignature = make_fourcc(0x58, 0x53, 0x46, 0x1A);

class DiscImageDevice : public Device {
public:
DiscImageDevice(const std::string_view mount_path,
Expand Down
5 changes: 5 additions & 0 deletions src/xenia/vfs/devices/disc_zarchive_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
namespace xe {
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 DiscZarchiveDevice : public Device {
Expand Down
Loading

0 comments on commit b05600f

Please sign in to comment.