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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user