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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -39,11 +39,10 @@ void DiscordPresence::NotPlaying() {
Discord_UpdatePresence(&discordPresence);
}
void DiscordPresence::PlayingTitle(const std::wstring& game_title) {
auto discord_game_title = xe::to_string(game_title);
void DiscordPresence::PlayingTitle(const std::string_view game_title) {
DiscordRichPresence discordPresence = {};
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.
// discordPresence.smallImageKey = "app";
// discordPresence.largeImageKey = "state_ingame";

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -19,7 +19,7 @@ class DiscordPresence {
public:
static void Initialize();
static void NotPlaying();
static void PlayingTitle(const std::wstring& game_title);
static void PlayingTitle(const std::string_view game_title);
static void Shutdown();
};

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -44,7 +44,7 @@ class EmulatorWindow {
bool Initialize();
void FileDrop(const wchar_t* filename);
void FileDrop(const std::filesystem::path& filename);
void FileOpen();
void FileClose();
void ShowContentDirectory();
@ -62,7 +62,7 @@ class EmulatorWindow {
Emulator* emulator_;
std::unique_ptr<ui::Loop> loop_;
std::unique_ptr<ui::Window> window_;
std::wstring base_title_;
std::string base_title_;
uint64_t cursor_hide_time_ = 0;
bool initializing_shader_storage_ = false;
};

View File

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

View File

@ -44,6 +44,7 @@
#include "xenia/hid/xinput/xinput_hid.h"
#endif // XE_PLATFORM_WIN32
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/xbyak/xbyak/xbyak_util.h"
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_string(
DEFINE_path(
storage_root, "",
"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 "
"the emulator executable directory if portable.txt is present in it.",
"Storage");
DEFINE_string(
DEFINE_path(
content_root, "",
"Root path for guest content storage (saves, etc.), or empty to use the "
"content folder under the storage root.",
@ -69,9 +70,9 @@ DEFINE_string(
DEFINE_bool(mount_scratch, false, "Enable scratch mount", "Storage");
DEFINE_bool(mount_cache, false, "Enable cache mount", "Storage");
DEFINE_transient_string(target, "",
"Specifies the target .xex or .iso to execute.",
"General");
DEFINE_transient_path(target, "",
"Specifies the target .xex or .iso to execute.",
"General");
DECLARE_bool(debug);
DEFINE_bool(discord, true, "Enable Discord rich presence", "General");
@ -91,25 +92,25 @@ class Factory {
std::vector<Creator> creators_;
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) {
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) {
auto always_available = []() { return true; };
Add(name, always_available, instantiate);
}
template <typename DT>
void Add(const std::string& name) {
void Add(const std::string_view name) {
Add(name, DT::IsAvailable, [](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") {
auto it = std::find_if(
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) {
std::vector<std::unique_ptr<T>> instances;
if (!name.empty() && name != "any") {
@ -206,35 +207,34 @@ std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
return drivers;
}
int xenia_main(const std::vector<std::wstring>& args) {
int xenia_main(const std::vector<std::string>& args) {
Profiler::Initialize();
Profiler::ThreadEnter("main");
// 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()) {
storage_root = xe::filesystem::GetExecutableFolder();
if (!xe::filesystem::PathExists(
xe::join_paths(storage_root, L"portable.txt"))) {
if (!xe::filesystem::PathExists(storage_root / "portable.txt")) {
storage_root = xe::filesystem::GetUserFolder();
#if defined(XE_PLATFORM_WIN32) || defined(XE_PLATFORM_LINUX)
storage_root = xe::join_paths(storage_root, L"Xenia");
storage_root = storage_root / "Xenia";
#else
#warning Unhandled platform for the data root.
storage_root = xe::join_paths(storage_root, L"Xenia");
storage_root = storage_root / "Xenia";
#endif
}
}
storage_root = xe::to_absolute_path(storage_root);
storage_root = std::filesystem::absolute(storage_root);
XELOGI("Storage root: %S", storage_root.c_str());
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()) {
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());
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.
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.
auto emulator_window = EmulatorWindow::Create(emulator.get());
@ -260,7 +260,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
if (cvars::mount_scratch) {
auto scratch_device = std::make_unique<xe::vfs::HostPathDevice>(
"\\SCRATCH", L"scratch", false);
"\\SCRATCH", "scratch", false);
if (!scratch_device->Initialize()) {
XELOGE("Unable to scan scratch path");
} else {
@ -274,7 +274,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
if (cvars::mount_cache) {
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()) {
XELOGE("Unable to scan cache0 path");
} else {
@ -286,7 +286,7 @@ int xenia_main(const std::vector<std::wstring>& args) {
}
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()) {
XELOGE("Unable to scan cache1 path");
} 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) {
if (cvars::discord) {
discord::DiscordPresence::PlayingTitle(
game_title.empty() ? L"Unknown Title" : game_title);
game_title.empty() ? "Unknown Title" : std::string(game_title));
}
emulator_window->UpdateTitle();
evt->Set();
@ -365,9 +365,9 @@ int xenia_main(const std::vector<std::wstring>& args) {
emulator_window->window()->EnableMainMenu();
// Grab path from the flag or unnamed argument.
std::wstring path;
std::filesystem::path path;
if (!cvars::target.empty()) {
path = xe::to_wstring(cvars::target);
path = cvars::target;
}
// Toggles fullscreen
@ -375,10 +375,10 @@ int xenia_main(const std::vector<std::wstring>& args) {
if (!path.empty()) {
// 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);
if (XFAILED(result)) {
xe::FatalError("Failed to launch target: %.8X", result);
xe::FatalError(fmt::format("Failed to launch target: {:08X}", result));
emulator.reset();
emulator_window.reset();
return 1;
@ -416,5 +416,5 @@ int xenia_main(const std::vector<std::wstring>& args) {
} // namespace app
} // 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");

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -58,11 +58,11 @@ bool XAudio2AudioDriver::Initialize() {
// 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 -
// 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_) {
api_minor_version_ = 8;
} else {
xaudio2_module_ = reinterpret_cast<void*>(LoadLibrary(L"XAudio2_7.dll"));
xaudio2_module_ = reinterpret_cast<void*>(LoadLibraryW(L"XAudio2_7.dll"));
if (xaudio2_module_) {
api_minor_version_ = 7;
} else {

View File

@ -91,7 +91,7 @@ void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
StringBuffer buff;
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) {

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -40,8 +40,8 @@ inline int16_t byte_swap(int16_t value) {
inline uint16_t byte_swap(uint16_t value) {
return XENIA_BASE_BYTE_SWAP_16(value);
}
inline uint16_t byte_swap(wchar_t value) {
return static_cast<wchar_t>(XENIA_BASE_BYTE_SWAP_16(value));
inline uint16_t byte_swap(char16_t value) {
return static_cast<char16_t>(XENIA_BASE_BYTE_SWAP_16(value));
}
inline int32_t byte_swap(int32_t value) {
return static_cast<int32_t>(

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -47,8 +47,8 @@ std::string ByteStream::Read() {
}
template <>
std::wstring ByteStream::Read() {
std::wstring str;
std::u16string ByteStream::Read() {
std::u16string str;
uint32_t len = Read<uint32_t>();
str.resize(len);

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -51,14 +51,14 @@ class ByteStream {
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(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(str.c_str(), str.length() * 2);
Write(str.data(), str.length() * sizeof(char16_t));
}
private:
@ -71,7 +71,7 @@ template <>
std::string ByteStream::Read();
template <>
std::wstring ByteStream::Read();
std::u16string ByteStream::Read();
} // namespace xe

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -36,12 +36,10 @@
#error "No cpu instruction wrappers for current compiler implemented."
#endif
#define CLOCK_FATAL(msg) \
xe::FatalError( \
"The raw clock source is not supported on your CPU. \n" \
"%s \n" \
"Set the cvar 'clock_source_raw' to 'false'.", \
(msg));
#define CLOCK_FATAL(msg) \
xe::FatalError("The raw clock source is not supported on your CPU.\n" msg \
"\n" \
"Set the cvar 'clock_source_raw' to 'false'.");
namespace xe {
// 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 *
******************************************************************************
* 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. *
******************************************************************************
*/
#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 {
cxxopts::Options options("xenia", "Xbox 360 Emulator");
@ -22,37 +31,45 @@ void PrintHelpAndExit() {
exit(0);
}
void ParseLaunchArguments(int argc, char** argv,
const std::string& positional_help,
void ParseLaunchArguments(int& argc, char**& argv,
const std::string_view positional_help,
const std::vector<std::string>& positional_options) {
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) {
auto cmdVar = it.second;
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;
configVar->AddToLaunchOptions(&options);
}
try {
options.positional_help(positional_help);
options.positional_help(std::string(positional_help));
options.parse_positional(positional_options);
auto result = options.parse(argc, argv);
if (result.count("help")) {
PrintHelpAndExit();
}
for (auto& it : *CmdVars) {
auto cmdVar = static_cast<ICommandVar*>(it.second);
if (result.count(cmdVar->name())) {
cmdVar->LoadFromLaunchOptions(&result);
}
}
for (auto& it : *ConfigVars) {
auto configVar = static_cast<IConfigVar*>(it.second);
if (result.count(configVar->name())) {
@ -67,48 +84,46 @@ void ParseLaunchArguments(int argc, char** argv,
namespace toml {
std::string EscapeBasicString(const std::string& str) {
std::string EscapeBasicString(const std::string_view view) {
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') {
result += "\\b";
result += u8"\\b";
} else if (c == '\t') {
result += "\\t";
result += u8"\\t";
} else if (c == '\n') {
result += "\\n";
result += u8"\\n";
} else if (c == '\f') {
result += "\\f";
result += u8"\\f";
} else if (c == '\r') {
result += "\\r";
result += u8"\\r";
} else if (c == '"') {
result += "\\\"";
result += u8"\\\"";
} else if (c == '\\') {
result += "\\\\";
} else if (static_cast<uint32_t>(c) < 0x20 ||
static_cast<uint32_t>(c) == 0x7F) {
auto v = static_cast<uint32_t>(c);
int w;
if (v <= 0xFFFF) {
result += "\\u";
w = 4;
result += u8"\\\\";
} else if (c < 0x20 || c == 0x7F) {
if (c <= 0xFFFF) {
result += fmt::format(u8"\\u{:04X}", c);
} else {
result += "\\U";
w = 8;
result += fmt::format(u8"\\u{:08X}", c);
}
std::stringstream ss;
ss << std::hex << std::setw(w) << std::setfill('0') << v;
result += ss.str();
} else {
result += c;
utfcpp::append(static_cast<char32_t>(c), result);
}
}
return result;
}
std::string EscapeMultilineBasicString(const std::string& str) {
std::string EscapeMultilineBasicString(const std::string_view view) {
std::string result;
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 (c == '"') {
++quote_run;
@ -116,74 +131,67 @@ std::string EscapeMultilineBasicString(const std::string& str) {
}
for (int i = 0; i < quote_run; ++i) {
if ((i % 3) == 2) {
result += "\\";
result += u8"\\";
}
result += '"';
result += u8"\"";
}
quote_run = 0;
}
if (c == '\b') {
result += "\\b";
result += u8"\\b";
} else if (c == '\t' || c == '\n') {
result += c;
} else if (c == '\f') {
result += "\\f";
result += u8"\\f";
} else if (c == '\r') {
// Silently drop \r.
// result += c;
} else if (c == '"') {
quote_run = 1;
} else if (c == '\\') {
result += "\\\\";
} else if (static_cast<uint32_t>(c) < 0x20 ||
static_cast<uint32_t>(c) == 0x7F) {
auto v = static_cast<uint32_t>(c);
int w;
if (v <= 0xFFFF) {
result += "\\u";
w = 4;
result += u8"\\\\";
} else if (c < 0x20 || c == 0x7F) {
if (c <= 0xFFFF) {
result += fmt::format(u8"\\u{:04X}", c);
} else {
result += "\\U";
w = 8;
result += fmt::format(u8"\\u{:08X}", c);
}
std::stringstream ss;
ss << std::hex << std::setw(w) << std::setfill('0') << v;
result += ss.str();
} else {
result += c;
utfcpp::append(static_cast<char32_t>(c), result);
}
}
for (int i = 0; i < quote_run; ++i) {
if ((i % 3) == 2) {
result += "\\";
result += u8"\\";
}
result += '"';
result += u8"\"";
}
return result;
}
std::string EscapeString(const std::string& val) {
const char multiline_chars[] = "\r\n";
const char escape_chars[] =
std::string EscapeString(const std::string_view view) {
const auto multiline_chars = std::string_view("\r\n");
const auto escape_chars = std::string_view(
"\0\b\v\f"
"\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"
"'"
"\x7F";
if (val.find_first_of(multiline_chars) == std::string::npos) {
"\x7F");
if (xe::utf8::find_any_of(view, multiline_chars) == std::string_view::npos) {
// single line
if (val.find_first_of(escape_chars) == std::string::npos) {
return "'" + val + "'";
if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos) {
return "'" + std::string(view) + "'";
} else {
return "\"" + toml::EscapeBasicString(val) + "\"";
return "\"" + toml::EscapeBasicString(view) + "\"";
}
} else {
// multi line
if (val.find_first_of(escape_chars) == std::string::npos &&
val.find("'''") == std::string::npos) {
return "'''\n" + val + "'''";
if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos &&
xe::utf8::find_first_of(view, u8"'''") == std::string_view::npos) {
return "'''\n" + std::string(view) + "'''";
} 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 *
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,18 +10,20 @@
#ifndef XENIA_CVAR_H_
#define XENIA_CVAR_H_
#include <filesystem>
#include <map>
#include <string>
#include <vector>
#include "cpptoml/include/cpptoml.h"
#include "cxxopts/include/cxxopts.hpp"
#include "xenia/base/filesystem.h"
#include "xenia/base/string_util.h"
namespace cvar {
namespace toml {
std::string EscapeString(const std::string& str);
std::string EscapeString(const std::string_view str);
}
class ICommandVar {
@ -116,10 +118,22 @@ template <class T>
void ConfigVar<T>::LoadConfigValue(std::shared_ptr<cpptoml::base> 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>
void ConfigVar<T>::LoadGameConfigValue(std::shared_ptr<cpptoml::base> 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>
CommandVar<T>::CommandVar(const char* name, T* default_value,
const char* description)
@ -158,6 +172,11 @@ template <>
inline std::string CommandVar<std::string>::Convert(std::string val) {
return val;
}
template <>
inline std::filesystem::path CommandVar<std::filesystem::path>::Convert(
std::string val) {
return xe::to_path(val);
}
template <>
inline std::string CommandVar<bool>::ToString(bool val) {
@ -167,6 +186,12 @@ template <>
inline std::string CommandVar<std::string>::ToString(std::string 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>
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*>();
CmdVars->insert(std::pair<std::string, ICommandVar*>(cv->name(), cv));
}
void ParseLaunchArguments(int argc, char** argv,
const std::string& positional_help,
void ParseLaunchArguments(int& argc, char**& argv,
const std::string_view positional_help,
const std::vector<std::string>& positional_options);
template <typename T>
@ -237,6 +262,9 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
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_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_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_CVar(name, default_value, description, category, true, std::string)
#define DEFINE_bool(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, false, bool)
#define DEFINE_transient_path(name, default_value, description, category) \
DEFINE_CVar(name, default_value, description, category, true, \
std::filesystem::path)
#define DEFINE_CVar(name, default_value, description, category, is_transient, \
type) \
@ -275,16 +308,18 @@ T* define_cmdvar(const char* name, T* default_value, const char* 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_string(name) DECLARE_CVar(name, std::string)
#define DECLARE_int32(name) DECLARE_CVar(name, int32_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) \
namespace cvars { \
extern type name; \

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -14,96 +14,14 @@
namespace xe {
namespace filesystem {
std::string CanonicalizePath(const std::string& original_path) {
char path_sep(xe::kPathSeparator);
std::string path(xe::fix_path_separators(original_path, path_sep));
std::vector<std::string::size_type> path_breaks;
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();
bool CreateParentFolder(const std::filesystem::path& path) {
if (path.has_parent_path()) {
auto parent_path = path.parent_path();
if (!PathExists(parent_path)) {
return CreateFolder(parent_path);
}
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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,6 +10,7 @@
#ifndef XENIA_BASE_FILESYSTEM_H_
#define XENIA_BASE_FILESYSTEM_H_
#include <filesystem>
#include <iterator>
#include <memory>
#include <string>
@ -18,45 +19,48 @@
#include "xenia/base/string.h"
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 {
// Get executable path.
std::wstring GetExecutablePath();
std::filesystem::path GetExecutablePath();
// Get executable folder.
std::wstring GetExecutableFolder();
std::filesystem::path GetExecutableFolder();
// Get user folder.
std::wstring GetUserFolder();
// Canonicalizes a path, removing ..'s.
std::string CanonicalizePath(const std::string& original_path);
std::filesystem::path GetUserFolder();
// 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.
// This can be used to ensure the destination path for a new file exists before
// attempting to create it.
bool CreateParentFolder(const std::wstring& path);
bool CreateParentFolder(const std::filesystem::path& path);
// Creates a folder at the specified path.
// 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.
// 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.
bool IsFolder(const std::wstring& path);
bool IsFolder(const std::filesystem::path& 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.
// 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.
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.
// Returns true if the file was found and removed.
bool DeleteFile(const std::wstring& path);
bool DeleteFile(const std::filesystem::path& path);
struct FileAccess {
// Implies kFileReadData.
@ -89,12 +93,12 @@ class FileHandle {
public:
// Opens the file, failing if it doesn't exist.
// The desired_access bitmask denotes the permissions on the file.
static std::unique_ptr<FileHandle> OpenExisting(std::wstring path,
uint32_t desired_access);
static std::unique_ptr<FileHandle> OpenExisting(
const std::filesystem::path& path, uint32_t desired_access);
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
// offset. The total number of bytes read is returned only if the complete
@ -115,9 +119,9 @@ class FileHandle {
virtual void Flush() = 0;
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 {
@ -126,15 +130,15 @@ struct FileInfo {
kDirectory,
};
Type type;
std::wstring name;
std::wstring path;
std::filesystem::path name;
std::filesystem::path path;
size_t total_size;
uint64_t create_timestamp;
uint64_t access_timestamp;
uint64_t write_timestamp;
};
bool GetInfo(const std::wstring& path, FileInfo* out_info);
std::vector<FileInfo> ListFiles(const std::wstring& path);
bool GetInfo(const std::filesystem::path& path, FileInfo* out_info);
std::vector<FileInfo> ListFiles(const std::filesystem::path& path);
} // namespace filesystem
} // namespace xe

View File

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

View File

@ -2,18 +2,19 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#include "xenia/base/filesystem_wildcard.h"
#include "xenia/base/assert.h"
#include <algorithm>
namespace xe {
namespace filesystem {
#include "xenia/base/assert.h"
#include "xenia/base/string.h"
namespace xe::filesystem {
WildcardFlags WildcardFlags::FIRST(true, false, false);
WildcardFlags WildcardFlags::LAST(false, true, false);
@ -25,47 +26,45 @@ WildcardFlags::WildcardFlags()
WildcardFlags::WildcardFlags(bool start, bool end, bool 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)
: match(str_match), rules(flags) {
std::transform(match.begin(), match.end(), match.begin(), tolower);
}
: match_(utf8::lower_ascii(match)), rules_(flags) {}
bool WildcardRule::Check(const std::string& str_lower,
bool WildcardRule::Check(const std::string_view lower,
std::string::size_type* offset) const {
if (match.empty()) {
if (match_.empty()) {
return true;
}
if ((str_lower.size() - *offset) < match.size()) {
if ((lower.size() - *offset) < match_.size()) {
return false;
}
if (rules.ExactLength) {
*offset += match.size();
if (rules_.ExactLength) {
*offset += match_.size();
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 (rules.FromStart && result != *offset) {
if (result != std::string_view::npos) {
if (rules_.FromStart && result != *offset) {
return false;
}
if (rules.ToEnd && result != (str_lower.size() - match.size())) {
if (rules_.ToEnd && result != (lower.size() - match_.size())) {
return false;
}
*offset = (result + match.size());
*offset = (result + match_.size());
return true;
}
return false;
}
void WildcardEngine::PreparePattern(const std::string& pattern) {
rules.clear();
void WildcardEngine::PreparePattern(const std::string_view pattern) {
rules_.clear();
WildcardFlags flags(WildcardFlags::FIRST);
size_t n = 0;
@ -73,12 +72,12 @@ void WildcardEngine::PreparePattern(const std::string& pattern) {
while ((n = pattern.find_first_of("*?", last)) != pattern.npos) {
if (last != n) {
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] == '?') {
auto end = pattern.find_first_not_of('?', n + 1);
auto count = end == pattern.npos ? (pattern.size() - n) : (end - n);
rules.push_back(
rules_.push_back(
WildcardRule(pattern.substr(n, count), WildcardFlags::ANY));
last = n + count;
} else if (pattern[n] == '*') {
@ -90,20 +89,18 @@ void WildcardEngine::PreparePattern(const std::string& pattern) {
}
if (last != pattern.size()) {
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);
}
bool WildcardEngine::Match(const std::string& str) const {
std::string str_lc;
std::transform(str.begin(), str.end(), std::back_inserter(str_lc), tolower);
bool WildcardEngine::Match(const std::string_view str) const {
std::string str_lc = utf8::lower_ascii(str);
std::string::size_type offset(0);
for (const auto& rule : rules) {
for (const auto& rule : rules_) {
if (!(rule.Check(str_lc, &offset))) {
return false;
}
@ -112,5 +109,4 @@ bool WildcardEngine::Match(const std::string& str) const {
return true;
}
} // namespace filesystem
} // namespace xe
} // namespace xe::filesystem

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -34,25 +34,25 @@ class WildcardFlags {
class WildcardRule {
public:
WildcardRule(const std::string& str_match, const WildcardFlags& flags);
bool Check(const std::string& str_lower,
std::string::size_type* offset) const;
WildcardRule(const std::string_view match, const WildcardFlags& flags);
bool Check(const std::string_view lower,
std::string_view::size_type* offset) const;
private:
std::string match;
WildcardFlags rules;
std::string match_;
WildcardFlags rules_;
};
class WildcardEngine {
public:
void SetRule(const std::string& pattern);
void SetRule(const std::string_view pattern);
// Always ignoring case
bool Match(const std::string& str) const;
bool Match(const std::string_view str) const;
private:
std::vector<WildcardRule> rules;
void PreparePattern(const std::string& pattern);
std::vector<WildcardRule> rules_;
void PreparePattern(const std::string_view pattern);
};
} // namespace filesystem

View File

@ -2,37 +2,56 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include <string>
#include <io.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/string.h"
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 {
std::wstring GetExecutablePath() {
std::filesystem::path GetExecutablePath() {
wchar_t* 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() {
auto path = GetExecutablePath();
return xe::find_base_path(path);
std::filesystem::path GetExecutableFolder() {
return GetExecutablePath().parent_path();
}
std::wstring GetUserFolder() {
std::wstring result;
std::filesystem::path GetUserFolder() {
std::filesystem::path result;
PWSTR path;
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT,
nullptr, &path))) {
@ -42,22 +61,22 @@ std::wstring GetUserFolder() {
return result;
}
bool PathExists(const std::wstring& path) {
bool PathExists(const std::filesystem::path& path) {
DWORD attrib = GetFileAttributes(path.c_str());
return attrib != INVALID_FILE_ATTRIBUTES;
}
bool CreateFolder(const std::wstring& path) {
size_t pos = 0;
do {
pos = path.find_first_of(xe::kWPathSeparator, pos + 1);
CreateDirectoryW(path.substr(0, pos).c_str(), nullptr);
} while (pos != std::string::npos);
bool CreateFolder(const std::filesystem::path& path) {
std::filesystem::path create_path;
for (auto it = path.begin(); it != path.end(); ++it) {
create_path /= *it;
CreateDirectoryW(create_path.c_str(), nullptr);
}
return PathExists(path);
}
bool DeleteFolder(const std::wstring& path) {
auto double_null_path = path + std::wstring(L"\0", 1);
bool DeleteFolder(const std::filesystem::path& path) {
auto double_null_path = path.wstring() + std::wstring(L"\0", 1);
SHFILEOPSTRUCT op = {0};
op.wFunc = FO_DELETE;
op.pFrom = double_null_path.c_str();
@ -65,14 +84,13 @@ bool DeleteFolder(const std::wstring& path) {
return SHFileOperation(&op) == 0;
}
bool IsFolder(const std::wstring& path) {
bool IsFolder(const std::filesystem::path& path) {
DWORD attrib = GetFileAttributes(path.c_str());
return attrib != INVALID_FILE_ATTRIBUTES &&
(attrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
}
#undef CreateFile
bool CreateFile(const std::wstring& path) {
bool CreateFile(const std::filesystem::path& path) {
auto handle = CreateFileW(path.c_str(), 0, 0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
@ -83,9 +101,10 @@ bool CreateFile(const std::wstring& path) {
return true;
}
FILE* OpenFile(const std::wstring& path, const char* mode) {
auto fixed_path = xe::fix_path_separators(path);
return _wfopen(fixed_path.c_str(), xe::to_wstring(mode).c_str());
FILE* OpenFile(const std::filesystem::path& path, const std::string_view mode) {
// Dumb, but OK.
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) {
@ -114,14 +133,14 @@ bool TruncateStdioFile(FILE* file, uint64_t length) {
return true;
}
bool DeleteFile(const std::wstring& path) {
bool DeleteFile(const std::filesystem::path& path) {
return DeleteFileW(path.c_str()) ? true : false;
}
class Win32FileHandle : public FileHandle {
public:
Win32FileHandle(std::wstring path, HANDLE handle)
: FileHandle(std::move(path)), handle_(handle) {}
Win32FileHandle(const std::filesystem::path& path, HANDLE handle)
: FileHandle(path), handle_(handle) {}
~Win32FileHandle() override {
CloseHandle(handle_);
handle_ = nullptr;
@ -181,8 +200,8 @@ class Win32FileHandle : public FileHandle {
HANDLE handle_ = nullptr;
};
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
uint32_t desired_access) {
std::unique_ptr<FileHandle> FileHandle::OpenExisting(
const std::filesystem::path& path, uint32_t desired_access) {
DWORD open_access = 0;
if (desired_access & FileAccess::kGenericRead) {
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)
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));
WIN32_FILE_ATTRIBUTE_DATA data = {0};
if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &data)) {
@ -234,19 +253,19 @@ bool GetInfo(const std::wstring& path, FileInfo* out_info) {
out_info->total_size =
(data.nFileSizeHigh * (size_t(MAXDWORD) + 1)) + data.nFileSizeLow;
}
out_info->path = xe::find_base_path(path);
out_info->name = xe::find_name_from_path(path);
out_info->path = path.parent_path();
out_info->name = path.filename();
out_info->create_timestamp = COMBINE_TIME(data.ftCreationTime);
out_info->access_timestamp = COMBINE_TIME(data.ftLastAccessTime);
out_info->write_timestamp = COMBINE_TIME(data.ftLastWriteTime);
return true;
}
std::vector<FileInfo> ListFiles(const std::wstring& path) {
std::vector<FileInfo> ListFiles(const std::filesystem::path& path) {
std::vector<FileInfo> result;
WIN32_FIND_DATA ffd;
HANDLE handle = FindFirstFile((path + L"\\*").c_str(), &ffd);
HANDLE handle = FindFirstFileW((path / "*").c_str(), &ffd);
if (handle == INVALID_HANDLE_VALUE) {
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 *
******************************************************************************
* 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. *
******************************************************************************
*/
@ -24,8 +24,8 @@
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/ring_buffer.h"
#include "xenia/base/string.h"
#include "xenia/base/threading.h"
//#include "xenia/base/cvar.h"
// For MessageBox:
// TODO(benvanik): generic API? logging_win.cc?
@ -33,7 +33,9 @@
#include "xenia/base/platform_win.h"
#endif // XE_PLATFORM_WIN32
DEFINE_string(
#include "third_party/fmt/include/fmt/format.h"
DEFINE_path(
log_file, "",
"Logs are written to the given file (specify stdout for command line)",
"Logging");
@ -54,19 +56,19 @@ thread_local std::vector<char> log_format_buffer_(64 * 1024);
class Logger {
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()) {
// 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);
file_ = xe::filesystem::OpenFile(file_path, "wt");
} else {
if (cvars::log_file == "stdout") {
file_ = stdout;
} else {
auto file_path = xe::to_wstring(cvars::log_file);
xe::filesystem::CreateParentFolder(file_path);
file_ = xe::filesystem::OpenFile(file_path, "wt");
xe::filesystem::CreateParentFolder(cvars::log_file);
file_ = xe::filesystem::OpenFile(cvars::log_file, "wt");
}
}
@ -243,7 +245,7 @@ class Logger {
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);
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);
}
void LogLine(LogLevel log_level, const char prefix_char, const char* str,
size_t str_length) {
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) {
void logging::AppendLogLine(LogLevel log_level, const char prefix_char,
const std::string_view str) {
if (!logger_) {
return;
}
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, ...) {
va_list args;
va_start(args, fmt);
LogLineVarargs(LogLevel::Error, 'X', fmt, args);
va_end(args);
void FatalError(const std::string_view str) {
LogLine(LogLevel::Error, 'X', str);
logging::AppendLogLine(LogLevel::Error, 'X', str);
#if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) {
va_start(args, fmt);
std::vsnprintf(log_format_buffer_.data(), log_format_buffer_.capacity(),
fmt, args);
va_end(args);
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
MessageBoxW(NULL, (LPCWSTR)xe::to_utf16(str).c_str(), L"Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
}
#endif // WIN32
@ -335,27 +320,4 @@ void FatalError(const char* fmt, ...) {
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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,6 +10,7 @@
#ifndef XENIA_BASE_LOGGING_H_
#define XENIA_BASE_LOGGING_H_
#include <cstdarg>
#include <cstdint>
#include <string>
@ -34,7 +35,7 @@ enum class LogLevel {
// Initializes the logging system and any outputs requested.
// Must be called on startup.
void InitializeLogging(const std::wstring& app_name);
void InitializeLogging(const std::string_view app_name);
void ShutdownLogging();
// 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,
va_list args);
// Appends a line to the log.
void LogLine(LogLevel log_level, const char prefix_char, const char* str,
size_t str_length = std::string::npos);
void LogLine(LogLevel log_level, const char prefix_char,
const std::string& str);
void LogLine(LogLevel log_level, const char prefix_char, std::string_view 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.
void FatalError(const std::string& str);
void FatalError(const std::wstring& str);
void FatalError(const std::string_view str);
#if XE_OPTION_ENABLE_LOGGING
#define XELOGCORE(level, prefix, fmt, ...) \

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -24,10 +24,10 @@ bool has_console_attached();
// Extern defined by user code. This must be present for the application to
// launch.
struct EntryInfo {
std::wstring name;
std::string name;
std::string positional_usage;
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();

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,6 +10,7 @@
#include "xenia/base/cvar.h"
#include "xenia/base/main.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.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,
entry_info.positional_options);
std::vector<std::wstring> args;
std::vector<std::string> args;
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.

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,6 +10,7 @@
#ifndef XENIA_BASE_MAPPED_MEMORY_H_
#define XENIA_BASE_MAPPED_MEMORY_H_
#include <filesystem>
#include <memory>
#include <string>
@ -22,13 +23,14 @@ class MappedMemory {
kReadWrite,
};
static std::unique_ptr<MappedMemory> Open(const std::wstring& path, Mode mode,
size_t offset = 0,
static std::unique_ptr<MappedMemory> Open(const std::filesystem::path& path,
Mode mode, size_t offset = 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) {}
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) {}
virtual ~MappedMemory() = default;
@ -48,7 +50,7 @@ class MappedMemory {
virtual bool Remap(size_t offset, size_t length) { return false; }
protected:
std::wstring path_;
std::filesystem::path path_;
Mode mode_;
void* data_;
size_t size_;
@ -59,7 +61,7 @@ class ChunkedMappedMemoryWriter {
virtual ~ChunkedMappedMemoryWriter() = default;
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);
virtual uint8_t* Allocate(size_t length) = 0;
@ -67,13 +69,13 @@ class ChunkedMappedMemoryWriter {
virtual void FlushNew() = 0;
protected:
ChunkedMappedMemoryWriter(const std::wstring& path, size_t chunk_size,
bool low_address_space)
ChunkedMappedMemoryWriter(const std::filesystem::path& path,
size_t chunk_size, bool low_address_space)
: path_(path),
chunk_size_(chunk_size),
low_address_space_(low_address_space) {}
std::wstring path_;
std::filesystem::path path_;
size_t chunk_size_;
bool low_address_space_;
};

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -19,7 +19,7 @@ namespace xe {
class PosixMappedMemory : public MappedMemory {
public:
PosixMappedMemory(const std::wstring& path, Mode mode)
PosixMappedMemory(const std::filesystem::path& path, Mode mode)
: MappedMemory(path, mode), file_handle(nullptr) {}
~PosixMappedMemory() override {
@ -34,9 +34,9 @@ class PosixMappedMemory : public MappedMemory {
FILE* file_handle;
};
std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
Mode mode, size_t offset,
size_t length) {
std::unique_ptr<MappedMemory> MappedMemory::Open(
const std::filesystem::path& path, Mode mode, size_t offset,
size_t length) {
const char* mode_str;
int prot;
switch (mode) {
@ -53,7 +53,7 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
auto mm =
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) {
return nullptr;
}
@ -77,7 +77,8 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
}
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)
return nullptr;
}

View File

@ -2,18 +2,18 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#include "xenia/base/mapped_memory.h"
#include <memory>
#include <mutex>
#include <vector>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/logging.h"
#include "xenia/base/mapped_memory.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/platform_win.h"
@ -22,7 +22,7 @@ namespace xe {
class Win32MappedMemory : public MappedMemory {
public:
Win32MappedMemory(const std::wstring& path, Mode mode)
Win32MappedMemory(const std::filesystem::path& path, Mode mode)
: MappedMemory(path, mode) {}
~Win32MappedMemory() override {
@ -88,9 +88,9 @@ class Win32MappedMemory : public MappedMemory {
DWORD view_access_ = 0;
};
std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
Mode mode, size_t offset,
size_t length) {
std::unique_ptr<MappedMemory> MappedMemory::Open(
const std::filesystem::path& path, Mode mode, size_t offset,
size_t length) {
DWORD file_access = 0;
DWORD file_share = 0;
DWORD create_mode = 0;
@ -157,8 +157,8 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(const std::wstring& path,
class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
public:
Win32ChunkedMappedMemoryWriter(const std::wstring& path, size_t chunk_size,
bool low_address_space)
Win32ChunkedMappedMemoryWriter(const std::filesystem::path& path,
size_t chunk_size, bool low_address_space)
: ChunkedMappedMemoryWriter(path, chunk_size, low_address_space) {}
~Win32ChunkedMappedMemoryWriter() override {
@ -175,7 +175,8 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
}
}
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_)) {
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_share = FILE_SHARE_READ;
DWORD create_mode = CREATE_ALWAYS;
@ -300,7 +301,8 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
};
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;
GetSystemInfo(&system_info);
size_t aligned_chunk_size =

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -12,6 +12,7 @@
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <functional>
#include <string>
@ -97,8 +98,9 @@ void AlignedFree(T* ptr) {
typedef void* FileMappingHandle;
FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
PageAccess access, bool commit);
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
size_t length, PageAccess access,
bool commit);
void CloseFileMappingHandle(FileMappingHandle handle);
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
PageAccess access, size_t file_offset);
@ -281,8 +283,8 @@ inline std::string load_and_swap<std::string>(const void* mem) {
return value;
}
template <>
inline std::wstring load_and_swap<std::wstring>(const void* mem) {
std::wstring value;
inline std::u16string load_and_swap<std::u16string>(const void* mem) {
std::u16string value;
for (int i = 0;; ++i) {
auto c =
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;
}
template <typename T>
inline void store(const void* mem, const T& value) {
if (sizeof(T) == 1) {
constexpr inline void store(const void* mem, const T& value) {
if constexpr (sizeof(T) == 1) {
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));
} else if (sizeof(T) == 4) {
} else if constexpr (sizeof(T) == 4) {
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));
} 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);
}
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) {
xe::store_and_swap<uint8_t>(reinterpret_cast<uint8_t*>(mem) + i, value[i]);
}
}
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) {
xe::store_and_swap<uint16_t>(reinterpret_cast<uint16_t*>(mem) + 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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -61,8 +61,9 @@ bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
return false;
}
FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
PageAccess access, bool commit) {
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
size_t length, PageAccess access,
bool commit) {
int oflag;
switch (access) {
case PageAccess::kNoAccess:
@ -81,7 +82,7 @@ FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
}
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) {
ftruncate64(ret, length);
}

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -142,8 +142,9 @@ bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
return true;
}
FileMappingHandle CreateFileMappingHandle(std::wstring path, size_t length,
PageAccess access, bool commit) {
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
size_t length, PageAccess access,
bool commit) {
DWORD protect =
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect,

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -94,15 +94,11 @@ namespace xe {
#if XE_PLATFORM_WIN32
const char kPathSeparator = '\\';
const wchar_t kWPathSeparator = L'\\';
#else
const char kPathSeparator = '/';
const wchar_t kWPathSeparator = L'/';
const size_t kMaxPath = 1024; // PATH_MAX
#endif // XE_PLATFORM_WIN32
// Launches a web browser to the given URL.
void LaunchBrowser(const wchar_t* url);
const char kGuestPathSeparator = '\\';
} // namespace xe

View File

@ -2,13 +2,13 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_BASE_PLATFORM_X11_H_
#define XENIA_BASE_PLATFORM_X11_H_
#ifndef XENIA_BASE_PLATFORM_LINUX_H_
#define XENIA_BASE_PLATFORM_LINUX_H_
// NOTE: if you're including this file it means you are explicitly depending
// on Linux headers. Including this file outside of linux platform specific
@ -17,17 +17,4 @@
#include "xenia/base/platform.h"
// Xlib/Xcb is used only for GLX/Vulkan interaction, the window management
// 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_
#endif // XENIA_BASE_PLATFORM_LINUX_H_

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -91,7 +91,7 @@ class Socket {
// Asynchronously sends a string buffer.
// 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());
}
};

View File

@ -2,359 +2,29 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#include "xenia/base/string.h"
// codecvt existence check
#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 <algorithm>
#include <locale>
#define UTF_CPP_CPLUSPLUS 201703L
#include "third_party/utfcpp/source/utf8.h"
namespace utfcpp = utf8;
namespace xe {
std::string to_string(const std::wstring& source) {
#if NO_CODECVT
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::string to_utf8(const std::u16string_view source) {
return utfcpp::utf16to8(source);
}
std::wstring to_wstring(const std::string& source) {
#if NO_CODECVT
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;
std::u16string to_utf16(const std::string_view source) {
return utfcpp::utf8to16(source);
}
} // namespace xe

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,99 +10,14 @@
#ifndef XENIA_BASE_STRING_H_
#define XENIA_BASE_STRING_H_
#include <cstdarg>
#include <cstdio>
#include <string>
#include <utility>
#include <vector>
#include "xenia/base/platform.h"
#include "utf8.h"
namespace xe {
std::string to_string(const std::wstring& source);
std::wstring to_wstring(const std::string& 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);
}
std::string to_utf8(const std::u16string_view source);
std::u16string to_utf16(const std::string_view source);
} // namespace xe

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -52,17 +52,10 @@ void StringBuffer::Append(const char* 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());
}
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) {
int length = vsnprintf(nullptr, 0, format, args);
Grow(length + 1);
@ -78,15 +71,15 @@ void StringBuffer::AppendBytes(const uint8_t* buffer, size_t length) {
buffer_[buffer_offset_] = 0;
}
const char* StringBuffer::GetString() const { return buffer_; }
std::string StringBuffer::to_string() {
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::memcpy(bytes.data(), buffer_, buffer_offset_);
return bytes;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -14,6 +14,8 @@
#include <string>
#include <vector>
#include "third_party/fmt/include/fmt/format.h"
namespace xe {
class StringBuffer {
@ -21,21 +23,27 @@ class StringBuffer {
explicit StringBuffer(size_t initial_capacity = 0);
~StringBuffer();
char* buffer() const { return buffer_; }
size_t length() const { return buffer_offset_; }
void Reset();
void Append(char c);
void Append(const char* value);
void Append(const std::string& value);
void AppendFormat(const char* format, ...);
void Append(const std::string_view value);
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 AppendBytes(const uint8_t* buffer, size_t length);
const char* GetString() const;
std::string to_string();
char* ToString();
std::vector<uint8_t> ToBytes() const;
std::string_view to_string_view() const;
std::vector<uint8_t> to_bytes() const;
private:
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 *
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,214 +10,290 @@
#ifndef XENIA_BASE_STRING_UTIL_H_
#define XENIA_BASE_STRING_UTIL_H_
#include <cinttypes>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <charconv>
#include <string>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/platform.h"
#include "xenia/base/string.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 string_util {
// TODO(gibbed): Figure out why clang doesn't line forward declarations of
// inline functions.
inline std::string to_hex_string(uint32_t value) {
return fmt::format("{:08X}", value);
}
std::string to_hex_string(uint32_t value);
std::string to_hex_string(uint64_t value);
inline std::string to_hex_string(uint64_t value) {
return fmt::format("{:016X}", value);
}
inline std::string to_hex_string(float value) {
union {
uint32_t ui;
float flt;
} v;
v.flt = value;
return to_hex_string(v.ui);
static_assert(sizeof(uint32_t) == sizeof(value));
uint32_t pun;
std::memcpy(&pun, &value, sizeof(value));
return to_hex_string(pun);
}
inline std::string to_hex_string(double value) {
union {
uint64_t ui;
double dbl;
} v;
v.dbl = value;
return to_hex_string(v.ui);
static_assert(sizeof(uint64_t) == sizeof(value));
uint64_t pun;
std::memcpy(&pun, &value, sizeof(value));
return to_hex_string(pun);
}
std::string to_hex_string(const vec128_t& value);
#if XE_ARCH_AMD64
// 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
inline std::string to_hex_string(const vec128_t& value) {
return fmt::format("[{:08X} {:08X} {:08X} {:08X} {:08X}]", value.u32[0],
value.u32[1], value.u32[2], value.u32[3]);
}
template <typename T>
inline T from_string(const char* value, bool force_hex = false) {
// Missing implementation for converting type T to string
inline T from_string(const std::string_view value, bool force_hex = false) {
// Missing implementation for converting type T from string
throw;
}
template <>
inline bool from_string<bool>(const char* value, bool force_hex) {
return std::strcmp(value, "true") == 0 || value[0] == '1';
}
namespace internal {
template <>
inline int32_t from_string<int32_t>(const char* value, bool force_hex) {
if (force_hex || std::strchr(value, 'h') != nullptr) {
return std::strtol(value, nullptr, 16);
template <typename T, typename V = std::make_signed_t<T>>
inline T make_negative(T value) {
if constexpr (std::is_unsigned_v<T>) {
value = static_cast<T>(-static_cast<V>(value));
} else {
return std::strtol(value, nullptr, 0);
value = -value;
}
return value;
}
template <>
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
// integral_from_string
template <typename T>
inline T from_string(const std::string& value, bool force_hex = false) {
return from_string<T>(value.c_str(), force_hex);
inline T ifs(const std::string_view value, bool 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

View File

@ -2,17 +2,24 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#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 {
void LaunchBrowser(const wchar_t* url) {
ShellExecuteW(NULL, L"open", url, NULL, NULL, SW_SHOWNORMAL);
}
void LaunchWebBrowser(const std::string& url);
void LaunchFileExplorer(const std::filesystem::path& path);
} // namespace xe
#endif // XENIA_BASE_SYSTEM_H_

View File

@ -2,21 +2,27 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#include "xenia/base/platform_linux.h"
#include <stdlib.h>
#include <string>
#include "xenia/base/assert.h"
#include "xenia/base/platform_linux.h"
#include "xenia/base/string.h"
#include "xenia/base/system.h"
namespace xe {
void LaunchBrowser(const wchar_t* url) {
auto cmd = std::string("xdg-open " + xe::to_string(url));
void LaunchWebBrowser(const std::string& url) {
auto cmd = "xdg-open " + url;
system(cmd.c_str());
}
void LaunchFileExplorer(const std::filesystem::path& path) { assert_always(); }
} // 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 *
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,6 +10,7 @@
#include <cstddef>
#include <string>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/math.h"
#include "xenia/base/platform.h"
#include "xenia/base/vec128.h"
@ -17,10 +18,7 @@
namespace xe {
std::string to_string(const vec128_t& value) {
char buffer[128];
std::snprintf(buffer, sizeof(buffer), "(%g, %g, %g, %g)", value.x, value.y,
value.z, value.w);
return std::string(buffer);
return fmt::format("({}, {}, {}, {})", value.x, value.y, value.z, value.w);
}
} // 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 "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/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/string.h"
std::shared_ptr<cpptoml::table> ParseFile(const std::wstring& filename) {
#if XE_PLATFORM_WIN32
std::shared_ptr<cpptoml::table> ParseFile(
const std::filesystem::path& filename) {
std::ifstream file(filename);
#else
std::ifstream file(xe::to_string(filename));
#endif
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");
}
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.");
namespace config {
std::wstring config_name = L"xenia.config.toml";
std::wstring config_folder;
std::wstring config_path;
std::wstring game_config_suffix = L".config.toml";
std::string config_name = "xenia.config.toml";
std::filesystem::path config_folder;
std::filesystem::path config_path;
std::string game_config_suffix = ".config.toml";
bool sortCvar(cvar::IConfigVar* a, cvar::IConfigVar* b) {
if (a->category() < b->category()) return true;
@ -33,17 +41,18 @@ bool sortCvar(cvar::IConfigVar* a, cvar::IConfigVar* b) {
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 {
return ParseFile(config_path);
} catch (cpptoml::parse_exception e) {
xe::FatalError(L"Failed to parse config file '%s':\n\n%s",
config_path.c_str(), xe::to_wstring(e.what()).c_str());
xe::FatalError(fmt::format("Failed to parse config file '{}':\n\n{}",
xe::path_to_utf8(config_path), e.what()));
return nullptr;
}
}
void ReadConfig(const std::wstring& file_path) {
void ReadConfig(const std::filesystem::path& file_path) {
const auto config = ParseConfig(file_path);
for (auto& it : *cvar::ConfigVars) {
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));
}
}
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);
for (auto& it : *cvar::ConfigVars) {
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));
}
}
XELOGI("Loaded game config: %S", file_path.c_str());
XELOGI("Loaded game config: %s", xe::path_to_utf8(file_path).c_str());
}
void SaveConfig() {
@ -84,31 +93,29 @@ void SaveConfig() {
output << std::endl;
}
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();
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(' ')
<< xe::format_string("%s = %s", config_var->name().c_str(),
config_var->config_value().c_str());
<< fmt::format("{} = {}", config_var->name(),
config_var->config_value());
} else {
auto lines = xe::split_string(value, "\n");
auto lines = xe::utf8::split(value, "\n");
auto first_it = lines.cbegin();
output << xe::format_string("%s = %s\n", config_var->name().c_str(),
(*first_it).c_str());
output << fmt::format("{} = {}\n", config_var->name(), *first_it);
auto last_it = std::prev(lines.cend());
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(' ')
<< (*last_it).c_str();
output << std::left << std::setw(40) << std::setfill(' ') << *last_it;
}
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)) {
std::ifstream existingConfigStream(xe::to_string(config_path).c_str());
std::ifstream existingConfigStream(config_path);
const std::string existingConfig(
(std::istreambuf_iterator<char>(existingConfigStream)),
std::istreambuf_iterator<char>());
@ -119,20 +126,16 @@ void SaveConfig() {
// save the config file
xe::filesystem::CreateParentFolder(config_path);
std::ofstream file;
#if XE_PLATFORM_WIN32
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.close();
}
void SetupConfig(const std::wstring& config_folder) {
void SetupConfig(const std::filesystem::path& config_folder) {
config::config_folder = config_folder;
// check if the user specified a specific config to load
if (!cvars::config.empty()) {
config_path = xe::to_wstring(cvars::config);
config_path = xe::to_path(cvars::config);
if (xe::filesystem::PathExists(config_path)) {
ReadConfig(config_path);
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,
// let's also load the default config
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)) {
ReadConfig(config_path);
}
@ -151,9 +154,10 @@ void SetupConfig(const std::wstring& config_folder) {
}
}
void LoadGameConfig(const std::wstring& title_id) {
const auto game_config_path = xe::join_paths(
xe::join_paths(config_folder, L"config"), title_id + game_config_suffix);
void LoadGameConfig(const std::string_view title_id) {
const auto game_config_folder = config_folder / "config";
const auto game_config_path =
game_config_folder / (std::string(title_id) + game_config_suffix);
if (xe::filesystem::PathExists(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_
#define XENIA_CONFIG_H_
#include <string>
#include <filesystem>
namespace config {
void SetupConfig(const std::wstring& config_folder);
void LoadGameConfig(const std::wstring& title_id);
void SetupConfig(const std::filesystem::path& config_folder);
void LoadGameConfig(const std::string_view title_id);
} // namespace config
#endif // XENIA_CONFIG_H_

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -23,7 +23,7 @@ class CodeCache {
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 total_size() const = 0;

View File

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

View File

@ -86,7 +86,7 @@ bool X64Assembler::Assemble(GuestFunction* function, HIRBuilder* builder,
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
DumpMachineCode(machine_code, code_size, function->source_map(),
&string_buffer_);
debug_info->set_machine_code_disasm(string_buffer_.ToString());
debug_info->set_machine_code_disasm(strdup(string_buffer_.buffer()));
string_buffer_.Reset();
}
@ -126,7 +126,7 @@ void X64Assembler::DumpMachineCode(
if (code_offset >= next_code_offset &&
source_map_index < source_map.size()) {
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;
next_code_offset = source_map_index < source_map.size()
? source_map[source_map_index].code_offset
@ -135,7 +135,7 @@ void X64Assembler::DumpMachineCode(
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);
}
}

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -17,6 +17,7 @@
#pragma comment(lib, "../third_party/vtune/lib64/jitprofiling.lib")
#endif
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/clock.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.
file_name_ = std::wstring(L"Local\\xenia_code_cache_") +
std::to_wstring(Clock::QueryHostTickCount());
file_name_ =
fmt::format("Local\\xenia_code_cache_{}", Clock::QueryHostTickCount());
mapping_ = xe::memory::CreateFileMappingHandle(
file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite,
false);

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -45,7 +45,7 @@ class X64CodeCache : public CodeCache {
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 total_size() const override { return kGeneratedCodeSize; }
@ -99,7 +99,7 @@ class X64CodeCache : public CodeCache {
const EmitFunctionInfo& func_info, void* code_address,
UnwindReservation unwind_reservation) {}
std::wstring file_name_;
std::filesystem::path file_name_;
xe::memory::FileMappingHandle mapping_ = nullptr;
// NOTE: the global critical region must be held when manipulating the offsets

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -10,9 +10,11 @@
#include "xenia/cpu/backend/x64/x64_emitter.h"
#include <stddef.h>
#include <climits>
#include <cstring>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/assert.h"
#include "xenia/base/atomic.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) {
auto function = reinterpret_cast<Function*>(function_ptr);
if (!cvars::ignore_undefined_externs) {
xe::FatalError("undefined extern call to %.8X %s", function->address(),
function->name().c_str());
xe::FatalError(fmt::format("undefined extern call to {:08X} {}",
function->address(), function->name().c_str()));
} else {
XELOGE("undefined extern call to %.8X %s", function->address(),
function->name().c_str());

View File

@ -2,13 +2,14 @@
******************************************************************************
* 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. *
******************************************************************************
*/
#include "xenia/cpu/compiler/passes/finalization_pass.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/profiling.h"
#include "xenia/cpu/backend/backend.h"
#include "xenia/cpu/compiler/compiler.h"
@ -43,9 +44,11 @@ bool FinalizationPass::Run(HIRBuilder* builder) {
auto label = block->label_head;
while (label) {
if (!label->name) {
const size_t label_len = 6 + 4 + 1;
char* name = reinterpret_cast<char*>(arena->Alloc(label_len));
snprintf(name, label_len, "_label%d", label->id);
const size_t label_len = 6 + 4;
char* name = reinterpret_cast<char*>(arena->Alloc(label_len + 1));
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 = label->next;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -58,7 +58,7 @@ bool ElfModule::is_executable() const {
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) {
name_ = name;
path_ = path;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -34,7 +34,7 @@ class ElfModule : public xe::cpu::Module {
bool is_executable() const override;
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);
bool Unload();

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -11,20 +11,15 @@
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/base/string.h"
namespace xe {
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)
: exports_by_ordinal_(exports_by_ordinal) {
auto dot_pos = std::strrchr(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);
}
module_name_ = utf8::find_base_name_from_guest_path(module_name);
exports_by_name_.reserve(exports_by_ordinal_->size());
for (size_t i = 0; i < exports_by_ordinal_->size(); ++i) {
@ -43,7 +38,8 @@ ExportResolver::ExportResolver() = default;
ExportResolver::~ExportResolver() = default;
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);
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* ExportResolver::GetExportByOrdinal(const char* module_name,
Export* ExportResolver::GetExportByOrdinal(const std::string_view module_name,
uint16_t ordinal) {
for (const auto& table : tables_) {
if (std::strncmp(module_name, table.module_name(),
std::strlen(table.module_name())) == 0) {
if (xe::utf8::starts_with_case(module_name, table.module_name())) {
if (ordinal > table.exports_by_ordinal().size()) {
return nullptr;
}
@ -72,7 +67,7 @@ Export* ExportResolver::GetExportByOrdinal(const char* module_name,
return nullptr;
}
void ExportResolver::SetVariableMapping(const char* module_name,
void ExportResolver::SetVariableMapping(const std::string_view module_name,
uint16_t ordinal, uint32_t value) {
auto export_entry = GetExportByOrdinal(module_name, ordinal);
assert_not_null(export_entry);
@ -80,7 +75,7 @@ void ExportResolver::SetVariableMapping(const char* module_name,
export_entry->variable_ptr = value;
}
void ExportResolver::SetFunctionMapping(const char* module_name,
void ExportResolver::SetFunctionMapping(const std::string_view module_name,
uint16_t ordinal,
xe_kernel_export_shim_fn shim) {
auto export_entry = GetExportByOrdinal(module_name, ordinal);
@ -89,7 +84,7 @@ void ExportResolver::SetFunctionMapping(const char* module_name,
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,
ExportTrampoline trampoline) {
auto export_entry = GetExportByOrdinal(module_name, ordinal);

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -117,9 +117,10 @@ class ExportResolver {
public:
class Table {
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 {
return *exports_by_ordinal_;
}
@ -128,7 +129,7 @@ class ExportResolver {
}
private:
char module_name_[32] = {0};
std::string module_name_;
const std::vector<Export*>* exports_by_ordinal_ = nullptr;
std::vector<Export*> exports_by_name_;
};
@ -136,20 +137,21 @@ class 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<Table>& tables() const { return tables_; }
const std::vector<Export*>& all_exports_by_name() const {
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);
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);
void SetFunctionMapping(const char* module_name, uint16_t ordinal,
void SetFunctionMapping(const std::string_view module_name, uint16_t ordinal,
ExportTrampoline trampoline);
private:

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -110,25 +110,25 @@ void HIRBuilder::DumpValue(StringBuffer* str, Value* value) {
if (value->IsConstant()) {
switch (value->type) {
case INT8_TYPE:
str->AppendFormat("%X", value->constant.i8);
str->AppendFormat("{:X}", value->constant.i8);
break;
case INT16_TYPE:
str->AppendFormat("%X", value->constant.i16);
str->AppendFormat("{:X}", value->constant.i16);
break;
case INT32_TYPE:
str->AppendFormat("%X", value->constant.i32);
str->AppendFormat("{:X}", value->constant.i32);
break;
case INT64_TYPE:
str->AppendFormat("%" PRIX64, value->constant.i64);
str->AppendFormat("{:X}", value->constant.i64);
break;
case FLOAT32_TYPE:
str->AppendFormat("%F", value->constant.f32);
str->AppendFormat("{:F}", value->constant.f32);
break;
case FLOAT64_TYPE:
str->AppendFormat("%F", value->constant.f64);
str->AppendFormat("{:F}", value->constant.f64);
break;
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.w);
break;
@ -140,10 +140,10 @@ void HIRBuilder::DumpValue(StringBuffer* str, Value* value) {
static const char* type_names[] = {
"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) {
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) {
str->Append(op->label->name);
} else {
str->AppendFormat("label%d", op->label->id);
str->AppendFormat("label{}", op->label->id);
}
break;
case OPCODE_SIG_TYPE_O:
str->AppendFormat("+%lld", op->offset);
str->AppendFormat("+{}", op->offset);
break;
case OPCODE_SIG_TYPE_S:
if (true) {
@ -176,7 +176,7 @@ void HIRBuilder::DumpOp(StringBuffer* str, OpcodeSignatureType sig_type,
void HIRBuilder::Dump(StringBuffer* str) {
if (attributes_) {
str->AppendFormat("; attributes = %.8X\n", attributes_);
str->AppendFormat("; attributes = {:08X}\n", attributes_);
}
for (auto it = locals_.begin(); it != locals_.end(); ++it) {
@ -192,16 +192,16 @@ void HIRBuilder::Dump(StringBuffer* str) {
if (block == block_head_) {
str->Append("<entry>:\n");
} else if (!block->label_head) {
str->AppendFormat("<block%d>:\n", block_ordinal);
str->AppendFormat("<block{}>:\n", block_ordinal);
}
block_ordinal++;
Label* label = block->label_head;
while (label) {
if (label->name) {
str->AppendFormat("%s:\n", label->name);
str->AppendFormat("{}:\n", label->name);
} else {
str->AppendFormat("label%d:\n", label->id);
str->AppendFormat("label{}:\n", label->id);
}
label = label->next;
}
@ -210,13 +210,13 @@ void HIRBuilder::Dump(StringBuffer* str) {
while (incoming_edge) {
auto src_label = incoming_edge->src->label_head;
if (src_label && src_label->name) {
str->AppendFormat(" ; in: %s", src_label->name);
str->AppendFormat(" ; in: {}", src_label->name);
} else if (src_label) {
str->AppendFormat(" ; in: label%d", src_label->id);
str->AppendFormat(" ; in: label{}", src_label->id);
} 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::UNCONDITIONAL) ? 1 : 0);
incoming_edge = incoming_edge->incoming_next;
@ -225,13 +225,13 @@ void HIRBuilder::Dump(StringBuffer* str) {
while (outgoing_edge) {
auto dest_label = outgoing_edge->dest->label_head;
if (dest_label && dest_label->name) {
str->AppendFormat(" ; out: %s", dest_label->name);
str->AppendFormat(" ; out: {}", dest_label->name);
} else if (dest_label) {
str->AppendFormat(" ; out: label%d", dest_label->id);
str->AppendFormat(" ; out: label{}", dest_label->id);
} 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::UNCONDITIONAL) ? 1 : 0);
outgoing_edge = outgoing_edge->outgoing_next;
@ -244,7 +244,7 @@ void HIRBuilder::Dump(StringBuffer* str) {
continue;
}
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;
continue;
}
@ -260,7 +260,7 @@ void HIRBuilder::Dump(StringBuffer* str) {
str->Append(" = ");
}
if (i->flags) {
str->AppendFormat("%s.%d", info->name, i->flags);
str->AppendFormat("{}.{}", info->name, i->flags);
} else {
str->Append(info->name);
}
@ -734,13 +734,14 @@ Value* HIRBuilder::CloneValue(Value* source) {
return value;
}
void HIRBuilder::Comment(const char* value) {
size_t length = std::strlen(value);
if (!length) {
void HIRBuilder::Comment(std::string_view value) {
if (value.empty()) {
return;
}
void* p = arena_->Alloc(length + 1);
std::memcpy(p, value, length + 1);
auto size = value.size();
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);
i->src1.offset = (uint64_t)p;
i->src2.value = i->src3.value = NULL;
@ -750,22 +751,16 @@ void HIRBuilder::Comment(const StringBuffer& value) {
if (!value.length()) {
return;
}
void* p = arena_->Alloc(value.length() + 1);
std::memcpy(p, value.GetString(), value.length() + 1);
auto size = value.length();
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);
i->src1.offset = (uint64_t)p;
i->src2.value = i->src3.value = NULL;
}
void HIRBuilder::CommentFormat(const char* format, ...) {
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);
void HIRBuilder::CommentBuffer(const char* p) {
Instr* i = AppendInstr(OPCODE_COMMENT_info, 0);
i->src1.offset = (uint64_t)p;
i->src2.value = i->src3.value = NULL;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -12,6 +12,7 @@
#include <vector>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/arena.h"
#include "xenia/base/string_buffer.h"
#include "xenia/cpu/hir/block.h"
@ -68,9 +69,19 @@ class HIRBuilder {
// static allocations:
// Value* AllocStatic(size_t length);
void Comment(const char* value);
void Comment(const std::string_view 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();
@ -261,6 +272,7 @@ class HIRBuilder {
void EndBlock();
bool IsUnconditionalJump(Instr* instr);
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* VectorCompareXX(const OpcodeInfo& opcode, Value* value1, Value* value2,
TypeName part_type);

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -143,13 +143,12 @@ void PPCContext::SetRegFromString(const char* name, const char* value) {
}
bool PPCContext::CompareRegWithString(const char* name, const char* value,
char* out_value,
size_t out_value_size) const {
std::string& result) const {
int n;
if (sscanf(name, "r%d", &n) == 1) {
uint64_t expected = string_util::from_string<uint64_t>(value);
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 true;
@ -157,23 +156,17 @@ bool PPCContext::CompareRegWithString(const char* name, const char* value,
if (std::strstr(value, "0x")) {
// Special case: Treat float as integer.
uint64_t expected = string_util::from_string<uint64_t>(value, true);
union {
double f;
uint64_t u;
} f2u;
f2u.f = this->f[n];
if (f2u.u != expected) {
std::snprintf(out_value, out_value_size, "%016" PRIX64, f2u.u);
uint64_t pun;
std::memcpy(&pun, &this->f[n], sizeof(pun));
if (pun != expected) {
result = fmt::format("{:016X}", pun);
return false;
}
} else {
double expected = string_util::from_string<double>(value);
// TODO(benvanik): epsilon
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;
}
}
@ -181,9 +174,9 @@ bool PPCContext::CompareRegWithString(const char* name, const char* value,
} else if (sscanf(name, "v%d", &n) == 1) {
vec128_t expected = string_util::from_string<vec128_t>(value);
if (this->v[n] != expected) {
std::snprintf(out_value, out_value_size, "[%.8X, %.8X, %.8X, %.8X]",
this->v[n].i32[0], this->v[n].i32[1], this->v[n].i32[2],
this->v[n].i32[3]);
result =
fmt::format("[{:08X}, {:08X}, {:08X}, {:08X}]", this->v[n].i32[0],
this->v[n].i32[1], this->v[n].i32[2], this->v[n].i32[3]);
return false;
}
return true;
@ -191,7 +184,7 @@ bool PPCContext::CompareRegWithString(const char* name, const char* value,
uint64_t actual = this->cr();
uint64_t expected = string_util::from_string<uint64_t>(value);
if (actual != expected) {
std::snprintf(out_value, out_value_size, "%016" PRIX64, actual);
result = fmt::format("{:016X}", actual);
return false;
}
return true;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -432,7 +432,7 @@ typedef struct PPCContext_s {
void SetRegFromString(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;
#pragma pack(pop)
static_assert(sizeof(PPCContext) % 64 == 0, "64b padded");

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -12,6 +12,8 @@
#include <stddef.h>
#include <cstring>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/byte_order.h"
#include "xenia/base/logging.h"
#include "xenia/base/memory.h"
@ -48,10 +50,10 @@ void DumpAllOpcodeCounts() {
auto& disasm_info = GetOpcodeDisasmInfo(opcode);
auto translation_count = opcode_translation_counts[i];
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);
}
@ -83,7 +85,7 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
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_->name().c_str());
}
@ -127,7 +129,7 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
AnnotateLabel(address, label);
}
comment_buffer_.Reset();
comment_buffer_.AppendFormat("%.8X %.8X ", address, code);
comment_buffer_.AppendFormat("{:08X} {:08X} ", address, code);
DisasmPPC(address, code, &comment_buffer_);
Comment(comment_buffer_);
first_instr = last_instr();
@ -229,7 +231,8 @@ void PPCHIRBuilder::MaybeBreakOnInstruction(uint32_t address) {
void PPCHIRBuilder::AnnotateLabel(uint32_t address, Label* label) {
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));
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.AA()) str->Append('a');
PadStringBuffer(str, str_start, kNamePad);
str->AppendFormat("%d", bo);
str->AppendFormat("{}", bo);
str->Append(", ");
str->AppendFormat("%d", bi);
str->AppendFormat("{}", bi);
} else {
if (d.B.LK()) str->Append('l');
if (d.B.AA()) str->Append('a');
@ -162,11 +162,11 @@ void PrintDisasm_bcx(const PPCDecodeData& d, StringBuffer* str) {
}
PadStringBuffer(str, str_start, kNamePad);
str->AppendFormat("crf%d", bi / 4);
str->AppendFormat("crf{}", bi / 4);
}
str->Append(", ");
str->AppendFormat("0x%X", addr);
str->AppendFormat("0x{:X}", addr);
}
} // 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;
disasm_info.disasm(d, str);
} else {
str->AppendFormat("%-8s", disasm_info.name);
str->AppendFormat("{:<8}", disasm_info.name);
}
return true;
}

View File

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

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -87,7 +87,7 @@ class TestSuite {
return result;
}
TestCase* FindTestCase(const std::string& name) {
TestCase* FindTestCase(const std::string_view name) {
for (auto& test_case : test_cases) {
if (test_case.name == name) {
return &test_case;

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -42,8 +42,8 @@
DEFINE_bool(debug, DEFAULT_DEBUG_FLAG,
"Allow debugging and retain debug information.", "General");
DEFINE_string(trace_function_data_path, "", "File to write trace data to.",
"CPU");
DEFINE_path(trace_function_data_path, "", "File to write trace data to.",
"CPU");
DEFINE_bool(break_on_start, false, "Break into the debugger on startup.",
"CPU");
@ -140,7 +140,7 @@ bool Processor::Setup(std::unique_ptr<backend::Backend> backend) {
}
// 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()) {
functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
functions_trace_path_, 32 * 1024 * 1024, true);
@ -167,7 +167,7 @@ bool Processor::AddModule(std::unique_ptr<Module> module) {
return true;
}
Module* Processor::GetModule(const char* name) {
Module* Processor::GetModule(const std::string_view name) {
auto global_lock = global_critical_region_.Acquire();
for (const auto& module : modules_) {
if (module->name() == name) {
@ -186,7 +186,7 @@ std::vector<Module*> Processor::GetModules() {
return clone;
}
Function* Processor::DefineBuiltin(const std::string& name,
Function* Processor::DefineBuiltin(const std::string_view name,
BuiltinFunction::Handler handler, void* arg0,
void* arg1) {
uint32_t address = next_builtin_address_;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -99,12 +99,11 @@ class Processor {
}
bool AddModule(std::unique_ptr<Module> module);
Module* GetModule(const char* name);
Module* GetModule(const std::string& name) { return GetModule(name.c_str()); }
Module* GetModule(const std::string_view name);
std::vector<Module*> GetModules();
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,
void* arg1);
@ -245,7 +244,7 @@ class Processor {
// Which debug features are enabled in generated code.
uint32_t debug_info_flags_ = 0;
// 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<ppc::PPCFrontend> frontend_;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -23,9 +23,9 @@ RawModule::RawModule(Processor* processor)
RawModule::~RawModule() {}
bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) {
auto fixed_path = xe::fix_path_separators(path);
FILE* file = xe::filesystem::OpenFile(fixed_path, "rb");
bool RawModule::LoadFile(uint32_t base_address,
const std::filesystem::path& path) {
FILE* file = xe::filesystem::OpenFile(path, "rb");
fseek(file, 0, SEEK_END);
uint32_t file_length = static_cast<uint32_t>(ftell(file));
fseek(file, 0, SEEK_SET);
@ -48,12 +48,7 @@ bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) {
fclose(file);
// Setup debug info.
auto last_slash = fixed_path.find_last_of(xe::kPathSeparator);
if (last_slash != std::string::npos) {
name_ = xe::to_string(fixed_path.substr(last_slash + 1));
} else {
name_ = xe::to_string(fixed_path);
}
name_ = xe::path_to_utf8(path.filename());
// TODO(benvanik): debug info
low_address_ = base_address;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -22,7 +22,7 @@ class RawModule : public Module {
explicit RawModule(Processor* processor);
~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
// in it.
@ -30,7 +30,7 @@ class RawModule : public Module {
const std::string& name() const override { return name_; }
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; }
bool ContainsAddress(uint32_t address) override;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -45,7 +45,7 @@ class Symbol {
uint32_t address() const { return address_; }
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:
Type type_ = Type::kVariable;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -24,7 +24,7 @@ using xe::cpu::compiler::Compiler;
using xe::cpu::hir::HIRBuilder;
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(hir::HIRBuilder&)> generate)
: Module(processor),

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -24,7 +24,7 @@ namespace cpu {
class TestModule : public Module {
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(hir::HIRBuilder&)> generate);
~TestModule() override;

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -11,6 +11,8 @@
#include <algorithm>
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/base/byte_order.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
@ -156,7 +158,7 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
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_);
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]);
uint16_t ordinal = ordinal_table[i];
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!
return addr;
}
@ -865,7 +867,7 @@ int XexModule::ReadPEHeaders() {
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) {
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;
// Setup debug info.
name_ = std::string(name);
path_ = std::string(path);
name_ = name;
path_ = path;
uint8_t* data = memory()->TranslateVirtual(base_address_);
@ -1027,7 +1029,8 @@ bool XexModule::LoadContinue() {
assert_true(library_name_index <
opt_import_libraries->string_table.count);
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;
}
}
@ -1087,7 +1090,7 @@ bool XexModule::Unload() {
return true;
}
bool XexModule::SetupLibraryImports(const char* name,
bool XexModule::SetupLibraryImports(const std::string_view name,
const xex2_import_library* library) {
ExportResolver* kernel_resolver = nullptr;
if (kernel_state_->IsKernelModule(name)) {
@ -1096,14 +1099,10 @@ bool XexModule::SetupLibraryImports(const char* name,
auto user_module = kernel_state_->GetModule(name);
std::string libbasename = name;
auto dot = libbasename.find_last_of('.');
if (dot != libbasename.npos) {
libbasename = libbasename.substr(0, dot);
}
auto base_name = utf8::find_base_name_from_guest_path(name);
ImportLibrary library_info;
library_info.name = libbasename;
library_info.name = base_name;
library_info.id = library->id;
library_info.version.value = library->version.value;
library_info.min_version.value = library->version_min.value;
@ -1135,7 +1134,7 @@ bool XexModule::SetupLibraryImports(const char* name,
XELOGW(
"WARNING: an import variable was not resolved! (library: %s, import "
"lib: %s, ordinal: %.3X)",
name_.c_str(), name, ordinal);
name_.c_str(), name.c_str(), ordinal);
}
StringBuffer import_name;
@ -1147,11 +1146,11 @@ bool XexModule::SetupLibraryImports(const char* name,
import_info.value_address = record_addr;
library_info.imports.push_back(import_info);
import_name.AppendFormat("__imp__");
import_name.Append("__imp__");
if (kernel_export) {
import_name.AppendFormat("%s", kernel_export->name);
import_name.Append(kernel_export->name);
} else {
import_name.AppendFormat("%s_%.3X", libbasename.c_str(), ordinal);
import_name.AppendFormat("{}_{:03X}", base_name, ordinal);
}
if (kernel_export) {
@ -1180,7 +1179,7 @@ bool XexModule::SetupLibraryImports(const char* name,
// Setup a variable and define it.
Symbol* 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);
DefineVariable(var_info);
var_info->set_status(Symbol::Status::kDefined);
@ -1194,15 +1193,15 @@ bool XexModule::SetupLibraryImports(const char* name,
}
if (kernel_export) {
import_name.AppendFormat("%s", kernel_export->name);
import_name.Append(kernel_export->name);
} else {
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal);
import_name.AppendFormat("__{}_{:03X}", base_name, ordinal);
}
Function* function;
DeclareFunction(record_addr, &function);
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) {
// Rewrite PPC code to set r11 to the target address
@ -1253,7 +1252,7 @@ bool XexModule::SetupLibraryImports(const char* name,
}
} else {
XELOGW("WARNING: Imported kernel function %s is unimplemented!",
import_name.GetString());
import_name.buffer());
}
static_cast<GuestFunction*>(function)->SetupExtern(handler,
kernel_export);
@ -1488,11 +1487,12 @@ bool XexModule::FindSaveRest() {
if (gplr_start) {
uint32_t address = gplr_start;
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;
DeclareFunction(address, &function);
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 flags fn->flags |= FunctionSymbol::kFlagSaveGprLr;
function->set_behavior(Function::Behavior::kProlog);
@ -1501,11 +1501,12 @@ bool XexModule::FindSaveRest() {
}
address = gplr_start + 20 * 4;
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;
DeclareFunction(address, &function);
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 flags fn->flags |= FunctionSymbol::kFlagRestGprLr;
function->set_behavior(Function::Behavior::kEpilogReturn);
@ -1516,11 +1517,12 @@ bool XexModule::FindSaveRest() {
if (fpr_start) {
uint32_t address = fpr_start;
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;
DeclareFunction(address, &function);
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 flags fn->flags |= FunctionSymbol::kFlagSaveFpr;
function->set_behavior(Function::Behavior::kProlog);
@ -1529,11 +1531,12 @@ bool XexModule::FindSaveRest() {
}
address = fpr_start + (18 * 4) + (1 * 4);
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;
DeclareFunction(address, &function);
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 flags fn->flags |= FunctionSymbol::kFlagRestFpr;
function->set_behavior(Function::Behavior::kEpilog);
@ -1549,10 +1552,11 @@ bool XexModule::FindSaveRest() {
// 64-127 rest
uint32_t address = vmx_start;
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;
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 flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
function->set_behavior(Function::Behavior::kProlog);
@ -1561,10 +1565,11 @@ bool XexModule::FindSaveRest() {
}
address += 4;
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;
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 flags fn->flags |= FunctionSymbol::kFlagSaveVmx;
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);
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;
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 flags fn->flags |= FunctionSymbol::kFlagRestVmx;
function->set_behavior(Function::Behavior::kEpilog);
@ -1585,10 +1591,11 @@ bool XexModule::FindSaveRest() {
}
address += 4;
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;
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 flags fn->flags |= FunctionSymbol::kFlagRestVmx;
function->set_behavior(Function::Behavior::kEpilog);

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -132,10 +132,10 @@ class XexModule : public xe::cpu::Module {
const PESection* GetPESection(const char* name);
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);
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);
bool LoadContinue();
bool Unload();
@ -177,7 +177,7 @@ class XexModule : public xe::cpu::Module {
int ReadPEHeaders();
bool SetupLibraryImports(const char* name,
bool SetupLibraryImports(const std::string_view name,
const xex2_import_library* library);
bool FindSaveRest();

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -12,6 +12,7 @@
#include <cinttypes>
#include "config.h"
#include "third_party/fmt/include/fmt/format.h"
#include "xenia/apu/audio_system.h"
#include "xenia/base/assert.h"
#include "xenia/base/byte_stream.h"
@ -56,9 +57,9 @@ DEFINE_string(
namespace xe {
Emulator::Emulator(const std::wstring& command_line,
const std::wstring& storage_root,
const std::wstring& content_root)
Emulator::Emulator(const std::filesystem::path& command_line,
const std::filesystem::path& storage_root,
const std::filesystem::path& content_root)
: on_launch(),
on_terminate(),
on_exit(),
@ -241,27 +242,22 @@ X_STATUS Emulator::TerminateTitle() {
kernel_state_->TerminateTitle();
title_id_ = 0;
game_title_ = L"";
game_title_ = "";
on_terminate();
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.
// This is a silly guess based on file extension.
auto last_slash = path.find_last_of(xe::kPathSeparator);
auto last_dot = path.find_last_of('.');
if (last_dot < last_slash) {
last_dot = std::wstring::npos;
}
if (last_dot == std::wstring::npos) {
if (!path.has_extension()) {
// Likely an STFS container.
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(),
tolower);
if (extension == L".xex" || extension == L".elf" || extension == L".exe") {
if (extension == ".xex" || extension == ".elf" || extension == ".exe") {
// Treat as a naked xex file.
return LaunchXexFile(path);
} 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
// that to the game filesystem.
// 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";
// Register the local directory in the virtual filesystem.
auto parent_path = xe::find_base_path(path);
auto parent_path = path.parent_path();
auto device =
std::make_unique<vfs::HostPathDevice>(mount_path, parent_path, true);
if (!device->Initialize()) {
@ -298,14 +294,14 @@ X_STATUS Emulator::LaunchXexFile(std::wstring path) {
file_system_->RegisterSymbolicLink("d:", mount_path);
// Get just the filename (foo.xex).
auto file_name = xe::find_name_from_path(path);
auto file_name = path.filename();
// 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);
}
X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
X_STATUS Emulator::LaunchDiscImage(const std::filesystem::path& path) {
auto mount_path = "\\Device\\Cdrom0";
// Register the disc image in the virtual filesystem.
@ -328,7 +324,7 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring 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";
// 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();
filesystem::CreateFile(path);
@ -435,7 +431,7 @@ bool Emulator::SaveToFile(const std::wstring& path) {
return true;
}
bool Emulator::RestoreFromFile(const std::wstring& path) {
bool Emulator::RestoreFromFile(const std::filesystem::path& path) {
// Restore the emulator state from a file
auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite);
if (!map) {
@ -509,7 +505,7 @@ void Emulator::LaunchNextTitle() {
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
auto next_title = xam->loader_data().launch_path;
CompleteLaunch(L"", next_title);
CompleteLaunch("", next_title);
}
bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) {
@ -642,20 +638,20 @@ std::string Emulator::FindLaunchModule() {
return path + default_module;
}
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
const std::string& module_path) {
X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
const std::string_view module_path) {
// Reset state.
title_id_ = 0;
game_title_ = L"";
game_title_ = "";
display_window_->SetIcon(nullptr, 0);
// Allow xam to request module loads.
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
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) {
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;
}
@ -668,17 +664,16 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
// Try and load the resource database (xex only).
if (module->title_id()) {
char title_id[9] = {0};
std::snprintf(title_id, xe::countof(title_id), "%08X", module->title_id());
config::LoadGameConfig(xe::to_wstring(title_id));
auto title_id = fmt::format("{:08X}", module->title_id());
config::LoadGameConfig(title_id);
uint32_t resource_data = 0;
uint32_t resource_size = 0;
if (XSUCCEEDED(
module->GetSection(title_id, &resource_data, &resource_size))) {
if (XSUCCEEDED(module->GetSection(title_id.c_str(), &resource_data,
&resource_size))) {
kernel::util::XdbfGameData db(
module->memory()->TranslateVirtual(resource_data), resource_size);
if (db.is_valid()) {
game_title_ = xe::to_wstring(db.title());
game_title_ = db.title();
auto icon_block = db.icon();
if (icon_block) {
display_window_->SetIcon(icon_block.buffer, icon_block.size);

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -47,22 +47,22 @@ namespace xe {
// This is responsible for initializing and managing all the various subsystems.
class Emulator {
public:
explicit Emulator(const std::wstring& command_line,
const std::wstring& storage_root,
const std::wstring& content_root);
explicit Emulator(const std::filesystem::path& command_line,
const std::filesystem::path& storage_root,
const std::filesystem::path& content_root);
~Emulator();
// 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.
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.
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.
const std::wstring& game_title() const { return game_title_; }
const std::string& game_title() const { return game_title_; }
// Currently running title ID
uint32_t title_id() const { return title_id_; }
@ -123,24 +123,24 @@ class Emulator {
// Launches a game from the given file path.
// This will attempt to infer the type of the given file (such as an iso, etc)
// using heuristics.
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
// 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).
X_STATUS LaunchDiscImage(std::wstring path);
X_STATUS LaunchDiscImage(const std::filesystem::path& path);
// 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 Resume();
bool is_paused() const { return paused_; }
bool SaveToFile(const std::wstring& path);
bool RestoreFromFile(const std::wstring& path);
bool SaveToFile(const std::filesystem::path& path);
bool RestoreFromFile(const std::filesystem::path& path);
// The game can request another title to be loaded.
bool TitleRequested();
@ -149,7 +149,7 @@ class Emulator {
void WaitUntilExit();
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<> on_terminate;
xe::Delegate<> on_exit;
@ -160,14 +160,14 @@ class Emulator {
std::string FindLaunchModule();
X_STATUS CompleteLaunch(const std::wstring& path,
const std::string& module_path);
X_STATUS CompleteLaunch(const std::filesystem::path& path,
const std::string_view module_path);
std::wstring command_line_;
std::wstring storage_root_;
std::wstring content_root_;
std::filesystem::path command_line_;
std::filesystem::path storage_root_;
std::filesystem::path content_root_;
std::wstring game_title_;
std::string game_title_;
ui::Window* display_window_;

View File

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

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -133,11 +133,12 @@ class CommandProcessor {
// 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
// awaited.
virtual void InitializeShaderStorage(const std::wstring& storage_root,
uint32_t title_id, bool blocking);
virtual void InitializeShaderStorage(
const std::filesystem::path& storage_root, uint32_t title_id,
bool blocking);
virtual void RequestFrameTrace(const std::wstring& root_path);
virtual void BeginTracing(const std::wstring& root_path);
virtual void RequestFrameTrace(const std::filesystem::path& root_path);
virtual void BeginTracing(const std::filesystem::path& root_path);
virtual void EndTracing();
virtual void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) = 0;
@ -264,8 +265,8 @@ class CommandProcessor {
kSingleFrame,
};
TraceState trace_state_ = TraceState::kDisabled;
std::wstring trace_stream_path_;
std::wstring trace_frame_path_;
std::filesystem::path trace_stream_path_;
std::filesystem::path trace_frame_path_;
std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -85,12 +85,14 @@ void D3D12CommandProcessor::ClearCaches() {
}
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);
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.
if (GetD3D12Context()->GetD3D12Provider()->GetGraphicsAnalysis() != nullptr) {
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()) {
// Currently scaling is only supported with ROV.
if (texture_cache_ != nullptr && texture_cache_->IsResolutionScale2X()) {
return L"Direct3D 12 - ROV 2x";
return "Direct3D 12 - ROV 2x";
} else {
return L"Direct3D 12 - ROV";
return "Direct3D 12 - ROV";
}
} else {
return L"Direct3D 12 - RTV/DSV";
return "Direct3D 12 - RTV/DSV";
}
}

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -43,10 +43,10 @@ class D3D12CommandProcessor : public CommandProcessor {
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;
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;
@ -154,7 +154,7 @@ class D3D12CommandProcessor : public CommandProcessor {
bool changing_stencil_ref = false);
// 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();

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -31,13 +31,13 @@ bool D3D12GraphicsSystem::IsAvailable() {
return xe::ui::d3d12::D3D12Provider::IsD3D12APIAvailable();
}
std::wstring D3D12GraphicsSystem::name() const {
std::string D3D12GraphicsSystem::name() const {
auto d3d12_command_processor =
static_cast<D3D12CommandProcessor*>(command_processor());
if (d3d12_command_processor != nullptr) {
return d3d12_command_processor->GetWindowTitleText();
}
return L"Direct3D 12";
return "Direct3D 12";
}
X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor,

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -28,7 +28,7 @@ class D3D12GraphicsSystem : public GraphicsSystem {
static bool IsAvailable();
std::wstring name() const override;
std::string name() const override;
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
ui::Window* target_window) override;

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -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;
return trace_dump.Main(args);
}
@ -54,6 +54,6 @@ int trace_dump_main(const std::vector<std::wstring>& args) {
} // namespace gpu
} // 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",
"target_trace_file");

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -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;
return trace_viewer.Main(args);
}
@ -56,6 +56,6 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
} // namespace gpu
} // 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",
"target_trace_file");

View File

@ -2,7 +2,7 @@
******************************************************************************
* 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. *
******************************************************************************
*/
@ -18,8 +18,8 @@
#include <mutex>
#include <utility>
#include "third_party/fmt/include/fmt/format.h"
#include "third_party/xxhash/xxhash.h"
#include "xenia/base/assert.h"
#include "xenia/base/byte_order.h"
#include "xenia/base/clock.h"
@ -138,7 +138,7 @@ void PipelineCache::Shutdown() {
void PipelineCache::ClearCache(bool shutting_down) {
bool reinitialize_shader_storage =
!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_;
if (reinitialize_shader_storage) {
shader_storage_root = shader_storage_root_;
@ -188,19 +188,19 @@ void PipelineCache::ClearCache(bool shutting_down) {
}
}
void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
uint32_t title_id, bool blocking) {
void PipelineCache::InitializeShaderStorage(
const std::filesystem::path& storage_root, uint32_t title_id,
bool blocking) {
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.
// 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 -
// would need to invalidate them every commit likely, and additional I/O
// cost - though D3D's internal validation would possibly be enough to ensure
// they are up to date).
auto shader_storage_shareable_root =
xe::join_paths(shader_storage_root, L"shareable");
auto shader_storage_shareable_root = shader_storage_root / "shareable";
if (!xe::filesystem::CreateFolder(shader_storage_shareable_root)) {
return;
}
@ -215,8 +215,7 @@ void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
uint64_t shader_storage_initialization_start =
xe::Clock::QueryHostTickCount();
shader_storage_file_ = xe::filesystem::OpenFile(
xe::join_paths(shader_storage_shareable_root,
xe::format_string(L"%.8X.xsh", title_id)),
shader_storage_shareable_root / fmt::format("{:08X}.xsh", title_id),
"a+b");
if (!shader_storage_file_) {
return;
@ -388,11 +387,11 @@ void PipelineCache::InitializeShaderStorage(const std::wstring& storage_root,
// Initialize the pipeline state storage stream.
uint64_t pipeline_state_storage_initialization_start_ =
xe::Clock::QueryHostTickCount();
pipeline_state_storage_file_ = xe::filesystem::OpenFile(
xe::join_paths(shader_storage_shareable_root,
xe::format_string(L"%.8X.%s.d3d12.xpso", title_id,
edram_rov_used_ ? L"rov" : L"rtv")),
"a+b");
pipeline_state_storage_file_ =
xe::filesystem::OpenFile(shader_storage_shareable_root /
fmt::format("{:08X}.{}.d3d12.xpso", title_id,
edram_rov_used_ ? "rov" : "rtv"),
"a+b");
if (!pipeline_state_storage_file_) {
fclose(shader_storage_file_);
shader_storage_file_ = nullptr;
@ -1694,13 +1693,12 @@ ID3D12PipelineState* PipelineCache::CreateD3D12PipelineState(
}
std::wstring name;
if (runtime_description.pixel_shader != nullptr) {
name =
xe::format_string(L"VS %.16I64X, PS %.16I64X",
runtime_description.vertex_shader->ucode_data_hash(),
runtime_description.pixel_shader->ucode_data_hash());
name = fmt::format(L"VS {:016X}, PS {:016X}",
runtime_description.vertex_shader->ucode_data_hash(),
runtime_description.pixel_shader->ucode_data_hash());
} else {
name = xe::format_string(
L"VS %.16I64X", runtime_description.vertex_shader->ucode_data_hash());
name = fmt::format(L"VS {:016X}",
runtime_description.vertex_shader->ucode_data_hash());
}
state->SetName(name.c_str());
return state;

View File

@ -46,7 +46,7 @@ class PipelineCache {
void Shutdown();
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);
void ShutdownShaderStorage();
@ -262,7 +262,7 @@ class PipelineCache {
PipelineState* current_pipeline_state_ = nullptr;
// Currently open shader storage path.
std::wstring shader_storage_root_;
std::filesystem::path shader_storage_root_;
uint32_t shader_storage_title_id_ = 0;
// Shader storage output stream, for preload in the next emulator runs.

View File

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

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