C++17ification.

C++17ification!

- Filesystem interaction now uses std::filesystem::path.
- Usage of const char*, std::string have been changed to
  std::string_view where appropriate.
- Usage of printf-style functions changed to use fmt.
This commit is contained in:
gibbed 2020-03-02 09:37:11 -06:00 committed by Rick Gibbed
parent 114cea6fb7
commit 5bf0b34445
220 changed files with 4944 additions and 4294 deletions

View File

@ -91,15 +91,17 @@ filter({"configurations:Release", "platforms:Windows"})
filter("platforms:Linux") filter("platforms:Linux")
system("linux") system("linux")
toolset("clang") toolset("clang")
cppdialect("C++17")
buildoptions({ buildoptions({
-- "-mlzcnt", -- (don't) Assume lzcnt is supported. -- "-mlzcnt", -- (don't) Assume lzcnt is supported.
"`pkg-config --cflags gtk+-x11-3.0`", "`pkg-config --cflags gtk+-x11-3.0`",
"-fno-lto", -- Premake doesn't support LTO on clang "-fno-lto", -- Premake doesn't support LTO on clang
}) })
links({ links({
"pthread", "stdc++fs",
"dl", "dl",
"lz4", "lz4",
"pthread",
"rt", "rt",
}) })
linkoptions({ linkoptions({
@ -110,9 +112,7 @@ filter({"platforms:Linux", "kind:*App"})
linkgroups("On") linkgroups("On")
filter({"platforms:Linux", "language:C++", "toolset:gcc"}) filter({"platforms:Linux", "language:C++", "toolset:gcc"})
buildoptions({ cppdialect("C++17")
"-std=c++14",
})
links({ links({
}) })
disablewarnings({ disablewarnings({
@ -141,13 +141,13 @@ filter({"platforms:Linux", "language:C++", "toolset:clang"})
}) })
filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"}) filter({"platforms:Linux", "language:C++", "toolset:clang", "files:*.cc or *.cpp"})
buildoptions({ buildoptions({
"-std=c++14",
"-stdlib=libstdc++", "-stdlib=libstdc++",
}) })
filter("platforms:Windows") filter("platforms:Windows")
system("windows") system("windows")
toolset("msc") toolset("msc")
cppdialect("C++17")
buildoptions({ buildoptions({
"/MP", -- Multiprocessor compilation. "/MP", -- Multiprocessor compilation.
"/wd4100", -- Unreferenced parameters are ok. "/wd4100", -- Unreferenced parameters are ok.
@ -214,6 +214,7 @@ solution("xenia")
include("third_party/discord-rpc.lua") include("third_party/discord-rpc.lua")
include("third_party/cxxopts.lua") include("third_party/cxxopts.lua")
include("third_party/cpptoml.lua") include("third_party/cpptoml.lua")
include("third_party/fmt.lua")
include("third_party/glslang-spirv.lua") include("third_party/glslang-spirv.lua")
include("third_party/imgui.lua") include("third_party/imgui.lua")
include("third_party/libav.lua") include("third_party/libav.lua")

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -39,11 +39,10 @@ void DiscordPresence::NotPlaying() {
Discord_UpdatePresence(&discordPresence); Discord_UpdatePresence(&discordPresence);
} }
void DiscordPresence::PlayingTitle(const std::wstring& game_title) { void DiscordPresence::PlayingTitle(const std::string_view game_title) {
auto discord_game_title = xe::to_string(game_title);
DiscordRichPresence discordPresence = {}; DiscordRichPresence discordPresence = {};
discordPresence.state = "In Game"; discordPresence.state = "In Game";
discordPresence.details = discord_game_title.c_str(); discordPresence.details = std::string(game_title).c_str();
// TODO(gibbed): we don't have state icons yet. // TODO(gibbed): we don't have state icons yet.
// discordPresence.smallImageKey = "app"; // discordPresence.smallImageKey = "app";
// discordPresence.largeImageKey = "state_ingame"; // discordPresence.largeImageKey = "state_ingame";

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -19,7 +19,7 @@ class DiscordPresence {
public: public:
static void Initialize(); static void Initialize();
static void NotPlaying(); static void NotPlaying();
static void PlayingTitle(const std::wstring& game_title); static void PlayingTitle(const std::string_view game_title);
static void Shutdown(); static void Shutdown();
}; };

View File

@ -9,9 +9,7 @@
#include "xenia/app/emulator_window.h" #include "xenia/app/emulator_window.h"
// Autogenerated by `xb premake`. #include "third_party/fmt/include/fmt/format.h"
#include "build/version.h"
#include "third_party/imgui/imgui.h" #include "third_party/imgui/imgui.h"
#include "xenia/base/clock.h" #include "xenia/base/clock.h"
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
@ -19,14 +17,17 @@
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#include "xenia/base/profiling.h" #include "xenia/base/profiling.h"
#include "xenia/base/system.h"
#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/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"
// Autogenerated by `xb premake`.
#include "build/version.h"
DECLARE_bool(debug); DECLARE_bool(debug);
namespace xe { namespace xe {
@ -38,7 +39,7 @@ using xe::ui::MenuItem;
using xe::ui::MouseEvent; using xe::ui::MouseEvent;
using xe::ui::UIEvent; using xe::ui::UIEvent;
const std::wstring kBaseTitle = L"xenia"; const std::string kBaseTitle = "xenia";
EmulatorWindow::EmulatorWindow(Emulator* emulator) EmulatorWindow::EmulatorWindow(Emulator* emulator)
: emulator_(emulator), : emulator_(emulator),
@ -47,14 +48,13 @@ EmulatorWindow::EmulatorWindow(Emulator* emulator)
base_title_ = kBaseTitle + base_title_ = kBaseTitle +
#ifdef DEBUG #ifdef DEBUG
#if _NO_DEBUG_HEAP == 1 #if _NO_DEBUG_HEAP == 1
L" DEBUG" + " DEBUG"
#else #else
L" CHECKED" + " CHECKED"
#endif #endif
#endif #endif
L" (" + xe::to_wstring(XE_BUILD_BRANCH) + L"/" + " (" XE_BUILD_BRANCH "/" XE_BUILD_COMMIT_SHORT "/" XE_BUILD_DATE
xe::to_wstring(XE_BUILD_COMMIT_SHORT) + L"/" + ")";
xe::to_wstring(XE_BUILD_DATE) + L")";
} }
EmulatorWindow::~EmulatorWindow() { EmulatorWindow::~EmulatorWindow() {
@ -123,13 +123,13 @@ bool EmulatorWindow::Initialize() {
// Save to file // Save to file
// TODO: Choose path based on user input, or from options // TODO: Choose path based on user input, or from options
// TODO: Spawn a new thread to do this. // TODO: Spawn a new thread to do this.
emulator()->SaveToFile(L"test.sav"); emulator()->SaveToFile("test.sav");
} break; } break;
case 0x77: { // VK_F8 case 0x77: { // VK_F8
// Restore from file // Restore from file
// TODO: Choose path from user // TODO: Choose path from user
// TODO: Spawn a new thread to do this. // TODO: Spawn a new thread to do this.
emulator()->RestoreFromFile(L"test.sav"); emulator()->RestoreFromFile("test.sav");
} break; } break;
case 0x7A: { // VK_F11 case 0x7A: { // VK_F11
ToggleFullscreen(); ToggleFullscreen();
@ -182,105 +182,102 @@ bool EmulatorWindow::Initialize() {
// Main menu. // Main menu.
// FIXME: This code is really messy. // FIXME: This code is really messy.
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, "&File");
{ {
file_menu->AddChild( file_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, L"&Open...", L"Ctrl+O", MenuItem::Create(MenuItem::Type::kString, "&Open...", "Ctrl+O",
std::bind(&EmulatorWindow::FileOpen, this))); std::bind(&EmulatorWindow::FileOpen, this)));
file_menu->AddChild( file_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, L"Close", MenuItem::Create(MenuItem::Type::kString, "Close",
std::bind(&EmulatorWindow::FileClose, this))); std::bind(&EmulatorWindow::FileClose, this)));
file_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); file_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
file_menu->AddChild(MenuItem::Create( file_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"Show content directory...", MenuItem::Type::kString, "Show content directory...",
std::bind(&EmulatorWindow::ShowContentDirectory, this))); std::bind(&EmulatorWindow::ShowContentDirectory, this)));
file_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); file_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, L"E&xit", file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, "E&xit",
L"Alt+F4", "Alt+F4",
[this]() { window_->Close(); })); [this]() { window_->Close(); }));
} }
main_menu->AddChild(std::move(file_menu)); main_menu->AddChild(std::move(file_menu));
// CPU menu. // CPU menu.
auto cpu_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&CPU"); auto cpu_menu = MenuItem::Create(MenuItem::Type::kPopup, "&CPU");
{ {
cpu_menu->AddChild(MenuItem::Create( cpu_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"&Reset Time Scalar", L"Numpad *", MenuItem::Type::kString, "&Reset Time Scalar", "Numpad *",
std::bind(&EmulatorWindow::CpuTimeScalarReset, this))); std::bind(&EmulatorWindow::CpuTimeScalarReset, this)));
cpu_menu->AddChild(MenuItem::Create( cpu_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"Time Scalar /= 2", L"Numpad -", MenuItem::Type::kString, "Time Scalar /= 2", "Numpad -",
std::bind(&EmulatorWindow::CpuTimeScalarSetHalf, this))); std::bind(&EmulatorWindow::CpuTimeScalarSetHalf, this)));
cpu_menu->AddChild(MenuItem::Create( cpu_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"Time Scalar *= 2", L"Numpad +", MenuItem::Type::kString, "Time Scalar *= 2", "Numpad +",
std::bind(&EmulatorWindow::CpuTimeScalarSetDouble, this))); std::bind(&EmulatorWindow::CpuTimeScalarSetDouble, this)));
} }
cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
{ {
cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kString,
L"Toggle Profiler &Display", L"F3", "Toggle Profiler &Display", "F3",
[]() { Profiler::ToggleDisplay(); })); []() { Profiler::ToggleDisplay(); }));
cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kString,
L"&Pause/Resume Profiler", L"`", "&Pause/Resume Profiler", "`",
[]() { Profiler::TogglePause(); })); []() { Profiler::TogglePause(); }));
} }
cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); cpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
{ {
cpu_menu->AddChild(MenuItem::Create( cpu_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"&Break and Show Guest Debugger", MenuItem::Type::kString, "&Break and Show Guest Debugger",
L"Pause/Break", "Pause/Break", std::bind(&EmulatorWindow::CpuBreakIntoDebugger, this)));
std::bind(&EmulatorWindow::CpuBreakIntoDebugger, this)));
cpu_menu->AddChild(MenuItem::Create( cpu_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"&Break into Host Debugger", MenuItem::Type::kString, "&Break into Host Debugger",
L"Ctrl+Pause/Break", "Ctrl+Pause/Break",
std::bind(&EmulatorWindow::CpuBreakIntoHostDebugger, this))); std::bind(&EmulatorWindow::CpuBreakIntoHostDebugger, this)));
} }
main_menu->AddChild(std::move(cpu_menu)); main_menu->AddChild(std::move(cpu_menu));
// GPU menu. // GPU menu.
auto gpu_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&GPU"); auto gpu_menu = MenuItem::Create(MenuItem::Type::kPopup, "&GPU");
{ {
gpu_menu->AddChild( gpu_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, L"&Trace Frame", L"F4", MenuItem::Create(MenuItem::Type::kString, "&Trace Frame", "F4",
std::bind(&EmulatorWindow::GpuTraceFrame, this))); std::bind(&EmulatorWindow::GpuTraceFrame, this)));
} }
gpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); gpu_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
{ {
gpu_menu->AddChild(MenuItem::Create( gpu_menu->AddChild(
MenuItem::Type::kString, L"&Clear Runtime Caches", L"F5", MenuItem::Create(MenuItem::Type::kString, "&Clear Runtime Caches", "F5",
std::bind(&EmulatorWindow::GpuClearCaches, this))); std::bind(&EmulatorWindow::GpuClearCaches, this)));
} }
main_menu->AddChild(std::move(gpu_menu)); main_menu->AddChild(std::move(gpu_menu));
// Window menu. // Window menu.
auto window_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Window"); auto window_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Window");
{ {
window_menu->AddChild( window_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, L"&Fullscreen", L"F11", MenuItem::Create(MenuItem::Type::kString, "&Fullscreen", "F11",
std::bind(&EmulatorWindow::ToggleFullscreen, this))); std::bind(&EmulatorWindow::ToggleFullscreen, this)));
} }
main_menu->AddChild(std::move(window_menu)); main_menu->AddChild(std::move(window_menu));
// Help menu. // Help menu.
auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, L"&Help"); auto help_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Help");
{ {
help_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, "Build commit on GitHub...",
"F2", std::bind(&EmulatorWindow::ShowCommitID, this)));
help_menu->AddChild(MenuItem::Create( help_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"Build commit on GitHub...", L"F2", MenuItem::Type::kString, "Recent changes on GitHub...", [this]() {
std::bind(&EmulatorWindow::ShowCommitID, this))); LaunchWebBrowser(
help_menu->AddChild(MenuItem::Create( "https://github.com/xenia-project/xenia/compare/" XE_BUILD_COMMIT
MenuItem::Type::kString, L"Recent changes on GitHub...", [this]() { "..." XE_BUILD_BRANCH);
std::wstring url =
std::wstring(L"https://github.com/xenia-project/xenia/compare/") +
xe::to_wstring(XE_BUILD_COMMIT) + L"..." +
xe::to_wstring(XE_BUILD_BRANCH);
LaunchBrowser(url.c_str());
})); }));
help_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator)); help_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
help_menu->AddChild( help_menu->AddChild(
MenuItem::Create(MenuItem::Type::kString, L"&Website...", L"F1", MenuItem::Create(MenuItem::Type::kString, "&Website...", "F1",
std::bind(&EmulatorWindow::ShowHelpWebsite, this))); std::bind(&EmulatorWindow::ShowHelpWebsite, this)));
help_menu->AddChild(MenuItem::Create( help_menu->AddChild(MenuItem::Create(
MenuItem::Type::kString, L"&About...", MenuItem::Type::kString, "&About...",
[this]() { LaunchBrowser(L"https://xenia.jp/about/"); })); [this]() { LaunchWebBrowser("https://xenia.jp/about/"); }));
} }
main_menu->AddChild(std::move(help_menu)); main_menu->AddChild(std::move(help_menu));
@ -293,7 +290,7 @@ bool EmulatorWindow::Initialize() {
return true; return true;
} }
void EmulatorWindow::FileDrop(const wchar_t* filename) { void EmulatorWindow::FileDrop(const std::filesystem::path& filename) {
auto result = emulator_->LaunchPath(filename); auto result = emulator_->LaunchPath(filename);
if (XFAILED(result)) { if (XFAILED(result)) {
// TODO: Display a message box. // TODO: Display a message box.
@ -302,19 +299,19 @@ void EmulatorWindow::FileDrop(const wchar_t* filename) {
} }
void EmulatorWindow::FileOpen() { void EmulatorWindow::FileOpen() {
std::wstring path; std::filesystem::path path;
auto file_picker = xe::ui::FilePicker::Create(); auto file_picker = xe::ui::FilePicker::Create();
file_picker->set_mode(ui::FilePicker::Mode::kOpen); file_picker->set_mode(ui::FilePicker::Mode::kOpen);
file_picker->set_type(ui::FilePicker::Type::kFile); file_picker->set_type(ui::FilePicker::Type::kFile);
file_picker->set_multi_selection(false); file_picker->set_multi_selection(false);
file_picker->set_title(L"Select Content Package"); file_picker->set_title("Select Content Package");
file_picker->set_extensions({ file_picker->set_extensions({
{L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"}, {"Supported Files", "*.iso;*.xex;*.xcp;*.*"},
{L"Disc Image (*.iso)", L"*.iso"}, {"Disc Image (*.iso)", "*.iso"},
{L"Xbox Executable (*.xex)", L"*.xex"}, {"Xbox Executable (*.xex)", "*.xex"},
//{ L"Content Package (*.xcp)", L"*.xcp" }, //{"Content Package (*.xcp)", "*.xcp" },
{L"All Files (*.*)", L"*.*"}, {"All Files (*.*)", "*.*"},
}); });
if (file_picker->Show(window_->native_handle())) { if (file_picker->Show(window_->native_handle())) {
auto selected_files = file_picker->selected_files(); auto selected_files = file_picker->selected_files();
@ -325,8 +322,7 @@ void EmulatorWindow::FileOpen() {
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); auto abs_path = std::filesystem::absolute(path);
auto result = emulator_->LaunchPath(abs_path); auto result = emulator_->LaunchPath(abs_path);
if (XFAILED(result)) { if (XFAILED(result)) {
// TODO: Display a message box. // TODO: Display a message box.
@ -342,16 +338,16 @@ void EmulatorWindow::FileClose() {
} }
void EmulatorWindow::ShowContentDirectory() { void EmulatorWindow::ShowContentDirectory() {
std::wstring target_path; std::filesystem::path target_path;
auto content_root = emulator_->content_root(); auto content_root = emulator_->content_root();
if (!emulator_->is_title_open() || !emulator_->kernel_state()) { if (!emulator_->is_title_open() || !emulator_->kernel_state()) {
target_path = content_root; target_path = content_root;
} else { } else {
// TODO(gibbed): expose this via ContentManager? // TODO(gibbed): expose this via ContentManager?
wchar_t title_id[9] = L"00000000"; auto title_id =
std::swprintf(title_id, 9, L"%.8X", emulator_->kernel_state()->title_id()); fmt::format("{:08X}", emulator_->kernel_state()->title_id());
auto package_root = xe::join_paths(content_root, title_id); auto package_root = content_root / title_id;
target_path = package_root; target_path = package_root;
} }
@ -359,7 +355,7 @@ void EmulatorWindow::ShowContentDirectory() {
xe::filesystem::CreateFolder(target_path); xe::filesystem::CreateFolder(target_path);
} }
LaunchBrowser(target_path.c_str()); LaunchFileExplorer(target_path);
} }
void EmulatorWindow::CheckHideCursor() { void EmulatorWindow::CheckHideCursor() {
@ -426,36 +422,36 @@ void EmulatorWindow::ToggleFullscreen() {
} }
} }
void EmulatorWindow::ShowHelpWebsite() { LaunchBrowser(L"https://xenia.jp"); } void EmulatorWindow::ShowHelpWebsite() { LaunchWebBrowser("https://xenia.jp"); }
void EmulatorWindow::ShowCommitID() { void EmulatorWindow::ShowCommitID() {
std::wstring url = LaunchWebBrowser(
std::wstring(L"https://github.com/xenia-project/xenia/commit/") + "https://github.com/xenia-project/xenia/commit/" XE_BUILD_COMMIT "/");
xe::to_wstring(XE_BUILD_COMMIT) + L"/";
LaunchBrowser(url.c_str());
} }
void EmulatorWindow::UpdateTitle() { void EmulatorWindow::UpdateTitle() {
std::wstring title(base_title_); std::string title(base_title_);
if (emulator()->is_title_open()) { if (emulator()->is_title_open()) {
auto game_title = emulator()->game_title(); auto game_title = emulator()->game_title();
title += xe::format_string(L" | [%.8X] %s", emulator()->title_id(), title += fmt::format(" | [{:08X}] {}", emulator()->title_id(), game_title);
game_title.c_str());
} }
auto graphics_system = emulator()->graphics_system(); auto graphics_system = emulator()->graphics_system();
if (graphics_system) { if (graphics_system) {
auto graphics_name = graphics_system->name(); auto graphics_name = graphics_system->name();
title += L" <" + graphics_name + L">"; title += fmt::format(" <{}>", graphics_name);
} }
if (Clock::guest_time_scalar() != 1.0) { if (Clock::guest_time_scalar() != 1.0) {
title += xe::format_string(L" (@%.2fx)", Clock::guest_time_scalar()); title += fmt::format(" (@{:.2f}x)", Clock::guest_time_scalar());
} }
if (initializing_shader_storage_) { if (initializing_shader_storage_) {
title += L" (Preloading shaders\u2026)"; title +=
" (Preloading shaders"
u8"\u2026"
")";
} }
window_->set_title(title); window_->set_title(title);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -44,7 +44,7 @@ class EmulatorWindow {
bool Initialize(); bool Initialize();
void FileDrop(const wchar_t* filename); void FileDrop(const std::filesystem::path& filename);
void FileOpen(); void FileOpen();
void FileClose(); void FileClose();
void ShowContentDirectory(); void ShowContentDirectory();
@ -62,7 +62,7 @@ class EmulatorWindow {
Emulator* emulator_; Emulator* emulator_;
std::unique_ptr<ui::Loop> loop_; std::unique_ptr<ui::Loop> loop_;
std::unique_ptr<ui::Window> window_; std::unique_ptr<ui::Window> window_;
std::wstring base_title_; std::string base_title_;
uint64_t cursor_hide_time_ = 0; uint64_t cursor_hide_time_ = 0;
bool initializing_shader_storage_ = false; bool initializing_shader_storage_ = false;
}; };

View File

@ -10,6 +10,7 @@ project("xenia-app")
links({ links({
"aes_128", "aes_128",
"capstone", "capstone",
"fmt",
"dxbc", "dxbc",
"discord-rpc", "discord-rpc",
"glslang-spirv", "glslang-spirv",

View File

@ -44,6 +44,7 @@
#include "xenia/hid/xinput/xinput_hid.h" #include "xenia/hid/xinput/xinput_hid.h"
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/xbyak/xbyak/xbyak_util.h" #include "third_party/xbyak/xbyak/xbyak_util.h"
DEFINE_string(apu, "any", "Audio system. Use: [any, nop, sdl, xaudio2]", "APU"); DEFINE_string(apu, "any", "Audio system. Use: [any, nop, sdl, xaudio2]", "APU");
@ -54,13 +55,13 @@ DEFINE_string(hid, "any", "Input system. Use: [any, nop, sdl, winkey, xinput]",
DEFINE_bool(fullscreen, false, "Toggles fullscreen", "GPU"); DEFINE_bool(fullscreen, false, "Toggles fullscreen", "GPU");
DEFINE_string( DEFINE_path(
storage_root, "", storage_root, "",
"Root path for persistent internal data storage (config, etc.), or empty " "Root path for persistent internal data storage (config, etc.), or empty "
"to use the path preferred for the OS, such as the documents folder, or " "to use the path preferred for the OS, such as the documents folder, or "
"the emulator executable directory if portable.txt is present in it.", "the emulator executable directory if portable.txt is present in it.",
"Storage"); "Storage");
DEFINE_string( DEFINE_path(
content_root, "", content_root, "",
"Root path for guest content storage (saves, etc.), or empty to use the " "Root path for guest content storage (saves, etc.), or empty to use the "
"content folder under the storage root.", "content folder under the storage root.",
@ -69,9 +70,9 @@ DEFINE_string(
DEFINE_bool(mount_scratch, false, "Enable scratch mount", "Storage"); DEFINE_bool(mount_scratch, false, "Enable scratch mount", "Storage");
DEFINE_bool(mount_cache, false, "Enable cache mount", "Storage"); DEFINE_bool(mount_cache, false, "Enable cache mount", "Storage");
DEFINE_transient_string(target, "", DEFINE_transient_path(target, "",
"Specifies the target .xex or .iso to execute.", "Specifies the target .xex or .iso to execute.",
"General"); "General");
DECLARE_bool(debug); DECLARE_bool(debug);
DEFINE_bool(discord, true, "Enable Discord rich presence", "General"); DEFINE_bool(discord, true, "Enable Discord rich presence", "General");
@ -91,25 +92,25 @@ class Factory {
std::vector<Creator> creators_; std::vector<Creator> creators_;
public: public:
void Add(const std::string& name, std::function<bool()> is_available, void Add(const std::string_view name, std::function<bool()> is_available,
std::function<std::unique_ptr<T>(Args...)> instantiate) { std::function<std::unique_ptr<T>(Args...)> instantiate) {
creators_.push_back({name, is_available, instantiate}); creators_.push_back({std::string(name), is_available, instantiate});
} }
void Add(const std::string& name, void Add(const std::string_view name,
std::function<std::unique_ptr<T>(Args...)> instantiate) { std::function<std::unique_ptr<T>(Args...)> instantiate) {
auto always_available = []() { return true; }; auto always_available = []() { return true; };
Add(name, always_available, instantiate); Add(name, always_available, instantiate);
} }
template <typename DT> template <typename DT>
void Add(const std::string& name) { void Add(const std::string_view name) {
Add(name, DT::IsAvailable, [](Args... args) { Add(name, DT::IsAvailable, [](Args... args) {
return std::make_unique<DT>(std::forward<Args>(args)...); return std::make_unique<DT>(std::forward<Args>(args)...);
}); });
} }
std::unique_ptr<T> Create(const std::string& name, Args... args) { std::unique_ptr<T> Create(const std::string_view name, Args... args) {
if (!name.empty() && name != "any") { if (!name.empty() && name != "any") {
auto it = std::find_if( auto it = std::find_if(
creators_.cbegin(), creators_.cend(), creators_.cbegin(), creators_.cend(),
@ -129,7 +130,7 @@ class Factory {
} }
} }
std::vector<std::unique_ptr<T>> CreateAll(const std::string& name, std::vector<std::unique_ptr<T>> CreateAll(const std::string_view name,
Args... args) { Args... args) {
std::vector<std::unique_ptr<T>> instances; std::vector<std::unique_ptr<T>> instances;
if (!name.empty() && name != "any") { if (!name.empty() && name != "any") {
@ -206,35 +207,34 @@ std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
return drivers; return drivers;
} }
int xenia_main(const std::vector<std::wstring>& args) { int xenia_main(const std::vector<std::string>& args) {
Profiler::Initialize(); Profiler::Initialize();
Profiler::ThreadEnter("main"); Profiler::ThreadEnter("main");
// Figure out where internal files and content should go. // Figure out where internal files and content should go.
std::wstring storage_root = xe::to_wstring(cvars::storage_root); std::filesystem::path storage_root = cvars::storage_root;
if (storage_root.empty()) { if (storage_root.empty()) {
storage_root = xe::filesystem::GetExecutableFolder(); storage_root = xe::filesystem::GetExecutableFolder();
if (!xe::filesystem::PathExists( if (!xe::filesystem::PathExists(storage_root / "portable.txt")) {
xe::join_paths(storage_root, L"portable.txt"))) {
storage_root = xe::filesystem::GetUserFolder(); storage_root = xe::filesystem::GetUserFolder();
#if defined(XE_PLATFORM_WIN32) || defined(XE_PLATFORM_LINUX) #if defined(XE_PLATFORM_WIN32) || defined(XE_PLATFORM_LINUX)
storage_root = xe::join_paths(storage_root, L"Xenia"); storage_root = storage_root / "Xenia";
#else #else
#warning Unhandled platform for the data root. #warning Unhandled platform for the data root.
storage_root = xe::join_paths(storage_root, L"Xenia"); storage_root = storage_root / "Xenia";
#endif #endif
} }
} }
storage_root = xe::to_absolute_path(storage_root); storage_root = std::filesystem::absolute(storage_root);
XELOGI("Storage root: %S", storage_root.c_str()); XELOGI("Storage root: %S", storage_root.c_str());
config::SetupConfig(storage_root); config::SetupConfig(storage_root);
std::wstring content_root = xe::to_wstring(cvars::content_root); std::filesystem::path content_root = cvars::content_root;
if (content_root.empty()) { if (content_root.empty()) {
content_root = xe::join_paths(storage_root, L"content"); content_root = storage_root / "content";
} }
content_root = xe::to_absolute_path(content_root); content_root = std::filesystem::absolute(content_root);
XELOGI("Content root: %S", content_root.c_str()); XELOGI("Content root: %S", content_root.c_str());
if (cvars::discord) { if (cvars::discord) {
@ -243,7 +243,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
} }
// Create the emulator but don't initialize so we can setup the window. // Create the emulator but don't initialize so we can setup the window.
auto emulator = std::make_unique<Emulator>(L"", storage_root, content_root); auto emulator = std::make_unique<Emulator>("", storage_root, content_root);
// Main emulator display window. // Main emulator display window.
auto emulator_window = EmulatorWindow::Create(emulator.get()); auto emulator_window = EmulatorWindow::Create(emulator.get());
@ -260,7 +260,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
if (cvars::mount_scratch) { if (cvars::mount_scratch) {
auto scratch_device = std::make_unique<xe::vfs::HostPathDevice>( auto scratch_device = std::make_unique<xe::vfs::HostPathDevice>(
"\\SCRATCH", L"scratch", false); "\\SCRATCH", "scratch", false);
if (!scratch_device->Initialize()) { if (!scratch_device->Initialize()) {
XELOGE("Unable to scan scratch path"); XELOGE("Unable to scan scratch path");
} else { } else {
@ -274,7 +274,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
if (cvars::mount_cache) { if (cvars::mount_cache) {
auto cache0_device = auto cache0_device =
std::make_unique<xe::vfs::HostPathDevice>("\\CACHE0", L"cache0", false); std::make_unique<xe::vfs::HostPathDevice>("\\CACHE0", "cache0", false);
if (!cache0_device->Initialize()) { if (!cache0_device->Initialize()) {
XELOGE("Unable to scan cache0 path"); XELOGE("Unable to scan cache0 path");
} else { } else {
@ -286,7 +286,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
} }
auto cache1_device = auto cache1_device =
std::make_unique<xe::vfs::HostPathDevice>("\\CACHE1", L"cache1", false); std::make_unique<xe::vfs::HostPathDevice>("\\CACHE1", "cache1", false);
if (!cache1_device->Initialize()) { if (!cache1_device->Initialize()) {
XELOGE("Unable to scan cache1 path"); XELOGE("Unable to scan cache1 path");
} else { } else {
@ -325,7 +325,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
emulator->on_launch.AddListener([&](auto title_id, const auto& game_title) { emulator->on_launch.AddListener([&](auto title_id, const auto& game_title) {
if (cvars::discord) { if (cvars::discord) {
discord::DiscordPresence::PlayingTitle( discord::DiscordPresence::PlayingTitle(
game_title.empty() ? L"Unknown Title" : game_title); game_title.empty() ? "Unknown Title" : std::string(game_title));
} }
emulator_window->UpdateTitle(); emulator_window->UpdateTitle();
evt->Set(); evt->Set();
@ -365,9 +365,9 @@ int xenia_main(const std::vector<std::wstring>& args) {
emulator_window->window()->EnableMainMenu(); emulator_window->window()->EnableMainMenu();
// Grab path from the flag or unnamed argument. // Grab path from the flag or unnamed argument.
std::wstring path; std::filesystem::path path;
if (!cvars::target.empty()) { if (!cvars::target.empty()) {
path = xe::to_wstring(cvars::target); path = cvars::target;
} }
// Toggles fullscreen // Toggles fullscreen
@ -375,10 +375,10 @@ int xenia_main(const std::vector<std::wstring>& args) {
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); auto abs_path = std::filesystem::absolute(path);
result = emulator->LaunchPath(abs_path); result = emulator->LaunchPath(abs_path);
if (XFAILED(result)) { if (XFAILED(result)) {
xe::FatalError("Failed to launch target: %.8X", result); xe::FatalError(fmt::format("Failed to launch target: {:08X}", result));
emulator.reset(); emulator.reset();
emulator_window.reset(); emulator_window.reset();
return 1; return 1;
@ -416,5 +416,5 @@ int xenia_main(const std::vector<std::wstring>& args) {
} // namespace app } // namespace app
} // namespace xe } // namespace xe
DEFINE_ENTRY_POINT(L"xenia", xe::app::xenia_main, "[Path to .iso/.xex]", DEFINE_ENTRY_POINT("xenia", xe::app::xenia_main, "[Path to .iso/.xex]",
"target"); "target");

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -58,11 +58,11 @@ bool XAudio2AudioDriver::Initialize() {
// Windows 10 SDK references XAudio2_9.dll in it, which is only available in // Windows 10 SDK references XAudio2_9.dll in it, which is only available in
// Windows 10, and XAudio2_8.dll is linked through a different .lib - // Windows 10, and XAudio2_8.dll is linked through a different .lib -
// xaudio2_8.lib, so easier not to link the .lib at all. // xaudio2_8.lib, so easier not to link the .lib at all.
xaudio2_module_ = reinterpret_cast<void*>(LoadLibrary(L"XAudio2_8.dll")); xaudio2_module_ = reinterpret_cast<void*>(LoadLibraryW(L"XAudio2_8.dll"));
if (xaudio2_module_) { if (xaudio2_module_) {
api_minor_version_ = 8; api_minor_version_ = 8;
} else { } else {
xaudio2_module_ = reinterpret_cast<void*>(LoadLibrary(L"XAudio2_7.dll")); xaudio2_module_ = reinterpret_cast<void*>(LoadLibraryW(L"XAudio2_7.dll"));
if (xaudio2_module_) { if (xaudio2_module_) {
api_minor_version_ = 7; api_minor_version_ = 7;
} else { } else {

View File

@ -91,7 +91,7 @@ void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
StringBuffer buff; StringBuffer buff;
buff.AppendVarargs(fmt, va); buff.AppendVarargs(fmt, va);
xe::LogLineFormat(log_level, level_char, "libav: %s", buff.GetString()); xe::LogLineFormat(log_level, level_char, "libav: %s", buff.buffer());
} }
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -40,8 +40,8 @@ inline int16_t byte_swap(int16_t value) {
inline uint16_t byte_swap(uint16_t value) { inline uint16_t byte_swap(uint16_t value) {
return XENIA_BASE_BYTE_SWAP_16(value); return XENIA_BASE_BYTE_SWAP_16(value);
} }
inline uint16_t byte_swap(wchar_t value) { inline uint16_t byte_swap(char16_t value) {
return static_cast<wchar_t>(XENIA_BASE_BYTE_SWAP_16(value)); return static_cast<char16_t>(XENIA_BASE_BYTE_SWAP_16(value));
} }
inline int32_t byte_swap(int32_t value) { inline int32_t byte_swap(int32_t value) {
return static_cast<int32_t>( return static_cast<int32_t>(

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -47,8 +47,8 @@ std::string ByteStream::Read() {
} }
template <> template <>
std::wstring ByteStream::Read() { std::u16string ByteStream::Read() {
std::wstring str; std::u16string str;
uint32_t len = Read<uint32_t>(); uint32_t len = Read<uint32_t>();
str.resize(len); str.resize(len);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -51,14 +51,14 @@ class ByteStream {
Write(reinterpret_cast<uint8_t*>(&data), sizeof(T)); Write(reinterpret_cast<uint8_t*>(&data), sizeof(T));
} }
void Write(const std::string& str) { void Write(const std::string_view str) {
Write(uint32_t(str.length())); Write(uint32_t(str.length()));
Write(str.c_str(), str.length()); Write(str.data(), str.length() * sizeof(char));
} }
void Write(const std::wstring& str) { void Write(const std::u16string_view str) {
Write(uint32_t(str.length())); Write(uint32_t(str.length()));
Write(str.c_str(), str.length() * 2); Write(str.data(), str.length() * sizeof(char16_t));
} }
private: private:
@ -71,7 +71,7 @@ template <>
std::string ByteStream::Read(); std::string ByteStream::Read();
template <> template <>
std::wstring ByteStream::Read(); std::u16string ByteStream::Read();
} // namespace xe } // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -36,12 +36,10 @@
#error "No cpu instruction wrappers for current compiler implemented." #error "No cpu instruction wrappers for current compiler implemented."
#endif #endif
#define CLOCK_FATAL(msg) \ #define CLOCK_FATAL(msg) \
xe::FatalError( \ xe::FatalError("The raw clock source is not supported on your CPU.\n" msg \
"The raw clock source is not supported on your CPU. \n" \ "\n" \
"%s \n" \ "Set the cvar 'clock_source_raw' to 'false'.");
"Set the cvar 'clock_source_raw' to 'false'.", \
(msg));
namespace xe { namespace xe {
// Getting the TSC frequency can be a bit tricky. This method here only works on // Getting the TSC frequency can be a bit tricky. This method here only works on

View File

@ -2,13 +2,22 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "cvar.h" #include "cvar.h"
#include "utf8.h"
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
namespace utfcpp = utf8;
using u8_citer = utfcpp::iterator<std::string_view::const_iterator>;
namespace cvar { namespace cvar {
cxxopts::Options options("xenia", "Xbox 360 Emulator"); cxxopts::Options options("xenia", "Xbox 360 Emulator");
@ -22,37 +31,45 @@ void PrintHelpAndExit() {
exit(0); exit(0);
} }
void ParseLaunchArguments(int argc, char** argv, void ParseLaunchArguments(int& argc, char**& argv,
const std::string& positional_help, const std::string_view positional_help,
const std::vector<std::string>& positional_options) { const std::vector<std::string>& positional_options) {
options.add_options()("help", "Prints help and exit."); options.add_options()("help", "Prints help and exit.");
if (!CmdVars) CmdVars = new std::map<std::string, ICommandVar*>();
if (!ConfigVars) ConfigVars = new std::map<std::string, IConfigVar*>(); if (!CmdVars) {
CmdVars = new std::map<std::string, ICommandVar*>();
}
if (!ConfigVars) {
ConfigVars = new std::map<std::string, IConfigVar*>();
}
for (auto& it : *CmdVars) { for (auto& it : *CmdVars) {
auto cmdVar = it.second; auto cmdVar = it.second;
cmdVar->AddToLaunchOptions(&options); cmdVar->AddToLaunchOptions(&options);
} }
std::vector<IConfigVar*> vars;
for (const auto& s : *ConfigVars) vars.push_back(s.second);
for (auto& it : *ConfigVars) { for (const auto& it : *ConfigVars) {
auto configVar = it.second; auto configVar = it.second;
configVar->AddToLaunchOptions(&options); configVar->AddToLaunchOptions(&options);
} }
try { try {
options.positional_help(positional_help); options.positional_help(std::string(positional_help));
options.parse_positional(positional_options); options.parse_positional(positional_options);
auto result = options.parse(argc, argv); auto result = options.parse(argc, argv);
if (result.count("help")) { if (result.count("help")) {
PrintHelpAndExit(); PrintHelpAndExit();
} }
for (auto& it : *CmdVars) { for (auto& it : *CmdVars) {
auto cmdVar = static_cast<ICommandVar*>(it.second); auto cmdVar = static_cast<ICommandVar*>(it.second);
if (result.count(cmdVar->name())) { if (result.count(cmdVar->name())) {
cmdVar->LoadFromLaunchOptions(&result); cmdVar->LoadFromLaunchOptions(&result);
} }
} }
for (auto& it : *ConfigVars) { for (auto& it : *ConfigVars) {
auto configVar = static_cast<IConfigVar*>(it.second); auto configVar = static_cast<IConfigVar*>(it.second);
if (result.count(configVar->name())) { if (result.count(configVar->name())) {
@ -67,48 +84,46 @@ void ParseLaunchArguments(int argc, char** argv,
namespace toml { namespace toml {
std::string EscapeBasicString(const std::string& str) { std::string EscapeBasicString(const std::string_view view) {
std::string result; std::string result;
for (auto c : str) { auto begin = u8_citer(view.cbegin(), view.cbegin(), view.cend());
auto end = u8_citer(view.cend(), view.cbegin(), view.cend());
for (auto it = begin; it != end; ++it) {
auto c = *it;
if (c == '\b') { if (c == '\b') {
result += "\\b"; result += u8"\\b";
} else if (c == '\t') { } else if (c == '\t') {
result += "\\t"; result += u8"\\t";
} else if (c == '\n') { } else if (c == '\n') {
result += "\\n"; result += u8"\\n";
} else if (c == '\f') { } else if (c == '\f') {
result += "\\f"; result += u8"\\f";
} else if (c == '\r') { } else if (c == '\r') {
result += "\\r"; result += u8"\\r";
} else if (c == '"') { } else if (c == '"') {
result += "\\\""; result += u8"\\\"";
} else if (c == '\\') { } else if (c == '\\') {
result += "\\\\"; result += u8"\\\\";
} else if (static_cast<uint32_t>(c) < 0x20 || } else if (c < 0x20 || c == 0x7F) {
static_cast<uint32_t>(c) == 0x7F) { if (c <= 0xFFFF) {
auto v = static_cast<uint32_t>(c); result += fmt::format(u8"\\u{:04X}", c);
int w;
if (v <= 0xFFFF) {
result += "\\u";
w = 4;
} else { } else {
result += "\\U"; result += fmt::format(u8"\\u{:08X}", c);
w = 8;
} }
std::stringstream ss;
ss << std::hex << std::setw(w) << std::setfill('0') << v;
result += ss.str();
} else { } else {
result += c; utfcpp::append(static_cast<char32_t>(c), result);
} }
} }
return result; return result;
} }
std::string EscapeMultilineBasicString(const std::string& str) { std::string EscapeMultilineBasicString(const std::string_view view) {
std::string result; std::string result;
int quote_run = 0; int quote_run = 0;
for (char c : str) { auto begin = u8_citer(view.cbegin(), view.cbegin(), view.cend());
auto end = u8_citer(view.cend(), view.cbegin(), view.cend());
for (auto it = begin; it != end; ++it) {
auto c = *it;
if (quote_run > 0) { if (quote_run > 0) {
if (c == '"') { if (c == '"') {
++quote_run; ++quote_run;
@ -116,74 +131,67 @@ std::string EscapeMultilineBasicString(const std::string& str) {
} }
for (int i = 0; i < quote_run; ++i) { for (int i = 0; i < quote_run; ++i) {
if ((i % 3) == 2) { if ((i % 3) == 2) {
result += "\\"; result += u8"\\";
} }
result += '"'; result += u8"\"";
} }
quote_run = 0; quote_run = 0;
} }
if (c == '\b') { if (c == '\b') {
result += "\\b"; result += u8"\\b";
} else if (c == '\t' || c == '\n') { } else if (c == '\t' || c == '\n') {
result += c; result += c;
} else if (c == '\f') { } else if (c == '\f') {
result += "\\f"; result += u8"\\f";
} else if (c == '\r') { } else if (c == '\r') {
// Silently drop \r. // Silently drop \r.
// result += c; // result += c;
} else if (c == '"') { } else if (c == '"') {
quote_run = 1; quote_run = 1;
} else if (c == '\\') { } else if (c == '\\') {
result += "\\\\"; result += u8"\\\\";
} else if (static_cast<uint32_t>(c) < 0x20 || } else if (c < 0x20 || c == 0x7F) {
static_cast<uint32_t>(c) == 0x7F) { if (c <= 0xFFFF) {
auto v = static_cast<uint32_t>(c); result += fmt::format(u8"\\u{:04X}", c);
int w;
if (v <= 0xFFFF) {
result += "\\u";
w = 4;
} else { } else {
result += "\\U"; result += fmt::format(u8"\\u{:08X}", c);
w = 8;
} }
std::stringstream ss;
ss << std::hex << std::setw(w) << std::setfill('0') << v;
result += ss.str();
} else { } else {
result += c; utfcpp::append(static_cast<char32_t>(c), result);
} }
} }
for (int i = 0; i < quote_run; ++i) { for (int i = 0; i < quote_run; ++i) {
if ((i % 3) == 2) { if ((i % 3) == 2) {
result += "\\"; result += u8"\\";
} }
result += '"'; result += u8"\"";
} }
return result; return result;
} }
std::string EscapeString(const std::string& val) { std::string EscapeString(const std::string_view view) {
const char multiline_chars[] = "\r\n"; const auto multiline_chars = std::string_view("\r\n");
const char escape_chars[] = const auto escape_chars = std::string_view(
"\0\b\v\f" "\0\b\v\f"
"\x01\x02\x03\x04\x05\x06\x07\x0E\x0F" "\x01\x02\x03\x04\x05\x06\x07\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"'" "'"
"\x7F"; "\x7F");
if (val.find_first_of(multiline_chars) == std::string::npos) {
if (xe::utf8::find_any_of(view, multiline_chars) == std::string_view::npos) {
// single line // single line
if (val.find_first_of(escape_chars) == std::string::npos) { if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos) {
return "'" + val + "'"; return "'" + std::string(view) + "'";
} else { } else {
return "\"" + toml::EscapeBasicString(val) + "\""; return "\"" + toml::EscapeBasicString(view) + "\"";
} }
} else { } else {
// multi line // multi line
if (val.find_first_of(escape_chars) == std::string::npos && if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos &&
val.find("'''") == std::string::npos) { xe::utf8::find_first_of(view, u8"'''") == std::string_view::npos) {
return "'''\n" + val + "'''"; return "'''\n" + std::string(view) + "'''";
} else { } else {
return "\"\"\"\n" + toml::EscapeMultilineBasicString(val) + "\"\"\""; return u8"\"\"\"\n" + toml::EscapeMultilineBasicString(view) + u8"\"\"\"";
} }
} }
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,18 +10,20 @@
#ifndef XENIA_CVAR_H_ #ifndef XENIA_CVAR_H_
#define XENIA_CVAR_H_ #define XENIA_CVAR_H_
#include <filesystem>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "cpptoml/include/cpptoml.h" #include "cpptoml/include/cpptoml.h"
#include "cxxopts/include/cxxopts.hpp" #include "cxxopts/include/cxxopts.hpp"
#include "xenia/base/filesystem.h"
#include "xenia/base/string_util.h" #include "xenia/base/string_util.h"
namespace cvar { namespace cvar {
namespace toml { namespace toml {
std::string EscapeString(const std::string& str); std::string EscapeString(const std::string_view str);
} }
class ICommandVar { class ICommandVar {
@ -116,10 +118,22 @@ template <class T>
void ConfigVar<T>::LoadConfigValue(std::shared_ptr<cpptoml::base> result) { void ConfigVar<T>::LoadConfigValue(std::shared_ptr<cpptoml::base> result) {
SetConfigValue(*cpptoml::get_impl<T>(result)); SetConfigValue(*cpptoml::get_impl<T>(result));
} }
template <>
inline void ConfigVar<std::filesystem::path>::LoadConfigValue(
std::shared_ptr<cpptoml::base> result) {
SetConfigValue(
xe::utf8::fix_path_separators(*cpptoml::get_impl<std::string>(result)));
}
template <class T> template <class T>
void ConfigVar<T>::LoadGameConfigValue(std::shared_ptr<cpptoml::base> result) { void ConfigVar<T>::LoadGameConfigValue(std::shared_ptr<cpptoml::base> result) {
SetGameConfigValue(*cpptoml::get_impl<T>(result)); SetGameConfigValue(*cpptoml::get_impl<T>(result));
} }
template <>
inline void ConfigVar<std::filesystem::path>::LoadGameConfigValue(
std::shared_ptr<cpptoml::base> result) {
SetGameConfigValue(
xe::utf8::fix_path_separators(*cpptoml::get_impl<std::string>(result)));
}
template <class T> template <class T>
CommandVar<T>::CommandVar(const char* name, T* default_value, CommandVar<T>::CommandVar(const char* name, T* default_value,
const char* description) const char* description)
@ -158,6 +172,11 @@ template <>
inline std::string CommandVar<std::string>::Convert(std::string val) { inline std::string CommandVar<std::string>::Convert(std::string val) {
return val; return val;
} }
template <>
inline std::filesystem::path CommandVar<std::filesystem::path>::Convert(
std::string val) {
return xe::to_path(val);
}
template <> template <>
inline std::string CommandVar<bool>::ToString(bool val) { inline std::string CommandVar<bool>::ToString(bool val) {
@ -167,6 +186,12 @@ template <>
inline std::string CommandVar<std::string>::ToString(std::string val) { inline std::string CommandVar<std::string>::ToString(std::string val) {
return toml::EscapeString(val); return toml::EscapeString(val);
} }
template <>
inline std::string CommandVar<std::filesystem::path>::ToString(
std::filesystem::path val) {
return toml::EscapeString(
xe::utf8::fix_path_separators(xe::path_to_utf8(val), '/'));
}
template <class T> template <class T>
std::string CommandVar<T>::ToString(T val) { std::string CommandVar<T>::ToString(T val) {
@ -217,8 +242,8 @@ inline void AddCommandVar(ICommandVar* cv) {
if (!CmdVars) CmdVars = new std::map<std::string, ICommandVar*>(); if (!CmdVars) CmdVars = new std::map<std::string, ICommandVar*>();
CmdVars->insert(std::pair<std::string, ICommandVar*>(cv->name(), cv)); CmdVars->insert(std::pair<std::string, ICommandVar*>(cv->name(), cv));
} }
void ParseLaunchArguments(int argc, char** argv, void ParseLaunchArguments(int& argc, char**& argv,
const std::string& positional_help, const std::string_view positional_help,
const std::vector<std::string>& positional_options); const std::vector<std::string>& positional_options);
template <typename T> template <typename T>
@ -237,6 +262,9 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
return default_value; return default_value;
} }
#define DEFINE_bool(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, false, bool)
#define DEFINE_int32(name, default_value, description, category) \ #define DEFINE_int32(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, false, int32_t) DEFINE_CVar(name, default_value, description, category, false, int32_t)
@ -249,11 +277,16 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
#define DEFINE_string(name, default_value, description, category) \ #define DEFINE_string(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, false, std::string) DEFINE_CVar(name, default_value, description, category, false, std::string)
#define DEFINE_path(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, false, \
std::filesystem::path)
#define DEFINE_transient_string(name, default_value, description, category) \ #define DEFINE_transient_string(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, true, std::string) DEFINE_CVar(name, default_value, description, category, true, std::string)
#define DEFINE_bool(name, default_value, description, category) \ #define DEFINE_transient_path(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, false, bool) DEFINE_CVar(name, default_value, description, category, true, \
std::filesystem::path)
#define DEFINE_CVar(name, default_value, description, category, is_transient, \ #define DEFINE_CVar(name, default_value, description, category, is_transient, \
type) \ type) \
@ -275,16 +308,18 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
cvar::define_cmdvar(#name, &cvars::name, description); \ cvar::define_cmdvar(#name, &cvars::name, description); \
} }
#define DECLARE_double(name) DECLARE_CVar(name, double)
#define DECLARE_bool(name) DECLARE_CVar(name, bool) #define DECLARE_bool(name) DECLARE_CVar(name, bool)
#define DECLARE_string(name) DECLARE_CVar(name, std::string)
#define DECLARE_int32(name) DECLARE_CVar(name, int32_t) #define DECLARE_int32(name) DECLARE_CVar(name, int32_t)
#define DECLARE_uint64(name) DECLARE_CVar(name, uint64_t) #define DECLARE_uint64(name) DECLARE_CVar(name, uint64_t)
#define DECLARE_double(name) DECLARE_CVar(name, double)
#define DECLARE_string(name) DECLARE_CVar(name, std::string)
#define DECLARE_path(name) DECLARE_CVar(name, std::filesystem::path)
#define DECLARE_CVar(name, type) \ #define DECLARE_CVar(name, type) \
namespace cvars { \ namespace cvars { \
extern type name; \ extern type name; \

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -14,96 +14,14 @@
namespace xe { namespace xe {
namespace filesystem { namespace filesystem {
std::string CanonicalizePath(const std::string& original_path) { bool CreateParentFolder(const std::filesystem::path& path) {
char path_sep(xe::kPathSeparator); if (path.has_parent_path()) {
std::string path(xe::fix_path_separators(original_path, path_sep)); auto parent_path = path.parent_path();
if (!PathExists(parent_path)) {
std::vector<std::string::size_type> path_breaks; return CreateFolder(parent_path);
std::string::size_type pos(path.find_first_of(path_sep));
std::string::size_type pos_n(std::string::npos);
while (pos != std::string::npos) {
if ((pos_n = path.find_first_of(path_sep, pos + 1)) == std::string::npos) {
pos_n = path.size();
} }
auto diff(pos_n - pos);
switch (diff) {
case 0:
pos_n = std::string::npos;
break;
case 1:
// Duplicate separators.
path.erase(pos, 1);
pos_n -= 1;
break;
case 2:
// Potential marker for current directory.
if (path[pos + 1] == '.') {
path.erase(pos, 2);
pos_n -= 2;
} else {
path_breaks.push_back(pos);
}
break;
case 3:
// Potential marker for parent directory.
if (path[pos + 1] == '.' && path[pos + 2] == '.') {
if (path_breaks.empty()) {
// Ensure we don't override the device name.
std::string::size_type loc(path.find_first_of(':'));
auto req(pos + 3);
if (loc == std::string::npos || loc > req) {
path.erase(0, req);
pos_n -= req;
} else {
path.erase(loc + 1, req - (loc + 1));
pos_n -= req - (loc + 1);
}
} else {
auto last(path_breaks.back());
auto last_diff((pos + 3) - last);
path.erase(last, last_diff);
pos_n = last;
// Also remove path reference.
path_breaks.erase(path_breaks.end() - 1);
}
} else {
path_breaks.push_back(pos);
}
break;
default:
path_breaks.push_back(pos);
break;
}
pos = pos_n;
}
// Remove trailing seperator.
if (!path.empty() && path.back() == path_sep) {
path.erase(path.size() - 1);
}
// Final sanity check for dead paths.
if ((path.size() == 1 && (path[0] == '.' || path[0] == path_sep)) ||
(path.size() == 2 && path[0] == '.' && path[1] == '.')) {
return "";
}
return path;
}
bool CreateParentFolder(const std::wstring& path) {
auto fixed_path = xe::fix_path_separators(path, xe::kWPathSeparator);
auto base_path = xe::find_base_path(fixed_path, xe::kWPathSeparator);
if (!base_path.empty() && !PathExists(base_path)) {
return CreateFolder(base_path);
} else {
return true;
} }
return true;
} }
} // namespace filesystem } // namespace filesystem

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
#ifndef XENIA_BASE_FILESYSTEM_H_ #ifndef XENIA_BASE_FILESYSTEM_H_
#define XENIA_BASE_FILESYSTEM_H_ #define XENIA_BASE_FILESYSTEM_H_
#include <filesystem>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <string> #include <string>
@ -18,45 +19,48 @@
#include "xenia/base/string.h" #include "xenia/base/string.h"
namespace xe { namespace xe {
std::string path_to_utf8(const std::filesystem::path& path);
std::u16string path_to_utf16(const std::filesystem::path& path);
std::filesystem::path to_path(const std::string_view source);
std::filesystem::path to_path(const std::u16string_view source);
namespace filesystem { namespace filesystem {
// Get executable path. // Get executable path.
std::wstring GetExecutablePath(); std::filesystem::path GetExecutablePath();
// Get executable folder. // Get executable folder.
std::wstring GetExecutableFolder(); std::filesystem::path GetExecutableFolder();
// Get user folder. // Get user folder.
std::wstring GetUserFolder(); std::filesystem::path GetUserFolder();
// Canonicalizes a path, removing ..'s.
std::string CanonicalizePath(const std::string& original_path);
// Returns true of the specified path exists as either a directory or file. // Returns true of the specified path exists as either a directory or file.
bool PathExists(const std::wstring& path); bool PathExists(const std::filesystem::path& path);
// Creates the parent folder of the specified path if needed. // Creates the parent folder of the specified path if needed.
// This can be used to ensure the destination path for a new file exists before // This can be used to ensure the destination path for a new file exists before
// attempting to create it. // attempting to create it.
bool CreateParentFolder(const std::wstring& path); bool CreateParentFolder(const std::filesystem::path& path);
// Creates a folder at the specified path. // Creates a folder at the specified path.
// Returns true if the path was created. // Returns true if the path was created.
bool CreateFolder(const std::wstring& path); bool CreateFolder(const std::filesystem::path& path);
// Recursively deletes the files and folders at the specified path. // Recursively deletes the files and folders at the specified path.
// Returns true if the path was found and removed. // Returns true if the path was found and removed.
bool DeleteFolder(const std::wstring& path); bool DeleteFolder(const std::filesystem::path& path);
// Returns true if the given path exists and is a folder. // Returns true if the given path exists and is a folder.
bool IsFolder(const std::wstring& path); bool IsFolder(const std::filesystem::path& path);
// Creates an empty file at the given path. // Creates an empty file at the given path.
bool CreateFile(const std::wstring& path); bool CreateFile(const std::filesystem::path& path);
// Opens the file at the given path with the specified mode. // Opens the file at the given path with the specified mode.
// This behaves like fopen and the returned handle can be used with stdio. // This behaves like fopen and the returned handle can be used with stdio.
FILE* OpenFile(const std::wstring& path, const char* mode); FILE* OpenFile(const std::filesystem::path& path, const std::string_view mode);
// Wrapper for the 64-bit version of fseek, returns true on success. // Wrapper for the 64-bit version of fseek, returns true on success.
bool Seek(FILE* file, int64_t offset, int origin); bool Seek(FILE* file, int64_t offset, int origin);
@ -71,7 +75,7 @@ bool TruncateStdioFile(FILE* file, uint64_t length);
// Deletes the file at the given path. // Deletes the file at the given path.
// Returns true if the file was found and removed. // Returns true if the file was found and removed.
bool DeleteFile(const std::wstring& path); bool DeleteFile(const std::filesystem::path& path);
struct FileAccess { struct FileAccess {
// Implies kFileReadData. // Implies kFileReadData.
@ -89,12 +93,12 @@ class FileHandle {
public: public:
// Opens the file, failing if it doesn't exist. // Opens the file, failing if it doesn't exist.
// The desired_access bitmask denotes the permissions on the file. // The desired_access bitmask denotes the permissions on the file.
static std::unique_ptr<FileHandle> OpenExisting(std::wstring path, static std::unique_ptr<FileHandle> OpenExisting(
uint32_t desired_access); const std::filesystem::path& path, uint32_t desired_access);
virtual ~FileHandle() = default; virtual ~FileHandle() = default;
std::wstring path() const { return path_; } const std::filesystem::path& path() const { return path_; }
// Reads the requested number of bytes from the file starting at the given // Reads the requested number of bytes from the file starting at the given
// offset. The total number of bytes read is returned only if the complete // offset. The total number of bytes read is returned only if the complete
@ -115,9 +119,9 @@ class FileHandle {
virtual void Flush() = 0; virtual void Flush() = 0;
protected: protected:
explicit FileHandle(std::wstring path) : path_(std::move(path)) {} explicit FileHandle(const std::filesystem::path& path) : path_(path) {}
std::wstring path_; std::filesystem::path path_;
}; };
struct FileInfo { struct FileInfo {
@ -126,15 +130,15 @@ struct FileInfo {
kDirectory, kDirectory,
}; };
Type type; Type type;
std::wstring name; std::filesystem::path name;
std::wstring path; std::filesystem::path path;
size_t total_size; size_t total_size;
uint64_t create_timestamp; uint64_t create_timestamp;
uint64_t access_timestamp; uint64_t access_timestamp;
uint64_t write_timestamp; uint64_t write_timestamp;
}; };
bool GetInfo(const std::wstring& path, FileInfo* out_info); bool GetInfo(const std::filesystem::path& path, FileInfo* out_info);
std::vector<FileInfo> ListFiles(const std::wstring& path); std::vector<FileInfo> ListFiles(const std::filesystem::path& path);
} // namespace filesystem } // namespace filesystem
} // namespace xe } // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2017 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -26,54 +26,64 @@
#include <iostream> #include <iostream>
namespace xe { namespace xe {
std::string path_to_utf8(const std::filesystem::path& path) {
return path.string();
}
std::u16string path_to_utf16(const std::filesystem::path& path) {
return xe::to_utf16(path.string());
}
std::filesystem::path to_path(const std::string_view source) { return source; }
std::filesystem::path to_path(const std::u16string_view source) {
return xe::to_utf8(source);
}
namespace filesystem { namespace filesystem {
std::wstring GetExecutablePath() { std::filesystem::path GetExecutablePath() {
char buff[FILENAME_MAX] = ""; char buff[FILENAME_MAX] = "";
readlink("/proc/self/exe", buff, FILENAME_MAX); readlink("/proc/self/exe", buff, FILENAME_MAX);
std::string s(buff); std::string s(buff);
return to_wstring(s); return s;
} }
std::wstring GetExecutableFolder() { std::filesystem::path GetExecutableFolder() {
auto path = GetExecutablePath(); return GetExecutablePath().parent_path();
return xe::find_base_path(path);
} }
std::wstring GetUserFolder() { std::filesystem::path GetUserFolder() {
// get preferred data home // get preferred data home
char* dataHome = std::getenv("XDG_DATA_HOME"); char* home = std::getenv("XDG_DATA_HOME");
if (home) {
// if XDG_DATA_HOME not set, fallback to HOME directory return std::string(home);
if (dataHome == NULL) {
dataHome = std::getenv("HOME");
} else {
std::string home(dataHome);
return to_wstring(home);
} }
// if XDG_DATA_HOME not set, fallback to HOME directory
home = std::getenv("HOME");
// if HOME not set, fall back to this // if HOME not set, fall back to this
if (dataHome == NULL) { if (home == NULL) {
struct passwd pw1; struct passwd pw1;
struct passwd* pw; struct passwd* pw;
char buf[4096]; // could potentionally lower this char buf[4096]; // could potentionally lower this
getpwuid_r(getuid(), &pw1, buf, sizeof(buf), &pw); getpwuid_r(getuid(), &pw1, buf, sizeof(buf), &pw);
assert(&pw1 == pw); // sanity check assert(&pw1 == pw); // sanity check
dataHome = pw->pw_dir; home = pw->pw_dir;
} }
std::string home(dataHome); return std::filesystem::path(home) / ".local" / "share";
return to_wstring(home + "/.local/share");
} }
bool PathExists(const std::wstring& path) { bool PathExists(const std::filesystem::path& path) {
struct stat st; struct stat st;
return stat(xe::to_string(path).c_str(), &st) == 0; return stat(path.c_str(), &st) == 0;
} }
FILE* OpenFile(const std::wstring& path, const char* mode) { FILE* OpenFile(const std::filesystem::path& path, const std::string_view mode) {
auto fixed_path = xe::fix_path_separators(path); return fopen(path.c_str(), std::string(mode).c_str());
return fopen(xe::to_string(fixed_path).c_str(), mode);
} }
bool Seek(FILE* file, int64_t offset, int origin) { bool Seek(FILE* file, int64_t offset, int origin) {
@ -101,8 +111,8 @@ bool TruncateStdioFile(FILE* file, uint64_t length) {
return true; return true;
} }
bool CreateFolder(const std::wstring& path) { bool CreateFolder(const std::filesystem::path& path) {
return mkdir(xe::to_string(path).c_str(), 0774); return mkdir(path.c_str(), 0774);
} }
static int removeCallback(const char* fpath, const struct stat* sb, static int removeCallback(const char* fpath, const struct stat* sb,
@ -111,9 +121,8 @@ static int removeCallback(const char* fpath, const struct stat* sb,
return rv; return rv;
} }
bool DeleteFolder(const std::wstring& path) { bool DeleteFolder(const std::filesystem::path& path) {
return nftw(xe::to_string(path).c_str(), removeCallback, 64, return nftw(path.c_str(), removeCallback, 64, FTW_DEPTH | FTW_PHYS) == 0
FTW_DEPTH | FTW_PHYS) == 0
? true ? true
: false; : false;
} }
@ -128,16 +137,16 @@ static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) {
return filetime; return filetime;
} }
bool IsFolder(const std::wstring& path) { bool IsFolder(const std::filesystem::path& path) {
struct stat st; struct stat st;
if (stat(xe::to_string(path).c_str(), &st) == 0) { if (stat(path.c_str(), &st) == 0) {
if (S_ISDIR(st.st_mode)) return true; if (S_ISDIR(st.st_mode)) return true;
} }
return false; return false;
} }
bool CreateFile(const std::wstring& path) { bool CreateFile(const std::filesystem::path& path) {
int file = creat(xe::to_string(path).c_str(), 0774); int file = creat(path.c_str(), 0774);
if (file >= 0) { if (file >= 0) {
close(file); close(file);
return true; return true;
@ -145,13 +154,14 @@ bool CreateFile(const std::wstring& path) {
return false; return false;
} }
bool DeleteFile(const std::wstring& path) { bool DeleteFile(const std::filesystem::path& path) {
return (xe::to_string(path).c_str()) == 0 ? true : false; // TODO: proper implementation.
return (path.c_str()) == 0 ? true : false;
} }
class PosixFileHandle : public FileHandle { class PosixFileHandle : public FileHandle {
public: public:
PosixFileHandle(std::wstring path, int handle) PosixFileHandle(std::filesystem::path path, int handle)
: FileHandle(std::move(path)), handle_(handle) {} : FileHandle(std::move(path)), handle_(handle) {}
~PosixFileHandle() override { ~PosixFileHandle() override {
close(handle_); close(handle_);
@ -178,8 +188,8 @@ class PosixFileHandle : public FileHandle {
int handle_ = -1; int handle_ = -1;
}; };
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path, std::unique_ptr<FileHandle> FileHandle::OpenExisting(
uint32_t desired_access) { const std::filesystem::path& path, uint32_t desired_access) {
int open_access = 0; int open_access = 0;
if (desired_access & FileAccess::kGenericRead) { if (desired_access & FileAccess::kGenericRead) {
open_access |= O_RDONLY; open_access |= O_RDONLY;
@ -202,7 +212,7 @@ std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
if (desired_access & FileAccess::kFileAppendData) { if (desired_access & FileAccess::kFileAppendData) {
open_access |= O_APPEND; open_access |= O_APPEND;
} }
int handle = open(xe::to_string(path).c_str(), open_access); int handle = open(path.c_str(), open_access);
if (handle == -1) { if (handle == -1) {
// TODO(benvanik): pick correct response. // TODO(benvanik): pick correct response.
return nullptr; return nullptr;
@ -210,9 +220,9 @@ std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
return std::make_unique<PosixFileHandle>(path, handle); return std::make_unique<PosixFileHandle>(path, handle);
} }
bool GetInfo(const std::wstring& path, FileInfo* out_info) { bool GetInfo(const std::filesystem::path& path, FileInfo* out_info) {
struct stat st; struct stat st;
if (stat(xe::to_string(path).c_str(), &st) == 0) { if (stat(path.c_str(), &st) == 0) {
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
out_info->type = FileInfo::Type::kDirectory; out_info->type = FileInfo::Type::kDirectory;
} else { } else {
@ -226,10 +236,10 @@ bool GetInfo(const std::wstring& path, FileInfo* out_info) {
return false; return false;
} }
std::vector<FileInfo> ListFiles(const std::wstring& path) { std::vector<FileInfo> ListFiles(const std::filesystem::path& path) {
std::vector<FileInfo> result; std::vector<FileInfo> result;
DIR* dir = opendir(xe::to_string(path).c_str()); DIR* dir = opendir(path.c_str());
if (!dir) { if (!dir) {
return result; return result;
} }
@ -237,9 +247,9 @@ std::vector<FileInfo> ListFiles(const std::wstring& path) {
while (auto ent = readdir(dir)) { while (auto ent = readdir(dir)) {
FileInfo info; FileInfo info;
info.name = xe::to_wstring(ent->d_name); info.name = ent->d_name;
struct stat st; struct stat st;
stat((xe::to_string(path) + xe::to_string(info.name)).c_str(), &st); stat((path / info.name).c_str(), &st);
info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime); info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime); info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime); info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);

View File

@ -2,18 +2,19 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/filesystem_wildcard.h" #include "xenia/base/filesystem_wildcard.h"
#include "xenia/base/assert.h"
#include <algorithm> #include <algorithm>
namespace xe { #include "xenia/base/assert.h"
namespace filesystem { #include "xenia/base/string.h"
namespace xe::filesystem {
WildcardFlags WildcardFlags::FIRST(true, false, false); WildcardFlags WildcardFlags::FIRST(true, false, false);
WildcardFlags WildcardFlags::LAST(false, true, false); WildcardFlags WildcardFlags::LAST(false, true, false);
@ -25,47 +26,45 @@ WildcardFlags::WildcardFlags()
WildcardFlags::WildcardFlags(bool start, bool end, bool exact_length) WildcardFlags::WildcardFlags(bool start, bool end, bool exact_length)
: FromStart(start), ToEnd(end), ExactLength(exact_length) {} : FromStart(start), ToEnd(end), ExactLength(exact_length) {}
WildcardRule::WildcardRule(const std::string& str_match, WildcardRule::WildcardRule(const std::string_view match,
const WildcardFlags& flags) const WildcardFlags& flags)
: match(str_match), rules(flags) { : match_(utf8::lower_ascii(match)), rules_(flags) {}
std::transform(match.begin(), match.end(), match.begin(), tolower);
}
bool WildcardRule::Check(const std::string& str_lower, bool WildcardRule::Check(const std::string_view lower,
std::string::size_type* offset) const { std::string::size_type* offset) const {
if (match.empty()) { if (match_.empty()) {
return true; return true;
} }
if ((str_lower.size() - *offset) < match.size()) { if ((lower.size() - *offset) < match_.size()) {
return false; return false;
} }
if (rules.ExactLength) { if (rules_.ExactLength) {
*offset += match.size(); *offset += match_.size();
return true; return true;
} }
std::string::size_type result(str_lower.find(match, *offset)); std::string_view::size_type result(lower.find(match_, *offset));
if (result != std::string::npos) { if (result != std::string_view::npos) {
if (rules.FromStart && result != *offset) { if (rules_.FromStart && result != *offset) {
return false; return false;
} }
if (rules.ToEnd && result != (str_lower.size() - match.size())) { if (rules_.ToEnd && result != (lower.size() - match_.size())) {
return false; return false;
} }
*offset = (result + match.size()); *offset = (result + match_.size());
return true; return true;
} }
return false; return false;
} }
void WildcardEngine::PreparePattern(const std::string& pattern) { void WildcardEngine::PreparePattern(const std::string_view pattern) {
rules.clear(); rules_.clear();
WildcardFlags flags(WildcardFlags::FIRST); WildcardFlags flags(WildcardFlags::FIRST);
size_t n = 0; size_t n = 0;
@ -73,12 +72,12 @@ void WildcardEngine::PreparePattern(const std::string& pattern) {
while ((n = pattern.find_first_of("*?", last)) != pattern.npos) { while ((n = pattern.find_first_of("*?", last)) != pattern.npos) {
if (last != n) { if (last != n) {
std::string str_str(pattern.substr(last, n - last)); std::string str_str(pattern.substr(last, n - last));
rules.push_back(WildcardRule(str_str, flags)); rules_.push_back(WildcardRule(str_str, flags));
} }
if (pattern[n] == '?') { if (pattern[n] == '?') {
auto end = pattern.find_first_not_of('?', n + 1); auto end = pattern.find_first_not_of('?', n + 1);
auto count = end == pattern.npos ? (pattern.size() - n) : (end - n); auto count = end == pattern.npos ? (pattern.size() - n) : (end - n);
rules.push_back( rules_.push_back(
WildcardRule(pattern.substr(n, count), WildcardFlags::ANY)); WildcardRule(pattern.substr(n, count), WildcardFlags::ANY));
last = n + count; last = n + count;
} else if (pattern[n] == '*') { } else if (pattern[n] == '*') {
@ -90,20 +89,18 @@ void WildcardEngine::PreparePattern(const std::string& pattern) {
} }
if (last != pattern.size()) { if (last != pattern.size()) {
std::string str_str(pattern.substr(last)); std::string str_str(pattern.substr(last));
rules.push_back(WildcardRule(str_str, WildcardFlags::LAST)); rules_.push_back(WildcardRule(str_str, WildcardFlags::LAST));
} }
} }
void WildcardEngine::SetRule(const std::string& pattern) { void WildcardEngine::SetRule(const std::string_view pattern) {
PreparePattern(pattern); PreparePattern(pattern);
} }
bool WildcardEngine::Match(const std::string& str) const { bool WildcardEngine::Match(const std::string_view str) const {
std::string str_lc; std::string str_lc = utf8::lower_ascii(str);
std::transform(str.begin(), str.end(), std::back_inserter(str_lc), tolower);
std::string::size_type offset(0); std::string::size_type offset(0);
for (const auto& rule : rules) { for (const auto& rule : rules_) {
if (!(rule.Check(str_lc, &offset))) { if (!(rule.Check(str_lc, &offset))) {
return false; return false;
} }
@ -112,5 +109,4 @@ bool WildcardEngine::Match(const std::string& str) const {
return true; return true;
} }
} // namespace filesystem } // namespace xe::filesystem
} // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -34,25 +34,25 @@ class WildcardFlags {
class WildcardRule { class WildcardRule {
public: public:
WildcardRule(const std::string& str_match, const WildcardFlags& flags); WildcardRule(const std::string_view match, const WildcardFlags& flags);
bool Check(const std::string& str_lower, bool Check(const std::string_view lower,
std::string::size_type* offset) const; std::string_view::size_type* offset) const;
private: private:
std::string match; std::string match_;
WildcardFlags rules; WildcardFlags rules_;
}; };
class WildcardEngine { class WildcardEngine {
public: public:
void SetRule(const std::string& pattern); void SetRule(const std::string_view pattern);
// Always ignoring case // Always ignoring case
bool Match(const std::string& str) const; bool Match(const std::string_view str) const;
private: private:
std::vector<WildcardRule> rules; std::vector<WildcardRule> rules_;
void PreparePattern(const std::string& pattern); void PreparePattern(const std::string_view pattern);
}; };
} // namespace filesystem } // namespace filesystem

View File

@ -2,37 +2,56 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include <string>
#include <io.h> #include <io.h>
#include <shlobj.h> #include <shlobj.h>
#include <string>
#undef CreateFile
#undef DeleteFile
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include "xenia/base/string.h"
namespace xe { namespace xe {
std::string path_to_utf8(const std::filesystem::path& path) {
return xe::to_utf8(path.u16string());
}
std::u16string path_to_utf16(const std::filesystem::path& path) {
return path.u16string();
}
std::filesystem::path to_path(const std::string_view source) {
return xe::to_utf16(source);
}
std::filesystem::path to_path(const std::u16string_view source) {
return source;
}
namespace filesystem { namespace filesystem {
std::wstring GetExecutablePath() { std::filesystem::path GetExecutablePath() {
wchar_t* path; wchar_t* path;
auto error = _get_wpgmptr(&path); auto error = _get_wpgmptr(&path);
return !error ? std::wstring(path) : std::wstring(); return !error ? std::filesystem::path(path) : std::filesystem::path();
} }
std::wstring GetExecutableFolder() { std::filesystem::path GetExecutableFolder() {
auto path = GetExecutablePath(); return GetExecutablePath().parent_path();
return xe::find_base_path(path);
} }
std::wstring GetUserFolder() { std::filesystem::path GetUserFolder() {
std::wstring result; std::filesystem::path result;
PWSTR path; PWSTR path;
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT,
nullptr, &path))) { nullptr, &path))) {
@ -42,22 +61,22 @@ std::wstring GetUserFolder() {
return result; return result;
} }
bool PathExists(const std::wstring& path) { bool PathExists(const std::filesystem::path& path) {
DWORD attrib = GetFileAttributes(path.c_str()); DWORD attrib = GetFileAttributes(path.c_str());
return attrib != INVALID_FILE_ATTRIBUTES; return attrib != INVALID_FILE_ATTRIBUTES;
} }
bool CreateFolder(const std::wstring& path) { bool CreateFolder(const std::filesystem::path& path) {
size_t pos = 0; std::filesystem::path create_path;
do { for (auto it = path.begin(); it != path.end(); ++it) {
pos = path.find_first_of(xe::kWPathSeparator, pos + 1); create_path /= *it;
CreateDirectoryW(path.substr(0, pos).c_str(), nullptr); CreateDirectoryW(create_path.c_str(), nullptr);
} while (pos != std::string::npos); }
return PathExists(path); return PathExists(path);
} }
bool DeleteFolder(const std::wstring& path) { bool DeleteFolder(const std::filesystem::path& path) {
auto double_null_path = path + std::wstring(L"\0", 1); auto double_null_path = path.wstring() + std::wstring(L"\0", 1);
SHFILEOPSTRUCT op = {0}; SHFILEOPSTRUCT op = {0};
op.wFunc = FO_DELETE; op.wFunc = FO_DELETE;
op.pFrom = double_null_path.c_str(); op.pFrom = double_null_path.c_str();
@ -65,14 +84,13 @@ bool DeleteFolder(const std::wstring& path) {
return SHFileOperation(&op) == 0; return SHFileOperation(&op) == 0;
} }
bool IsFolder(const std::wstring& path) { bool IsFolder(const std::filesystem::path& path) {
DWORD attrib = GetFileAttributes(path.c_str()); DWORD attrib = GetFileAttributes(path.c_str());
return attrib != INVALID_FILE_ATTRIBUTES && return attrib != INVALID_FILE_ATTRIBUTES &&
(attrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; (attrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
} }
#undef CreateFile bool CreateFile(const std::filesystem::path& path) {
bool CreateFile(const std::wstring& path) {
auto handle = CreateFileW(path.c_str(), 0, 0, nullptr, CREATE_ALWAYS, auto handle = CreateFileW(path.c_str(), 0, 0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, nullptr); FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE) { if (handle == INVALID_HANDLE_VALUE) {
@ -83,9 +101,10 @@ bool CreateFile(const std::wstring& path) {
return true; return true;
} }
FILE* OpenFile(const std::wstring& path, const char* mode) { FILE* OpenFile(const std::filesystem::path& path, const std::string_view mode) {
auto fixed_path = xe::fix_path_separators(path); // Dumb, but OK.
return _wfopen(fixed_path.c_str(), xe::to_wstring(mode).c_str()); const auto wmode = xe::to_utf16(mode);
return _wfopen(path.c_str(), reinterpret_cast<const wchar_t*>(wmode.c_str()));
} }
bool Seek(FILE* file, int64_t offset, int origin) { bool Seek(FILE* file, int64_t offset, int origin) {
@ -114,14 +133,14 @@ bool TruncateStdioFile(FILE* file, uint64_t length) {
return true; return true;
} }
bool DeleteFile(const std::wstring& path) { bool DeleteFile(const std::filesystem::path& path) {
return DeleteFileW(path.c_str()) ? true : false; return DeleteFileW(path.c_str()) ? true : false;
} }
class Win32FileHandle : public FileHandle { class Win32FileHandle : public FileHandle {
public: public:
Win32FileHandle(std::wstring path, HANDLE handle) Win32FileHandle(const std::filesystem::path& path, HANDLE handle)
: FileHandle(std::move(path)), handle_(handle) {} : FileHandle(path), handle_(handle) {}
~Win32FileHandle() override { ~Win32FileHandle() override {
CloseHandle(handle_); CloseHandle(handle_);
handle_ = nullptr; handle_ = nullptr;
@ -181,8 +200,8 @@ class Win32FileHandle : public FileHandle {
HANDLE handle_ = nullptr; HANDLE handle_ = nullptr;
}; };
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path, std::unique_ptr<FileHandle> FileHandle::OpenExisting(
uint32_t desired_access) { const std::filesystem::path& path, uint32_t desired_access) {
DWORD open_access = 0; DWORD open_access = 0;
if (desired_access & FileAccess::kGenericRead) { if (desired_access & FileAccess::kGenericRead) {
open_access |= GENERIC_READ; open_access |= GENERIC_READ;
@ -220,7 +239,7 @@ std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
#define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime) #define COMBINE_TIME(t) (((uint64_t)t.dwHighDateTime << 32) | t.dwLowDateTime)
bool GetInfo(const std::wstring& path, FileInfo* out_info) { bool GetInfo(const std::filesystem::path& path, FileInfo* out_info) {
std::memset(out_info, 0, sizeof(FileInfo)); std::memset(out_info, 0, sizeof(FileInfo));
WIN32_FILE_ATTRIBUTE_DATA data = {0}; WIN32_FILE_ATTRIBUTE_DATA data = {0};
if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &data)) { if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &data)) {
@ -234,19 +253,19 @@ bool GetInfo(const std::wstring& path, FileInfo* out_info) {
out_info->total_size = out_info->total_size =
(data.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + data.nFileSizeLow; (data.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + data.nFileSizeLow;
} }
out_info->path = xe::find_base_path(path); out_info->path = path.parent_path();
out_info->name = xe::find_name_from_path(path); out_info->name = path.filename();
out_info->create_timestamp = COMBINE_TIME(data.ftCreationTime); out_info->create_timestamp = COMBINE_TIME(data.ftCreationTime);
out_info->access_timestamp = COMBINE_TIME(data.ftLastAccessTime); out_info->access_timestamp = COMBINE_TIME(data.ftLastAccessTime);
out_info->write_timestamp = COMBINE_TIME(data.ftLastWriteTime); out_info->write_timestamp = COMBINE_TIME(data.ftLastWriteTime);
return true; return true;
} }
std::vector<FileInfo> ListFiles(const std::wstring& path) { std::vector<FileInfo> ListFiles(const std::filesystem::path& path) {
std::vector<FileInfo> result; std::vector<FileInfo> result;
WIN32_FIND_DATA ffd; WIN32_FIND_DATA ffd;
HANDLE handle = FindFirstFile((path + L"\\*").c_str(), &ffd); HANDLE handle = FindFirstFileW((path / "*").c_str(), &ffd);
if (handle == INVALID_HANDLE_VALUE) { if (handle == INVALID_HANDLE_VALUE) {
return result; return result;
} }

55
src/xenia/base/fuzzy.cc Normal file
View File

@ -0,0 +1,55 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/fuzzy.h"
#include <cstring>
#include <iostream>
// TODO(gibbed): UTF8 support.
namespace xe {
int fuzzy_match(const std::string_view pattern, const char* value) {
// https://github.com/mattyork/fuzzy/blob/master/lib/fuzzy.js
// TODO(benvanik): look at
// https://github.com/atom/fuzzaldrin/tree/master/src This does not weight
// complete substrings or prefixes right, which kind of sucks.
size_t pattern_index = 0;
size_t value_length = std::strlen(value);
int total_score = 0;
int local_score = 0;
for (size_t i = 0; i < value_length; ++i) {
if (std::tolower(value[i]) == std::tolower(pattern[pattern_index])) {
++pattern_index;
local_score += 1 + local_score;
} else {
local_score = 0;
}
total_score += local_score;
}
return total_score;
}
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string_view pattern,
const void* const* entries,
size_t entry_count,
size_t string_offset) {
std::vector<std::pair<size_t, int>> results;
results.reserve(entry_count);
for (size_t i = 0; i < entry_count; ++i) {
auto entry_value =
reinterpret_cast<const char*>(entries[i]) + string_offset;
int score = fuzzy_match(pattern, entry_value);
results.emplace_back(i, score);
}
return results;
}
} // namespace xe

41
src/xenia/base/fuzzy.h Normal file
View File

@ -0,0 +1,41 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_FUZZY_H_
#define XENIA_BASE_FUZZY_H_
#include <string>
#include <vector>
namespace xe {
// Tests a match against a case-insensitive fuzzy filter.
// Returns the score of the match or 0 if none.
int fuzzy_match(const std::string_view pattern, const char* value);
// Applies a case-insensitive fuzzy filter to the given entries and ranks
// results.
// Entries is a list of pointers to opaque structs, each of which contains a
// char* string at the given offset.
// Returns an unsorted list of {original index, score}.
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string_view pattern,
const void* const* entries,
size_t entry_count,
size_t string_offset);
template <typename T>
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string_view pattern,
const std::vector<T>& entries,
size_t string_offset) {
return fuzzy_filter(pattern, reinterpret_cast<void* const*>(entries.data()),
entries.size(), string_offset);
}
} // namespace xe
#endif // XENIA_BASE_FUZZY_H_

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -24,8 +24,8 @@
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/memory.h" #include "xenia/base/memory.h"
#include "xenia/base/ring_buffer.h" #include "xenia/base/ring_buffer.h"
#include "xenia/base/string.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
//#include "xenia/base/cvar.h"
// For MessageBox: // For MessageBox:
// TODO(benvanik): generic API? logging_win.cc? // TODO(benvanik): generic API? logging_win.cc?
@ -33,7 +33,9 @@
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
DEFINE_string( #include "third_party/fmt/include/fmt/format.h"
DEFINE_path(
log_file, "", log_file, "",
"Logs are written to the given file (specify stdout for command line)", "Logs are written to the given file (specify stdout for command line)",
"Logging"); "Logging");
@ -54,19 +56,19 @@ thread_local std::vector<char> log_format_buffer_(64 * 1024);
class Logger { class Logger {
public: public:
explicit Logger(const std::wstring& app_name) : running_(true) { explicit Logger(const std::string_view app_name) : running_(true) {
if (cvars::log_file.empty()) { if (cvars::log_file.empty()) {
// Default to app name. // Default to app name.
auto file_path = app_name + L".log"; auto file_name = fmt::format("{}.log", app_name);
auto file_path = std::filesystem::path(file_name);
xe::filesystem::CreateParentFolder(file_path); xe::filesystem::CreateParentFolder(file_path);
file_ = xe::filesystem::OpenFile(file_path, "wt"); file_ = xe::filesystem::OpenFile(file_path, "wt");
} else { } else {
if (cvars::log_file == "stdout") { if (cvars::log_file == "stdout") {
file_ = stdout; file_ = stdout;
} else { } else {
auto file_path = xe::to_wstring(cvars::log_file); xe::filesystem::CreateParentFolder(cvars::log_file);
xe::filesystem::CreateParentFolder(file_path); file_ = xe::filesystem::OpenFile(cvars::log_file, "wt");
file_ = xe::filesystem::OpenFile(file_path, "wt");
} }
} }
@ -243,7 +245,7 @@ class Logger {
std::unique_ptr<xe::threading::Thread> write_thread_; std::unique_ptr<xe::threading::Thread> write_thread_;
}; };
void InitializeLogging(const std::wstring& app_name) { void InitializeLogging(const std::string_view app_name) {
auto mem = memory::AlignedAlloc<Logger>(0x10); auto mem = memory::AlignedAlloc<Logger>(0x10);
logger_ = new (mem) Logger(app_name); logger_ = new (mem) Logger(app_name);
} }
@ -294,40 +296,23 @@ void LogLineVarargs(LogLevel log_level, const char prefix_char, const char* fmt,
prefix_char, log_format_buffer_.data(), size); prefix_char, log_format_buffer_.data(), size);
} }
void LogLine(LogLevel log_level, const char prefix_char, const char* str, void logging::AppendLogLine(LogLevel log_level, const char prefix_char,
size_t str_length) { const std::string_view str) {
if (!logger_) {
return;
}
logger_->AppendLine(
xe::threading::current_thread_id(), log_level, prefix_char, str,
str_length == std::string::npos ? std::strlen(str) : str_length);
}
void LogLine(LogLevel log_level, const char prefix_char,
const std::string& str) {
if (!logger_) { if (!logger_) {
return; return;
} }
logger_->AppendLine(xe::threading::current_thread_id(), log_level, logger_->AppendLine(xe::threading::current_thread_id(), log_level,
prefix_char, str.c_str(), str.length()); prefix_char, str.data(), str.length());
} }
void FatalError(const char* fmt, ...) { void FatalError(const std::string_view str) {
va_list args; LogLine(LogLevel::Error, 'X', str);
va_start(args, fmt); logging::AppendLogLine(LogLevel::Error, 'X', str);
LogLineVarargs(LogLevel::Error, 'X', fmt, args);
va_end(args);
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) { if (!xe::has_console_attached()) {
va_start(args, fmt); MessageBoxW(NULL, (LPCWSTR)xe::to_utf16(str).c_str(), L"Xenia Error",
std::vsnprintf(log_format_buffer_.data(), log_format_buffer_.capacity(),
fmt, args);
va_end(args);
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND); MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
} }
#endif // WIN32 #endif // WIN32
@ -335,27 +320,4 @@ void FatalError(const char* fmt, ...) {
std::exit(1); std::exit(1);
} }
void FatalError(const wchar_t* fmt, ...) {
va_list args;
va_start(args, fmt);
std::vswprintf((wchar_t*)log_format_buffer_.data(),
log_format_buffer_.capacity() >> 1, fmt, args);
va_end(args);
LogLine(LogLevel::Error, 'X',
xe::to_string((wchar_t*)log_format_buffer_.data()));
#if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) {
MessageBoxW(NULL, (wchar_t*)log_format_buffer_.data(), L"Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
}
#endif // WIN32
ShutdownLogging();
std::exit(1);
}
void FatalError(const std::string& str) { FatalError(str.c_str()); }
void FatalError(const std::wstring& str) { FatalError(str.c_str()); }
} // namespace xe } // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
#ifndef XENIA_BASE_LOGGING_H_ #ifndef XENIA_BASE_LOGGING_H_
#define XENIA_BASE_LOGGING_H_ #define XENIA_BASE_LOGGING_H_
#include <cstdarg>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
@ -34,7 +35,7 @@ enum class LogLevel {
// Initializes the logging system and any outputs requested. // Initializes the logging system and any outputs requested.
// Must be called on startup. // Must be called on startup.
void InitializeLogging(const std::wstring& app_name); void InitializeLogging(const std::string_view app_name);
void ShutdownLogging(); void ShutdownLogging();
// Appends a line to the log with printf-style formatting. // Appends a line to the log with printf-style formatting.
@ -43,17 +44,10 @@ void LogLineFormat(LogLevel log_level, const char prefix_char, const char* fmt,
void LogLineVarargs(LogLevel log_level, const char prefix_char, const char* fmt, void LogLineVarargs(LogLevel log_level, const char prefix_char, const char* fmt,
va_list args); va_list args);
// Appends a line to the log. // Appends a line to the log.
void LogLine(LogLevel log_level, const char prefix_char, const char* str, void LogLine(LogLevel log_level, const char prefix_char, std::string_view str);
size_t str_length = std::string::npos);
void LogLine(LogLevel log_level, const char prefix_char,
const std::string& str);
// Logs a fatal error with printf-style formatting and aborts the program.
void FatalError(const char* fmt, ...);
void FatalError(const wchar_t* fmt, ...);
// Logs a fatal error and aborts the program. // Logs a fatal error and aborts the program.
void FatalError(const std::string& str); void FatalError(const std::string_view str);
void FatalError(const std::wstring& str);
#if XE_OPTION_ENABLE_LOGGING #if XE_OPTION_ENABLE_LOGGING
#define XELOGCORE(level, prefix, fmt, ...) \ #define XELOGCORE(level, prefix, fmt, ...) \

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -24,10 +24,10 @@ bool has_console_attached();
// Extern defined by user code. This must be present for the application to // Extern defined by user code. This must be present for the application to
// launch. // launch.
struct EntryInfo { struct EntryInfo {
std::wstring name; std::string name;
std::string positional_usage; std::string positional_usage;
std::vector<std::string> positional_options; std::vector<std::string> positional_options;
int (*entry_point)(const std::vector<std::wstring>& args); int (*entry_point)(const std::vector<std::string>& args);
}; };
EntryInfo GetEntryInfo(); EntryInfo GetEntryInfo();

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/base/main.h" #include "xenia/base/main.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
@ -25,9 +26,9 @@ extern "C" int main(int argc, char** argv) {
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage, cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage,
entry_info.positional_options); entry_info.positional_options);
std::vector<std::wstring> args; std::vector<std::string> args;
for (int n = 0; n < argc; n++) { for (int n = 0; n < argc; n++) {
args.push_back(xe::to_wstring(argv[n])); args.push_back(argv[n]);
} }
// Initialize logging. Needs parsed FLAGS. // Initialize logging. Needs parsed FLAGS.

View File

@ -2,30 +2,31 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/main.h"
#include <fcntl.h> #include <fcntl.h>
#include <io.h> #include <io.h>
#include <cstdlib> #include <cstdlib>
// Autogenerated by `xb premake`. #include "xenia/base/cvar.h"
#include "build/version.h"
#include "xenia/base/filesystem.h" #include "xenia/base/filesystem.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "third_party/xbyak/xbyak/xbyak_util.h" // Autogenerated by `xb premake`.
#include "build/version.h"
#include <bcrypt.h> // For RequestHighPerformance.
#include "xenia/base/cvar.h" #include <winternl.h>
// Includes Windows headers, so it goes here.
#include "third_party/xbyak/xbyak/xbyak_util.h"
DEFINE_bool(win32_high_freq, true, DEFINE_bool(win32_high_freq, true,
"Requests high performance from the NT kernel", "Kernel"); "Requests high performance from the NT kernel", "Kernel");
@ -71,9 +72,9 @@ static void RequestHighPerformance() {
OUT PULONG CurrentResolution); OUT PULONG CurrentResolution);
NtQueryTimerResolution = (decltype(NtQueryTimerResolution))GetProcAddress( NtQueryTimerResolution = (decltype(NtQueryTimerResolution))GetProcAddress(
GetModuleHandle(L"ntdll.dll"), "NtQueryTimerResolution"); GetModuleHandleW(L"ntdll.dll"), "NtQueryTimerResolution");
NtSetTimerResolution = (decltype(NtSetTimerResolution))GetProcAddress( NtSetTimerResolution = (decltype(NtSetTimerResolution))GetProcAddress(
GetModuleHandle(L"ntdll.dll"), "NtSetTimerResolution"); GetModuleHandleW(L"ntdll.dll"), "NtSetTimerResolution");
if (!NtQueryTimerResolution || !NtSetTimerResolution) { if (!NtQueryTimerResolution || !NtSetTimerResolution) {
return; return;
} }
@ -85,37 +86,44 @@ static void RequestHighPerformance() {
#endif #endif
} }
int Main() { static bool parse_launch_arguments(const xe::EntryInfo& entry_info,
auto entry_info = xe::GetEntryInfo(); std::vector<std::string>& args) {
// Convert command line to an argv-like format so we can share code/use
auto command_line = GetCommandLineW(); auto command_line = GetCommandLineW();
int argc;
wchar_t** argv = CommandLineToArgvW(command_line, &argc); int wargc;
if (!argv) { wchar_t** wargv = CommandLineToArgvW(command_line, &wargc);
return 1; if (!wargv) {
return false;
} }
// Convert all args to narrow, as cxxopts doesn't support wchar. // Convert all args to narrow, as cxxopts doesn't support wchar.
int argca = argc; int argc = wargc;
char** argva = reinterpret_cast<char**>(alloca(sizeof(char*) * argca)); char** argv = reinterpret_cast<char**>(alloca(sizeof(char*) * argc));
for (int n = 0; n < argca; n++) { for (int n = 0; n < argc; n++) {
size_t len = std::wcstombs(nullptr, argv[n], 0); size_t len = std::wcstombs(nullptr, wargv[n], 0);
argva[n] = reinterpret_cast<char*>(alloca(sizeof(char) * (len + 1))); argv[n] = reinterpret_cast<char*>(alloca(sizeof(char) * (len + 1)));
std::wcstombs(argva[n], argv[n], len + 1); std::wcstombs(argv[n], wargv[n], len + 1);
} }
cvar::ParseLaunchArguments(argca, argva, entry_info.positional_usage, LocalFree(wargv);
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage,
entry_info.positional_options); entry_info.positional_options);
// Widen all remaining flags and convert to usable strings. args.clear();
std::vector<std::wstring> args;
for (int n = 0; n < argc; n++) { for (int n = 0; n < argc; n++) {
size_t len = std::mbstowcs(nullptr, argva[n], 0); args.push_back(std::string(argv[n]));
auto argvw = }
reinterpret_cast<wchar_t*>(alloca(sizeof(wchar_t) * (len + 1)));
std::mbstowcs(argvw, argva[n], len + 1); return true;
args.push_back(std::wstring(argvw)); }
int Main() {
auto entry_info = xe::GetEntryInfo();
std::vector<std::string> args;
if (!parse_launch_arguments(entry_info, args)) {
return 1;
} }
// Setup COM on the main thread. // Setup COM on the main thread.
@ -147,7 +155,6 @@ int Main() {
int result = entry_info.entry_point(args); int result = entry_info.entry_point(args);
xe::ShutdownLogging(); xe::ShutdownLogging();
LocalFree(argv);
return result; return result;
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
#ifndef XENIA_BASE_MAPPED_MEMORY_H_ #ifndef XENIA_BASE_MAPPED_MEMORY_H_
#define XENIA_BASE_MAPPED_MEMORY_H_ #define XENIA_BASE_MAPPED_MEMORY_H_
#include <filesystem>
#include <memory> #include <memory>
#include <string> #include <string>
@ -22,13 +23,14 @@ class MappedMemory {
kReadWrite, kReadWrite,
}; };
static std::unique_ptr<MappedMemory> Open(const std::wstring& path, Mode mode, static std::unique_ptr<MappedMemory> Open(const std::filesystem::path& path,
size_t offset = 0, Mode mode, size_t offset = 0,
size_t length = 0); size_t length = 0);
MappedMemory(const std::wstring& path, Mode mode) MappedMemory(const std::filesystem::path& path, Mode mode)
: path_(path), mode_(mode), data_(nullptr), size_(0) {} : path_(path), mode_(mode), data_(nullptr), size_(0) {}
MappedMemory(const std::wstring& path, Mode mode, void* data, size_t size) MappedMemory(const std::filesystem::path& path, Mode mode, void* data,
size_t size)
: path_(path), mode_(mode), data_(data), size_(size) {} : path_(path), mode_(mode), data_(data), size_(size) {}
virtual ~MappedMemory() = default; virtual ~MappedMemory() = default;
@ -48,7 +50,7 @@ class MappedMemory {
virtual bool Remap(size_t offset, size_t length) { return false; } virtual bool Remap(size_t offset, size_t length) { return false; }
protected: protected:
std::wstring path_; std::filesystem::path path_;
Mode mode_; Mode mode_;
void* data_; void* data_;
size_t size_; size_t size_;
@ -59,7 +61,7 @@ class ChunkedMappedMemoryWriter {
virtual ~ChunkedMappedMemoryWriter() = default; virtual ~ChunkedMappedMemoryWriter() = default;
static std::unique_ptr<ChunkedMappedMemoryWriter> Open( static std::unique_ptr<ChunkedMappedMemoryWriter> Open(
const std::wstring& path, size_t chunk_size, const std::filesystem::path& path, size_t chunk_size,
bool low_address_space = false); bool low_address_space = false);
virtual uint8_t* Allocate(size_t length) = 0; virtual uint8_t* Allocate(size_t length) = 0;
@ -67,13 +69,13 @@ class ChunkedMappedMemoryWriter {
virtual void FlushNew() = 0; virtual void FlushNew() = 0;
protected: protected:
ChunkedMappedMemoryWriter(const std::wstring& path, size_t chunk_size, ChunkedMappedMemoryWriter(const std::filesystem::path& path,
bool low_address_space) size_t chunk_size, bool low_address_space)
: path_(path), : path_(path),
chunk_size_(chunk_size), chunk_size_(chunk_size),
low_address_space_(low_address_space) {} low_address_space_(low_address_space) {}
std::wstring path_; std::filesystem::path path_;
size_t chunk_size_; size_t chunk_size_;
bool low_address_space_; bool low_address_space_;
}; };

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -19,7 +19,7 @@ namespace xe {
class PosixMappedMemory : public MappedMemory { class PosixMappedMemory : public MappedMemory {
public: public:
PosixMappedMemory(const std::wstring& path, Mode mode) PosixMappedMemory(const std::filesystem::path& path, Mode mode)
: MappedMemory(path, mode), file_handle(nullptr) {} : MappedMemory(path, mode), file_handle(nullptr) {}
~PosixMappedMemory() override { ~PosixMappedMemory() override {
@ -34,9 +34,9 @@ class PosixMappedMemory : public MappedMemory {
FILE* file_handle; FILE* file_handle;
}; };
std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path, std::unique_ptr<MappedMemory> MappedMemory::Open(
Mode mode, size_t offset, const std::filesystem::path& path, Mode mode, size_t offset,
size_t length) { size_t length) {
const char* mode_str; const char* mode_str;
int prot; int prot;
switch (mode) { switch (mode) {
@ -53,7 +53,7 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
auto mm = auto mm =
std::unique_ptr<PosixMappedMemory>(new PosixMappedMemory(path, mode)); std::unique_ptr<PosixMappedMemory>(new PosixMappedMemory(path, mode));
mm->file_handle = fopen(xe::to_string(path).c_str(), mode_str); mm->file_handle = fopen(path.c_str(), mode_str);
if (!mm->file_handle) { if (!mm->file_handle) {
return nullptr; return nullptr;
} }
@ -77,7 +77,8 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
} }
std::unique_ptr<ChunkedMappedMemoryWriter> ChunkedMappedMemoryWriter::Open( std::unique_ptr<ChunkedMappedMemoryWriter> ChunkedMappedMemoryWriter::Open(
const std::wstring& path, size_t chunk_size, bool low_address_space) { const std::filesystem::path& path, size_t chunk_size,
bool low_address_space) {
// TODO(DrChat) // TODO(DrChat)
return nullptr; return nullptr;
} }

View File

@ -2,18 +2,18 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/mapped_memory.h"
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/mapped_memory.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/memory.h" #include "xenia/base/memory.h"
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
@ -22,7 +22,7 @@ namespace xe {
class Win32MappedMemory : public MappedMemory { class Win32MappedMemory : public MappedMemory {
public: public:
Win32MappedMemory(const std::wstring& path, Mode mode) Win32MappedMemory(const std::filesystem::path& path, Mode mode)
: MappedMemory(path, mode) {} : MappedMemory(path, mode) {}
~Win32MappedMemory() override { ~Win32MappedMemory() override {
@ -88,9 +88,9 @@ class Win32MappedMemory : public MappedMemory {
DWORD view_access_ = 0; DWORD view_access_ = 0;
}; };
std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path, std::unique_ptr<MappedMemory> MappedMemory::Open(
Mode mode, size_t offset, const std::filesystem::path& path, Mode mode, size_t offset,
size_t length) { size_t length) {
DWORD file_access = 0; DWORD file_access = 0;
DWORD file_share = 0; DWORD file_share = 0;
DWORD create_mode = 0; DWORD create_mode = 0;
@ -157,8 +157,8 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter { class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
public: public:
Win32ChunkedMappedMemoryWriter(const std::wstring& path, size_t chunk_size, Win32ChunkedMappedMemoryWriter(const std::filesystem::path& path,
bool low_address_space) size_t chunk_size, bool low_address_space)
: ChunkedMappedMemoryWriter(path, chunk_size, low_address_space) {} : ChunkedMappedMemoryWriter(path, chunk_size, low_address_space) {}
~Win32ChunkedMappedMemoryWriter() override { ~Win32ChunkedMappedMemoryWriter() override {
@ -175,7 +175,8 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
} }
} }
auto chunk = std::make_unique<Chunk>(chunk_size_); auto chunk = std::make_unique<Chunk>(chunk_size_);
auto chunk_path = path_ + L"." + std::to_wstring(chunks_.size()); auto chunk_path =
path_.replace_extension(fmt::format(".{}", chunks_.size()));
if (!chunk->Open(chunk_path, low_address_space_)) { if (!chunk->Open(chunk_path, low_address_space_)) {
return nullptr; return nullptr;
} }
@ -221,7 +222,7 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
} }
} }
bool Open(const std::wstring& path, bool low_address_space) { bool Open(const std::filesystem::path& path, bool low_address_space) {
DWORD file_access = GENERIC_READ | GENERIC_WRITE; DWORD file_access = GENERIC_READ | GENERIC_WRITE;
DWORD file_share = FILE_SHARE_READ; DWORD file_share = FILE_SHARE_READ;
DWORD create_mode = CREATE_ALWAYS; DWORD create_mode = CREATE_ALWAYS;
@ -300,7 +301,8 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
}; };
std::unique_ptr<ChunkedMappedMemoryWriter> ChunkedMappedMemoryWriter::Open( std::unique_ptr<ChunkedMappedMemoryWriter> ChunkedMappedMemoryWriter::Open(
const std::wstring& path, size_t chunk_size, bool low_address_space) { const std::filesystem::path& path, size_t chunk_size,
bool low_address_space) {
SYSTEM_INFO system_info; SYSTEM_INFO system_info;
GetSystemInfo(&system_info); GetSystemInfo(&system_info);
size_t aligned_chunk_size = size_t aligned_chunk_size =

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -12,6 +12,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <filesystem>
#include <functional> #include <functional>
#include <string> #include <string>
@ -97,8 +98,9 @@ void AlignedFree(T* ptr) {
typedef void* FileMappingHandle; typedef void* FileMappingHandle;
FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length, FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
PageAccess access, bool commit); size_t length, PageAccess access,
bool commit);
void CloseFileMappingHandle(FileMappingHandle handle); void CloseFileMappingHandle(FileMappingHandle handle);
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
PageAccess access, size_t file_offset); PageAccess access, size_t file_offset);
@ -281,8 +283,8 @@ inline std::string load_and_swap<std::string>(const void* mem) {
return value; return value;
} }
template <> template <>
inline std::wstring load_and_swap<std::wstring>(const void* mem) { inline std::u16string load_and_swap<std::u16string>(const void* mem) {
std::wstring value; std::u16string value;
for (int i = 0;; ++i) { for (int i = 0;; ++i) {
auto c = auto c =
xe::load_and_swap<uint16_t>(reinterpret_cast<const uint16_t*>(mem) + i); xe::load_and_swap<uint16_t>(reinterpret_cast<const uint16_t*>(mem) + i);
@ -337,17 +339,17 @@ inline void store<double>(void* mem, const double& value) {
*reinterpret_cast<double*>(mem) = value; *reinterpret_cast<double*>(mem) = value;
} }
template <typename T> template <typename T>
inline void store(const void* mem, const T& value) { constexpr inline void store(const void* mem, const T& value) {
if (sizeof(T) == 1) { if constexpr (sizeof(T) == 1) {
store<uint8_t>(mem, static_cast<uint8_t>(value)); store<uint8_t>(mem, static_cast<uint8_t>(value));
} else if (sizeof(T) == 2) { } else if constexpr (sizeof(T) == 2) {
store<uint8_t>(mem, static_cast<uint16_t>(value)); store<uint8_t>(mem, static_cast<uint16_t>(value));
} else if (sizeof(T) == 4) { } else if constexpr (sizeof(T) == 4) {
store<uint8_t>(mem, static_cast<uint32_t>(value)); store<uint8_t>(mem, static_cast<uint32_t>(value));
} else if (sizeof(T) == 8) { } else if constexpr (sizeof(T) == 8) {
store<uint8_t>(mem, static_cast<uint64_t>(value)); store<uint8_t>(mem, static_cast<uint64_t>(value));
} else { } else {
assert_always("Invalid xe::store size"); static_assert("Invalid xe::store size");
} }
} }
@ -394,18 +396,29 @@ inline void store_and_swap<double>(void* mem, const double& value) {
*reinterpret_cast<double*>(mem) = byte_swap(value); *reinterpret_cast<double*>(mem) = byte_swap(value);
} }
template <> template <>
inline void store_and_swap<std::string>(void* mem, const std::string& value) { inline void store_and_swap<std::string_view>(void* mem,
const std::string_view& value) {
for (auto i = 0; i < value.size(); ++i) { for (auto i = 0; i < value.size(); ++i) {
xe::store_and_swap<uint8_t>(reinterpret_cast<uint8_t*>(mem) + i, value[i]); xe::store_and_swap<uint8_t>(reinterpret_cast<uint8_t*>(mem) + i, value[i]);
} }
} }
template <> template <>
inline void store_and_swap<std::wstring>(void* mem, const std::wstring& value) { inline void store_and_swap<std::string>(void* mem, const std::string& value) {
return store_and_swap<std::string_view>(mem, value);
}
template <>
inline void store_and_swap<std::u16string_view>(
void* mem, const std::u16string_view& value) {
for (auto i = 0; i < value.size(); ++i) { for (auto i = 0; i < value.size(); ++i) {
xe::store_and_swap<uint16_t>(reinterpret_cast<uint16_t*>(mem) + i, xe::store_and_swap<uint16_t>(reinterpret_cast<uint16_t*>(mem) + i,
value[i]); value[i]);
} }
} }
template <>
inline void store_and_swap<std::u16string>(void* mem,
const std::u16string& value) {
return store_and_swap<std::u16string_view>(mem, value);
}
} // namespace xe } // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2017 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -61,8 +61,9 @@ bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
return false; return false;
} }
FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length, FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
PageAccess access, bool commit) { size_t length, PageAccess access,
bool commit) {
int oflag; int oflag;
switch (access) { switch (access) {
case PageAccess::kNoAccess: case PageAccess::kNoAccess:
@ -81,7 +82,7 @@ FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
} }
oflag |= O_CREAT; oflag |= O_CREAT;
int ret = shm_open(xe::to_string(path).c_str(), oflag, 0777); int ret = shm_open(path.c_str(), oflag, 0777);
if (ret > 0) { if (ret > 0) {
ftruncate64(ret, length); ftruncate64(ret, length);
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -142,8 +142,9 @@ bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
return true; return true;
} }
FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length, FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
PageAccess access, bool commit) { size_t length, PageAccess access,
bool commit) {
DWORD protect = DWORD protect =
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE); ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect, return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect,

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -94,15 +94,11 @@ namespace xe {
#if XE_PLATFORM_WIN32 #if XE_PLATFORM_WIN32
const char kPathSeparator = '\\'; const char kPathSeparator = '\\';
const wchar_t kWPathSeparator = L'\\';
#else #else
const char kPathSeparator = '/'; const char kPathSeparator = '/';
const wchar_t kWPathSeparator = L'/';
const size_t kMaxPath = 1024; // PATH_MAX
#endif // XE_PLATFORM_WIN32 #endif // XE_PLATFORM_WIN32
// Launches a web browser to the given URL. const char kGuestPathSeparator = '\\';
void LaunchBrowser(const wchar_t* url);
} // namespace xe } // namespace xe

View File

@ -2,13 +2,13 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#ifndef XENIA_BASE_PLATFORM_X11_H_ #ifndef XENIA_BASE_PLATFORM_LINUX_H_
#define XENIA_BASE_PLATFORM_X11_H_ #define XENIA_BASE_PLATFORM_LINUX_H_
// NOTE: if you're including this file it means you are explicitly depending // NOTE: if you're including this file it means you are explicitly depending
// on Linux headers. Including this file outside of linux platform specific // on Linux headers. Including this file outside of linux platform specific
@ -17,17 +17,4 @@
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
// Xlib/Xcb is used only for GLX/Vulkan interaction, the window management #endif // XENIA_BASE_PLATFORM_LINUX_H_
// and input events are done with gtk/gdk
#include <X11/Xlib-xcb.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xutil.h>
#include <xcb/xcb.h>
// Used for window management. Gtk is for GUI and wigets, gdk is for lower
// level events like key presses, mouse events, window handles, etc
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#endif // XENIA_BASE_PLATFORM_X11_H_

View File

@ -5,6 +5,9 @@ project("xenia-base")
uuid("aeadaf22-2b20-4941-b05f-a802d5679c11") uuid("aeadaf22-2b20-4941-b05f-a802d5679c11")
kind("StaticLib") kind("StaticLib")
language("C++") language("C++")
links({
"fmt"
})
defines({ defines({
}) })
local_platform_files() local_platform_files()

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -91,7 +91,7 @@ class Socket {
// Asynchronously sends a string buffer. // Asynchronously sends a string buffer.
// Returns false if the socket is disconnected or the data cannot be sent. // Returns false if the socket is disconnected or the data cannot be sent.
bool Send(const std::string& value) { bool Send(const std::string_view value) {
return Send(value.data(), value.size()); return Send(value.data(), value.size());
} }
}; };

View File

@ -2,359 +2,29 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/string.h" #include "xenia/base/string.h"
// codecvt existence check #include <algorithm>
#ifdef __clang__
// using clang
#if (__clang_major__ < 4) // 3.3 has it but I think we need at least 4 anyway
// insufficient clang version
#define NO_CODECVT 1
#else
#include <codecvt>
#endif
#elif defined(__GNUC__) || defined(__GNUG__)
// using gcc
#if (__GNUC__ < 5)
// insufficient clang version
#define NO_CODECVT 1
#else
#include <codecvt>
#endif
// since the windows 10 sdk is required, this shouldn't be an issue
#elif defined(_MSC_VER)
#include <codecvt>
#endif
#include <cctype>
#include <cstring>
#include <locale> #include <locale>
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
namespace utfcpp = utf8;
namespace xe { namespace xe {
std::string to_string(const std::wstring& source) { std::string to_utf8(const std::u16string_view source) {
#if NO_CODECVT return utfcpp::utf16to8(source);
return std::string(source.begin(), source.end());
#else
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(source);
#endif // XE_PLATFORM_LINUX
} }
std::wstring to_wstring(const std::string& source) { std::u16string to_utf16(const std::string_view source) {
#if NO_CODECVT return utfcpp::utf8to16(source);
return std::wstring(source.begin(), source.end());
#else
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(source);
#endif // XE_PLATFORM_LINUX
}
std::string format_string(const char* format, va_list args) {
if (!format) {
return "";
}
size_t max_len = 64;
std::string new_s;
while (true) {
new_s.resize(max_len);
int ret =
std::vsnprintf(const_cast<char*>(new_s.data()), max_len, format, args);
if (ret > max_len) {
// Needed size is known (+2 for termination and avoid ambiguity).
max_len = ret + 2;
} else if (ret == -1 || ret >= max_len - 1) {
// Handle some buggy vsnprintf implementations.
max_len *= 2;
} else {
// Everything fit for sure.
new_s.resize(ret);
return new_s;
}
}
}
std::wstring format_string(const wchar_t* format, va_list args) {
if (!format) {
return L"";
}
size_t max_len = 64;
std::wstring new_s;
while (true) {
new_s.resize(max_len);
int ret = std::vswprintf(const_cast<wchar_t*>(new_s.data()), max_len,
format, args);
if (ret > max_len) {
// Needed size is known (+2 for termination and avoid ambiguity).
max_len = ret + 2;
} else if (ret == -1 || ret >= max_len - 1) {
// Handle some buggy vsnprintf implementations.
max_len *= 2;
} else {
// Everything fit for sure.
new_s.resize(ret);
return new_s;
}
}
}
std::vector<std::string> split_string(const std::string& path,
const std::string& delimiters) {
std::vector<std::string> parts;
size_t n = 0;
size_t last = 0;
while ((n = path.find_first_of(delimiters, last)) != path.npos) {
if (last != n) {
parts.push_back(path.substr(last, n - last));
}
last = n + 1;
}
if (last != path.size()) {
parts.push_back(path.substr(last));
}
return parts;
}
std::vector<std::wstring> split_string(const std::wstring& path,
const std::wstring& delimiters) {
std::vector<std::wstring> parts;
size_t n = 0;
size_t last = 0;
while ((n = path.find_first_of(delimiters, last)) != path.npos) {
if (last != n) {
parts.push_back(path.substr(last, n - last));
}
last = n + 1;
}
if (last != path.size()) {
parts.push_back(path.substr(last));
}
return parts;
}
std::string::size_type find_first_of_case(const std::string& target,
const std::string& search) {
const char* str = target.c_str();
while (*str) {
if (!strncasecmp(str, search.c_str(), search.size())) {
break;
}
str++;
}
if (*str) {
return str - target.c_str();
} else {
return std::string::npos;
}
}
std::wstring to_absolute_path(const std::wstring& path) {
#if XE_PLATFORM_WIN32
std::wstring result;
wchar_t* buffer = _wfullpath(nullptr, path.c_str(), 0);
if (buffer != nullptr) {
result.assign(buffer);
free(buffer);
}
return result;
#else
char buffer[kMaxPath];
realpath(xe::to_string(path).c_str(), buffer);
return xe::to_wstring(buffer);
#endif // XE_PLATFORM_WIN32
}
std::vector<std::string> split_path(const std::string& path) {
return split_string(path, "\\/");
}
std::vector<std::wstring> split_path(const std::wstring& path) {
return split_string(path, L"\\/");
}
std::string join_paths(const std::string& left, const std::string& right,
char sep) {
if (!left.size()) {
return right;
} else if (!right.size()) {
return left;
}
if (left[left.size() - 1] == sep) {
return left + right;
} else {
return left + sep + right;
}
}
std::wstring join_paths(const std::wstring& left, const std::wstring& right,
wchar_t sep) {
if (!left.size()) {
return right;
} else if (!right.size()) {
return left;
}
if (left[left.size() - 1] == sep) {
return left + right;
} else {
return left + sep + right;
}
}
std::wstring fix_path_separators(const std::wstring& source, wchar_t new_sep) {
// Swap all separators to new_sep.
wchar_t old_sep = new_sep == '\\' ? '/' : '\\';
std::wstring::size_type pos = 0;
std::wstring dest = source;
while ((pos = source.find_first_of(old_sep, pos)) != std::wstring::npos) {
dest[pos] = new_sep;
++pos;
}
// Replace redundant separators.
pos = 0;
while ((pos = dest.find_first_of(new_sep, pos)) != std::wstring::npos) {
if (pos < dest.size() - 1) {
if (dest[pos + 1] == new_sep) {
dest.erase(pos + 1, 1);
}
}
++pos;
}
return dest;
}
std::string fix_path_separators(const std::string& source, char new_sep) {
// Swap all separators to new_sep.
char old_sep = new_sep == '\\' ? '/' : '\\';
std::string::size_type pos = 0;
std::string dest = source;
while ((pos = source.find_first_of(old_sep, pos)) != std::string::npos) {
dest[pos] = new_sep;
++pos;
}
// Replace redundant separators.
pos = 0;
while ((pos = dest.find_first_of(new_sep, pos)) != std::string::npos) {
if (pos < dest.size() - 1) {
if (dest[pos + 1] == new_sep) {
dest.erase(pos + 1, 1);
}
}
++pos;
}
return dest;
}
std::string find_name_from_path(const std::string& path, char sep) {
std::string name(path);
if (!path.empty()) {
std::string::size_type from(std::string::npos);
if (path.back() == sep) {
from = path.size() - 2;
}
auto pos(path.find_last_of(sep, from));
if (pos != std::string::npos) {
if (from == std::string::npos) {
name = path.substr(pos + 1);
} else {
auto len(from - pos);
name = path.substr(pos + 1, len);
}
}
}
return name;
}
std::wstring find_name_from_path(const std::wstring& path, wchar_t sep) {
std::wstring name(path);
if (!path.empty()) {
std::wstring::size_type from(std::wstring::npos);
if (path.back() == sep) {
from = path.size() - 2;
}
auto pos(path.find_last_of(sep, from));
if (pos != std::wstring::npos) {
if (from == std::wstring::npos) {
name = path.substr(pos + 1);
} else {
auto len(from - pos);
name = path.substr(pos + 1, len);
}
}
}
return name;
}
std::string find_base_path(const std::string& path, char sep) {
auto last_slash = path.find_last_of(sep);
if (last_slash == std::string::npos) {
return "";
} else if (last_slash == path.length() - 1) {
auto prev_slash = path.find_last_of(sep, last_slash - 1);
if (prev_slash == std::string::npos) {
return "";
} else {
return path.substr(0, prev_slash + 1);
}
} else {
return path.substr(0, last_slash + 1);
}
}
std::wstring find_base_path(const std::wstring& path, wchar_t sep) {
auto last_slash = path.find_last_of(sep);
if (last_slash == std::wstring::npos) {
return L"";
} else if (last_slash == path.length() - 1) {
auto prev_slash = path.find_last_of(sep, last_slash - 1);
if (prev_slash == std::wstring::npos) {
return L"";
} else {
return path.substr(0, prev_slash + 1);
}
} else {
return path.substr(0, last_slash + 1);
}
}
int fuzzy_match(const std::string& pattern, const char* value) {
// https://github.com/mattyork/fuzzy/blob/master/lib/fuzzy.js
// TODO(benvanik): look at https://github.com/atom/fuzzaldrin/tree/master/src
// This does not weight complete substrings or prefixes right, which
// kind of sucks.
size_t pattern_index = 0;
size_t value_length = std::strlen(value);
int total_score = 0;
int local_score = 0;
for (size_t i = 0; i < value_length; ++i) {
if (std::tolower(value[i]) == std::tolower(pattern[pattern_index])) {
++pattern_index;
local_score += 1 + local_score;
} else {
local_score = 0;
}
total_score += local_score;
}
return total_score;
}
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string& pattern,
const void* const* entries,
size_t entry_count,
size_t string_offset) {
std::vector<std::pair<size_t, int>> results;
results.reserve(entry_count);
for (size_t i = 0; i < entry_count; ++i) {
auto entry_value =
reinterpret_cast<const char*>(entries[i]) + string_offset;
int score = fuzzy_match(pattern, entry_value);
results.emplace_back(i, score);
}
return results;
} }
} // namespace xe } // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,99 +10,14 @@
#ifndef XENIA_BASE_STRING_H_ #ifndef XENIA_BASE_STRING_H_
#define XENIA_BASE_STRING_H_ #define XENIA_BASE_STRING_H_
#include <cstdarg>
#include <cstdio>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "xenia/base/platform.h" #include "utf8.h"
namespace xe { namespace xe {
std::string to_string(const std::wstring& source); std::string to_utf8(const std::u16string_view source);
std::wstring to_wstring(const std::string& source); std::u16string to_utf16(const std::string_view source);
std::string format_string(const char* format, va_list args);
inline std::string format_string(const char* format, ...) {
va_list va;
va_start(va, format);
auto result = format_string(format, va);
va_end(va);
return result;
}
std::wstring format_string(const wchar_t* format, va_list args);
inline std::wstring format_string(const wchar_t* format, ...) {
va_list va;
va_start(va, format);
auto result = format_string(format, va);
va_end(va);
return result;
}
// Splits the given string on any delimiters and returns all parts.
std::vector<std::string> split_string(const std::string& path,
const std::string& delimiters);
std::vector<std::wstring> split_string(const std::wstring& path,
const std::wstring& delimiters);
// find_first_of string, case insensitive.
std::string::size_type find_first_of_case(const std::string& target,
const std::string& search);
// Converts the given path to an absolute path based on cwd.
std::wstring to_absolute_path(const std::wstring& path);
// Splits the given path on any valid path separator and returns all parts.
std::vector<std::string> split_path(const std::string& path);
std::vector<std::wstring> split_path(const std::wstring& path);
// Joins two path segments with the given separator.
std::string join_paths(const std::string& left, const std::string& right,
char sep = xe::kPathSeparator);
std::wstring join_paths(const std::wstring& left, const std::wstring& right,
wchar_t sep = xe::kPathSeparator);
// Replaces all path separators with the given value and removes redundant
// separators.
std::wstring fix_path_separators(const std::wstring& source,
wchar_t new_sep = xe::kPathSeparator);
std::string fix_path_separators(const std::string& source,
char new_sep = xe::kPathSeparator);
// Find the top directory name or filename from a path.
std::string find_name_from_path(const std::string& path,
char sep = xe::kPathSeparator);
std::wstring find_name_from_path(const std::wstring& path,
wchar_t sep = xe::kPathSeparator);
// Get parent path of the given directory or filename.
std::string find_base_path(const std::string& path,
char sep = xe::kPathSeparator);
std::wstring find_base_path(const std::wstring& path,
wchar_t sep = xe::kPathSeparator);
// Tests a match against a case-insensitive fuzzy filter.
// Returns the score of the match or 0 if none.
int fuzzy_match(const std::string& pattern, const char* value);
// Applies a case-insensitive fuzzy filter to the given entries and ranks
// results.
// Entries is a list of pointers to opaque structs, each of which contains a
// char* string at the given offset.
// Returns an unsorted list of {original index, score}.
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string& pattern,
const void* const* entries,
size_t entry_count,
size_t string_offset);
template <typename T>
std::vector<std::pair<size_t, int>> fuzzy_filter(const std::string& pattern,
const std::vector<T>& entries,
size_t string_offset) {
return fuzzy_filter(pattern, reinterpret_cast<void* const*>(entries.data()),
entries.size(), string_offset);
}
} // namespace xe } // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -52,17 +52,10 @@ void StringBuffer::Append(const char* value) {
AppendBytes(reinterpret_cast<const uint8_t*>(value), std::strlen(value)); AppendBytes(reinterpret_cast<const uint8_t*>(value), std::strlen(value));
} }
void StringBuffer::Append(const std::string& value) { void StringBuffer::Append(const std::string_view value) {
AppendBytes(reinterpret_cast<const uint8_t*>(value.data()), value.size()); AppendBytes(reinterpret_cast<const uint8_t*>(value.data()), value.size());
} }
void StringBuffer::AppendFormat(const char* format, ...) {
va_list args;
va_start(args, format);
AppendVarargs(format, args);
va_end(args);
}
void StringBuffer::AppendVarargs(const char* format, va_list args) { void StringBuffer::AppendVarargs(const char* format, va_list args) {
int length = vsnprintf(nullptr, 0, format, args); int length = vsnprintf(nullptr, 0, format, args);
Grow(length + 1); Grow(length + 1);
@ -78,15 +71,15 @@ void StringBuffer::AppendBytes(const uint8_t* buffer, size_t length) {
buffer_[buffer_offset_] = 0; buffer_[buffer_offset_] = 0;
} }
const char* StringBuffer::GetString() const { return buffer_; }
std::string StringBuffer::to_string() { std::string StringBuffer::to_string() {
return std::string(buffer_, buffer_offset_); return std::string(buffer_, buffer_offset_);
} }
char* StringBuffer::ToString() { return strdup(buffer_); } std::string_view StringBuffer::to_string_view() const {
return std::string_view(buffer_, buffer_offset_);
}
std::vector<uint8_t> StringBuffer::ToBytes() const { std::vector<uint8_t> StringBuffer::to_bytes() const {
std::vector<uint8_t> bytes(buffer_offset_); std::vector<uint8_t> bytes(buffer_offset_);
std::memcpy(bytes.data(), buffer_, buffer_offset_); std::memcpy(bytes.data(), buffer_, buffer_offset_);
return bytes; return bytes;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -14,6 +14,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "third_party/fmt/include/fmt/format.h"
namespace xe { namespace xe {
class StringBuffer { class StringBuffer {
@ -21,21 +23,27 @@ class StringBuffer {
explicit StringBuffer(size_t initial_capacity = 0); explicit StringBuffer(size_t initial_capacity = 0);
~StringBuffer(); ~StringBuffer();
char* buffer() const { return buffer_; }
size_t length() const { return buffer_offset_; } size_t length() const { return buffer_offset_; }
void Reset(); void Reset();
void Append(char c); void Append(char c);
void Append(const char* value); void Append(const char* value);
void Append(const std::string& value); void Append(const std::string_view value);
void AppendFormat(const char* format, ...);
template <typename... Args>
void AppendFormat(const char* format, const Args&... args) {
auto s = fmt::format(format, args...);
Append(s.c_str());
}
void AppendVarargs(const char* format, va_list args); void AppendVarargs(const char* format, va_list args);
void AppendBytes(const uint8_t* buffer, size_t length); void AppendBytes(const uint8_t* buffer, size_t length);
const char* GetString() const;
std::string to_string(); std::string to_string();
char* ToString(); std::string_view to_string_view() const;
std::vector<uint8_t> ToBytes() const; std::vector<uint8_t> to_bytes() const;
private: private:
void Grow(size_t additional_length); void Grow(size_t additional_length);

103
src/xenia/base/string_key.h Normal file
View File

@ -0,0 +1,103 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_STRING_KEY_H_
#define XENIA_BASE_STRING_KEY_H_
#include <string>
#include <variant>
#include "utf8.h"
namespace xe {
namespace internal {
struct string_key_base {
private:
std::variant<std::string, std::string_view> value_;
public:
explicit string_key_base(const std::string_view value) : value_(value) {}
explicit string_key_base(std::string value) : value_(std::move(value)) {}
std::string_view view() const {
return std::holds_alternative<std::string>(value_)
? std::get<std::string>(value_)
: std::get<std::string_view>(value_);
}
};
} // namespace internal
struct string_key : internal::string_key_base {
public:
explicit string_key(const std::string_view value) : string_key_base(value) {}
explicit string_key(std::string value) : string_key_base(value) {}
static string_key create(const std::string_view value) {
return string_key(std::string(value));
}
static string_key create(std::string value) { return string_key(value); }
bool operator==(const string_key& other) const {
return other.view() == view();
}
size_t hash() const { return utf8::hash_fnv1a(view()); }
struct Hash {
size_t operator()(const string_key& t) const { return t.hash(); }
};
};
struct string_key_case : internal::string_key_base {
public:
explicit string_key_case(const std::string_view value)
: string_key_base(value) {}
explicit string_key_case(std::string value) : string_key_base(value) {}
static string_key_case create(const std::string_view value) {
return string_key_case(std::string(value));
}
static string_key_case create(std::string value) {
return string_key_case(value);
}
bool operator==(const string_key_case& other) const {
return utf8::equal_case(other.view(), view());
}
size_t hash() const { return utf8::hash_fnv1a_case(view()); }
struct Hash {
size_t operator()(const string_key_case& t) const { return t.hash(); }
};
};
} // namespace xe
namespace std {
template <>
struct std::hash<xe::string_key> {
std::size_t operator()(const xe::string_key& t) const { return t.hash(); }
};
template <>
struct std::hash<xe::string_key_case> {
std::size_t operator()(const xe::string_key_case& t) const {
return t.hash();
}
};
}; // namespace std
#endif // XENIA_BASE_STRING_KEY_H_

View File

@ -1,69 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/string_util.h"
#include <cinttypes>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <string>
#include "xenia/base/platform.h"
#include "xenia/base/vec128.h"
namespace xe {
namespace string_util {
std::string to_hex_string(uint32_t value) {
char buffer[21];
std::snprintf(buffer, sizeof(buffer), "%08" PRIX32, value);
return std::string(buffer);
}
std::string to_hex_string(uint64_t value) {
char buffer[21];
std::snprintf(buffer, sizeof(buffer), "%016" PRIX64, value);
return std::string(buffer);
}
std::string to_hex_string(const vec128_t& value) {
char buffer[128];
std::snprintf(buffer, sizeof(buffer), "[%.8X, %.8X, %.8X, %.8X]",
value.u32[0], value.u32[1], value.u32[2], value.u32[3]);
return std::string(buffer);
}
#if XE_ARCH_AMD64
// TODO(DrChat): This should not exist. Force the caller to use vec128.
std::string to_hex_string(const __m128& value) {
char buffer[128];
float f[4];
_mm_storeu_ps(f, value);
std::snprintf(
buffer, sizeof(buffer), "[%.8X, %.8X, %.8X, %.8X]",
*reinterpret_cast<uint32_t*>(&f[0]), *reinterpret_cast<uint32_t*>(&f[1]),
*reinterpret_cast<uint32_t*>(&f[2]), *reinterpret_cast<uint32_t*>(&f[3]));
return std::string(buffer);
}
std::string to_string(const __m128& value) {
char buffer[128];
float f[4];
_mm_storeu_ps(f, value);
std::snprintf(buffer, sizeof(buffer), "(%F, %F, %F, %F)", f[0], f[1], f[2],
f[3]);
return std::string(buffer);
}
#endif
} // namespace string_util
} // namespace xe

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,214 +10,290 @@
#ifndef XENIA_BASE_STRING_UTIL_H_ #ifndef XENIA_BASE_STRING_UTIL_H_
#define XENIA_BASE_STRING_UTIL_H_ #define XENIA_BASE_STRING_UTIL_H_
#include <cinttypes> #include <charconv>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <string> #include <string>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#include "xenia/base/string.h"
#include "xenia/base/vec128.h" #include "xenia/base/vec128.h"
// TODO(gibbed): Clang and GCC don't have std::from_chars for floating point(!)
// despite it being part of the C++17 standard. Check this in the future to see
// if it's been resolved.
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
#include <cstdlib>
#endif
namespace xe { namespace xe {
namespace string_util { namespace string_util {
// TODO(gibbed): Figure out why clang doesn't line forward declarations of inline std::string to_hex_string(uint32_t value) {
// inline functions. return fmt::format("{:08X}", value);
}
std::string to_hex_string(uint32_t value); inline std::string to_hex_string(uint64_t value) {
std::string to_hex_string(uint64_t value); return fmt::format("{:016X}", value);
}
inline std::string to_hex_string(float value) { inline std::string to_hex_string(float value) {
union { static_assert(sizeof(uint32_t) == sizeof(value));
uint32_t ui; uint32_t pun;
float flt; std::memcpy(&pun, &value, sizeof(value));
} v; return to_hex_string(pun);
v.flt = value;
return to_hex_string(v.ui);
} }
inline std::string to_hex_string(double value) { inline std::string to_hex_string(double value) {
union { static_assert(sizeof(uint64_t) == sizeof(value));
uint64_t ui; uint64_t pun;
double dbl; std::memcpy(&pun, &value, sizeof(value));
} v; return to_hex_string(pun);
v.dbl = value;
return to_hex_string(v.ui);
} }
std::string to_hex_string(const vec128_t& value); inline std::string to_hex_string(const vec128_t& value) {
return fmt::format("[{:08X} {:08X} {:08X} {:08X} {:08X}]", value.u32[0],
#if XE_ARCH_AMD64 value.u32[1], value.u32[2], value.u32[3]);
}
// TODO(DrChat): This should not exist. Force the caller to use vec128.
std::string to_hex_string(const __m128& value);
std::string to_string(const __m128& value);
#endif
template <typename T> template <typename T>
inline T from_string(const char* value, bool force_hex = false) { inline T from_string(const std::string_view value, bool force_hex = false) {
// Missing implementation for converting type T to string // Missing implementation for converting type T from string
throw; throw;
} }
template <> namespace internal {
inline bool from_string<bool>(const char* value, bool force_hex) {
return std::strcmp(value, "true") == 0 || value[0] == '1';
}
template <> template <typename T, typename V = std::make_signed_t<T>>
inline int32_t from_string<int32_t>(const char* value, bool force_hex) { inline T make_negative(T value) {
if (force_hex || std::strchr(value, 'h') != nullptr) { if constexpr (std::is_unsigned_v<T>) {
return std::strtol(value, nullptr, 16); value = static_cast<T>(-static_cast<V>(value));
} else { } else {
return std::strtol(value, nullptr, 0); value = -value;
} }
return value;
} }
template <> // integral_from_string
inline uint32_t from_string<uint32_t>(const char* value, bool force_hex) {
if (force_hex || std::strchr(value, 'h') != nullptr) {
return std::strtoul(value, nullptr, 16);
} else {
return std::strtoul(value, nullptr, 0);
}
}
template <>
inline int64_t from_string<int64_t>(const char* value, bool force_hex) {
if (force_hex || std::strchr(value, 'h') != nullptr) {
return std::strtoll(value, nullptr, 16);
} else {
return std::strtoll(value, nullptr, 0);
}
}
template <>
inline uint64_t from_string<uint64_t>(const char* value, bool force_hex) {
if (force_hex || std::strchr(value, 'h') != nullptr) {
return std::strtoull(value, nullptr, 16);
} else {
return std::strtoull(value, nullptr, 0);
}
}
template <>
inline float from_string<float>(const char* value, bool force_hex) {
if (force_hex || std::strstr(value, "0x") == value ||
std::strchr(value, 'h') != nullptr) {
union {
uint32_t ui;
float flt;
} v;
v.ui = from_string<uint32_t>(value, force_hex);
return v.flt;
}
return std::strtof(value, nullptr);
}
template <>
inline double from_string<double>(const char* value, bool force_hex) {
if (force_hex || std::strstr(value, "0x") == value ||
std::strchr(value, 'h') != nullptr) {
union {
uint64_t ui;
double dbl;
} v;
v.ui = from_string<uint64_t>(value, force_hex);
return v.dbl;
}
return std::strtod(value, nullptr);
}
template <>
inline vec128_t from_string<vec128_t>(const char* value, bool force_hex) {
vec128_t v;
char* p = const_cast<char*>(value);
bool hex_mode = force_hex;
if (*p == '[') {
hex_mode = true;
++p;
} else if (*p == '(') {
hex_mode = false;
++p;
} else {
// Assume hex?
hex_mode = true;
++p;
}
if (hex_mode) {
v.i32[0] = std::strtoul(p, &p, 16);
while (*p == ' ' || *p == ',') ++p;
v.i32[1] = std::strtoul(p, &p, 16);
while (*p == ' ' || *p == ',') ++p;
v.i32[2] = std::strtoul(p, &p, 16);
while (*p == ' ' || *p == ',') ++p;
v.i32[3] = std::strtoul(p, &p, 16);
} else {
v.f32[0] = std::strtof(p, &p);
while (*p == ' ' || *p == ',') ++p;
v.f32[1] = std::strtof(p, &p);
while (*p == ' ' || *p == ',') ++p;
v.f32[2] = std::strtof(p, &p);
while (*p == ' ' || *p == ',') ++p;
v.f32[3] = std::strtof(p, &p);
}
return v;
}
#if XE_ARCH_AMD64
// TODO(DrChat): ?? Why is this here? Force the caller to use vec128.
template <>
inline __m128 from_string<__m128>(const char* value, bool force_hex) {
__m128 v;
float f[4];
uint32_t u;
char* p = const_cast<char*>(value);
bool hex_mode = force_hex;
if (*p == '[') {
hex_mode = true;
++p;
} else if (*p == '(') {
hex_mode = false;
++p;
} else {
// Assume hex?
hex_mode = true;
++p;
}
if (hex_mode) {
u = std::strtoul(p, &p, 16);
f[0] = *reinterpret_cast<float*>(&u);
while (*p == ' ' || *p == ',') ++p;
u = std::strtoul(p, &p, 16);
f[1] = *reinterpret_cast<float*>(&u);
while (*p == ' ' || *p == ',') ++p;
u = std::strtoul(p, &p, 16);
f[2] = *reinterpret_cast<float*>(&u);
while (*p == ' ' || *p == ',') ++p;
u = std::strtoul(p, &p, 16);
f[3] = *reinterpret_cast<float*>(&u);
} else {
f[0] = std::strtof(p, &p);
while (*p == ' ' || *p == ',') ++p;
f[1] = std::strtof(p, &p);
while (*p == ' ' || *p == ',') ++p;
f[2] = std::strtof(p, &p);
while (*p == ' ' || *p == ',') ++p;
f[3] = std::strtof(p, &p);
}
v = _mm_loadu_ps(f);
return v;
}
#endif
template <typename T> template <typename T>
inline T from_string(const std::string& value, bool force_hex = false) { inline T ifs(const std::string_view value, bool force_hex) {
return from_string<T>(value.c_str(), force_hex); int base = 10;
std::string_view range = value;
bool is_hex = force_hex;
bool is_negative = false;
if (utf8::starts_with(range, "-")) {
is_negative = true;
range = range.substr(1);
}
if (utf8::starts_with(range, "0x")) {
is_hex = true;
range = range.substr(2);
}
if (utf8::ends_with(range, "h")) {
is_hex = true;
range = range.substr(0, range.length() - 1);
}
T result;
if (is_hex) {
base = 16;
}
// TODO(gibbed): do something more with errors?
auto [p, error] =
std::from_chars(range.data(), range.data() + range.size(), result, base);
if (error != std::errc()) {
assert_always();
return T();
}
if (is_negative) {
result = make_negative(result);
}
return result;
}
// floating_point_from_string
template <typename T, typename PUN>
inline T fpfs(const std::string_view value, bool force_hex) {
static_assert(sizeof(T) == sizeof(PUN));
std::string_view range = value;
bool is_hex = force_hex;
bool is_negative = false;
if (utf8::starts_with(range, "-")) {
is_negative = true;
range = range.substr(1);
}
if (utf8::starts_with(range, "0x")) {
is_hex = true;
range = range.substr(2);
}
if (utf8::ends_with(range, "h")) {
is_hex = true;
range = range.substr(0, range.length() - 1);
}
T result;
if (is_hex) {
PUN pun = from_string<PUN>(range, true);
if (is_negative) {
pun = make_negative(pun);
}
std::memcpy(&result, &pun, sizeof(PUN));
} else {
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
auto temp = std::string(range);
result = std::strtof(temp.c_str(), nullptr);
#else
auto [p, error] = std::from_chars(range.data(), range.data() + range.size(),
result, std::chars_format::general);
// TODO(gibbed): do something more with errors?
if (error != std::errc()) {
assert_always();
return T();
}
#endif
if (is_negative) {
result = -result;
}
}
return result;
}
} // namespace internal
template <>
inline bool from_string<bool>(const std::string_view value, bool force_hex) {
return value == "true" || value == "1";
}
template <>
inline int8_t from_string<int8_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<int8_t>(value, force_hex);
}
template <>
inline uint8_t from_string<uint8_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<uint8_t>(value, force_hex);
}
template <>
inline int16_t from_string<int16_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<int16_t>(value, force_hex);
}
template <>
inline uint16_t from_string<uint16_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<uint16_t>(value, force_hex);
}
template <>
inline int32_t from_string<int32_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<int32_t>(value, force_hex);
}
template <>
inline uint32_t from_string<uint32_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<uint32_t>(value, force_hex);
}
template <>
inline int64_t from_string<int64_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<int64_t>(value, force_hex);
}
template <>
inline uint64_t from_string<uint64_t>(const std::string_view value,
bool force_hex) {
return internal::ifs<uint64_t>(value, force_hex);
}
template <>
inline float from_string<float>(const std::string_view value, bool force_hex) {
return internal::fpfs<float, uint32_t>(value, force_hex);
}
template <>
inline double from_string<double>(const std::string_view value,
bool force_hex) {
return internal::fpfs<double, uint64_t>(value, force_hex);
}
template <>
inline vec128_t from_string<vec128_t>(const std::string_view value,
bool force_hex) {
if (!value.size()) {
return vec128_t();
}
vec128_t v;
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
auto temp = std::string(value);
auto p = temp.c_str();
auto end = temp.c_str() + temp.size();
#else
auto p = value.data();
auto end = value.data() + value.size();
#endif
bool is_hex = force_hex;
if (p != end && *p == '[') {
is_hex = true;
++p;
} else if (p != end && *p == '(') {
is_hex = false;
++p;
} else {
// Assume hex?
is_hex = true;
}
if (p == end) {
assert_always();
return vec128_t();
}
if (is_hex) {
for (size_t i = 0; i < 4; i++) {
while (p != end && (*p == ' ' || *p == ',')) {
++p;
}
if (p == end) {
assert_always();
return vec128_t();
}
auto result = std::from_chars(p, end, v.u32[i], 16);
if (result.ec != std::errc()) {
assert_always();
return vec128_t();
}
p = result.ptr;
}
} else {
for (size_t i = 0; i < 4; i++) {
while (p != end && (*p == ' ' || *p == ',')) {
++p;
}
if (p == end) {
assert_always();
return vec128_t();
}
#if XE_COMPILER_CLANG || XE_COMPILER_GNUC
char* next_p;
v.f32[i] = std::strtof(p, &next_p);
p = next_p;
#else
auto result =
std::from_chars(p, end, v.f32[i], std::chars_format::general);
if (result.ec != std::errc()) {
assert_always();
return vec128_t();
}
p = result.ptr;
#endif
}
}
return v;
} }
} // namespace string_util } // namespace string_util

View File

@ -2,17 +2,24 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/platform_win.h" #ifndef XENIA_BASE_SYSTEM_H_
#define XENIA_BASE_SYSTEM_H_
#include <filesystem>
#include <string>
#include "xenia/base/string.h"
namespace xe { namespace xe {
void LaunchBrowser(const wchar_t* url) { void LaunchWebBrowser(const std::string& url);
ShellExecuteW(NULL, L"open", url, NULL, NULL, SW_SHOWNORMAL); void LaunchFileExplorer(const std::filesystem::path& path);
}
} // namespace xe } // namespace xe
#endif // XENIA_BASE_SYSTEM_H_

View File

@ -2,21 +2,27 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/base/platform_linux.h"
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
#include "xenia/base/assert.h"
#include "xenia/base/platform_linux.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/base/system.h"
namespace xe { namespace xe {
void LaunchBrowser(const wchar_t* url) { void LaunchWebBrowser(const std::string& url) {
auto cmd = std::string("xdg-open " + xe::to_string(url)); auto cmd = "xdg-open " + url;
system(cmd.c_str()); system(cmd.c_str());
} }
void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); }
} // namespace xe } // namespace xe

View File

@ -0,0 +1,27 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/platform_win.h"
#include "xenia/base/string.h"
#include "xenia/base/system.h"
namespace xe {
void LaunchWebBrowser(const std::string& url) {
auto temp = xe::to_utf16(url);
ShellExecuteW(nullptr, L"open", reinterpret_cast<LPCWSTR>(url.c_str()),
nullptr, nullptr, SW_SHOWNORMAL);
}
void LaunchFileExplorer(const std::filesystem::path& url) {
ShellExecuteW(nullptr, L"explore", url.c_str(), nullptr, nullptr,
SW_SHOWNORMAL);
}
} // namespace xe

651
src/xenia/base/utf8.cc Normal file
View File

@ -0,0 +1,651 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/utf8.h"
#include <algorithm>
#include <locale>
#include <numeric>
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
namespace utfcpp = utf8;
using citer = std::string_view::const_iterator;
using criter = std::string_view::const_reverse_iterator;
using utf8_citer = utfcpp::iterator<std::string_view::const_iterator>;
using utf8_criter = utfcpp::iterator<std::string_view::const_reverse_iterator>;
namespace xe::utf8 {
uint32_t lower_ascii(const uint32_t c) {
return c >= 'A' && c <= 'Z' ? c + 32 : c;
}
uint32_t upper_ascii(const uint32_t c) {
return c >= 'A' && c <= 'Z' ? c + 32 : c;
}
bool equal_ascii_case(const uint32_t l, const uint32_t r) {
return l == r || lower_ascii(l) == lower_ascii(r);
}
std::pair<utf8_citer, utf8_citer> make_citer(const std::string_view view) {
return {utf8_citer(view.cbegin(), view.cbegin(), view.cend()),
utf8_citer(view.cend(), view.cbegin(), view.cend())};
}
std::pair<utf8_citer, utf8_citer> make_citer(const utf8_citer begin,
const utf8_citer end) {
return {utf8_citer(begin.base(), begin.base(), end.base()),
utf8_citer(end.base(), begin.base(), end.base())};
}
std::pair<utf8_criter, utf8_criter> make_criter(const std::string_view view) {
return {utf8_criter(view.crbegin(), view.crbegin(), view.crend()),
utf8_criter(view.crend(), view.crbegin(), view.crend())};
}
std::pair<utf8_criter, utf8_criter> make_criter(const utf8_criter begin,
const utf8_criter end) {
return {utf8_criter(begin.base(), begin.base(), end.base()),
utf8_criter(end.base(), begin.base(), end.base())};
}
size_t get_count(const std::string_view view) {
return size_t(utfcpp::distance(view.cbegin(), view.cend()));
}
size_t get_byte_length(utf8_citer begin, utf8_citer end) {
return size_t(std::distance(begin.base(), end.base()));
}
size_t get_byte_length(utf8_criter begin, utf8_criter end) {
return size_t(std::distance(begin.base(), end.base()));
}
std::string lower_ascii(const std::string_view view) {
auto [begin, end] = make_citer(view);
std::string result;
for (auto it = begin; it != end; ++it) {
utfcpp::append(char32_t(lower_ascii(*it)), result);
}
return result;
}
std::string upper_ascii(const std::string_view view) {
auto [begin, end] = make_citer(view);
std::string result;
for (auto it = begin; it != end; ++it) {
utfcpp::append(char32_t(upper_ascii(*it)), result);
}
return result;
}
template <bool LOWER>
inline size_t hash_fnv1a(const std::string_view view) {
const size_t offset_basis = 0xCBF29CE484222325ull;
const size_t prime = 0x00000100000001B3ull;
auto work = [&prime](size_t hash, uint8_t byte_of_data) {
hash ^= byte_of_data;
hash *= prime;
return hash;
};
auto hash = offset_basis;
auto [begin, end] = make_citer(view);
for (auto it = begin; it != end; ++it) {
uint32_t c;
if constexpr (LOWER) {
c = lower_ascii(*it);
} else {
c = *it;
}
hash = work(hash, uint8_t((c >> 0) & 0xFF));
hash = work(hash, uint8_t((c >> 8) & 0xFF));
hash = work(hash, uint8_t((c >> 16) & 0xFF));
hash = work(hash, uint8_t((c >> 24) & 0xFF));
}
return hash;
}
size_t hash_fnv1a(const std::string_view view) {
return hash_fnv1a<false>(view);
}
size_t hash_fnv1a_case(const std::string_view view) {
return hash_fnv1a<true>(view);
}
// TODO(gibbed): this is a separate inline function instead of inline within
// split due to a Clang bug: reference to local binding 'needle_begin' declared
// in enclosing function 'split'.
inline utf8_citer find_needle(utf8_citer haystack_it, utf8_citer haystack_end,
utf8_citer needle_begin, utf8_citer needle_end) {
return std::find_if(haystack_it, haystack_end, [&](const auto& c) {
for (auto needle = needle_begin; needle != needle_end; ++needle) {
if (c == *needle) {
return true;
}
}
return false;
});
}
inline utf8_citer find_needle_case(utf8_citer haystack_it,
utf8_citer haystack_end,
utf8_citer needle_begin,
utf8_citer needle_end) {
return std::find_if(haystack_it, haystack_end, [&](const auto& c) {
for (auto needle = needle_begin; needle != needle_end; ++needle) {
if (equal_ascii_case(c, *needle)) {
return true;
}
}
return false;
});
}
std::vector<std::string_view> split(const std::string_view haystack,
const std::string_view needles) {
std::vector<std::string_view> result;
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needles);
auto it = haystack_begin;
auto last = it;
for (;;) {
it = find_needle(it, haystack_end, needle_begin, needle_end);
if (it == haystack_end) {
break;
}
if (it != last) {
auto offset = get_byte_length(haystack_begin, last);
auto length = get_byte_length(haystack_begin, it) - offset;
result.push_back(haystack.substr(offset, length));
}
++it;
last = it;
}
if (last != haystack_end) {
auto offset = get_byte_length(haystack_begin, last);
result.push_back(haystack.substr(offset));
}
return result;
}
bool equal_z(const std::string_view left, const std::string_view right) {
if (!left.size()) {
return !right.size();
} else if (!right.size()) {
return false;
}
auto [left_begin, left_end] = make_citer(left);
auto [right_begin, right_end] = make_citer(right);
auto left_it = left_begin;
auto right_it = right_begin;
for (; left_it != left_end && *left_it != 0 && right_it != right_end &&
*right_it != 0;
++left_it, ++right_it) {
if (*left_it != *right_it) {
return false;
}
}
return (left_it == left_end || *left_it == 0) &&
(right_it == right_end || *right_it == 0);
}
bool equal_case(const std::string_view left, const std::string_view right) {
if (!left.size()) {
return !right.size();
} else if (!right.size()) {
return false;
}
auto [left_begin, left_end] = make_citer(left);
auto [right_begin, right_end] = make_citer(right);
return std::equal(left_begin, left_end, right_begin, right_end,
equal_ascii_case);
}
bool equal_case_z(const std::string_view left, const std::string_view right) {
if (!left.size()) {
return !right.size();
} else if (!right.size()) {
return false;
}
auto [left_begin, left_end] = make_citer(left);
auto [right_begin, right_end] = make_citer(right);
auto left_it = left_begin;
auto right_it = right_begin;
for (; left_it != left_end && *left_it != 0 && right_it != right_end &&
*right_it != 0;
++left_it, ++right_it) {
if (!equal_ascii_case(*left_it, *right_it)) {
return false;
}
}
return (left_it == left_end || *left_it == 0) &&
(right_it == right_end || *right_it == 0);
}
std::string_view::size_type find_any_of(const std::string_view haystack,
const std::string_view needles) {
if (needles.empty()) {
return std::string_view::size_type(0);
} else if (haystack.empty()) {
return std::string_view::npos;
}
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needles);
auto needle_count = get_count(needles);
auto it = find_needle(haystack_begin, haystack_end, needle_begin, needle_end);
if (it == haystack_end) {
return std::string_view::npos;
}
return std::string_view::size_type(get_byte_length(haystack_begin, it));
}
std::string_view::size_type find_any_of_case(const std::string_view haystack,
const std::string_view needles) {
if (needles.empty()) {
return std::string_view::size_type(0);
} else if (haystack.empty()) {
return std::string_view::npos;
}
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needles);
auto needle_count = get_count(needles);
auto it =
find_needle_case(haystack_begin, haystack_end, needle_begin, needle_end);
if (it == haystack_end) {
return std::string_view::npos;
}
return std::string_view::size_type(get_byte_length(haystack_begin, it));
}
std::string_view::size_type find_first_of(const std::string_view haystack,
const std::string_view needle) {
if (needle.empty()) {
return std::string_view::size_type(0);
} else if (haystack.empty()) {
return std::string_view::npos;
}
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needle);
auto needle_count = get_count(needle);
auto it = haystack_begin;
for (; it != haystack_end; ++it) {
it = std::find(it, haystack_end, *needle_begin);
if (it == haystack_end) {
return std::string_view::npos;
}
auto end = it;
for (size_t i = 0; i < needle_count; ++i) {
if (end == haystack_end) {
// not enough room in target for search
return std::string_view::npos;
}
++end;
}
auto [sub_start, sub_end] = make_citer(it, end);
if (std::equal(needle_begin, needle_end, sub_start, sub_end)) {
return std::string_view::size_type(get_byte_length(haystack_begin, it));
}
}
return std::string_view::npos;
}
std::string_view::size_type find_first_of_case(const std::string_view haystack,
const std::string_view needle) {
if (needle.empty()) {
return std::string_view::size_type(0);
} else if (haystack.empty()) {
return std::string_view::npos;
}
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needle);
auto needle_count = get_count(needle);
auto nc = *needle_begin;
auto it = haystack_begin;
for (; it != haystack_end; ++it) {
it = std::find_if(it, haystack_end, [&nc](const uint32_t& c) {
return equal_ascii_case(nc, c);
});
if (it == haystack_end) {
return std::string_view::npos;
}
auto end = it;
for (size_t i = 0; i < needle_count; ++i) {
if (end == haystack_end) {
// not enough room in target for search
return std::string_view::npos;
}
++end;
}
auto [sub_start, sub_end] = make_citer(it, end);
if (std::equal(needle_begin, needle_end, sub_start, sub_end,
equal_ascii_case)) {
return std::string_view::size_type(get_byte_length(haystack_begin, it));
}
}
return std::string_view::npos;
}
bool starts_with(const std::string_view haystack,
const std::string_view needle) {
if (needle.empty()) {
return true;
} else if (haystack.empty()) {
return false;
}
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needle);
auto needle_count = get_count(needle);
auto it = haystack_begin;
auto end = it;
for (size_t i = 0; i < needle_count; ++i) {
if (end == haystack_end) {
// not enough room in target for search
return false;
}
++end;
}
auto [sub_start, sub_end] = make_citer(it, end);
return std::equal(needle_begin, needle_end, sub_start, sub_end);
}
bool starts_with_case(const std::string_view haystack,
const std::string_view needle) {
if (needle.empty()) {
return true;
} else if (haystack.empty()) {
return false;
}
auto [haystack_begin, haystack_end] = make_citer(haystack);
auto [needle_begin, needle_end] = make_citer(needle);
auto needle_count = get_count(needle);
auto it = haystack_begin;
auto end = it;
for (size_t i = 0; i < needle_count; ++i) {
if (end == haystack_end) {
// not enough room in target for search
return false;
}
++end;
}
auto [sub_start, sub_end] = make_citer(it, end);
return std::equal(needle_begin, needle_end, sub_start, sub_end,
equal_ascii_case);
}
bool ends_with(const std::string_view haystack, const std::string_view needle) {
if (needle.empty()) {
return true;
} else if (haystack.empty()) {
return false;
}
auto [haystack_begin, haystack_end] = make_criter(haystack);
auto [needle_begin, needle_end] = make_criter(needle);
auto needle_count = get_count(needle);
auto it = haystack_begin;
auto end = it;
for (size_t i = 0; i < needle_count; ++i) {
if (end == haystack_end) {
// not enough room in target for search
return false;
}
++end;
}
auto [sub_start, sub_end] = make_criter(it, end);
return std::equal(needle_begin, needle_end, sub_start, sub_end);
}
bool ends_with_case(const std::string_view haystack,
const std::string_view needle) {
if (needle.empty()) {
return true;
} else if (haystack.empty()) {
return false;
}
auto [haystack_begin, haystack_end] = make_criter(haystack);
auto [needle_begin, needle_end] = make_criter(needle);
auto needle_count = get_count(needle);
auto it = haystack_begin;
auto end = it;
for (size_t i = 0; i < needle_count; ++i) {
if (end == haystack_end) {
// not enough room in target for search
return false;
}
++end;
}
auto [sub_start, sub_end] = make_criter(it, end);
return std::equal(needle_begin, needle_end, sub_start, sub_end,
equal_ascii_case);
}
std::vector<std::string_view> split_path(const std::string_view path) {
return split(path, u8"\\/");
}
std::string join_paths(const std::string_view left_path,
const std::string_view right_path, char32_t sep) {
if (!left_path.length()) {
return std::string(right_path);
} else if (!right_path.length()) {
return std::string(left_path);
}
auto [it, end] = make_criter(left_path);
std::string result = std::string(left_path);
if (*it != static_cast<uint32_t>(sep)) {
utfcpp::append(sep, result);
}
return result + std::string(right_path);
}
std::string join_paths(std::vector<std::string_view> paths, char32_t sep) {
std::string result;
for (const auto& path : paths) {
result = join_paths(result, path, sep);
}
return result;
}
std::string fix_path_separators(const std::string_view path, char32_t new_sep) {
if (path.empty()) {
return std::string();
}
// Swap all separators to new_sep.
const char32_t old_sep = new_sep == U'\\' ? U'/' : U'\\';
auto [path_begin, path_end] = make_citer(path);
std::string result;
auto it = path_begin;
auto last = it;
for (;;) {
it = std::find(it, path_end, uint32_t(old_sep));
if (it == path_end) {
break;
}
if (it != last) {
auto offset = get_byte_length(path_begin, last);
auto length = get_byte_length(path_begin, it) - offset;
result += path.substr(offset, length);
utfcpp::append(new_sep, result);
}
++it;
last = it;
}
if (last == path_begin) {
return std::string(path);
}
if (last != path_end) {
auto offset = get_byte_length(path_begin, last);
result += path.substr(offset);
}
return result;
}
std::string find_name_from_path(const std::string_view path, char32_t sep) {
if (path.empty()) {
return std::string();
}
auto [begin, end] = make_criter(path);
auto it = begin;
size_t padding = 0;
if (*it == uint32_t(sep)) {
++it;
padding = 1;
}
if (it == end) {
return std::string();
}
it = std::find(it, end, uint32_t(sep));
if (it == end) {
return std::string(path.substr(0, path.size() - padding));
}
auto length = get_byte_length(begin, it);
auto offset = path.length() - length;
return std::string(path.substr(offset, length - padding));
}
std::string find_base_name_from_path(const std::string_view path,
char32_t sep) {
auto name = find_name_from_path(path, sep);
if (!name.size()) {
return std::string();
}
auto [begin, end] = make_criter(name);
auto it = std::find(begin, end, uint32_t('.'));
if (it == end) {
return name;
}
it++;
if (it == end) {
return std::string();
}
auto length = name.length() - get_byte_length(begin, it);
return std::string(name.substr(0, length));
}
std::string find_base_path(const std::string_view path, char32_t sep) {
if (path.empty()) {
return std::string();
}
auto [begin, end] = make_criter(path);
auto it = begin;
if (*it == uint32_t(sep)) {
++it;
}
it = std::find(it, end, uint32_t(sep));
if (it == end) {
return std::string();
}
++it;
if (it == end) {
return std::string();
}
auto length = path.length() - get_byte_length(begin, it);
return std::string(path.substr(0, length));
}
std::string canonicalize_path(const std::string_view path, char32_t sep) {
if (path.empty()) {
return std::string();
}
auto parts = split_path(path);
std::vector<std::vector<std::string_view>::size_type> indices(parts.size());
std::iota(indices.begin(), indices.end(), 0);
for (auto it = indices.begin(); it != indices.end();) {
const auto& part = parts[*it];
if (part == ".") {
// Potential marker for current directory.
it = indices.erase(it);
} else if (part == "..") {
// Ensure we don't override the device name.
if (it != indices.begin()) {
auto prev = std::prev(it);
if (!ends_with(parts[*prev], ":")) {
it = indices.erase(prev);
}
}
it = indices.erase(it);
} else {
++it;
}
}
std::string result;
for (auto index : indices) {
result = join_paths(result, parts[index], sep);
}
return result == "." || result == ".." ? std::string() : result;
} // namespace utf8
} // namespace xe::utf8

137
src/xenia/base/utf8.h Normal file
View File

@ -0,0 +1,137 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_BASE_UTF8_H_
#define XENIA_BASE_UTF8_H_
#include <string>
#include <vector>
#include "xenia/base/platform.h"
namespace xe::utf8 {
std::string lower_ascii(const std::string_view view);
std::string upper_ascii(const std::string_view view);
size_t hash_fnv1a(const std::string_view view);
size_t hash_fnv1a_case(const std::string_view view);
// Splits the given string on any delimiters and returns all parts.
std::vector<std::string_view> split(const std::string_view path,
const std::string_view delimiters);
bool equal_z(const std::string_view left, const std::string_view right);
bool equal_case(const std::string_view left, const std::string_view right);
bool equal_case_z(const std::string_view left, const std::string_view right);
std::string_view::size_type find_any_of(const std::string_view haystack,
const std::string_view needles);
std::string_view::size_type find_any_of_case(const std::string_view haystack,
const std::string_view needles);
std::string_view::size_type find_first_of(const std::string_view haystack,
const std::string_view needle);
// find_first_of string, case insensitive.
std::string_view::size_type find_first_of_case(const std::string_view haystack,
const std::string_view needle);
bool starts_with(const std::string_view haystack,
const std::string_view needle);
bool starts_with_case(const std::string_view haystack,
const std::string_view needle);
bool ends_with(const std::string_view haystack, const std::string_view needle);
bool ends_with_case(const std::string_view haystack,
const std::string_view needle);
// Splits the given path on any valid path separator and returns all parts.
std::vector<std::string_view> split_path(const std::string_view path);
// Joins two path segments with the given separator.
std::string join_paths(const std::string_view left_path,
const std::string_view right_path,
char32_t sep = kPathSeparator);
std::string join_paths(std::vector<std::string_view> paths,
char32_t sep = kPathSeparator);
inline std::string join_paths(
std::initializer_list<const std::string_view> paths,
char32_t sep = kPathSeparator) {
std::string result;
for (auto path : paths) {
result = join_paths(result, path, sep);
}
return result;
}
inline std::string join_guest_paths(const std::string_view left_path,
const std::string_view right_path) {
return join_paths(left_path, right_path, kGuestPathSeparator);
}
inline std::string join_guest_paths(std::vector<std::string_view> paths) {
return join_paths(paths, kGuestPathSeparator);
}
inline std::string join_guest_paths(
std::initializer_list<const std::string_view> paths) {
return join_paths(paths, kGuestPathSeparator);
}
// Replaces all path separators with the given value and removes redundant
// separators.
std::string fix_path_separators(const std::string_view path,
char32_t new_sep = kPathSeparator);
inline std::string fix_guest_path_separators(const std::string_view path) {
return fix_path_separators(path, kGuestPathSeparator);
}
// Find the top directory name or filename from a path.
std::string find_name_from_path(const std::string_view path,
char32_t sep = kPathSeparator);
inline std::string find_name_from_guest_path(const std::string_view path) {
return find_name_from_path(path, kGuestPathSeparator);
}
std::string find_base_name_from_path(const std::string_view path,
char32_t sep = kPathSeparator);
inline std::string find_base_name_from_guest_path(const std::string_view path) {
return find_base_name_from_path(path, kGuestPathSeparator);
}
// Get parent path of the given directory or filename.
std::string find_base_path(const std::string_view path,
char32_t sep = kPathSeparator);
inline std::string find_base_guest_path(const std::string_view path) {
return find_base_path(path, kGuestPathSeparator);
}
// Canonicalizes a path, removing ..'s.
std::string canonicalize_path(const std::string_view path,
char32_t sep = kPathSeparator);
inline std::string canonicalize_guest_path(const std::string_view path) {
return canonicalize_path(path, kGuestPathSeparator);
}
} // namespace xe::utf8
#endif // XENIA_BASE_UTF8_H_

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,6 +10,7 @@
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#include "xenia/base/vec128.h" #include "xenia/base/vec128.h"
@ -17,10 +18,7 @@
namespace xe { namespace xe {
std::string to_string(const vec128_t& value) { std::string to_string(const vec128_t& value) {
char buffer[128]; return fmt::format("({}, {}, {}, {})", value.x, value.y, value.z, value.w);
std::snprintf(buffer, sizeof(buffer), "(%g, %g, %g, %g)", value.x, value.y,
value.z, value.w);
return std::string(buffer);
} }
} // namespace xe } // namespace xe

View File

@ -1,18 +1,26 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "config.h" #include "config.h"
#include "cpptoml/include/cpptoml.h"
#include "third_party/cpptoml/include/cpptoml.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/base/filesystem.h" #include "xenia/base/filesystem.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
std::shared_ptr<cpptoml::table> ParseFile(const std::wstring& filename) { std::shared_ptr<cpptoml::table> ParseFile(
#if XE_PLATFORM_WIN32 const std::filesystem::path& filename) {
std::ifstream file(filename); std::ifstream file(filename);
#else
std::ifstream file(xe::to_string(filename));
#endif
if (!file.is_open()) { if (!file.is_open()) {
throw cpptoml::parse_exception(xe::to_string(filename) + throw cpptoml::parse_exception(xe::path_to_utf8(filename) +
" could not be opened for parsing"); " could not be opened for parsing");
} }
cpptoml::parser p(file); cpptoml::parser p(file);
@ -21,10 +29,10 @@ std::shared_ptr<cpptoml::table> ParseFile(const std::wstring& filename) {
CmdVar(config, "", "Specifies the target config to load."); CmdVar(config, "", "Specifies the target config to load.");
namespace config { namespace config {
std::wstring config_name = L"xenia.config.toml"; std::string config_name = "xenia.config.toml";
std::wstring config_folder; std::filesystem::path config_folder;
std::wstring config_path; std::filesystem::path config_path;
std::wstring game_config_suffix = L".config.toml"; std::string game_config_suffix = ".config.toml";
bool sortCvar(cvar::IConfigVar* a, cvar::IConfigVar* b) { bool sortCvar(cvar::IConfigVar* a, cvar::IConfigVar* b) {
if (a->category() < b->category()) return true; if (a->category() < b->category()) return true;
@ -33,17 +41,18 @@ bool sortCvar(cvar::IConfigVar* a, cvar::IConfigVar* b) {
return false; return false;
} }
std::shared_ptr<cpptoml::table> ParseConfig(const std::wstring& config_path) { std::shared_ptr<cpptoml::table> ParseConfig(
const std::filesystem::path& config_path) {
try { try {
return ParseFile(config_path); return ParseFile(config_path);
} catch (cpptoml::parse_exception e) { } catch (cpptoml::parse_exception e) {
xe::FatalError(L"Failed to parse config file '%s':\n\n%s", xe::FatalError(fmt::format("Failed to parse config file '{}':\n\n{}",
config_path.c_str(), xe::to_wstring(e.what()).c_str()); xe::path_to_utf8(config_path), e.what()));
return nullptr; return nullptr;
} }
} }
void ReadConfig(const std::wstring& file_path) { void ReadConfig(const std::filesystem::path& file_path) {
const auto config = ParseConfig(file_path); const auto config = ParseConfig(file_path);
for (auto& it : *cvar::ConfigVars) { for (auto& it : *cvar::ConfigVars) {
auto config_var = static_cast<cvar::IConfigVar*>(it.second); auto config_var = static_cast<cvar::IConfigVar*>(it.second);
@ -52,10 +61,10 @@ void ReadConfig(const std::wstring& file_path) {
config_var->LoadConfigValue(config->get_qualified(config_key)); config_var->LoadConfigValue(config->get_qualified(config_key));
} }
} }
XELOGI("Loaded config: %S", file_path.c_str()); XELOGI("Loaded config: %s", xe::path_to_utf8(file_path).c_str());
} }
void ReadGameConfig(std::wstring file_path) { void ReadGameConfig(const std::filesystem::path& file_path) {
const auto config = ParseConfig(file_path); const auto config = ParseConfig(file_path);
for (auto& it : *cvar::ConfigVars) { for (auto& it : *cvar::ConfigVars) {
auto config_var = static_cast<cvar::IConfigVar*>(it.second); auto config_var = static_cast<cvar::IConfigVar*>(it.second);
@ -64,7 +73,7 @@ void ReadGameConfig(std::wstring file_path) {
config_var->LoadGameConfigValue(config->get_qualified(config_key)); config_var->LoadGameConfigValue(config->get_qualified(config_key));
} }
} }
XELOGI("Loaded game config: %S", file_path.c_str()); XELOGI("Loaded game config: %s", xe::path_to_utf8(file_path).c_str());
} }
void SaveConfig() { void SaveConfig() {
@ -84,31 +93,29 @@ void SaveConfig() {
output << std::endl; output << std::endl;
} }
last_category = config_var->category(); last_category = config_var->category();
output << xe::format_string("[%s]\n", last_category.c_str()); output << fmt::format("[{}]\n", last_category);
} }
auto value = config_var->config_value(); auto value = config_var->config_value();
if (value.find('\n') == std::string::npos) { if (xe::utf8::find_any_of(value, "\n") == std::string_view::npos) {
output << std::left << std::setw(40) << std::setfill(' ') output << std::left << std::setw(40) << std::setfill(' ')
<< xe::format_string("%s = %s", config_var->name().c_str(), << fmt::format("{} = {}", config_var->name(),
config_var->config_value().c_str()); config_var->config_value());
} else { } else {
auto lines = xe::split_string(value, "\n"); auto lines = xe::utf8::split(value, "\n");
auto first_it = lines.cbegin(); auto first_it = lines.cbegin();
output << xe::format_string("%s = %s\n", config_var->name().c_str(), output << fmt::format("{} = {}\n", config_var->name(), *first_it);
(*first_it).c_str());
auto last_it = std::prev(lines.cend()); auto last_it = std::prev(lines.cend());
for (auto it = std::next(first_it); it != last_it; ++it) { for (auto it = std::next(first_it); it != last_it; ++it) {
output << (*it).c_str() << "\n"; output << *it << "\n";
} }
output << std::left << std::setw(40) << std::setfill(' ') output << std::left << std::setw(40) << std::setfill(' ') << *last_it;
<< (*last_it).c_str();
} }
output << xe::format_string("\t# %s\n", config_var->description().c_str()); output << fmt::format("\t# {}\n", config_var->description());
} }
if (xe::filesystem::PathExists(config_path)) { if (xe::filesystem::PathExists(config_path)) {
std::ifstream existingConfigStream(xe::to_string(config_path).c_str()); std::ifstream existingConfigStream(config_path);
const std::string existingConfig( const std::string existingConfig(
(std::istreambuf_iterator<char>(existingConfigStream)), (std::istreambuf_iterator<char>(existingConfigStream)),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
@ -119,20 +126,16 @@ void SaveConfig() {
// save the config file // save the config file
xe::filesystem::CreateParentFolder(config_path); xe::filesystem::CreateParentFolder(config_path);
std::ofstream file; std::ofstream file;
#if XE_PLATFORM_WIN32
file.open(config_path, std::ios::out | std::ios::trunc); file.open(config_path, std::ios::out | std::ios::trunc);
#else
file.open(xe::to_string(config_path), std::ios::out | std::ios::trunc);
#endif
file << output.str(); file << output.str();
file.close(); file.close();
} }
void SetupConfig(const std::wstring& config_folder) { void SetupConfig(const std::filesystem::path& config_folder) {
config::config_folder = config_folder; config::config_folder = config_folder;
// check if the user specified a specific config to load // check if the user specified a specific config to load
if (!cvars::config.empty()) { if (!cvars::config.empty()) {
config_path = xe::to_wstring(cvars::config); config_path = xe::to_path(cvars::config);
if (xe::filesystem::PathExists(config_path)) { if (xe::filesystem::PathExists(config_path)) {
ReadConfig(config_path); ReadConfig(config_path);
return; return;
@ -141,7 +144,7 @@ void SetupConfig(const std::wstring& config_folder) {
// if the user specified a --config argument, but the file doesn't exist, // if the user specified a --config argument, but the file doesn't exist,
// let's also load the default config // let's also load the default config
if (!config_folder.empty()) { if (!config_folder.empty()) {
config_path = xe::join_paths(config_folder, config_name); config_path = config_folder / config_name;
if (xe::filesystem::PathExists(config_path)) { if (xe::filesystem::PathExists(config_path)) {
ReadConfig(config_path); ReadConfig(config_path);
} }
@ -151,9 +154,10 @@ void SetupConfig(const std::wstring& config_folder) {
} }
} }
void LoadGameConfig(const std::wstring& title_id) { void LoadGameConfig(const std::string_view title_id) {
const auto game_config_path = xe::join_paths( const auto game_config_folder = config_folder / "config";
xe::join_paths(config_folder, L"config"), title_id + game_config_suffix); const auto game_config_path =
game_config_folder / (std::string(title_id) + game_config_suffix);
if (xe::filesystem::PathExists(game_config_path)) { if (xe::filesystem::PathExists(game_config_path)) {
ReadGameConfig(game_config_path); ReadGameConfig(game_config_path);
} }

View File

@ -1,10 +1,20 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CONFIG_H_ #ifndef XENIA_CONFIG_H_
#define XENIA_CONFIG_H_ #define XENIA_CONFIG_H_
#include <string>
#include <filesystem>
namespace config { namespace config {
void SetupConfig(const std::wstring& config_folder); void SetupConfig(const std::filesystem::path& config_folder);
void LoadGameConfig(const std::wstring& title_id); void LoadGameConfig(const std::string_view title_id);
} // namespace config } // namespace config
#endif // XENIA_CONFIG_H_ #endif // XENIA_CONFIG_H_

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -23,7 +23,7 @@ class CodeCache {
CodeCache() = default; CodeCache() = default;
virtual ~CodeCache() = default; virtual ~CodeCache() = default;
virtual std::wstring file_name() const = 0; virtual const std::filesystem::path& file_name() const = 0;
virtual uint32_t base_address() const = 0; virtual uint32_t base_address() const = 0;
virtual uint32_t total_size() const = 0; virtual uint32_t total_size() const = 0;

View File

@ -8,6 +8,7 @@ project("xenia-cpu-backend-x64")
language("C++") language("C++")
links({ links({
"capstone", "capstone",
"fmt",
"xenia-base", "xenia-base",
"xenia-cpu", "xenia-cpu",
}) })

View File

@ -86,7 +86,7 @@ bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) { if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
DumpMachineCode(machine_code, code_size, function->source_map(), DumpMachineCode(machine_code, code_size, function->source_map(),
&string_buffer_); &string_buffer_);
debug_info->set_machine_code_disasm(string_buffer_.ToString()); debug_info->set_machine_code_disasm(strdup(string_buffer_.buffer()));
string_buffer_.Reset(); string_buffer_.Reset();
} }
@ -126,7 +126,7 @@ void X64Assembler::DumpMachineCode(
if (code_offset >= next_code_offset && if (code_offset >= next_code_offset &&
source_map_index < source_map.size()) { source_map_index < source_map.size()) {
auto& source_map_entry = source_map[source_map_index]; auto& source_map_entry = source_map[source_map_index];
str->AppendFormat("%.8X ", source_map_entry.guest_address); str->AppendFormat("{:08X} ", source_map_entry.guest_address);
++source_map_index; ++source_map_index;
next_code_offset = source_map_index < source_map.size() next_code_offset = source_map_index < source_map.size()
? source_map[source_map_index].code_offset ? source_map[source_map_index].code_offset
@ -135,7 +135,7 @@ void X64Assembler::DumpMachineCode(
str->Append(" "); str->Append(" ");
} }
str->AppendFormat("%.8X %-6s %s\n", uint32_t(insn.address), str->AppendFormat("{:08X} {:<6} {}\n", uint32_t(insn.address),
insn.mnemonic, insn.op_str); insn.mnemonic, insn.op_str);
} }
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -17,6 +17,7 @@
#pragma comment(lib, "../third_party/vtune/lib64/jitprofiling.lib") #pragma comment(lib, "../third_party/vtune/lib64/jitprofiling.lib")
#endif #endif
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/clock.h" #include "xenia/base/clock.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
@ -61,8 +62,8 @@ bool X64CodeCache::Initialize() {
} }
// Create mmap file. This allows us to share the code cache with the debugger. // Create mmap file. This allows us to share the code cache with the debugger.
file_name_ = std::wstring(L"Local\\xenia_code_cache_") + file_name_ =
std::to_wstring(Clock::QueryHostTickCount()); fmt::format("Local\\xenia_code_cache_{}", Clock::QueryHostTickCount());
mapping_ = xe::memory::CreateFileMappingHandle( mapping_ = xe::memory::CreateFileMappingHandle(
file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite, file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite,
false); false);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -45,7 +45,7 @@ class X64CodeCache : public CodeCache {
virtual bool Initialize(); virtual bool Initialize();
std::wstring file_name() const override { return file_name_; } const std::filesystem::path& file_name() const override { return file_name_; }
uint32_t base_address() const override { return kGeneratedCodeBase; } uint32_t base_address() const override { return kGeneratedCodeBase; }
uint32_t total_size() const override { return kGeneratedCodeSize; } uint32_t total_size() const override { return kGeneratedCodeSize; }
@ -99,7 +99,7 @@ class X64CodeCache : public CodeCache {
const EmitFunctionInfo& func_info, void* code_address, const EmitFunctionInfo& func_info, void* code_address,
UnwindReservation unwind_reservation) {} UnwindReservation unwind_reservation) {}
std::wstring file_name_; std::filesystem::path file_name_;
xe::memory::FileMappingHandle mapping_ = nullptr; xe::memory::FileMappingHandle mapping_ = nullptr;
// NOTE: the global critical region must be held when manipulating the offsets // NOTE: the global critical region must be held when manipulating the offsets

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -10,9 +10,11 @@
#include "xenia/cpu/backend/x64/x64_emitter.h" #include "xenia/cpu/backend/x64/x64_emitter.h"
#include <stddef.h> #include <stddef.h>
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/atomic.h" #include "xenia/base/atomic.h"
#include "xenia/base/debugging.h" #include "xenia/base/debugging.h"
@ -476,8 +478,8 @@ void X64Emitter::CallIndirect(const hir::Instr* instr,
uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) { uint64_t UndefinedCallExtern(void* raw_context, uint64_t function_ptr) {
auto function = reinterpret_cast<Function*>(function_ptr); auto function = reinterpret_cast<Function*>(function_ptr);
if (!cvars::ignore_undefined_externs) { if (!cvars::ignore_undefined_externs) {
xe::FatalError("undefined extern call to %.8X %s", function->address(), xe::FatalError(fmt::format("undefined extern call to {:08X} {}",
function->name().c_str()); function->address(), function->name().c_str()));
} else { } else {
XELOGE("undefined extern call to %.8X %s", function->address(), XELOGE("undefined extern call to %.8X %s", function->address(),
function->name().c_str()); function->name().c_str());

View File

@ -2,13 +2,14 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/cpu/compiler/passes/finalization_pass.h" #include "xenia/cpu/compiler/passes/finalization_pass.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/profiling.h" #include "xenia/base/profiling.h"
#include "xenia/cpu/backend/backend.h" #include "xenia/cpu/backend/backend.h"
#include "xenia/cpu/compiler/compiler.h" #include "xenia/cpu/compiler/compiler.h"
@ -43,9 +44,11 @@ bool FinalizationPass::Run(HIRBuilder* builder) {
auto label = block->label_head; auto label = block->label_head;
while (label) { while (label) {
if (!label->name) { if (!label->name) {
const size_t label_len = 6 + 4 + 1; const size_t label_len = 6 + 4;
char* name = reinterpret_cast<char*>(arena->Alloc(label_len)); char* name = reinterpret_cast<char*>(arena->Alloc(label_len + 1));
snprintf(name, label_len, "_label%d", label->id); assert_true(label->id <= 9999);
auto end = fmt::format_to_n(name, label_len, "_label{}", label->id);
name[end.size] = '\0';
label->name = name; label->name = name;
} }
label = label->next; label = label->next;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -58,7 +58,7 @@ bool ElfModule::is_executable() const {
return hdr->e_entry != 0; return hdr->e_entry != 0;
} }
bool ElfModule::Load(const std::string& name, const std::string& path, bool ElfModule::Load(const std::string_view name, const std::string_view path,
const void* elf_addr, size_t elf_length) { const void* elf_addr, size_t elf_length) {
name_ = name; name_ = name;
path_ = path; path_ = path;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -34,7 +34,7 @@ class ElfModule : public xe::cpu::Module {
bool is_executable() const override; bool is_executable() const override;
const std::string& path() const { return path_; } const std::string& path() const { return path_; }
bool Load(const std::string& name, const std::string& path, bool Load(const std::string_view name, const std::string_view path,
const void* elf_addr, size_t elf_length); const void* elf_addr, size_t elf_length);
bool Unload(); bool Unload();

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -11,20 +11,15 @@
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/string.h"
namespace xe { namespace xe {
namespace cpu { namespace cpu {
ExportResolver::Table::Table(const char* module_name, ExportResolver::Table::Table(const std::string_view module_name,
const std::vector<Export*>* exports_by_ordinal) const std::vector<Export*>* exports_by_ordinal)
: exports_by_ordinal_(exports_by_ordinal) { : exports_by_ordinal_(exports_by_ordinal) {
auto dot_pos = std::strrchr(module_name, '.'); module_name_ = utf8::find_base_name_from_guest_path(module_name);
if (dot_pos != nullptr) {
std::strncpy(module_name_, module_name,
static_cast<size_t>(dot_pos - module_name));
} else {
std::strncpy(module_name_, module_name, xe::countof(module_name_) - 1);
}
exports_by_name_.reserve(exports_by_ordinal_->size()); exports_by_name_.reserve(exports_by_ordinal_->size());
for (size_t i = 0; i < exports_by_ordinal_->size(); ++i) { for (size_t i = 0; i < exports_by_ordinal_->size(); ++i) {
@ -43,7 +38,8 @@ ExportResolver::ExportResolver() = default;
ExportResolver::~ExportResolver() = default; ExportResolver::~ExportResolver() = default;
void ExportResolver::RegisterTable( void ExportResolver::RegisterTable(
const char* module_name, const std::vector<xe::cpu::Export*>* exports) { const std::string_view module_name,
const std::vector<xe::cpu::Export*>* exports) {
tables_.emplace_back(module_name, exports); tables_.emplace_back(module_name, exports);
all_exports_by_name_.reserve(all_exports_by_name_.size() + exports->size()); all_exports_by_name_.reserve(all_exports_by_name_.size() + exports->size());
@ -58,11 +54,10 @@ void ExportResolver::RegisterTable(
[](Export* a, Export* b) { return std::strcmp(a->name, b->name) <= 0; }); [](Export* a, Export* b) { return std::strcmp(a->name, b->name) <= 0; });
} }
Export* ExportResolver::GetExportByOrdinal(const char* module_name, Export* ExportResolver::GetExportByOrdinal(const std::string_view module_name,
uint16_t ordinal) { uint16_t ordinal) {
for (const auto& table : tables_) { for (const auto& table : tables_) {
if (std::strncmp(module_name, table.module_name(), if (xe::utf8::starts_with_case(module_name, table.module_name())) {
std::strlen(table.module_name())) == 0) {
if (ordinal > table.exports_by_ordinal().size()) { if (ordinal > table.exports_by_ordinal().size()) {
return nullptr; return nullptr;
} }
@ -72,7 +67,7 @@ Export* ExportResolver::GetExportByOrdinal(const char* module_name,
return nullptr; return nullptr;
} }
void ExportResolver::SetVariableMapping(const char* module_name, void ExportResolver::SetVariableMapping(const std::string_view module_name,
uint16_t ordinal, uint32_t value) { uint16_t ordinal, uint32_t value) {
auto export_entry = GetExportByOrdinal(module_name, ordinal); auto export_entry = GetExportByOrdinal(module_name, ordinal);
assert_not_null(export_entry); assert_not_null(export_entry);
@ -80,7 +75,7 @@ void ExportResolver::SetVariableMapping(const char* module_name,
export_entry->variable_ptr = value; export_entry->variable_ptr = value;
} }
void ExportResolver::SetFunctionMapping(const char* module_name, void ExportResolver::SetFunctionMapping(const std::string_view module_name,
uint16_t ordinal, uint16_t ordinal,
xe_kernel_export_shim_fn shim) { xe_kernel_export_shim_fn shim) {
auto export_entry = GetExportByOrdinal(module_name, ordinal); auto export_entry = GetExportByOrdinal(module_name, ordinal);
@ -89,7 +84,7 @@ void ExportResolver::SetFunctionMapping(const char* module_name,
export_entry->function_data.shim = shim; export_entry->function_data.shim = shim;
} }
void ExportResolver::SetFunctionMapping(const char* module_name, void ExportResolver::SetFunctionMapping(const std::string_view module_name,
uint16_t ordinal, uint16_t ordinal,
ExportTrampoline trampoline) { ExportTrampoline trampoline) {
auto export_entry = GetExportByOrdinal(module_name, ordinal); auto export_entry = GetExportByOrdinal(module_name, ordinal);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -117,9 +117,10 @@ class ExportResolver {
public: public:
class Table { class Table {
public: public:
Table(const char* module_name, const std::vector<Export*>* exports); Table(const std::string_view module_name,
const std::vector<Export*>* exports);
const char* module_name() const { return module_name_; } const std::string& module_name() const { return module_name_; }
const std::vector<Export*>& exports_by_ordinal() const { const std::vector<Export*>& exports_by_ordinal() const {
return *exports_by_ordinal_; return *exports_by_ordinal_;
} }
@ -128,7 +129,7 @@ class ExportResolver {
} }
private: private:
char module_name_[32] = {0}; std::string module_name_;
const std::vector<Export*>* exports_by_ordinal_ = nullptr; const std::vector<Export*>* exports_by_ordinal_ = nullptr;
std::vector<Export*> exports_by_name_; std::vector<Export*> exports_by_name_;
}; };
@ -136,20 +137,21 @@ class ExportResolver {
ExportResolver(); ExportResolver();
~ExportResolver(); ~ExportResolver();
void RegisterTable(const char* module_name, void RegisterTable(const std::string_view module_name,
const std::vector<Export*>* exports); const std::vector<Export*>* exports);
const std::vector<Table>& tables() const { return tables_; } const std::vector<Table>& tables() const { return tables_; }
const std::vector<Export*>& all_exports_by_name() const { const std::vector<Export*>& all_exports_by_name() const {
return all_exports_by_name_; return all_exports_by_name_;
} }
Export* GetExportByOrdinal(const char* module_name, uint16_t ordinal); Export* GetExportByOrdinal(const std::string_view module_name,
uint16_t ordinal);
void SetVariableMapping(const char* module_name, uint16_t ordinal, void SetVariableMapping(const std::string_view module_name, uint16_t ordinal,
uint32_t value); uint32_t value);
void SetFunctionMapping(const char* module_name, uint16_t ordinal, void SetFunctionMapping(const std::string_view module_name, uint16_t ordinal,
xe_kernel_export_shim_fn shim); xe_kernel_export_shim_fn shim);
void SetFunctionMapping(const char* module_name, uint16_t ordinal, void SetFunctionMapping(const std::string_view module_name, uint16_t ordinal,
ExportTrampoline trampoline); ExportTrampoline trampoline);
private: private:

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -110,25 +110,25 @@ void HIRBuilder::DumpValue(StringBuffer* str, Value* value) {
if (value->IsConstant()) { if (value->IsConstant()) {
switch (value->type) { switch (value->type) {
case INT8_TYPE: case INT8_TYPE:
str->AppendFormat("%X", value->constant.i8); str->AppendFormat("{:X}", value->constant.i8);
break; break;
case INT16_TYPE: case INT16_TYPE:
str->AppendFormat("%X", value->constant.i16); str->AppendFormat("{:X}", value->constant.i16);
break; break;
case INT32_TYPE: case INT32_TYPE:
str->AppendFormat("%X", value->constant.i32); str->AppendFormat("{:X}", value->constant.i32);
break; break;
case INT64_TYPE: case INT64_TYPE:
str->AppendFormat("%" PRIX64, value->constant.i64); str->AppendFormat("{:X}", value->constant.i64);
break; break;
case FLOAT32_TYPE: case FLOAT32_TYPE:
str->AppendFormat("%F", value->constant.f32); str->AppendFormat("{:F}", value->constant.f32);
break; break;
case FLOAT64_TYPE: case FLOAT64_TYPE:
str->AppendFormat("%F", value->constant.f64); str->AppendFormat("{:F}", value->constant.f64);
break; break;
case VEC128_TYPE: case VEC128_TYPE:
str->AppendFormat("(%F,%F,%F,%F)", value->constant.v128.x, str->AppendFormat("({:F},{:F},{:F},{:F})", value->constant.v128.x,
value->constant.v128.y, value->constant.v128.z, value->constant.v128.y, value->constant.v128.z,
value->constant.v128.w); value->constant.v128.w);
break; break;
@ -140,10 +140,10 @@ void HIRBuilder::DumpValue(StringBuffer* str, Value* value) {
static const char* type_names[] = { static const char* type_names[] = {
"i8", "i16", "i32", "i64", "f32", "f64", "v128", "i8", "i16", "i32", "i64", "f32", "f64", "v128",
}; };
str->AppendFormat("v%d.%s", value->ordinal, type_names[value->type]); str->AppendFormat("v{}.{}", value->ordinal, type_names[value->type]);
} }
if (value->reg.index != -1) { if (value->reg.index != -1) {
str->AppendFormat("<%s%d>", value->reg.set->name, value->reg.index); str->AppendFormat("<{}{}>", value->reg.set->name, value->reg.index);
} }
} }
@ -156,11 +156,11 @@ void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type,
if (op->label->name) { if (op->label->name) {
str->Append(op->label->name); str->Append(op->label->name);
} else { } else {
str->AppendFormat("label%d", op->label->id); str->AppendFormat("label{}", op->label->id);
} }
break; break;
case OPCODE_SIG_TYPE_O: case OPCODE_SIG_TYPE_O:
str->AppendFormat("+%lld", op->offset); str->AppendFormat("+{}", op->offset);
break; break;
case OPCODE_SIG_TYPE_S: case OPCODE_SIG_TYPE_S:
if (true) { if (true) {
@ -176,7 +176,7 @@ void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type,
void HIRBuilder::Dump(StringBuffer* str) { void HIRBuilder::Dump(StringBuffer* str) {
if (attributes_) { if (attributes_) {
str->AppendFormat("; attributes = %.8X\n", attributes_); str->AppendFormat("; attributes = {:08X}\n", attributes_);
} }
for (auto it = locals_.begin(); it != locals_.end(); ++it) { for (auto it = locals_.begin(); it != locals_.end(); ++it) {
@ -192,16 +192,16 @@ void HIRBuilder::Dump(StringBuffer* str) {
if (block == block_head_) { if (block == block_head_) {
str->Append("<entry>:\n"); str->Append("<entry>:\n");
} else if (!block->label_head) { } else if (!block->label_head) {
str->AppendFormat("<block%d>:\n", block_ordinal); str->AppendFormat("<block{}>:\n", block_ordinal);
} }
block_ordinal++; block_ordinal++;
Label* label = block->label_head; Label* label = block->label_head;
while (label) { while (label) {
if (label->name) { if (label->name) {
str->AppendFormat("%s:\n", label->name); str->AppendFormat("{}:\n", label->name);
} else { } else {
str->AppendFormat("label%d:\n", label->id); str->AppendFormat("label{}:\n", label->id);
} }
label = label->next; label = label->next;
} }
@ -210,13 +210,13 @@ void HIRBuilder::Dump(StringBuffer* str) {
while (incoming_edge) { while (incoming_edge) {
auto src_label = incoming_edge->src->label_head; auto src_label = incoming_edge->src->label_head;
if (src_label && src_label->name) { if (src_label && src_label->name) {
str->AppendFormat(" ; in: %s", src_label->name); str->AppendFormat(" ; in: {}", src_label->name);
} else if (src_label) { } else if (src_label) {
str->AppendFormat(" ; in: label%d", src_label->id); str->AppendFormat(" ; in: label{}", src_label->id);
} else { } else {
str->AppendFormat(" ; in: <block%d>", incoming_edge->src->ordinal); str->AppendFormat(" ; in: <block{}>", incoming_edge->src->ordinal);
} }
str->AppendFormat(", dom:%d, uncond:%d\n", str->AppendFormat(", dom:{}, uncond:{}\n",
(incoming_edge->flags & Edge::DOMINATES) ? 1 : 0, (incoming_edge->flags & Edge::DOMINATES) ? 1 : 0,
(incoming_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0); (incoming_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0);
incoming_edge = incoming_edge->incoming_next; incoming_edge = incoming_edge->incoming_next;
@ -225,13 +225,13 @@ void HIRBuilder::Dump(StringBuffer* str) {
while (outgoing_edge) { while (outgoing_edge) {
auto dest_label = outgoing_edge->dest->label_head; auto dest_label = outgoing_edge->dest->label_head;
if (dest_label && dest_label->name) { if (dest_label && dest_label->name) {
str->AppendFormat(" ; out: %s", dest_label->name); str->AppendFormat(" ; out: {}", dest_label->name);
} else if (dest_label) { } else if (dest_label) {
str->AppendFormat(" ; out: label%d", dest_label->id); str->AppendFormat(" ; out: label{}", dest_label->id);
} else { } else {
str->AppendFormat(" ; out: <block%d>", outgoing_edge->dest->ordinal); str->AppendFormat(" ; out: <block{}>", outgoing_edge->dest->ordinal);
} }
str->AppendFormat(", dom:%d, uncond:%d\n", str->AppendFormat(", dom:{}, uncond:{}\n",
(outgoing_edge->flags & Edge::DOMINATES) ? 1 : 0, (outgoing_edge->flags & Edge::DOMINATES) ? 1 : 0,
(outgoing_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0); (outgoing_edge->flags & Edge::UNCONDITIONAL) ? 1 : 0);
outgoing_edge = outgoing_edge->outgoing_next; outgoing_edge = outgoing_edge->outgoing_next;
@ -244,7 +244,7 @@ void HIRBuilder::Dump(StringBuffer* str) {
continue; continue;
} }
if (i->opcode == &OPCODE_COMMENT_info) { if (i->opcode == &OPCODE_COMMENT_info) {
str->AppendFormat(" ; %s\n", reinterpret_cast<char*>(i->src1.offset)); str->AppendFormat(" ; {}\n", reinterpret_cast<char*>(i->src1.offset));
i = i->next; i = i->next;
continue; continue;
} }
@ -260,7 +260,7 @@ void HIRBuilder::Dump(StringBuffer* str) {
str->Append(" = "); str->Append(" = ");
} }
if (i->flags) { if (i->flags) {
str->AppendFormat("%s.%d", info->name, i->flags); str->AppendFormat("{}.{}", info->name, i->flags);
} else { } else {
str->Append(info->name); str->Append(info->name);
} }
@ -734,13 +734,14 @@ Value* HIRBuilder::CloneValue(Value* source) {
return value; return value;
} }
void HIRBuilder::Comment(const char* value) { void HIRBuilder::Comment(std::string_view value) {
size_t length = std::strlen(value); if (value.empty()) {
if (!length) {
return; return;
} }
void* p = arena_->Alloc(length + 1); auto size = value.size();
std::memcpy(p, value, length + 1); auto p = reinterpret_cast<char*>(arena_->Alloc(size + 1));
std::memcpy(p, value.data(), size);
p[size] = '\0';
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0); Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);
i->src1.offset = (uint64_t)p; i->src1.offset = (uint64_t)p;
i->src2.value = i->src3.value = NULL; i->src2.value = i->src3.value = NULL;
@ -750,22 +751,16 @@ void HIRBuilder::Comment(const StringBuffer& value) {
if (!value.length()) { if (!value.length()) {
return; return;
} }
void* p = arena_->Alloc(value.length() + 1); auto size = value.length();
std::memcpy(p, value.GetString(), value.length() + 1); auto p = reinterpret_cast<char*>(arena_->Alloc(size + 1));
std::memcpy(p, value.buffer(), size);
p[size] = '\0';
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0); Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);
i->src1.offset = (uint64_t)p; i->src1.offset = (uint64_t)p;
i->src2.value = i->src3.value = NULL; i->src2.value = i->src3.value = NULL;
} }
void HIRBuilder::CommentFormat(const char* format, ...) { void HIRBuilder::CommentBuffer(const char* p) {
static const uint32_t kMaxCommentSize = 1024;
char* p = reinterpret_cast<char*>(arena_->Alloc(kMaxCommentSize));
va_list args;
va_start(args, format);
size_t chars_written = vsnprintf(p, kMaxCommentSize - 1, format, args);
va_end(args);
size_t rewind = kMaxCommentSize - chars_written - 1;
arena_->Rewind(rewind);
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0); Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);
i->src1.offset = (uint64_t)p; i->src1.offset = (uint64_t)p;
i->src2.value = i->src3.value = NULL; i->src2.value = i->src3.value = NULL;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/arena.h" #include "xenia/base/arena.h"
#include "xenia/base/string_buffer.h" #include "xenia/base/string_buffer.h"
#include "xenia/cpu/hir/block.h" #include "xenia/cpu/hir/block.h"
@ -68,9 +69,19 @@ class HIRBuilder {
// static allocations: // static allocations:
// Value* AllocStatic(size_t length); // Value* AllocStatic(size_t length);
void Comment(const char* value); void Comment(const std::string_view value);
void Comment(const StringBuffer& value); void Comment(const StringBuffer& value);
void CommentFormat(const char* format, ...);
template <typename... Args>
void CommentFormat(const std::string_view format, const Args&... args) {
static const uint32_t kMaxCommentSize = 1024;
char* p = reinterpret_cast<char*>(arena_->Alloc(kMaxCommentSize));
auto result = fmt::format_to_n(p, kMaxCommentSize - 1, format, args...);
p[result.size] = '\0';
size_t rewind = kMaxCommentSize - 1 - result.size;
arena_->Rewind(rewind);
CommentBuffer(p);
}
void Nop(); void Nop();
@ -261,6 +272,7 @@ class HIRBuilder {
void EndBlock(); void EndBlock();
bool IsUnconditionalJump(Instr* instr); bool IsUnconditionalJump(Instr* instr);
Instr* AppendInstr(const OpcodeInfo& opcode, uint16_t flags, Value* dest = 0); Instr* AppendInstr(const OpcodeInfo& opcode, uint16_t flags, Value* dest = 0);
void CommentBuffer(const char* p);
Value* CompareXX(const OpcodeInfo& opcode, Value* value1, Value* value2); Value* CompareXX(const OpcodeInfo& opcode, Value* value1, Value* value2);
Value* VectorCompareXX(const OpcodeInfo& opcode, Value* value1, Value* value2, Value* VectorCompareXX(const OpcodeInfo& opcode, Value* value1, Value* value2,
TypeName part_type); TypeName part_type);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -143,13 +143,12 @@ void PPCContext::SetRegFromString(const char* name, const char* value) {
} }
bool PPCContext::CompareRegWithString(const char* name, const char* value, bool PPCContext::CompareRegWithString(const char* name, const char* value,
char* out_value, std::string& result) const {
size_t out_value_size) const {
int n; int n;
if (sscanf(name, "r%d", &n) == 1) { if (sscanf(name, "r%d", &n) == 1) {
uint64_t expected = string_util::from_string<uint64_t>(value); uint64_t expected = string_util::from_string<uint64_t>(value);
if (this->r[n] != expected) { if (this->r[n] != expected) {
std::snprintf(out_value, out_value_size, "%016" PRIX64, this->r[n]); result = fmt::format("{:016X}", this->r[n]);
return false; return false;
} }
return true; return true;
@ -157,23 +156,17 @@ bool PPCContext::CompareRegWithString(const char* name, const char* value,
if (std::strstr(value, "0x")) { if (std::strstr(value, "0x")) {
// Special case: Treat float as integer. // Special case: Treat float as integer.
uint64_t expected = string_util::from_string<uint64_t>(value, true); uint64_t expected = string_util::from_string<uint64_t>(value, true);
uint64_t pun;
union { std::memcpy(&pun, &this->f[n], sizeof(pun));
double f; if (pun != expected) {
uint64_t u; result = fmt::format("{:016X}", pun);
} f2u;
f2u.f = this->f[n];
if (f2u.u != expected) {
std::snprintf(out_value, out_value_size, "%016" PRIX64, f2u.u);
return false; return false;
} }
} else { } else {
double expected = string_util::from_string<double>(value); double expected = string_util::from_string<double>(value);
// TODO(benvanik): epsilon // TODO(benvanik): epsilon
if (this->f[n] != expected) { if (this->f[n] != expected) {
std::snprintf(out_value, out_value_size, "%f", this->f[n]); result = fmt::format("{:.17f}", this->f[n]);
return false; return false;
} }
} }
@ -181,9 +174,9 @@ bool PPCContext::CompareRegWithString(const char* name, const char* value,
} else if (sscanf(name, "v%d", &n) == 1) { } else if (sscanf(name, "v%d", &n) == 1) {
vec128_t expected = string_util::from_string<vec128_t>(value); vec128_t expected = string_util::from_string<vec128_t>(value);
if (this->v[n] != expected) { if (this->v[n] != expected) {
std::snprintf(out_value, out_value_size, "[%.8X, %.8X, %.8X, %.8X]", result =
this->v[n].i32[0], this->v[n].i32[1], this->v[n].i32[2], fmt::format("[{:08X}, {:08X}, {:08X}, {:08X}]", this->v[n].i32[0],
this->v[n].i32[3]); this->v[n].i32[1], this->v[n].i32[2], this->v[n].i32[3]);
return false; return false;
} }
return true; return true;
@ -191,7 +184,7 @@ bool PPCContext::CompareRegWithString(const char* name, const char* value,
uint64_t actual = this->cr(); uint64_t actual = this->cr();
uint64_t expected = string_util::from_string<uint64_t>(value); uint64_t expected = string_util::from_string<uint64_t>(value);
if (actual != expected) { if (actual != expected) {
std::snprintf(out_value, out_value_size, "%016" PRIX64, actual); result = fmt::format("{:016X}", actual);
return false; return false;
} }
return true; return true;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -432,7 +432,7 @@ typedef struct PPCContext_s {
void SetRegFromString(const char* name, const char* value); void SetRegFromString(const char* name, const char* value);
bool CompareRegWithString(const char* name, const char* value, bool CompareRegWithString(const char* name, const char* value,
char* out_value, size_t out_value_size) const; std::string& result) const;
} PPCContext; } PPCContext;
#pragma pack(pop) #pragma pack(pop)
static_assert(sizeof(PPCContext) % 64 == 0, "64b padded"); static_assert(sizeof(PPCContext) % 64 == 0, "64b padded");

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -12,6 +12,8 @@
#include <stddef.h> #include <stddef.h>
#include <cstring> #include <cstring>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/byte_order.h" #include "xenia/base/byte_order.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/memory.h" #include "xenia/base/memory.h"
@ -48,10 +50,10 @@ void DumpAllOpcodeCounts() {
auto& disasm_info = GetOpcodeDisasmInfo(opcode); auto& disasm_info = GetOpcodeDisasmInfo(opcode);
auto translation_count = opcode_translation_counts[i]; auto translation_count = opcode_translation_counts[i];
if (translation_count) { if (translation_count) {
sb.AppendFormat("%8d : %s\n", translation_count, disasm_info.name); sb.AppendFormat("{:8d} : {}\n", translation_count, disasm_info.name);
} }
} }
fprintf(stdout, "%s", sb.GetString()); fprintf(stdout, "%s", sb.to_string().c_str());
fflush(stdout); fflush(stdout);
} }
@ -83,7 +85,7 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS; with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
if (with_debug_info_) { if (with_debug_info_) {
CommentFormat("%s fn %.8X-%.8X %s", function_->module()->name().c_str(), CommentFormat("{} fn {:08X}-{:08X} {}", function_->module()->name().c_str(),
function_->address(), function_->end_address(), function_->address(), function_->end_address(),
function_->name().c_str()); function_->name().c_str());
} }
@ -127,7 +129,7 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
AnnotateLabel(address, label); AnnotateLabel(address, label);
} }
comment_buffer_.Reset(); comment_buffer_.Reset();
comment_buffer_.AppendFormat("%.8X %.8X ", address, code); comment_buffer_.AppendFormat("{:08X} {:08X} ", address, code);
DisasmPPC(address, code, &comment_buffer_); DisasmPPC(address, code, &comment_buffer_);
Comment(comment_buffer_); Comment(comment_buffer_);
first_instr = last_instr(); first_instr = last_instr();
@ -229,7 +231,8 @@ void PPCHIRBuilder::MaybeBreakOnInstruction(uint32_t address) {
void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) { void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) {
char name_buffer[13]; char name_buffer[13];
snprintf(name_buffer, xe::countof(name_buffer), "loc_%.8X", address); auto format_result = fmt::format_to_n(name_buffer, 12, "loc_{:08X}", address);
name_buffer[format_result.size] = '\0';
label->name = (char*)arena_->Alloc(sizeof(name_buffer)); label->name = (char*)arena_->Alloc(sizeof(name_buffer));
memcpy(label->name, name_buffer, sizeof(name_buffer)); memcpy(label->name, name_buffer, sizeof(name_buffer));
} }

View File

@ -148,9 +148,9 @@ void PrintDisasm_bcx(const PPCDecodeData& d, StringBuffer* str) {
if (d.B.LK()) str->Append('l'); if (d.B.LK()) str->Append('l');
if (d.B.AA()) str->Append('a'); if (d.B.AA()) str->Append('a');
PadStringBuffer(str, str_start, kNamePad); PadStringBuffer(str, str_start, kNamePad);
str->AppendFormat("%d", bo); str->AppendFormat("{}", bo);
str->Append(", "); str->Append(", ");
str->AppendFormat("%d", bi); str->AppendFormat("{}", bi);
} else { } else {
if (d.B.LK()) str->Append('l'); if (d.B.LK()) str->Append('l');
if (d.B.AA()) str->Append('a'); if (d.B.AA()) str->Append('a');
@ -162,11 +162,11 @@ void PrintDisasm_bcx(const PPCDecodeData& d, StringBuffer* str) {
} }
PadStringBuffer(str, str_start, kNamePad); PadStringBuffer(str, str_start, kNamePad);
str->AppendFormat("crf%d", bi / 4); str->AppendFormat("crf{}", bi / 4);
} }
str->Append(", "); str->Append(", ");
str->AppendFormat("0x%X", addr); str->AppendFormat("0x{:X}", addr);
} }
} // namespace ppc } // namespace ppc

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ bool DisasmPPC(uint32_t address, uint32_t code, StringBuffer* str) {
d.code = code; d.code = code;
disasm_info.disasm(d, str); disasm_info.disasm(d, str);
} else { } else {
str->AppendFormat("%-8s", disasm_info.name); str->AppendFormat("{:<8}", disasm_info.name);
} }
return true; return true;
} }

View File

@ -155,7 +155,7 @@ bool PPCTranslator::Translate(GuestFunction* function,
// Stash source. // Stash source.
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) { if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmSource) {
DumpSource(function, &string_buffer_); DumpSource(function, &string_buffer_);
debug_info->set_source_disasm(string_buffer_.ToString()); debug_info->set_source_disasm(strdup(string_buffer_.buffer()));
string_buffer_.Reset(); string_buffer_.Reset();
} }
@ -171,7 +171,7 @@ bool PPCTranslator::Translate(GuestFunction* function,
// Stash raw HIR. // Stash raw HIR.
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmRawHir) { if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmRawHir) {
builder_->Dump(&string_buffer_); builder_->Dump(&string_buffer_);
debug_info->set_raw_hir_disasm(string_buffer_.ToString()); debug_info->set_raw_hir_disasm(strdup(string_buffer_.buffer()));
string_buffer_.Reset(); string_buffer_.Reset();
} }
@ -183,7 +183,7 @@ bool PPCTranslator::Translate(GuestFunction* function,
// Stash optimized HIR. // Stash optimized HIR.
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmHir) { if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmHir) {
builder_->Dump(&string_buffer_); builder_->Dump(&string_buffer_);
debug_info->set_hir_disasm(string_buffer_.ToString()); debug_info->set_hir_disasm(strdup(string_buffer_.buffer()));
string_buffer_.Reset(); string_buffer_.Reset();
} }
@ -201,7 +201,7 @@ void PPCTranslator::DumpSource(GuestFunction* function,
Memory* memory = frontend_->memory(); Memory* memory = frontend_->memory();
string_buffer->AppendFormat( string_buffer->AppendFormat(
"%s fn %.8X-%.8X %s\n", function->module()->name().c_str(), "{} fn {:08X}-{:08X} {}\n", function->module()->name().c_str(),
function->address(), function->end_address(), function->name().c_str()); function->address(), function->end_address(), function->name().c_str());
auto blocks = scanner_->FindBlocks(function); auto blocks = scanner_->FindBlocks(function);
@ -216,12 +216,12 @@ void PPCTranslator::DumpSource(GuestFunction* function,
// Check labels. // Check labels.
if (block_it != blocks.end() && block_it->start_address == address) { if (block_it != blocks.end() && block_it->start_address == address) {
string_buffer->AppendFormat("%.8X loc_%.8X:\n", address, string_buffer->AppendFormat("{:08X} loc_{:08X}:\n", address,
address); address);
++block_it; ++block_it;
} }
string_buffer->AppendFormat("%.8X %.8X ", address, code); string_buffer->AppendFormat("{:08X} {:08X} ", address, code);
DisasmPPC(address, code, string_buffer); DisasmPPC(address, code, string_buffer);
string_buffer->Append('\n'); string_buffer->Append('\n');
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -23,10 +23,10 @@
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#endif // XE_COMPILER_MSVC #endif // XE_COMPILER_MSVC
DEFINE_string(test_path, "src/xenia/cpu/ppc/testing/", DEFINE_path(test_path, "src/xenia/cpu/ppc/testing/",
"Directory scanned for test files.", "Other"); "Directory scanned for test files.", "Other");
DEFINE_string(test_bin_path, "src/xenia/cpu/ppc/testing/bin/", DEFINE_path(test_bin_path, "src/xenia/cpu/ppc/testing/bin/",
"Directory with binary outputs of the test files.", "Other"); "Directory with binary outputs of the test files.", "Other");
DEFINE_transient_string(test_name, "", "Specifies test name.", "General"); DEFINE_transient_string(test_name, "", "Specifies test name.", "General");
namespace xe { namespace xe {
@ -49,43 +49,45 @@ struct TestCase {
class TestSuite { class TestSuite {
public: public:
TestSuite(const std::wstring& src_file_path) : src_file_path(src_file_path) { TestSuite(const std::filesystem::path& src_file_path)
name = src_file_path.substr(src_file_path.find_last_of(xe::kPathSeparator) + : src_file_path_(src_file_path) {
1); auto name = src_file_path.filename();
name = ReplaceExtension(name, L""); name = name.replace_extension();
map_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".map";
bin_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".bin"; name_ = xe::path_to_utf8(name);
map_file_path_ = cvars::test_bin_path / name.replace_extension(".map");
bin_file_path_ = cvars::test_bin_path / name.replace_extension(".bin");
} }
bool Load() { bool Load() {
if (!ReadMap(map_file_path)) { if (!ReadMap()) {
XELOGE("Unable to read map for test %ls", src_file_path.c_str()); XELOGE("Unable to read map for test %s",
xe::path_to_utf8(src_file_path_).c_str());
return false; return false;
} }
if (!ReadAnnotations(src_file_path)) { if (!ReadAnnotations()) {
XELOGE("Unable to read annotations for test %ls", src_file_path.c_str()); XELOGE("Unable to read annotations for test %s",
xe::path_to_utf8(src_file_path_).c_str());
return false; return false;
} }
return true; return true;
} }
std::wstring name; const std::string& name() const { return name_; }
std::wstring src_file_path; const std::filesystem::path& src_file_path() const { return src_file_path_; }
std::wstring map_file_path; const std::filesystem::path& map_file_path() const { return map_file_path_; }
std::wstring bin_file_path; const std::filesystem::path& bin_file_path() const { return bin_file_path_; }
std::vector<TestCase> test_cases; std::vector<TestCase>& test_cases() { return test_cases_; }
private: private:
std::wstring ReplaceExtension(const std::wstring& path, std::string name_;
const std::wstring& new_extension) { std::filesystem::path src_file_path_;
std::wstring result = path; std::filesystem::path map_file_path_;
auto last_dot = result.find_last_of('.'); std::filesystem::path bin_file_path_;
result.replace(result.begin() + last_dot, result.end(), new_extension); std::vector<TestCase> test_cases_;
return result;
}
TestCase* FindTestCase(const std::string& name) { TestCase* FindTestCase(const std::string_view name) {
for (auto& test_case : test_cases) { for (auto& test_case : test_cases_) {
if (test_case.name == name) { if (test_case.name == name) {
return &test_case; return &test_case;
} }
@ -93,8 +95,8 @@ class TestSuite {
return nullptr; return nullptr;
} }
bool ReadMap(const std::wstring& map_file_path) { bool ReadMap() {
FILE* f = fopen(xe::to_string(map_file_path).c_str(), "r"); FILE* f = filesystem::OpenFile(map_file_path_, "r");
if (!f) { if (!f) {
return false; return false;
} }
@ -114,15 +116,16 @@ class TestSuite {
} }
std::string address(line_buffer, t_test_ - line_buffer); std::string address(line_buffer, t_test_ - line_buffer);
std::string name(t_test_ + strlen(" t test_")); std::string name(t_test_ + strlen(" t test_"));
test_cases.emplace_back(START_ADDRESS + std::stoul(address, 0, 16), name); test_cases_.emplace_back(START_ADDRESS + std::stoul(address, 0, 16),
name);
} }
fclose(f); fclose(f);
return true; return true;
} }
bool ReadAnnotations(const std::wstring& src_file_path) { bool ReadAnnotations() {
TestCase* current_test_case = nullptr; TestCase* current_test_case = nullptr;
FILE* f = fopen(xe::to_string(src_file_path).c_str(), "r"); FILE* f = filesystem::OpenFile(src_file_path_, "r");
if (!f) { if (!f) {
return false; return false;
} }
@ -141,8 +144,8 @@ class TestSuite {
std::string label(start + strlen("test_"), strchr(start, ':')); std::string label(start + strlen("test_"), strchr(start, ':'));
current_test_case = FindTestCase(label); current_test_case = FindTestCase(label);
if (!current_test_case) { if (!current_test_case) {
XELOGE("Test case %s not found in corresponding map for %ls", XELOGE("Test case %s not found in corresponding map for %s",
label.c_str(), src_file_path.c_str()); label.c_str(), xe::path_to_utf8(src_file_path_).c_str());
return false; return false;
} }
} else if (strlen(start) > 3 && start[0] == '#' && start[1] == '_') { } else if (strlen(start) > 3 && start[0] == '#' && start[1] == '_') {
@ -157,8 +160,8 @@ class TestSuite {
value.erase(value.end() - 1); value.erase(value.end() - 1);
} }
if (!current_test_case) { if (!current_test_case) {
XELOGE("Annotation outside of test case in %ls", XELOGE("Annotation outside of test case in %s",
src_file_path.c_str()); xe::path_to_utf8(src_file_path_).c_str());
return false; return false;
} }
current_test_case->annotations.emplace_back(key, value); current_test_case->annotations.emplace_back(key, value);
@ -172,21 +175,20 @@ class TestSuite {
class TestRunner { class TestRunner {
public: public:
TestRunner() { TestRunner() : memory_size_(64 * 1024 * 1024) {
memory_size = 64 * 1024 * 1024; memory_.reset(new Memory());
memory.reset(new Memory()); memory_->Initialize();
memory->Initialize();
} }
~TestRunner() { ~TestRunner() {
thread_state.reset(); thread_state_.reset();
processor.reset(); processor_.reset();
memory.reset(); memory_.reset();
} }
bool Setup(TestSuite& suite) { bool Setup(TestSuite& suite) {
// Reset memory. // Reset memory.
memory->Reset(); memory_->Reset();
std::unique_ptr<xe::cpu::backend::Backend> backend; std::unique_ptr<xe::cpu::backend::Backend> backend;
if (!backend) { if (!backend) {
@ -205,23 +207,24 @@ class TestRunner {
} }
// Setup a fresh processor. // Setup a fresh processor.
processor.reset(new Processor(memory.get(), nullptr)); processor_.reset(new Processor(memory_.get(), nullptr));
processor->Setup(std::move(backend)); processor_->Setup(std::move(backend));
processor->set_debug_info_flags(DebugInfoFlags::kDebugInfoAll); processor_->set_debug_info_flags(DebugInfoFlags::kDebugInfoAll);
// Load the binary module. // Load the binary module.
auto module = std::make_unique<xe::cpu::RawModule>(processor.get()); auto module = std::make_unique<xe::cpu::RawModule>(processor_.get());
if (!module->LoadFile(START_ADDRESS, suite.bin_file_path)) { if (!module->LoadFile(START_ADDRESS, suite.bin_file_path())) {
XELOGE("Unable to load test binary %ls", suite.bin_file_path.c_str()); XELOGE("Unable to load test binary %s",
xe::path_to_utf8(suite.bin_file_path).c_str());
return false; return false;
} }
processor->AddModule(std::move(module)); processor_->AddModule(std::move(module));
processor->backend()->CommitExecutableRange(START_ADDRESS, processor_->backend()->CommitExecutableRange(START_ADDRESS,
START_ADDRESS + 1024 * 1024); START_ADDRESS + 1024 * 1024);
// Add dummy space for memory. // Add dummy space for memory.
processor->memory()->LookupHeap(0)->AllocFixed( processor_->memory()->LookupHeap(0)->AllocFixed(
0x10001000, 0xEFFF, 0, 0x10001000, 0xEFFF, 0,
kMemoryAllocationReserve | kMemoryAllocationCommit, kMemoryAllocationReserve | kMemoryAllocationCommit,
kMemoryProtectRead | kMemoryProtectWrite); kMemoryProtectRead | kMemoryProtectWrite);
@ -230,8 +233,8 @@ class TestRunner {
uint32_t stack_size = 64 * 1024; uint32_t stack_size = 64 * 1024;
uint32_t stack_address = START_ADDRESS - stack_size; uint32_t stack_address = START_ADDRESS - stack_size;
uint32_t pcr_address = stack_address - 0x1000; uint32_t pcr_address = stack_address - 0x1000;
thread_state.reset( thread_state_.reset(
new ThreadState(processor.get(), 0x100, stack_address, pcr_address)); new ThreadState(processor_.get(), 0x100, stack_address, pcr_address));
return true; return true;
} }
@ -244,15 +247,15 @@ class TestRunner {
} }
// Execute test. // Execute test.
auto fn = processor->ResolveFunction(test_case.address); auto fn = processor_->ResolveFunction(test_case.address);
if (!fn) { if (!fn) {
XELOGE("Entry function not found"); XELOGE("Entry function not found");
return false; return false;
} }
auto ctx = thread_state->context(); auto ctx = thread_state_->context();
ctx->lr = 0xBCBCBCBC; ctx->lr = 0xBCBCBCBC;
fn->Call(thread_state.get(), uint32_t(ctx->lr)); fn->Call(thread_state_.get(), uint32_t(ctx->lr));
// Assert test state expectations. // Assert test state expectations.
bool result = CheckTestResults(test_case); bool result = CheckTestResults(test_case);
@ -267,7 +270,7 @@ class TestRunner {
} }
bool SetupTestState(TestCase& test_case) { bool SetupTestState(TestCase& test_case) {
auto ppc_context = thread_state->context(); auto ppc_context = thread_state_->context();
for (auto& it : test_case.annotations) { for (auto& it : test_case.annotations) {
if (it.first == "REGISTER_IN") { if (it.first == "REGISTER_IN") {
size_t space_pos = it.second.find(" "); size_t space_pos = it.second.find(" ");
@ -279,7 +282,7 @@ class TestRunner {
auto address_str = it.second.substr(0, space_pos); auto address_str = it.second.substr(0, space_pos);
auto bytes_str = it.second.substr(space_pos + 1); auto bytes_str = it.second.substr(space_pos + 1);
uint32_t address = std::strtoul(address_str.c_str(), nullptr, 16); uint32_t address = std::strtoul(address_str.c_str(), nullptr, 16);
auto p = memory->TranslateVirtual(address); auto p = memory_->TranslateVirtual(address);
const char* c = bytes_str.c_str(); const char* c = bytes_str.c_str();
while (*c) { while (*c) {
while (*c == ' ') ++c; while (*c == ' ') ++c;
@ -298,9 +301,7 @@ class TestRunner {
} }
bool CheckTestResults(TestCase& test_case) { bool CheckTestResults(TestCase& test_case) {
auto ppc_context = thread_state->context(); auto ppc_context = thread_state_->context();
char actual_value[2048];
bool any_failed = false; bool any_failed = false;
for (auto& it : test_case.annotations) { for (auto& it : test_case.annotations) {
@ -308,9 +309,9 @@ class TestRunner {
size_t space_pos = it.second.find(" "); size_t space_pos = it.second.find(" ");
auto reg_name = it.second.substr(0, space_pos); auto reg_name = it.second.substr(0, space_pos);
auto reg_value = it.second.substr(space_pos + 1); auto reg_value = it.second.substr(space_pos + 1);
if (!ppc_context->CompareRegWithString(reg_name.c_str(), std::string actual_value;
reg_value.c_str(), actual_value, if (!ppc_context->CompareRegWithString(
xe::countof(actual_value))) { reg_name.c_str(), reg_value.c_str(), actual_value)) {
any_failed = true; any_failed = true;
XELOGE("Register %s assert failed:\n", reg_name.c_str()); XELOGE("Register %s assert failed:\n", reg_name.c_str());
XELOGE(" Expected: %s == %s\n", reg_name.c_str(), reg_value.c_str()); XELOGE(" Expected: %s == %s\n", reg_name.c_str(), reg_value.c_str());
@ -321,7 +322,7 @@ class TestRunner {
auto address_str = it.second.substr(0, space_pos); auto address_str = it.second.substr(0, space_pos);
auto bytes_str = it.second.substr(space_pos + 1); auto bytes_str = it.second.substr(space_pos + 1);
uint32_t address = std::strtoul(address_str.c_str(), nullptr, 16); uint32_t address = std::strtoul(address_str.c_str(), nullptr, 16);
auto base_address = memory->TranslateVirtual(address); auto base_address = memory_->TranslateVirtual(address);
auto p = base_address; auto p = base_address;
const char* c = bytes_str.c_str(); const char* c = bytes_str.c_str();
while (*c) { while (*c) {
@ -348,19 +349,18 @@ class TestRunner {
return !any_failed; return !any_failed;
} }
size_t memory_size; size_t memory_size_;
std::unique_ptr<Memory> memory; std::unique_ptr<Memory> memory_;
std::unique_ptr<Processor> processor; std::unique_ptr<Processor> processor_;
std::unique_ptr<ThreadState> thread_state; std::unique_ptr<ThreadState> thread_state_;
}; };
bool DiscoverTests(std::wstring& test_path, bool DiscoverTests(const std::filesystem::path& test_path,
std::vector<std::wstring>& test_files) { std::vector<std::filesystem::path>& test_files) {
auto file_infos = xe::filesystem::ListFiles(test_path); auto file_infos = xe::filesystem::ListFiles(test_path);
for (auto& file_info : file_infos) { for (auto& file_info : file_infos) {
if (file_info.name != L"." && file_info.name != L".." && if (file_info.name.extension() == ".s") {
file_info.name.rfind(L".s") == file_info.name.size() - 2) { test_files.push_back(test_path / file_info.name);
test_files.push_back(xe::join_paths(test_path, file_info.name));
} }
} }
return true; return true;
@ -401,14 +401,16 @@ void ProtectedRunTest(TestSuite& test_suite, TestRunner& runner,
#endif // XE_COMPILER_MSVC #endif // XE_COMPILER_MSVC
} }
bool RunTests(const std::wstring& test_name) { bool RunTests(const std::string_view test_name) {
int result_code = 1; int result_code = 1;
int failed_count = 0; int failed_count = 0;
int passed_count = 0; int passed_count = 0;
auto test_path_root = XELOGI("Haswell instruction usage {}.",
xe::fix_path_separators(xe::to_wstring(cvars::test_path)); cvars::use_haswell_instructions ? "enabled" : "disabled");
std::vector<std::wstring> test_files;
auto test_path_root = cvars::test_path;
std::vector<std::filesystem::path> test_files;
if (!DiscoverTests(test_path_root, test_files)) { if (!DiscoverTests(test_path_root, test_files)) {
return false; return false;
} }
@ -423,11 +425,12 @@ bool RunTests(const std::wstring& test_name) {
bool load_failed = false; bool load_failed = false;
for (auto& test_path : test_files) { for (auto& test_path : test_files) {
TestSuite test_suite(test_path); TestSuite test_suite(test_path);
if (!test_name.empty() && test_suite.name != test_name) { if (!test_name.empty() && test_suite.name() != test_name) {
continue; continue;
} }
if (!test_suite.Load()) { if (!test_suite.Load()) {
XELOGE("TEST SUITE %ls FAILED TO LOAD", test_path.c_str()); XELOGE("TEST SUITE %s FAILED TO LOAD",
xe::path_to_utf8(test_path).c_str());
load_failed = true; load_failed = true;
continue; continue;
} }
@ -440,9 +443,9 @@ bool RunTests(const std::wstring& test_name) {
XELOGI("%d tests loaded.", (int)test_suites.size()); XELOGI("%d tests loaded.", (int)test_suites.size());
TestRunner runner; TestRunner runner;
for (auto& test_suite : test_suites) { for (auto& test_suite : test_suites) {
XELOGI("%ls.s:", test_suite.name.c_str()); XELOGI("%s.s:", xe::path_to_utf8(test_suite.name()).c_str());
for (auto& test_case : test_suite.test_cases) { for (auto& test_case : test_suite.test_cases()) {
XELOGI(" - %s", test_case.name.c_str()); XELOGI(" - %s", test_case.name.c_str());
ProtectedRunTest(test_suite, runner, test_case, failed_count, ProtectedRunTest(test_suite, runner, test_case, failed_count,
passed_count); passed_count);
@ -459,9 +462,9 @@ bool RunTests(const std::wstring& test_name) {
return failed_count ? false : true; return failed_count ? false : true;
} }
int main(const std::vector<std::wstring>& args) { int main(const std::vector<std::string>& args) {
// Grab test name, if present. // Grab test name, if present.
std::wstring test_name; std::string test_name;
if (args.size() >= 2) { if (args.size() >= 2) {
test_name = args[1]; test_name = args[1];
} }
@ -473,5 +476,5 @@ int main(const std::vector<std::wstring>& args) {
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
DEFINE_ENTRY_POINT(L"xenia-cpu-ppc-test", xe::cpu::test::main, "[test name]", DEFINE_ENTRY_POINT("xenia-cpu-ppc-test", xe::cpu::test::main, "[test name]",
"test_name"); "test_name");

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2017 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -87,7 +87,7 @@ class TestSuite {
return result; return result;
} }
TestCase* FindTestCase(const std::string& name) { TestCase* FindTestCase(const std::string_view name) {
for (auto& test_case : test_cases) { for (auto& test_case : test_cases) {
if (test_case.name == name) { if (test_case.name == name) {
return &test_case; return &test_case;

View File

@ -7,12 +7,13 @@ project("xenia-cpu-ppc-tests")
kind("ConsoleApp") kind("ConsoleApp")
language("C++") language("C++")
links({ links({
"capstone", -- cpu-backend-x64
"fmt",
"mspack",
"xenia-core", "xenia-core",
"xenia-cpu-backend-x64", "xenia-cpu-backend-x64",
"xenia-cpu", "xenia-cpu",
"xenia-base", "xenia-base",
"capstone", -- cpu-backend-x64
"mspack",
}) })
files({ files({
"ppc_testing_main.cc", "ppc_testing_main.cc",
@ -40,6 +41,7 @@ project("xenia-cpu-ppc-nativetests")
kind("ConsoleApp") kind("ConsoleApp")
language("C++") language("C++")
links({ links({
"fmt",
"xenia-base", "xenia-base",
}) })
files({ files({

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -42,8 +42,8 @@
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG, DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
"Allow debugging and retain debug information.", "General"); "Allow debugging and retain debug information.", "General");
DEFINE_string(trace_function_data_path, "", "File to write trace data to.", DEFINE_path(trace_function_data_path, "", "File to write trace data to.",
"CPU"); "CPU");
DEFINE_bool(break_on_start, false, "Break into the debugger on startup.", DEFINE_bool(break_on_start, false, "Break into the debugger on startup.",
"CPU"); "CPU");
@ -140,7 +140,7 @@ bool Processor::Setup(std::unique_ptr<backend::Backend> backend) {
} }
// Open the trace data path, if requested. // Open the trace data path, if requested.
functions_trace_path_ = xe::to_wstring(cvars::trace_function_data_path); functions_trace_path_ = cvars::trace_function_data_path;
if (!functions_trace_path_.empty()) { if (!functions_trace_path_.empty()) {
functions_trace_file_ = ChunkedMappedMemoryWriter::Open( functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
functions_trace_path_, 32 * 1024 * 1024, true); functions_trace_path_, 32 * 1024 * 1024, true);
@ -167,7 +167,7 @@ bool Processor::AddModule(std::unique_ptr<Module> module) {
return true; return true;
} }
Module* Processor::GetModule(const char* name) { Module* Processor::GetModule(const std::string_view name) {
auto global_lock = global_critical_region_.Acquire(); auto global_lock = global_critical_region_.Acquire();
for (const auto& module : modules_) { for (const auto& module : modules_) {
if (module->name() == name) { if (module->name() == name) {
@ -186,7 +186,7 @@ std::vector<Module*> Processor::GetModules() {
return clone; return clone;
} }
Function* Processor::DefineBuiltin(const std::string& name, Function* Processor::DefineBuiltin(const std::string_view name,
BuiltinFunction::Handler handler, void* arg0, BuiltinFunction::Handler handler, void* arg0,
void* arg1) { void* arg1) {
uint32_t address = next_builtin_address_; uint32_t address = next_builtin_address_;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -99,12 +99,11 @@ class Processor {
} }
bool AddModule(std::unique_ptr<Module> module); bool AddModule(std::unique_ptr<Module> module);
Module* GetModule(const char* name); Module* GetModule(const std::string_view name);
Module* GetModule(const std::string& name) { return GetModule(name.c_str()); }
std::vector<Module*> GetModules(); std::vector<Module*> GetModules();
Module* builtin_module() const { return builtin_module_; } Module* builtin_module() const { return builtin_module_; }
Function* DefineBuiltin(const std::string& name, Function* DefineBuiltin(const std::string_view name,
BuiltinFunction::Handler handler, void* arg0, BuiltinFunction::Handler handler, void* arg0,
void* arg1); void* arg1);
@ -245,7 +244,7 @@ class Processor {
// Which debug features are enabled in generated code. // Which debug features are enabled in generated code.
uint32_t debug_info_flags_ = 0; uint32_t debug_info_flags_ = 0;
// If specified, the file trace data gets written to when running. // If specified, the file trace data gets written to when running.
std::wstring functions_trace_path_; std::filesystem::path functions_trace_path_;
std::unique_ptr<ChunkedMappedMemoryWriter> functions_trace_file_; std::unique_ptr<ChunkedMappedMemoryWriter> functions_trace_file_;
std::unique_ptr<ppc::PPCFrontend> frontend_; std::unique_ptr<ppc::PPCFrontend> frontend_;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -23,9 +23,9 @@ RawModule::RawModule(Processor* processor)
RawModule::~RawModule() {} RawModule::~RawModule() {}
bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) { bool RawModule::LoadFile(uint32_t base_address,
auto fixed_path = xe::fix_path_separators(path); const std::filesystem::path& path) {
FILE* file = xe::filesystem::OpenFile(fixed_path, "rb"); FILE* file = xe::filesystem::OpenFile(path, "rb");
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
uint32_t file_length = static_cast<uint32_t>(ftell(file)); uint32_t file_length = static_cast<uint32_t>(ftell(file));
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
@ -48,12 +48,7 @@ bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) {
fclose(file); fclose(file);
// Setup debug info. // Setup debug info.
auto last_slash = fixed_path.find_last_of(xe::kPathSeparator); name_ = xe::path_to_utf8(path.filename());
if (last_slash != std::string::npos) {
name_ = xe::to_string(fixed_path.substr(last_slash + 1));
} else {
name_ = xe::to_string(fixed_path);
}
// TODO(benvanik): debug info // TODO(benvanik): debug info
low_address_ = base_address; low_address_ = base_address;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -22,7 +22,7 @@ class RawModule : public Module {
explicit RawModule(Processor* processor); explicit RawModule(Processor* processor);
~RawModule() override; ~RawModule() override;
bool LoadFile(uint32_t base_address, const std::wstring& path); bool LoadFile(uint32_t base_address, const std::filesystem::path& path);
// Set address range if you've already allocated memory and placed code // Set address range if you've already allocated memory and placed code
// in it. // in it.
@ -30,7 +30,7 @@ class RawModule : public Module {
const std::string& name() const override { return name_; } const std::string& name() const override { return name_; }
bool is_executable() const override { return is_executable_; } bool is_executable() const override { return is_executable_; }
void set_name(const std::string& name) { name_ = name; } void set_name(const std::string_view name) { name_ = name; }
void set_executable(bool is_executable) { is_executable_ = is_executable; } void set_executable(bool is_executable) { is_executable_ = is_executable; }
bool ContainsAddress(uint32_t address) override; bool ContainsAddress(uint32_t address) override;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -45,7 +45,7 @@ class Symbol {
uint32_t address() const { return address_; } uint32_t address() const { return address_; }
const std::string& name() const { return name_; } const std::string& name() const { return name_; }
void set_name(const std::string& value) { name_ = value; } void set_name(const std::string_view value) { name_ = value; }
protected: protected:
Type type_ = Type::kVariable; Type type_ = Type::kVariable;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -24,7 +24,7 @@ using xe::cpu::compiler::Compiler;
using xe::cpu::hir::HIRBuilder; using xe::cpu::hir::HIRBuilder;
namespace passes = xe::cpu::compiler::passes; namespace passes = xe::cpu::compiler::passes;
TestModule::TestModule(Processor* processor, const std::string& name, TestModule::TestModule(Processor* processor, const std::string_view name,
std::function<bool(uint32_t)> contains_address, std::function<bool(uint32_t)> contains_address,
std::function<bool(hir::HIRBuilder&)> generate) std::function<bool(hir::HIRBuilder&)> generate)
: Module(processor), : Module(processor),

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -24,7 +24,7 @@ namespace cpu {
class TestModule : public Module { class TestModule : public Module {
public: public:
TestModule(Processor* processor, const std::string& name, TestModule(Processor* processor, const std::string_view name,
std::function<bool(uint32_t)> contains_address, std::function<bool(uint32_t)> contains_address,
std::function<bool(hir::HIRBuilder&)> generate); std::function<bool(hir::HIRBuilder&)> generate);
~TestModule() override; ~TestModule() override;

View File

@ -4,6 +4,7 @@ include(project_root.."/tools/build")
test_suite("xenia-cpu-tests", project_root, ".", { test_suite("xenia-cpu-tests", project_root, ".", {
links = { links = {
"capstone", "capstone",
"fmt",
"xenia-base", "xenia-base",
"xenia-core", "xenia-core",
"xenia-cpu", "xenia-cpu",

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -11,6 +11,8 @@
#include <algorithm> #include <algorithm>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/byte_order.h" #include "xenia/base/byte_order.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
@ -156,7 +158,7 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
return 0; return 0;
} }
uint32_t XexModule::GetProcAddress(const char* name) const { uint32_t XexModule::GetProcAddress(const std::string_view name) const {
assert_not_zero(base_address_); assert_not_zero(base_address_);
xex2_opt_data_directory* pe_export_directory = 0; xex2_opt_data_directory* pe_export_directory = 0;
@ -185,7 +187,7 @@ uint32_t XexModule::GetProcAddress(const char* name) const {
auto fn_name = reinterpret_cast<const char*>(uintptr_t(e) + name_table[i]); auto fn_name = reinterpret_cast<const char*>(uintptr_t(e) + name_table[i]);
uint16_t ordinal = ordinal_table[i]; uint16_t ordinal = ordinal_table[i];
uint32_t addr = base_address_ + function_table[ordinal]; uint32_t addr = base_address_ + function_table[ordinal];
if (!std::strcmp(name, fn_name)) { if (name == std::string_view(fn_name)) {
// We have a match! // We have a match!
return addr; return addr;
} }
@ -865,7 +867,7 @@ int XexModule::ReadPEHeaders() {
return 0; return 0;
} }
bool XexModule::Load(const std::string& name, const std::string& path, bool XexModule::Load(const std::string_view name, const std::string_view path,
const void* xex_addr, size_t xex_length) { const void* xex_addr, size_t xex_length) {
auto src_header = reinterpret_cast<const xex2_header*>(xex_addr); auto src_header = reinterpret_cast<const xex2_header*>(xex_addr);
@ -922,8 +924,8 @@ bool XexModule::Load(const std::string& name, const std::string& path,
base_address_ = *base_addr_opt; base_address_ = *base_addr_opt;
// Setup debug info. // Setup debug info.
name_ = std::string(name); name_ = name;
path_ = std::string(path); path_ = path;
uint8_t* data = memory()->TranslateVirtual(base_address_); uint8_t* data = memory()->TranslateVirtual(base_address_);
@ -1027,7 +1029,8 @@ bool XexModule::LoadContinue() {
assert_true(library_name_index < assert_true(library_name_index <
opt_import_libraries->string_table.count); opt_import_libraries->string_table.count);
assert_not_null(string_table[library_name_index]); assert_not_null(string_table[library_name_index]);
SetupLibraryImports(string_table[library_name_index], library); auto library_name = std::string(string_table[library_name_index]);
SetupLibraryImports(library_name, library);
library_offset += library->size; library_offset += library->size;
} }
} }
@ -1087,7 +1090,7 @@ bool XexModule::Unload() {
return true; return true;
} }
bool XexModule::SetupLibraryImports(const char* name, bool XexModule::SetupLibraryImports(const std::string_view name,
const xex2_import_library* library) { const xex2_import_library* library) {
ExportResolver* kernel_resolver = nullptr; ExportResolver* kernel_resolver = nullptr;
if (kernel_state_->IsKernelModule(name)) { if (kernel_state_->IsKernelModule(name)) {
@ -1096,14 +1099,10 @@ bool XexModule::SetupLibraryImports(const char* name,
auto user_module = kernel_state_->GetModule(name); auto user_module = kernel_state_->GetModule(name);
std::string libbasename = name; auto base_name = utf8::find_base_name_from_guest_path(name);
auto dot = libbasename.find_last_of('.');
if (dot != libbasename.npos) {
libbasename = libbasename.substr(0, dot);
}
ImportLibrary library_info; ImportLibrary library_info;
library_info.name = libbasename; library_info.name = base_name;
library_info.id = library->id; library_info.id = library->id;
library_info.version.value = library->version.value; library_info.version.value = library->version.value;
library_info.min_version.value = library->version_min.value; library_info.min_version.value = library->version_min.value;
@ -1135,7 +1134,7 @@ bool XexModule::SetupLibraryImports(const char* name,
XELOGW( XELOGW(
"WARNING: an import variable was not resolved! (library: %s, import " "WARNING: an import variable was not resolved! (library: %s, import "
"lib: %s, ordinal: %.3X)", "lib: %s, ordinal: %.3X)",
name_.c_str(), name, ordinal); name_.c_str(), name.c_str(), ordinal);
} }
StringBuffer import_name; StringBuffer import_name;
@ -1147,11 +1146,11 @@ bool XexModule::SetupLibraryImports(const char* name,
import_info.value_address = record_addr; import_info.value_address = record_addr;
library_info.imports.push_back(import_info); library_info.imports.push_back(import_info);
import_name.AppendFormat("__imp__"); import_name.Append("__imp__");
if (kernel_export) { if (kernel_export) {
import_name.AppendFormat("%s", kernel_export->name); import_name.Append(kernel_export->name);
} else { } else {
import_name.AppendFormat("%s_%.3X", libbasename.c_str(), ordinal); import_name.AppendFormat("{}_{:03X}", base_name, ordinal);
} }
if (kernel_export) { if (kernel_export) {
@ -1180,7 +1179,7 @@ bool XexModule::SetupLibraryImports(const char* name,
// Setup a variable and define it. // Setup a variable and define it.
Symbol* var_info; Symbol* var_info;
DeclareVariable(record_addr, &var_info); DeclareVariable(record_addr, &var_info);
var_info->set_name(import_name.GetString()); var_info->set_name(import_name.to_string_view());
var_info->set_status(Symbol::Status::kDeclared); var_info->set_status(Symbol::Status::kDeclared);
DefineVariable(var_info); DefineVariable(var_info);
var_info->set_status(Symbol::Status::kDefined); var_info->set_status(Symbol::Status::kDefined);
@ -1194,15 +1193,15 @@ bool XexModule::SetupLibraryImports(const char* name,
} }
if (kernel_export) { if (kernel_export) {
import_name.AppendFormat("%s", kernel_export->name); import_name.Append(kernel_export->name);
} else { } else {
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal); import_name.AppendFormat("__{}_{:03X}", base_name, ordinal);
} }
Function* function; Function* function;
DeclareFunction(record_addr, &function); DeclareFunction(record_addr, &function);
function->set_end_address(record_addr + 16 - 4); function->set_end_address(record_addr + 16 - 4);
function->set_name(import_name.GetString()); function->set_name(import_name.to_string_view());
if (user_export_addr) { if (user_export_addr) {
// Rewrite PPC code to set r11 to the target address // Rewrite PPC code to set r11 to the target address
@ -1253,7 +1252,7 @@ bool XexModule::SetupLibraryImports(const char* name,
} }
} else { } else {
XELOGW("WARNING: Imported kernel function %s is unimplemented!", XELOGW("WARNING: Imported kernel function %s is unimplemented!",
import_name.GetString()); import_name.buffer());
} }
static_cast<GuestFunction*>(function)->SetupExtern(handler, static_cast<GuestFunction*>(function)->SetupExtern(handler,
kernel_export); kernel_export);
@ -1488,11 +1487,12 @@ bool XexModule::FindSaveRest() {
if (gplr_start) { if (gplr_start) {
uint32_t address = gplr_start; uint32_t address = gplr_start;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__savegprlr_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__savegprlr_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_end_address(address + (31 - n) * 4 + 2 * 4); function->set_end_address(address + (31 - n) * 4 + 2 * 4);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveGprLr;
function->set_behavior(Function::Behavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
@ -1501,11 +1501,12 @@ bool XexModule::FindSaveRest() {
} }
address = gplr_start + 20 * 4; address = gplr_start + 20 * 4;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__restgprlr_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__restgprlr_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_end_address(address + (31 - n) * 4 + 3 * 4); function->set_end_address(address + (31 - n) * 4 + 3 * 4);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestGprLr;
function->set_behavior(Function::Behavior::kEpilogReturn); function->set_behavior(Function::Behavior::kEpilogReturn);
@ -1516,11 +1517,12 @@ bool XexModule::FindSaveRest() {
if (fpr_start) { if (fpr_start) {
uint32_t address = fpr_start; uint32_t address = fpr_start;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__savefpr_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__savefpr_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_end_address(address + (31 - n) * 4 + 1 * 4); function->set_end_address(address + (31 - n) * 4 + 1 * 4);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveFpr;
function->set_behavior(Function::Behavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
@ -1529,11 +1531,12 @@ bool XexModule::FindSaveRest() {
} }
address = fpr_start + (18 * 4) + (1 * 4); address = fpr_start + (18 * 4) + (1 * 4);
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__restfpr_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__restfpr_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_end_address(address + (31 - n) * 4 + 1 * 4); function->set_end_address(address + (31 - n) * 4 + 1 * 4);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestFpr;
function->set_behavior(Function::Behavior::kEpilog); function->set_behavior(Function::Behavior::kEpilog);
@ -1549,10 +1552,11 @@ bool XexModule::FindSaveRest() {
// 64-127 rest // 64-127 rest
uint32_t address = vmx_start; uint32_t address = vmx_start;
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__savevmx_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__savevmx_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
function->set_behavior(Function::Behavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
@ -1561,10 +1565,11 @@ bool XexModule::FindSaveRest() {
} }
address += 4; address += 4;
for (int n = 64; n <= 127; n++) { for (int n = 64; n <= 127; n++) {
snprintf(name, xe::countof(name), "__savevmx_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__savevmx_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
function->set_behavior(Function::Behavior::kProlog); function->set_behavior(Function::Behavior::kProlog);
@ -1573,10 +1578,11 @@ bool XexModule::FindSaveRest() {
} }
address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4); address = vmx_start + (18 * 2 * 4) + (1 * 4) + (64 * 2 * 4) + (1 * 4);
for (int n = 14; n <= 31; n++) { for (int n = 14; n <= 31; n++) {
snprintf(name, xe::countof(name), "__restvmx_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__restvmx_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
function->set_behavior(Function::Behavior::kEpilog); function->set_behavior(Function::Behavior::kEpilog);
@ -1585,10 +1591,11 @@ bool XexModule::FindSaveRest() {
} }
address += 4; address += 4;
for (int n = 64; n <= 127; n++) { for (int n = 64; n <= 127; n++) {
snprintf(name, xe::countof(name), "__restvmx_%d", n); auto format_result =
fmt::format_to_n(name, xe::countof(name), "__restvmx_{}", n);
Function* function; Function* function;
DeclareFunction(address, &function); DeclareFunction(address, &function);
function->set_name(name); function->set_name(std::string_view(name, format_result.size));
// TODO(benvanik): set type fn->type = FunctionSymbol::User; // TODO(benvanik): set type fn->type = FunctionSymbol::User;
// TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx; // TODO(benvanik): set flags fn->flags |= FunctionSymbol::kFlagRestVmx;
function->set_behavior(Function::Behavior::kEpilog); function->set_behavior(Function::Behavior::kEpilog);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -132,10 +132,10 @@ class XexModule : public xe::cpu::Module {
const PESection* GetPESection(const char* name); const PESection* GetPESection(const char* name);
uint32_t GetProcAddress(uint16_t ordinal) const; uint32_t GetProcAddress(uint16_t ordinal) const;
uint32_t GetProcAddress(const char* name) const; uint32_t GetProcAddress(const std::string_view name) const;
int ApplyPatch(XexModule* module); int ApplyPatch(XexModule* module);
bool Load(const std::string& name, const std::string& path, bool Load(const std::string_view name, const std::string_view path,
const void* xex_addr, size_t xex_length); const void* xex_addr, size_t xex_length);
bool LoadContinue(); bool LoadContinue();
bool Unload(); bool Unload();
@ -177,7 +177,7 @@ class XexModule : public xe::cpu::Module {
int ReadPEHeaders(); int ReadPEHeaders();
bool SetupLibraryImports(const char* name, bool SetupLibraryImports(const std::string_view name,
const xex2_import_library* library); const xex2_import_library* library);
bool FindSaveRest(); bool FindSaveRest();

View File

@ -18,6 +18,7 @@
#include "third_party/imgui/imgui.h" #include "third_party/imgui/imgui.h"
#include "third_party/imgui/imgui_internal.h" #include "third_party/imgui/imgui_internal.h"
#include "xenia/base/clock.h" #include "xenia/base/clock.h"
#include "xenia/base/fuzzy.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
@ -47,7 +48,7 @@ using xe::ui::MenuItem;
using xe::ui::MouseEvent; using xe::ui::MouseEvent;
using xe::ui::UIEvent; using xe::ui::UIEvent;
const std::wstring kBaseTitle = L"Xenia Debugger"; const std::string kBaseTitle = "Xenia Debugger";
DebugWindow::DebugWindow(Emulator* emulator, xe::ui::Loop* loop) DebugWindow::DebugWindow(Emulator* emulator, xe::ui::Loop* loop)
: emulator_(emulator), : emulator_(emulator),
@ -90,10 +91,10 @@ bool DebugWindow::Initialize() {
// Main menu. // Main menu.
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, "&File");
{ {
file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, L"&Close", file_menu->AddChild(MenuItem::Create(MenuItem::Type::kString, "&Close",
L"Alt+F4", "Alt+F4",
[this]() { window_->Close(); })); [this]() { window_->Close(); }));
} }
main_menu->AddChild(std::move(file_menu)); main_menu->AddChild(std::move(file_menu));
@ -291,7 +292,7 @@ void DebugWindow::DrawToolbar() {
++i; ++i;
} }
if (ImGui::Combo("##thread_combo", &current_thread_index, if (ImGui::Combo("##thread_combo", &current_thread_index,
thread_combo.GetString(), 10)) { thread_combo.buffer(), 10)) {
// Thread changed. // Thread changed.
SelectThreadStackFrame(cache_.thread_debug_infos[current_thread_index], 0, SelectThreadStackFrame(cache_.thread_debug_infos[current_thread_index], 0,
true); true);
@ -509,7 +510,7 @@ void DebugWindow::DrawGuestFunctionSource() {
uint32_t code = uint32_t code =
xe::load_and_swap<uint32_t>(memory->TranslateVirtual(address)); xe::load_and_swap<uint32_t>(memory->TranslateVirtual(address));
cpu::ppc::DisasmPPC(address, code, &str); cpu::ppc::DisasmPPC(address, code, &str);
ImGui::Text("%.8X %.8X %s", address, code, str.GetString()); ImGui::Text("%.8X %.8X %s", address, code, str.buffer());
str.Reset(); str.Reset();
if (is_current_instr) { if (is_current_instr) {
@ -1425,19 +1426,19 @@ void DebugWindow::UpdateCache() {
auto object_table = kernel_state->object_table(); auto object_table = kernel_state->object_table();
loop_->Post([this]() { loop_->Post([this]() {
std::wstring title = kBaseTitle; std::string title = kBaseTitle;
switch (processor_->execution_state()) { switch (processor_->execution_state()) {
case cpu::ExecutionState::kEnded: case cpu::ExecutionState::kEnded:
title += L" (ended)"; title += " (ended)";
break; break;
case cpu::ExecutionState::kPaused: case cpu::ExecutionState::kPaused:
title += L" (paused)"; title += " (paused)";
break; break;
case cpu::ExecutionState::kRunning: case cpu::ExecutionState::kRunning:
title += L" (running)"; title += " (running)";
break; break;
case cpu::ExecutionState::kStepping: case cpu::ExecutionState::kStepping:
title += L" (stepping)"; title += " (stepping)";
break; break;
} }
window_->set_title(title); window_->set_title(title);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -12,6 +12,7 @@
#include <cinttypes> #include <cinttypes>
#include "config.h" #include "config.h"
#include "third_party/fmt/include/fmt/format.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"
@ -56,9 +57,9 @@ DEFINE_string(
namespace xe { namespace xe {
Emulator::Emulator(const std::wstring& command_line, Emulator::Emulator(const std::filesystem::path& command_line,
const std::wstring& storage_root, const std::filesystem::path& storage_root,
const std::wstring& content_root) const std::filesystem::path& content_root)
: on_launch(), : on_launch(),
on_terminate(), on_terminate(),
on_exit(), on_exit(),
@ -241,27 +242,22 @@ X_STATUS Emulator::TerminateTitle() {
kernel_state_->TerminateTitle(); kernel_state_->TerminateTitle();
title_id_ = 0; title_id_ = 0;
game_title_ = L""; game_title_ = "";
on_terminate(); on_terminate();
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;
} }
X_STATUS Emulator::LaunchPath(std::wstring 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.
auto last_slash = path.find_last_of(xe::kPathSeparator); if (!path.has_extension()) {
auto last_dot = path.find_last_of('.');
if (last_dot < last_slash) {
last_dot = std::wstring::npos;
}
if (last_dot == std::wstring::npos) {
// Likely an STFS container. // Likely an STFS container.
return LaunchStfsContainer(path); return LaunchStfsContainer(path);
}; };
auto extension = path.substr(last_dot); auto extension = xe::path_to_utf8(path.extension());
std::transform(extension.begin(), extension.end(), extension.begin(), std::transform(extension.begin(), extension.end(), extension.begin(),
tolower); tolower);
if (extension == L".xex" || extension == L".elf" || extension == L".exe") { if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
// Treat as a naked xex file. // Treat as a naked xex file.
return LaunchXexFile(path); return LaunchXexFile(path);
} else { } else {
@ -270,7 +266,7 @@ X_STATUS Emulator::LaunchPath(std::wstring path) {
} }
} }
X_STATUS Emulator::LaunchXexFile(std::wstring path) { X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) {
// 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:
@ -281,7 +277,7 @@ X_STATUS Emulator::LaunchXexFile(std::wstring path) {
auto mount_path = "\\Device\\Harddisk0\\Partition0"; auto mount_path = "\\Device\\Harddisk0\\Partition0";
// Register the local directory in the virtual filesystem. // Register the local directory in the virtual filesystem.
auto parent_path = xe::find_base_path(path); auto parent_path = path.parent_path();
auto device = auto device =
std::make_unique<vfs::HostPathDevice>(mount_path, parent_path, true); std::make_unique<vfs::HostPathDevice>(mount_path, parent_path, true);
if (!device->Initialize()) { if (!device->Initialize()) {
@ -298,14 +294,14 @@ X_STATUS Emulator::LaunchXexFile(std::wstring path) {
file_system_->RegisterSymbolicLink("d:", mount_path); file_system_->RegisterSymbolicLink("d:", mount_path);
// Get just the filename (foo.xex). // Get just the filename (foo.xex).
auto file_name = xe::find_name_from_path(path); auto file_name = path.filename();
// Launch the game. // Launch the game.
std::string fs_path = "game:\\" + xe::to_string(file_name); auto fs_path = "game:\\" + xe::path_to_utf8(file_name);
return CompleteLaunch(path, fs_path); return CompleteLaunch(path, fs_path);
} }
X_STATUS Emulator::LaunchDiscImage(std::wstring path) { X_STATUS Emulator::LaunchDiscImage(const std::filesystem::path& path) {
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.
@ -328,7 +324,7 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
return CompleteLaunch(path, module_path); return CompleteLaunch(path, module_path);
} }
X_STATUS Emulator::LaunchStfsContainer(std::wstring path) { X_STATUS Emulator::LaunchStfsContainer(const std::filesystem::path& path) {
auto mount_path = "\\Device\\Cdrom0"; auto mount_path = "\\Device\\Cdrom0";
// Register the container in the virtual filesystem. // Register the container in the virtual filesystem.
@ -407,7 +403,7 @@ void Emulator::Resume() {
} }
} }
bool Emulator::SaveToFile(const std::wstring& path) { bool Emulator::SaveToFile(const std::filesystem::path& path) {
Pause(); Pause();
filesystem::CreateFile(path); filesystem::CreateFile(path);
@ -435,7 +431,7 @@ bool Emulator::SaveToFile(const std::wstring& path) {
return true; return true;
} }
bool Emulator::RestoreFromFile(const std::wstring& path) { bool Emulator::RestoreFromFile(const std::filesystem::path& path) {
// Restore the emulator state from a file // Restore the emulator state from a file
auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite); auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite);
if (!map) { if (!map) {
@ -509,7 +505,7 @@ 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;
CompleteLaunch(L"", next_title); CompleteLaunch("", next_title);
} }
bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) { bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) {
@ -642,20 +638,20 @@ std::string Emulator::FindLaunchModule() {
return path + default_module; return path + default_module;
} }
X_STATUS Emulator::CompleteLaunch(const std::wstring& path, X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
const std::string& module_path) { const std::string_view module_path) {
// Reset state. // Reset state.
title_id_ = 0; title_id_ = 0;
game_title_ = L""; game_title_ = "";
display_window_->SetIcon(nullptr, 0); display_window_->SetIcon(nullptr, 0);
// 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");
XELOGI("Launching module %s", module_path.c_str()); XELOGI("Launching module %s", module_path.c_str());
auto module = kernel_state_->LoadUserModule(module_path.c_str()); auto module = kernel_state_->LoadUserModule(module_path);
if (!module) { if (!module) {
XELOGE("Failed to load user module %S", path.c_str()); XELOGE("Failed to load user module %s", xe::path_to_utf8(path).c_str());
return X_STATUS_NOT_FOUND; return X_STATUS_NOT_FOUND;
} }
@ -668,17 +664,16 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
// Try and load the resource database (xex only). // Try and load the resource database (xex only).
if (module->title_id()) { if (module->title_id()) {
char title_id[9] = {0}; auto title_id = fmt::format("{:08X}", module->title_id());
std::snprintf(title_id, xe::countof(title_id), "%08X", module->title_id()); config::LoadGameConfig(title_id);
config::LoadGameConfig(xe::to_wstring(title_id));
uint32_t resource_data = 0; uint32_t resource_data = 0;
uint32_t resource_size = 0; uint32_t resource_size = 0;
if (XSUCCEEDED( if (XSUCCEEDED(module->GetSection(title_id.c_str(), &resource_data,
module->GetSection(title_id, &resource_data, &resource_size))) { &resource_size))) {
kernel::util::XdbfGameData db( kernel::util::XdbfGameData db(
module->memory()->TranslateVirtual(resource_data), resource_size); module->memory()->TranslateVirtual(resource_data), resource_size);
if (db.is_valid()) { if (db.is_valid()) {
game_title_ = xe::to_wstring(db.title()); game_title_ = db.title();
auto icon_block = db.icon(); auto icon_block = db.icon();
if (icon_block) { if (icon_block) {
display_window_->SetIcon(icon_block.buffer, icon_block.size); display_window_->SetIcon(icon_block.buffer, icon_block.size);

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -47,22 +47,22 @@ namespace xe {
// This is responsible for initializing and managing all the various subsystems. // This is responsible for initializing and managing all the various subsystems.
class Emulator { class Emulator {
public: public:
explicit Emulator(const std::wstring& command_line, explicit Emulator(const std::filesystem::path& command_line,
const std::wstring& storage_root, const std::filesystem::path& storage_root,
const std::wstring& content_root); const std::filesystem::path& content_root);
~Emulator(); ~Emulator();
// Full command line used when launching the process. // Full command line used when launching the process.
const std::wstring& command_line() const { return command_line_; } const std::filesystem::path& command_line() const { return command_line_; }
// Folder persistent internal emulator data is stored in. // Folder persistent internal emulator data is stored in.
const std::wstring& storage_root() const { return storage_root_; } const std::filesystem::path& storage_root() const { return storage_root_; }
// Folder guest content is stored in. // Folder guest content is stored in.
const std::wstring& content_root() const { return content_root_; } const std::filesystem::path& content_root() const { return content_root_; }
// Title of the game in the default language. // Title of the game in the default language.
const std::wstring& game_title() const { return game_title_; } const std::string& game_title() const { return game_title_; }
// Currently running title ID // Currently running title ID
uint32_t title_id() const { return title_id_; } uint32_t title_id() const { return title_id_; }
@ -123,24 +123,24 @@ 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); X_STATUS LaunchPath(const std::filesystem::path& 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); X_STATUS LaunchXexFile(const std::filesystem::path& 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); X_STATUS LaunchDiscImage(const std::filesystem::path& 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(const std::filesystem::path& path);
void Pause(); void Pause();
void Resume(); void Resume();
bool is_paused() const { return paused_; } bool is_paused() const { return paused_; }
bool SaveToFile(const std::wstring& path); bool SaveToFile(const std::filesystem::path& path);
bool RestoreFromFile(const std::wstring& path); bool RestoreFromFile(const std::filesystem::path& path);
// The game can request another title to be loaded. // The game can request another title to be loaded.
bool TitleRequested(); bool TitleRequested();
@ -149,7 +149,7 @@ class Emulator {
void WaitUntilExit(); void WaitUntilExit();
public: public:
xe::Delegate<uint32_t, const std::wstring&> on_launch; xe::Delegate<uint32_t, const std::string_view> on_launch;
xe::Delegate<bool> on_shader_storage_initialization; xe::Delegate<bool> on_shader_storage_initialization;
xe::Delegate<> on_terminate; xe::Delegate<> on_terminate;
xe::Delegate<> on_exit; xe::Delegate<> on_exit;
@ -160,14 +160,14 @@ class Emulator {
std::string FindLaunchModule(); std::string FindLaunchModule();
X_STATUS CompleteLaunch(const std::wstring& path, X_STATUS CompleteLaunch(const std::filesystem::path& path,
const std::string& module_path); const std::string_view module_path);
std::wstring command_line_; std::filesystem::path command_line_;
std::wstring storage_root_; std::filesystem::path storage_root_;
std::wstring content_root_; std::filesystem::path content_root_;
std::wstring game_title_; std::string game_title_;
ui::Window* display_window_; ui::Window* display_window_;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -13,6 +13,7 @@
#include <cinttypes> #include <cinttypes>
#include <cmath> #include <cmath>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/byte_stream.h" #include "xenia/base/byte_stream.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
@ -87,11 +88,12 @@ void CommandProcessor::Shutdown() {
worker_thread_.reset(); worker_thread_.reset();
} }
void CommandProcessor::InitializeShaderStorage(const std::wstring& storage_root, void CommandProcessor::InitializeShaderStorage(
uint32_t title_id, const std::filesystem::path& storage_root, uint32_t title_id,
bool blocking) {} bool blocking) {}
void CommandProcessor::RequestFrameTrace(const std::wstring& root_path) { void CommandProcessor::RequestFrameTrace(
const std::filesystem::path& root_path) {
if (trace_state_ == TraceState::kStreaming) { if (trace_state_ == TraceState::kStreaming) {
XELOGE("Streaming trace; cannot also trace frame."); XELOGE("Streaming trace; cannot also trace frame.");
return; return;
@ -104,7 +106,7 @@ void CommandProcessor::RequestFrameTrace(const std::wstring& root_path) {
trace_frame_path_ = root_path; trace_frame_path_ = root_path;
} }
void CommandProcessor::BeginTracing(const std::wstring& root_path) { void CommandProcessor::BeginTracing(const std::filesystem::path& root_path) {
if (trace_state_ == TraceState::kStreaming) { if (trace_state_ == TraceState::kStreaming) {
XELOGE("Streaming already active; ignoring request."); XELOGE("Streaming already active; ignoring request.");
return; return;
@ -440,8 +442,8 @@ uint32_t CommandProcessor::ExecutePrimaryBuffer(uint32_t read_index,
uint32_t title_id = kernel_state_->GetExecutableModule() uint32_t title_id = kernel_state_->GetExecutableModule()
? kernel_state_->GetExecutableModule()->title_id() ? kernel_state_->GetExecutableModule()->title_id()
: 0; : 0;
auto file_name = xe::format_string(L"%8X_stream.xtr", title_id); auto file_name = fmt::format("{:8X}_stream.xtr", title_id);
auto path = trace_stream_path_ + file_name; auto path = trace_stream_path_ / file_name;
trace_writer_.Open(path, title_id); trace_writer_.Open(path, title_id);
InitializeTrace(); InitializeTrace();
} }
@ -754,8 +756,8 @@ bool CommandProcessor::ExecutePacketType3(RingBuffer* reader, uint32_t packet) {
} else if (trace_state_ == TraceState::kSingleFrame) { } else if (trace_state_ == TraceState::kSingleFrame) {
// New trace request - we only start tracing at the beginning of a frame. // New trace request - we only start tracing at the beginning of a frame.
uint32_t title_id = kernel_state_->GetExecutableModule()->title_id(); uint32_t title_id = kernel_state_->GetExecutableModule()->title_id();
auto file_name = xe::format_string(L"%8X_%u.xtr", title_id, counter_ - 1); auto file_name = fmt::format("{:8X}_{}.xtr", title_id, counter_ - 1);
auto path = trace_frame_path_ + file_name; auto path = trace_frame_path_ / file_name;
trace_writer_.Open(path, title_id); trace_writer_.Open(path, title_id);
InitializeTrace(); InitializeTrace();
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -133,11 +133,12 @@ class CommandProcessor {
// May be called not only from the command processor thread when the command // May be called not only from the command processor thread when the command
// processor is paused, and the termination of this function may be explicitly // processor is paused, and the termination of this function may be explicitly
// awaited. // awaited.
virtual void InitializeShaderStorage(const std::wstring& storage_root, virtual void InitializeShaderStorage(
uint32_t title_id, bool blocking); const std::filesystem::path& storage_root, uint32_t title_id,
bool blocking);
virtual void RequestFrameTrace(const std::wstring& root_path); virtual void RequestFrameTrace(const std::filesystem::path& root_path);
virtual void BeginTracing(const std::wstring& root_path); virtual void BeginTracing(const std::filesystem::path& root_path);
virtual void EndTracing(); virtual void EndTracing();
virtual void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) = 0; virtual void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) = 0;
@ -264,8 +265,8 @@ class CommandProcessor {
kSingleFrame, kSingleFrame,
}; };
TraceState trace_state_ = TraceState::kDisabled; TraceState trace_state_ = TraceState::kDisabled;
std::wstring trace_stream_path_; std::filesystem::path trace_stream_path_;
std::wstring trace_frame_path_; std::filesystem::path trace_frame_path_;
std::atomic<bool> worker_running_; std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_; kernel::object_ref<kernel::XHostThread> worker_thread_;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -85,12 +85,14 @@ void D3D12CommandProcessor::ClearCaches() {
} }
void D3D12CommandProcessor::InitializeShaderStorage( void D3D12CommandProcessor::InitializeShaderStorage(
const std::wstring& storage_root, uint32_t title_id, bool blocking) { const std::filesystem::path& storage_root, uint32_t title_id,
bool blocking) {
CommandProcessor::InitializeShaderStorage(storage_root, title_id, blocking); CommandProcessor::InitializeShaderStorage(storage_root, title_id, blocking);
pipeline_cache_->InitializeShaderStorage(storage_root, title_id, blocking); pipeline_cache_->InitializeShaderStorage(storage_root, title_id, blocking);
} }
void D3D12CommandProcessor::RequestFrameTrace(const std::wstring& root_path) { void D3D12CommandProcessor::RequestFrameTrace(
const std::filesystem::path& root_path) {
// Capture with PIX if attached. // Capture with PIX if attached.
if (GetD3D12Context()->GetD3D12Provider()->GetGraphicsAnalysis() != nullptr) { if (GetD3D12Context()->GetD3D12Provider()->GetGraphicsAnalysis() != nullptr) {
pix_capture_requested_.store(true, std::memory_order_relaxed); pix_capture_requested_.store(true, std::memory_order_relaxed);
@ -668,16 +670,16 @@ void D3D12CommandProcessor::SetExternalGraphicsPipeline(
} }
} }
std::wstring D3D12CommandProcessor::GetWindowTitleText() const { std::string D3D12CommandProcessor::GetWindowTitleText() const {
if (IsROVUsedForEDRAM()) { if (IsROVUsedForEDRAM()) {
// Currently scaling is only supported with ROV. // Currently scaling is only supported with ROV.
if (texture_cache_ != nullptr && texture_cache_->IsResolutionScale2X()) { if (texture_cache_ != nullptr && texture_cache_->IsResolutionScale2X()) {
return L"Direct3D 12 - ROV 2x"; return "Direct3D 12 - ROV 2x";
} else { } else {
return L"Direct3D 12 - ROV"; return "Direct3D 12 - ROV";
} }
} else { } else {
return L"Direct3D 12 - RTV/DSV"; return "Direct3D 12 - RTV/DSV";
} }
} }

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -43,10 +43,10 @@ class D3D12CommandProcessor : public CommandProcessor {
void ClearCaches() override; void ClearCaches() override;
void InitializeShaderStorage(const std::wstring& storage_root, void InitializeShaderStorage(const std::filesystem::path& storage_root,
uint32_t title_id, bool blocking) override; uint32_t title_id, bool blocking) override;
void RequestFrameTrace(const std::wstring& root_path) override; void RequestFrameTrace(const std::filesystem::path& root_path) override;
void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) override; void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) override;
@ -154,7 +154,7 @@ class D3D12CommandProcessor : public CommandProcessor {
bool changing_stencil_ref = false); bool changing_stencil_ref = false);
// Returns the text to display in the GPU backend name in the window title. // Returns the text to display in the GPU backend name in the window title.
std::wstring GetWindowTitleText() const; std::string GetWindowTitleText() const;
std::unique_ptr<xe::ui::RawImage> Capture(); std::unique_ptr<xe::ui::RawImage> Capture();

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -31,13 +31,13 @@ bool D3D12GraphicsSystem::IsAvailable() {
return xe::ui::d3d12::D3D12Provider::IsD3D12APIAvailable(); return xe::ui::d3d12::D3D12Provider::IsD3D12APIAvailable();
} }
std::wstring D3D12GraphicsSystem::name() const { std::string D3D12GraphicsSystem::name() const {
auto d3d12_command_processor = auto d3d12_command_processor =
static_cast<D3D12CommandProcessor*>(command_processor()); static_cast<D3D12CommandProcessor*>(command_processor());
if (d3d12_command_processor != nullptr) { if (d3d12_command_processor != nullptr) {
return d3d12_command_processor->GetWindowTitleText(); return d3d12_command_processor->GetWindowTitleText();
} }
return L"Direct3D 12"; return "Direct3D 12";
} }
X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor, X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor,

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -28,7 +28,7 @@ class D3D12GraphicsSystem : public GraphicsSystem {
static bool IsAvailable(); static bool IsAvailable();
std::wstring name() const override; std::string name() const override;
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::Window* target_window) override; ui::Window* target_window) override;

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -45,7 +45,7 @@ class D3D12TraceDump : public TraceDump {
} }
}; };
int trace_dump_main(const std::vector<std::wstring>& args) { int trace_dump_main(const std::vector<std::string>& args) {
D3D12TraceDump trace_dump; D3D12TraceDump trace_dump;
return trace_dump.Main(args); return trace_dump.Main(args);
} }
@ -54,6 +54,6 @@ int trace_dump_main(const std::vector<std::wstring>& args) {
} // namespace gpu } // namespace gpu
} // namespace xe } // namespace xe
DEFINE_ENTRY_POINT(L"xenia-gpu-d3d12-trace-dump", DEFINE_ENTRY_POINT("xenia-gpu-d3d12-trace-dump",
xe::gpu::d3d12::trace_dump_main, "some.trace", xe::gpu::d3d12::trace_dump_main, "some.trace",
"target_trace_file"); "target_trace_file");

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -47,7 +47,7 @@ class D3D12TraceViewer : public TraceViewer {
} }
}; };
int trace_viewer_main(const std::vector<std::wstring>& args) { int trace_viewer_main(const std::vector<std::string>& args) {
D3D12TraceViewer trace_viewer; D3D12TraceViewer trace_viewer;
return trace_viewer.Main(args); return trace_viewer.Main(args);
} }
@ -56,6 +56,6 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
} // namespace gpu } // namespace gpu
} // namespace xe } // namespace xe
DEFINE_ENTRY_POINT(L"xenia-gpu-d3d12-trace-viewer", DEFINE_ENTRY_POINT("xenia-gpu-d3d12-trace-viewer",
xe::gpu::d3d12::trace_viewer_main, "some.trace", xe::gpu::d3d12::trace_viewer_main, "some.trace",
"target_trace_file"); "target_trace_file");

View File

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2018 Ben Vanik. All rights reserved. * * Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -18,8 +18,8 @@
#include <mutex> #include <mutex>
#include <utility> #include <utility>
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/xxhash/xxhash.h" #include "third_party/xxhash/xxhash.h"
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/byte_order.h" #include "xenia/base/byte_order.h"
#include "xenia/base/clock.h" #include "xenia/base/clock.h"
@ -138,7 +138,7 @@ void PipelineCache::Shutdown() {
void PipelineCache::ClearCache(bool shutting_down) { void PipelineCache::ClearCache(bool shutting_down) {
bool reinitialize_shader_storage = bool reinitialize_shader_storage =
!shutting_down && storage_write_thread_ != nullptr; !shutting_down && storage_write_thread_ != nullptr;
std::wstring shader_storage_root; std::filesystem::path shader_storage_root;
uint32_t shader_storage_title_id = shader_storage_title_id_; uint32_t shader_storage_title_id = shader_storage_title_id_;
if (reinitialize_shader_storage) { if (reinitialize_shader_storage) {
shader_storage_root = shader_storage_root_; shader_storage_root = shader_storage_root_;
@ -188,19 +188,19 @@ void PipelineCache::ClearCache(bool shutting_down) {
} }
} }
void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root, void PipelineCache::InitializeShaderStorage(
uint32_t title_id, bool blocking) { const std::filesystem::path& storage_root, uint32_t title_id,
bool blocking) {
ShutdownShaderStorage(); ShutdownShaderStorage();
auto shader_storage_root = xe::join_paths(storage_root, L"shaders"); auto shader_storage_root = storage_root / "shaders";
// For files that can be moved between different hosts. // For files that can be moved between different hosts.
// Host PSO blobs - if ever added - should be stored in shaders/local/ (they // Host PSO blobs - if ever added - should be stored in shaders/local/ (they
// currently aren't used because because they may be not very practical - // currently aren't used because because they may be not very practical -
// would need to invalidate them every commit likely, and additional I/O // would need to invalidate them every commit likely, and additional I/O
// cost - though D3D's internal validation would possibly be enough to ensure // cost - though D3D's internal validation would possibly be enough to ensure
// they are up to date). // they are up to date).
auto shader_storage_shareable_root = auto shader_storage_shareable_root = shader_storage_root / "shareable";
xe::join_paths(shader_storage_root, L"shareable");
if (!xe::filesystem::CreateFolder(shader_storage_shareable_root)) { if (!xe::filesystem::CreateFolder(shader_storage_shareable_root)) {
return; return;
} }
@ -215,8 +215,7 @@ void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
uint64_t shader_storage_initialization_start = uint64_t shader_storage_initialization_start =
xe::Clock::QueryHostTickCount(); xe::Clock::QueryHostTickCount();
shader_storage_file_ = xe::filesystem::OpenFile( shader_storage_file_ = xe::filesystem::OpenFile(
xe::join_paths(shader_storage_shareable_root, shader_storage_shareable_root / fmt::format("{:08X}.xsh", title_id),
xe::format_string(L"%.8X.xsh", title_id)),
"a+b"); "a+b");
if (!shader_storage_file_) { if (!shader_storage_file_) {
return; return;
@ -388,11 +387,11 @@ void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
// Initialize the pipeline state storage stream. // Initialize the pipeline state storage stream.
uint64_t pipeline_state_storage_initialization_start_ = uint64_t pipeline_state_storage_initialization_start_ =
xe::Clock::QueryHostTickCount(); xe::Clock::QueryHostTickCount();
pipeline_state_storage_file_ = xe::filesystem::OpenFile( pipeline_state_storage_file_ =
xe::join_paths(shader_storage_shareable_root, xe::filesystem::OpenFile(shader_storage_shareable_root /
xe::format_string(L"%.8X.%s.d3d12.xpso", title_id, fmt::format("{:08X}.{}.d3d12.xpso", title_id,
edram_rov_used_ ? L"rov" : L"rtv")), edram_rov_used_ ? "rov" : "rtv"),
"a+b"); "a+b");
if (!pipeline_state_storage_file_) { if (!pipeline_state_storage_file_) {
fclose(shader_storage_file_); fclose(shader_storage_file_);
shader_storage_file_ = nullptr; shader_storage_file_ = nullptr;
@ -1694,13 +1693,12 @@ ID3D12PipelineState* PipelineCache::CreateD3D12PipelineState(
} }
std::wstring name; std::wstring name;
if (runtime_description.pixel_shader != nullptr) { if (runtime_description.pixel_shader != nullptr) {
name = name = fmt::format(L"VS {:016X}, PS {:016X}",
xe::format_string(L"VS %.16I64X, PS %.16I64X", runtime_description.vertex_shader->ucode_data_hash(),
runtime_description.vertex_shader->ucode_data_hash(), runtime_description.pixel_shader->ucode_data_hash());
runtime_description.pixel_shader->ucode_data_hash());
} else { } else {
name = xe::format_string( name = fmt::format(L"VS {:016X}",
L"VS %.16I64X", runtime_description.vertex_shader->ucode_data_hash()); runtime_description.vertex_shader->ucode_data_hash());
} }
state->SetName(name.c_str()); state->SetName(name.c_str());
return state; return state;

View File

@ -46,7 +46,7 @@ class PipelineCache {
void Shutdown(); void Shutdown();
void ClearCache(bool shutting_down = false); void ClearCache(bool shutting_down = false);
void InitializeShaderStorage(const std::wstring& storage_root, void InitializeShaderStorage(const std::filesystem::path& storage_root,
uint32_t title_id, bool blocking); uint32_t title_id, bool blocking);
void ShutdownShaderStorage(); void ShutdownShaderStorage();
@ -262,7 +262,7 @@ class PipelineCache {
PipelineState* current_pipeline_state_ = nullptr; PipelineState* current_pipeline_state_ = nullptr;
// Currently open shader storage path. // Currently open shader storage path.
std::wstring shader_storage_root_; std::filesystem::path shader_storage_root_;
uint32_t shader_storage_title_id_ = 0; uint32_t shader_storage_title_id_ = 0;
// Shader storage output stream, for preload in the next emulator runs. // Shader storage output stream, for preload in the next emulator runs.

View File

@ -7,6 +7,7 @@ project("xenia-gpu-d3d12")
kind("StaticLib") kind("StaticLib")
language("C++") language("C++")
links({ links({
"fmt",
"xenia-base", "xenia-base",
"xenia-gpu", "xenia-gpu",
"xenia-ui", "xenia-ui",
@ -27,6 +28,7 @@ project("xenia-gpu-d3d12-trace-viewer")
"aes_128", "aes_128",
"capstone", "capstone",
"dxbc", "dxbc",
"fmt",
"imgui", "imgui",
"libavcodec", "libavcodec",
"libavutil", "libavutil",
@ -71,6 +73,7 @@ project("xenia-gpu-d3d12-trace-dump")
"aes_128", "aes_128",
"capstone", "capstone",
"dxbc", "dxbc",
"fmt",
"imgui", "imgui",
"libavcodec", "libavcodec",
"libavutil", "libavutil",

Some files were not shown because too many files have changed in this diff Show More