mirror of
https://github.com/xenia-project/xenia.git
synced 2024-11-23 11:39:45 +00:00
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:
parent
114cea6fb7
commit
5bf0b34445
11
premake5.lua
11
premake5.lua
@ -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")
|
||||
|
@ -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";
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ project("xenia-app")
|
||||
links({
|
||||
"aes_128",
|
||||
"capstone",
|
||||
"fmt",
|
||||
"dxbc",
|
||||
"discord-rpc",
|
||||
"glslang-spirv",
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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>(
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"\"\"\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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; \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
55
src/xenia/base/fuzzy.cc
Normal 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
41
src/xenia/base/fuzzy.h
Normal 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_
|
@ -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
|
||||
|
@ -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, ...) \
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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_
|
||||
|
@ -5,6 +5,9 @@ project("xenia-base")
|
||||
uuid("aeadaf22-2b20-4941-b05f-a802d5679c11")
|
||||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"fmt"
|
||||
})
|
||||
defines({
|
||||
})
|
||||
local_platform_files()
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
103
src/xenia/base/string_key.h
Normal 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_
|
@ -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
|
@ -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
|
||||
|
@ -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_
|
@ -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
|
27
src/xenia/base/system_win.cc
Normal file
27
src/xenia/base/system_win.cc
Normal 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
651
src/xenia/base/utf8.cc
Normal 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
137
src/xenia/base/utf8.h
Normal 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_
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
|
||||
|
@ -8,6 +8,7 @@ project("xenia-cpu-backend-x64")
|
||||
language("C++")
|
||||
links({
|
||||
"capstone",
|
||||
"fmt",
|
||||
"xenia-base",
|
||||
"xenia-cpu",
|
||||
})
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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({
|
||||
|
@ -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_;
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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", ¤t_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);
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user