C++17ification.

C++17ification!

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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