Merge remote-tracking branch 'bwr/linux_filesystem' into canary
This commit is contained in:
commit
3bbc8431ad
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/platform_linux.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace xe {
|
||||||
namespace filesystem {
|
namespace filesystem {
|
||||||
|
|
||||||
std::string CanonicalizePath(const std::string& original_path) {
|
std::string CanonicalizePath(const std::string& original_path) {
|
||||||
char path_sep(xe::kPathSeparator);
|
auto path_sep = '\\';
|
||||||
std::string path(xe::fix_path_separators(original_path, path_sep));
|
std::string path(xe::fix_path_separators(original_path, path_sep));
|
||||||
|
|
||||||
std::vector<std::string::size_type> path_breaks;
|
std::vector<std::string::size_type> path_breaks;
|
||||||
|
@ -82,7 +82,7 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
pos = pos_n;
|
pos = pos_n;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing seperator.
|
// Remove trailing separator.
|
||||||
if (!path.empty() && path.back() == path_sep) {
|
if (!path.empty() && path.back() == path_sep) {
|
||||||
path.erase(path.size() - 1);
|
path.erase(path.size() - 1);
|
||||||
}
|
}
|
||||||
|
@ -97,8 +97,8 @@ std::string CanonicalizePath(const std::string& original_path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CreateParentFolder(const std::wstring& path) {
|
bool CreateParentFolder(const std::wstring& path) {
|
||||||
auto fixed_path = xe::fix_path_separators(path, xe::kWPathSeparator);
|
auto fixed_path = xe::fix_path_separators(path);
|
||||||
auto base_path = xe::find_base_path(fixed_path, xe::kWPathSeparator);
|
auto base_path = xe::find_base_path(fixed_path);
|
||||||
if (!base_path.empty() && !PathExists(base_path)) {
|
if (!base_path.empty() && !PathExists(base_path)) {
|
||||||
return CreateFolder(base_path);
|
return CreateFolder(base_path);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,20 +9,19 @@
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/logging.h"
|
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <ftw.h>
|
#include <ftw.h>
|
||||||
#include <libgen.h>
|
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -45,7 +44,7 @@ std::wstring GetUserFolder() {
|
||||||
char* dataHome = std::getenv("XDG_DATA_HOME");
|
char* dataHome = std::getenv("XDG_DATA_HOME");
|
||||||
|
|
||||||
// if XDG_DATA_HOME not set, fallback to HOME directory
|
// if XDG_DATA_HOME not set, fallback to HOME directory
|
||||||
if (dataHome == NULL) {
|
if (dataHome == nullptr) {
|
||||||
dataHome = std::getenv("HOME");
|
dataHome = std::getenv("HOME");
|
||||||
} else {
|
} else {
|
||||||
std::string home(dataHome);
|
std::string home(dataHome);
|
||||||
|
@ -53,17 +52,17 @@ std::wstring GetUserFolder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if HOME not set, fall back to this
|
// if HOME not set, fall back to this
|
||||||
if (dataHome == NULL) {
|
if (dataHome == nullptr) {
|
||||||
struct passwd pw1;
|
struct passwd pw1;
|
||||||
struct passwd* pw;
|
struct passwd* pw;
|
||||||
char buf[4096]; // could potentionally lower this
|
char buf[4096]; // could potentially lower this
|
||||||
getpwuid_r(getuid(), &pw1, buf, sizeof(buf), &pw);
|
getpwuid_r(getuid(), &pw1, buf, sizeof(buf), &pw);
|
||||||
assert(&pw1 == pw); // sanity check
|
assert_true(&pw1 == pw); // sanity check
|
||||||
dataHome = pw->pw_dir;
|
dataHome = pw->pw_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string home(dataHome);
|
std::string home(dataHome);
|
||||||
return to_wstring(home + "/.local/share");
|
return xe::join_paths(to_wstring(home), L".local/share");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PathExists(const std::wstring& path) {
|
bool PathExists(const std::wstring& path) {
|
||||||
|
@ -88,18 +87,16 @@ static int removeCallback(const char* fpath, const struct stat* sb,
|
||||||
|
|
||||||
bool DeleteFolder(const std::wstring& path) {
|
bool DeleteFolder(const std::wstring& path) {
|
||||||
return nftw(xe::to_string(path).c_str(), removeCallback, 64,
|
return nftw(xe::to_string(path).c_str(), removeCallback, 64,
|
||||||
FTW_DEPTH | FTW_PHYS) == 0
|
FTW_DEPTH | FTW_PHYS) == 0;
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) {
|
static uint64_t convertUnixtimeToWinFiletime(const timespec& unixtime) {
|
||||||
// Linux uses number of seconds since 1/1/1970, and Windows uses
|
// Linux uses number of nanoseconds since 1/1/1970, and Windows uses
|
||||||
// number of nanoseconds since 1/1/1601
|
// number of nanoseconds since 1/1/1601
|
||||||
// so we convert linux time to nanoseconds and then add the number of
|
// so we add the number of nanoseconds from 1601 to 1970
|
||||||
// nanoseconds from 1601 to 1970
|
|
||||||
// see https://msdn.microsoft.com/en-us/library/ms724228
|
// see https://msdn.microsoft.com/en-us/library/ms724228
|
||||||
uint64_t filetime = filetime = (unixtime * 10000000) + 116444736000000000;
|
uint64_t filetime =
|
||||||
|
(unixtime.tv_sec * 10000000) + unixtime.tv_nsec + 116444736000000000;
|
||||||
return filetime;
|
return filetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +118,7 @@ bool CreateFile(const std::wstring& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeleteFile(const std::wstring& path) {
|
bool DeleteFile(const std::wstring& path) {
|
||||||
return (xe::to_string(path).c_str()) == 0 ? true : false;
|
return xe::to_string(path).c_str() == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PosixFileHandle : public FileHandle {
|
class PosixFileHandle : public FileHandle {
|
||||||
|
@ -136,16 +133,16 @@ class PosixFileHandle : public FileHandle {
|
||||||
size_t* out_bytes_read) override {
|
size_t* out_bytes_read) override {
|
||||||
ssize_t out = pread(handle_, buffer, buffer_length, file_offset);
|
ssize_t out = pread(handle_, buffer, buffer_length, file_offset);
|
||||||
*out_bytes_read = out;
|
*out_bytes_read = out;
|
||||||
return out >= 0 ? true : false;
|
return out >= 0;
|
||||||
}
|
}
|
||||||
bool Write(size_t file_offset, const void* buffer, size_t buffer_length,
|
bool Write(size_t file_offset, const void* buffer, size_t buffer_length,
|
||||||
size_t* out_bytes_written) override {
|
size_t* out_bytes_written) override {
|
||||||
ssize_t out = pwrite(handle_, buffer, buffer_length, file_offset);
|
ssize_t out = pwrite(handle_, buffer, buffer_length, file_offset);
|
||||||
*out_bytes_written = out;
|
*out_bytes_written = out;
|
||||||
return out >= 0 ? true : false;
|
return out >= 0;
|
||||||
}
|
}
|
||||||
bool SetLength(size_t length) override {
|
bool SetLength(size_t length) override {
|
||||||
return ftruncate(handle_, length) >= 0 ? true : false;
|
return ftruncate(handle_, length) >= 0;
|
||||||
}
|
}
|
||||||
void Flush() override { fsync(handle_); }
|
void Flush() override { fsync(handle_); }
|
||||||
|
|
||||||
|
@ -155,7 +152,7 @@ class PosixFileHandle : public FileHandle {
|
||||||
|
|
||||||
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
|
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
|
||||||
uint32_t desired_access) {
|
uint32_t desired_access) {
|
||||||
int open_access;
|
int open_access = 0;
|
||||||
if (desired_access & FileAccess::kGenericRead) {
|
if (desired_access & FileAccess::kGenericRead) {
|
||||||
open_access |= O_RDONLY;
|
open_access |= O_RDONLY;
|
||||||
}
|
}
|
||||||
|
@ -190,12 +187,17 @@ bool GetInfo(const std::wstring& path, FileInfo* out_info) {
|
||||||
if (stat(xe::to_string(path).c_str(), &st) == 0) {
|
if (stat(xe::to_string(path).c_str(), &st) == 0) {
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
out_info->type = FileInfo::Type::kDirectory;
|
out_info->type = FileInfo::Type::kDirectory;
|
||||||
|
// On Linux st.st_size can have non-zero size (generally 4096) so make 0
|
||||||
|
out_info->total_size = 0;
|
||||||
} else {
|
} else {
|
||||||
out_info->type = FileInfo::Type::kFile;
|
out_info->type = FileInfo::Type::kFile;
|
||||||
|
out_info->total_size = st.st_size;
|
||||||
}
|
}
|
||||||
out_info->create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
|
out_info->name = find_name_from_path(path);
|
||||||
out_info->access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
|
out_info->path = find_base_path(path);
|
||||||
out_info->write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
|
out_info->create_timestamp = convertUnixtimeToWinFiletime(st.st_ctim);
|
||||||
|
out_info->access_timestamp = convertUnixtimeToWinFiletime(st.st_atim);
|
||||||
|
out_info->write_timestamp = convertUnixtimeToWinFiletime(st.st_mtim);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -210,20 +212,27 @@ std::vector<FileInfo> ListFiles(const std::wstring& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (auto ent = readdir(dir)) {
|
while (auto ent = readdir(dir)) {
|
||||||
|
if (std::strcmp(ent->d_name, ".") == 0 ||
|
||||||
|
std::strcmp(ent->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
|
|
||||||
info.name = xe::to_wstring(ent->d_name);
|
info.name = xe::to_wstring(ent->d_name);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
stat((xe::to_string(path) + xe::to_string(info.name)).c_str(), &st);
|
auto full_path = xe::to_string(xe::join_paths(path, info.name));
|
||||||
info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
|
auto ret = stat(full_path.c_str(), &st);
|
||||||
info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
|
assert_zero(ret);
|
||||||
info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
|
info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctim);
|
||||||
|
info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atim);
|
||||||
|
info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtim);
|
||||||
if (ent->d_type == DT_DIR) {
|
if (ent->d_type == DT_DIR) {
|
||||||
info.type = FileInfo::Type::kDirectory;
|
info.type = FileInfo::Type::kDirectory;
|
||||||
info.total_size = 0;
|
info.total_size = 0;
|
||||||
} else {
|
} else {
|
||||||
info.type = FileInfo::Type::kFile;
|
info.type = FileInfo::Type::kFile;
|
||||||
info.total_size = st.st_size;
|
info.total_size = static_cast<size_t>(st.st_size);
|
||||||
}
|
}
|
||||||
result.push_back(info);
|
result.push_back(info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ bool PathExists(const std::wstring& path) {
|
||||||
bool CreateFolder(const std::wstring& path) {
|
bool CreateFolder(const std::wstring& path) {
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
do {
|
do {
|
||||||
pos = path.find_first_of(xe::kWPathSeparator, pos + 1);
|
pos = path.find_first_of(xe::kPathSeparator<wchar_t>, pos + 1);
|
||||||
CreateDirectoryW(path.substr(0, pos).c_str(), nullptr);
|
CreateDirectoryW(path.substr(0, pos).c_str(), nullptr);
|
||||||
} while (pos != std::string::npos);
|
} while (pos != std::string::npos);
|
||||||
return PathExists(path);
|
return PathExists(path);
|
||||||
|
|
|
@ -29,13 +29,14 @@
|
||||||
|
|
||||||
// For MessageBox:
|
// For MessageBox:
|
||||||
// TODO(benvanik): generic API? logging_win.cc?
|
// TODO(benvanik): generic API? logging_win.cc?
|
||||||
|
// TODO(dougvj): For now I just inlined a small difference for linux
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_WIN32
|
||||||
#include "xenia/base/platform_win.h"
|
#include "xenia/base/platform_win.h"
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
|
||||||
DEFINE_string(
|
DEFINE_string(log_file, "",
|
||||||
log_file, "",
|
"Logs are written to the given file (specify stdout/stderr "
|
||||||
"Logs are written to the given file (specify stdout for command line)",
|
"for command line)",
|
||||||
"Logging");
|
"Logging");
|
||||||
DEFINE_bool(log_to_debugprint, false, "Dump the log to DebugPrint.", "Logging");
|
DEFINE_bool(log_to_debugprint, false, "Dump the log to DebugPrint.", "Logging");
|
||||||
DEFINE_bool(flush_log, true, "Flush log file after each log line batch.",
|
DEFINE_bool(flush_log, true, "Flush log file after each log line batch.",
|
||||||
|
@ -63,6 +64,8 @@ class Logger {
|
||||||
} else {
|
} else {
|
||||||
if (cvars::log_file == "stdout") {
|
if (cvars::log_file == "stdout") {
|
||||||
file_ = stdout;
|
file_ = stdout;
|
||||||
|
} else if (cvars::log_file == "stderr") {
|
||||||
|
file_ = stderr;
|
||||||
} else {
|
} else {
|
||||||
auto file_path = xe::to_wstring(cvars::log_file);
|
auto file_path = xe::to_wstring(cvars::log_file);
|
||||||
xe::filesystem::CreateParentFolder(file_path);
|
xe::filesystem::CreateParentFolder(file_path);
|
||||||
|
@ -330,6 +333,8 @@ void FatalError(const char* fmt, ...) {
|
||||||
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
|
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
|
||||||
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
|
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
|
||||||
}
|
}
|
||||||
|
#elif XE_PLATFORM_LINUX
|
||||||
|
fprintf(stderr, fmt, args);
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
ShutdownLogging();
|
ShutdownLogging();
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "build/version.h"
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/main.h"
|
#include "xenia/base/main.h"
|
||||||
|
|
||||||
|
@ -33,6 +34,10 @@ extern "C" int main(int argc, char** argv) {
|
||||||
// Initialize logging. Needs parsed FLAGS.
|
// Initialize logging. Needs parsed FLAGS.
|
||||||
xe::InitializeLogging(entry_info.name);
|
xe::InitializeLogging(entry_info.name);
|
||||||
|
|
||||||
|
// Print version info.
|
||||||
|
XELOGI("Build: %s / %s on %s", XE_BUILD_BRANCH, XE_BUILD_COMMIT,
|
||||||
|
XE_BUILD_DATE);
|
||||||
|
|
||||||
// Call app-provided entry point.
|
// Call app-provided entry point.
|
||||||
int result = entry_info.entry_point(args);
|
int result = entry_info.entry_point(args);
|
||||||
|
|
||||||
|
|
|
@ -93,14 +93,16 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_WIN32
|
||||||
const char kPathSeparator = '\\';
|
template <typename T>
|
||||||
const wchar_t kWPathSeparator = L'\\';
|
const T kPathSeparator = T('\\');
|
||||||
const size_t kMaxPath = 260; // _MAX_PATH
|
const size_t kMaxPath = 260; // _MAX_PATH
|
||||||
#else
|
#else
|
||||||
const char kPathSeparator = '/';
|
template <typename T>
|
||||||
const wchar_t kWPathSeparator = L'/';
|
const T kPathSeparator = T('/');
|
||||||
const size_t kMaxPath = 1024; // PATH_MAX
|
const size_t kMaxPath = 1024; // PATH_MAX
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
template <typename T>
|
||||||
|
const T kAllPathSeparators[3] = {T('\\'), T('/'), '\0'};
|
||||||
|
|
||||||
// Launches a web browser to the given URL.
|
// Launches a web browser to the given URL.
|
||||||
void LaunchBrowser(const wchar_t* url);
|
void LaunchBrowser(const wchar_t* url);
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/base/platform_linux.h"
|
#include <cstdlib>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ std::wstring to_wstring(const std::string& source) {
|
||||||
#endif // XE_PLATFORM_LINUX
|
#endif // XE_PLATFORM_LINUX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
std::string format_string(const char* format, va_list args) {
|
std::string format_string(const char* format, va_list args) {
|
||||||
if (!format) {
|
if (!format) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -79,6 +80,7 @@ std::string format_string(const char* format, va_list args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
std::wstring format_string(const wchar_t* format, va_list args) {
|
std::wstring format_string(const wchar_t* format, va_list args) {
|
||||||
if (!format) {
|
if (!format) {
|
||||||
return L"";
|
return L"";
|
||||||
|
@ -153,26 +155,30 @@ std::string::size_type find_first_of_case(const std::string& target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string to_absolute_path(const std::string& path) {
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
|
char buffer[kMaxPath];
|
||||||
|
_fullpath(buffer, path.c_str(), sizeof(buffer) / sizeof(char));
|
||||||
|
return buffer;
|
||||||
|
#else
|
||||||
|
char buffer[kMaxPath];
|
||||||
|
realpath(path.c_str(), buffer);
|
||||||
|
return buffer;
|
||||||
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
std::wstring to_absolute_path(const std::wstring& path) {
|
std::wstring to_absolute_path(const std::wstring& path) {
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_WIN32
|
||||||
wchar_t buffer[kMaxPath];
|
wchar_t buffer[kMaxPath];
|
||||||
_wfullpath(buffer, path.c_str(), sizeof(buffer) / sizeof(wchar_t));
|
_wfullpath(buffer, path.c_str(), sizeof(buffer) / sizeof(wchar_t));
|
||||||
return buffer;
|
return buffer;
|
||||||
#else
|
#else
|
||||||
char buffer[kMaxPath];
|
return xe::to_wstring(to_absolute_path(xe::to_string(path)));
|
||||||
realpath(xe::to_string(path).c_str(), buffer);
|
|
||||||
return xe::to_wstring(buffer);
|
|
||||||
#endif // XE_PLATFORM_WIN32
|
#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,
|
std::string join_paths(const std::string& left, const std::string& right,
|
||||||
char sep) {
|
char sep) {
|
||||||
if (!left.size()) {
|
if (!left.size()) {
|
||||||
|
|
|
@ -23,16 +23,10 @@ namespace xe {
|
||||||
std::string to_string(const std::wstring& source);
|
std::string to_string(const std::wstring& source);
|
||||||
std::wstring to_wstring(const std::string& source);
|
std::wstring to_wstring(const std::string& source);
|
||||||
|
|
||||||
std::string format_string(const char* format, va_list args);
|
template <typename T>
|
||||||
inline std::string format_string(const char* format, ...) {
|
std::basic_string<T> format_string(const T* format, va_list args);
|
||||||
va_list va;
|
template <typename T>
|
||||||
va_start(va, format);
|
inline std::basic_string<T> format_string(const T* 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_list va;
|
||||||
va_start(va, format);
|
va_start(va, format);
|
||||||
auto result = format_string(format, va);
|
auto result = format_string(format, va);
|
||||||
|
@ -52,36 +46,170 @@ std::string::size_type find_first_of_case(const std::string& target,
|
||||||
const std::string& search);
|
const std::string& search);
|
||||||
|
|
||||||
// Converts the given path to an absolute path based on cwd.
|
// Converts the given path to an absolute path based on cwd.
|
||||||
std::wstring to_absolute_path(const std::wstring& path);
|
template <typename T>
|
||||||
|
std::basic_string<T> to_absolute_path(const std::basic_string<T>& path);
|
||||||
|
template <typename T>
|
||||||
|
inline std::basic_string<T> to_absolute_path(const T* path) {
|
||||||
|
return to_absolute_path(std::basic_string<T>(path));
|
||||||
|
}
|
||||||
|
|
||||||
// Splits the given path on any valid path separator and returns all parts.
|
// Splits the given path on any valid path separator and returns all parts.
|
||||||
std::vector<std::string> split_path(const std::string& path);
|
template <typename T>
|
||||||
std::vector<std::wstring> split_path(const std::wstring& path);
|
std::vector<std::basic_string<T>> split_path(const std::basic_string<T>& path) {
|
||||||
|
std::vector<std::basic_string<T>> parts;
|
||||||
|
size_t n = 0;
|
||||||
|
size_t last = 0;
|
||||||
|
while ((n = path.find_first_of(kAllPathSeparators<T>, last)) !=
|
||||||
|
std::basic_string<T>::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;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline std::vector<std::basic_string<T>> split_path(const T* path) {
|
||||||
|
return split_path(std::basic_string<T>(path));
|
||||||
|
}
|
||||||
|
|
||||||
// Joins two path segments with the given separator.
|
// Joins two path segments with the given separator.
|
||||||
std::string join_paths(const std::string& left, const std::string& right,
|
template <typename T>
|
||||||
char sep = xe::kPathSeparator);
|
std::basic_string<T> join_paths(const std::basic_string<T>& left,
|
||||||
std::wstring join_paths(const std::wstring& left, const std::wstring& right,
|
const std::basic_string<T>& right,
|
||||||
wchar_t sep = xe::kPathSeparator);
|
T sep = xe::kPathSeparator<T>) {
|
||||||
|
if (left.empty()) {
|
||||||
|
return right;
|
||||||
|
} else if (right.empty()) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
if (left[left.size() - 1] == sep) {
|
||||||
|
return left + right;
|
||||||
|
} else {
|
||||||
|
return left + sep + right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline std::basic_string<T> join_paths(const std::basic_string<T>& left,
|
||||||
|
const T* right,
|
||||||
|
T sep = xe::kPathSeparator<T>) {
|
||||||
|
return join_paths(left, std::basic_string<T>(right), sep);
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline std::basic_string<T> join_paths(const T* left,
|
||||||
|
const std::basic_string<T>& right,
|
||||||
|
T sep = xe::kPathSeparator<T>) {
|
||||||
|
return join_paths(std::basic_string<T>(left), right, sep);
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline std::basic_string<T> join_paths(const T* left, const T* right,
|
||||||
|
T sep = xe::kPathSeparator<T>) {
|
||||||
|
return join_paths(std::basic_string<T>(left), std::basic_string<T>(right),
|
||||||
|
sep);
|
||||||
|
}
|
||||||
|
|
||||||
// Replaces all path separators with the given value and removes redundant
|
// Replaces all path separators with the given value and removes redundant
|
||||||
// separators.
|
// separators.
|
||||||
std::wstring fix_path_separators(const std::wstring& source,
|
template <typename T>
|
||||||
wchar_t new_sep = xe::kPathSeparator);
|
std::basic_string<T> fix_path_separators(const std::basic_string<T>& source,
|
||||||
std::string fix_path_separators(const std::string& source,
|
T new_sep = xe::kPathSeparator<T>) {
|
||||||
char new_sep = xe::kPathSeparator);
|
// Swap all separators to new_sep.
|
||||||
|
T old_sep = new_sep == kAllPathSeparators<T>[0] ? kAllPathSeparators<T>[1]
|
||||||
|
: kAllPathSeparators<T>[0];
|
||||||
|
typename std::basic_string<T>::size_type pos = 0;
|
||||||
|
std::basic_string<T> dest = source;
|
||||||
|
while ((pos = source.find_first_of(old_sep, pos)) !=
|
||||||
|
std::basic_string<T>::npos) {
|
||||||
|
dest[pos] = new_sep;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
// Replace redundant separators.
|
||||||
|
pos = 0;
|
||||||
|
while ((pos = dest.find_first_of(new_sep, pos)) !=
|
||||||
|
std::basic_string<T>::npos) {
|
||||||
|
if (pos < dest.size() - 1) {
|
||||||
|
if (dest[pos + 1] == new_sep) {
|
||||||
|
dest.erase(pos + 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline static std::basic_string<T> fix_path_separators(
|
||||||
|
const T* source, T new_sep = xe::kPathSeparator<T>) {
|
||||||
|
return fix_path_separators(std::basic_string<T>(source), new_sep);
|
||||||
|
}
|
||||||
|
|
||||||
// Find the top directory name or filename from a path.
|
// Find the top directory name or filename from a path.
|
||||||
std::string find_name_from_path(const std::string& path,
|
template <typename T>
|
||||||
char sep = xe::kPathSeparator);
|
std::basic_string<T> find_name_from_path(const std::basic_string<T>& path,
|
||||||
std::wstring find_name_from_path(const std::wstring& path,
|
T sep = xe::kPathSeparator<T>) {
|
||||||
wchar_t sep = xe::kPathSeparator);
|
std::basic_string<T> name(path);
|
||||||
|
if (!path.empty()) {
|
||||||
|
typename std::basic_string<T>::size_type from(std::basic_string<T>::npos);
|
||||||
|
if (path.back() == sep) {
|
||||||
|
if (path.length() == 1) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
from = path.size() - 2;
|
||||||
|
}
|
||||||
|
auto pos(path.find_last_of(sep, from));
|
||||||
|
if (pos != std::basic_string<T>::npos && pos != from) {
|
||||||
|
if (from == std::basic_string<T>::npos) {
|
||||||
|
name = path.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
auto len(from - pos);
|
||||||
|
name = path.substr(pos + 1, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline std::basic_string<T> find_name_from_path(const T* path,
|
||||||
|
T sep = xe::kPathSeparator<T>) {
|
||||||
|
return find_name_from_path(std::basic_string<T>(path), sep);
|
||||||
|
}
|
||||||
|
|
||||||
// Get parent path of the given directory or filename.
|
// Get parent path of the given directory or filename.
|
||||||
std::string find_base_path(const std::string& path,
|
template <typename T>
|
||||||
char sep = xe::kPathSeparator);
|
std::basic_string<T> find_base_path(const std::basic_string<T>& path,
|
||||||
std::wstring find_base_path(const std::wstring& path,
|
T sep = xe::kPathSeparator<T>) {
|
||||||
wchar_t sep = xe::kPathSeparator);
|
auto path_start = path.find_first_of(T(':'));
|
||||||
|
if (path_start == std::basic_string<T>::npos) {
|
||||||
|
path_start = 0; // Unix-like or local file
|
||||||
|
} else {
|
||||||
|
path_start++; // Win32-like absolute path, i.e search after C:
|
||||||
|
}
|
||||||
|
auto last_slash = path.find_last_of(sep);
|
||||||
|
if (last_slash == std::basic_string<T>::npos) {
|
||||||
|
return std::basic_string<T>();
|
||||||
|
} else if (last_slash == path_start) { // Root directory
|
||||||
|
return path.substr(0, path_start + 1);
|
||||||
|
} else if (last_slash == path.length() - 1) {
|
||||||
|
auto prev_slash = path.find_last_of(sep, last_slash - 1);
|
||||||
|
if (prev_slash == std::basic_string<T>::npos) {
|
||||||
|
return std::basic_string<T>();
|
||||||
|
} else if (prev_slash == path_start) {
|
||||||
|
return path.substr(0, path_start + 1);
|
||||||
|
} else {
|
||||||
|
return path.substr(0, prev_slash);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return path.substr(0, last_slash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline std::basic_string<T> find_base_path(const T* path,
|
||||||
|
T sep = xe::kPathSeparator<T>) {
|
||||||
|
return find_base_path(std::basic_string<T>(path), sep);
|
||||||
|
}
|
||||||
|
|
||||||
// Tests a match against a case-insensitive fuzzy filter.
|
// Tests a match against a case-insensitive fuzzy filter.
|
||||||
// Returns the score of the match or 0 if none.
|
// Returns the score of the match or 0 if none.
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xenia/base/filesystem.h"
|
||||||
|
|
||||||
|
#include "third_party/catch/include/catch.hpp"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace base {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
TEST_CASE("file_get_info", "Get Info") {
|
||||||
|
auto test_file_name = L"test_file";
|
||||||
|
auto test_file_dir = xe::fix_path_separators(L"src/xenia/base/testing/res");
|
||||||
|
auto test_file_path = xe::join_paths(test_file_dir, test_file_name);
|
||||||
|
|
||||||
|
filesystem::FileInfo info = {};
|
||||||
|
|
||||||
|
REQUIRE(filesystem::GetInfo(test_file_path, &info));
|
||||||
|
|
||||||
|
CHECK(info.type == filesystem::FileInfo::Type::kFile);
|
||||||
|
CHECK(info.name == test_file_name);
|
||||||
|
CHECK(info.path == test_file_dir);
|
||||||
|
CHECK(info.total_size == 81);
|
||||||
|
CHECK(info.create_timestamp > 132111406279379842);
|
||||||
|
CHECK(info.access_timestamp > 132111406279379842);
|
||||||
|
CHECK(info.write_timestamp > 132111406279379842);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("folder_get_info", "Get Info") {
|
||||||
|
auto test_folder_name = L"res";
|
||||||
|
auto test_folder_dir = xe::fix_path_separators(L"src/xenia/base/testing");
|
||||||
|
auto test_folder_path = xe::join_paths(test_folder_dir, test_folder_name);
|
||||||
|
|
||||||
|
filesystem::FileInfo info = {};
|
||||||
|
|
||||||
|
REQUIRE(filesystem::GetInfo(test_folder_path, &info));
|
||||||
|
|
||||||
|
CHECK(info.type == filesystem::FileInfo::Type::kDirectory);
|
||||||
|
CHECK(info.name == test_folder_name);
|
||||||
|
CHECK(info.path == test_folder_dir);
|
||||||
|
CHECK(info.total_size == 0);
|
||||||
|
CHECK(info.create_timestamp > 132111406279379842);
|
||||||
|
CHECK(info.access_timestamp > 132111406279379842);
|
||||||
|
CHECK(info.write_timestamp > 132111406279379842);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace base
|
||||||
|
} // namespace xe
|
|
@ -6,3 +6,10 @@ test_suite("xenia-base-tests", project_root, ".", {
|
||||||
"xenia-base",
|
"xenia-base",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
files({
|
||||||
|
"res/*",
|
||||||
|
})
|
||||||
|
filter("files:res/*")
|
||||||
|
flags({"ExcludeFromBuild"})
|
||||||
|
filter("platforms:Windows")
|
||||||
|
debugdir(project_root)
|
|
@ -0,0 +1 @@
|
||||||
|
Test file to test the xe::filesystem::GetInfo function on both Windows and Linux
|
|
@ -16,6 +16,112 @@ namespace xe {
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
TEST_CASE("find_name_from_path", "string") {
|
||||||
|
REQUIRE(find_name_from_path("", '\\') == "");
|
||||||
|
REQUIRE(find_name_from_path(L"", L'\\') == L"");
|
||||||
|
REQUIRE(find_name_from_path("xe:\\\\xam.xex", '\\') == "xam.xex");
|
||||||
|
REQUIRE(find_name_from_path(L"xe:\\\\xam.xex", L'\\') == L"xam.xex");
|
||||||
|
REQUIRE(find_name_from_path("xe:\\\\", '\\') == "xe:\\\\");
|
||||||
|
REQUIRE(find_name_from_path(L"xe:\\\\", L'\\') == L"xe:\\\\");
|
||||||
|
REQUIRE(find_name_from_path("C:\\filename.txt", '\\') == "filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\filename.txt", L'\\') == L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("C:\\Windows\\Users\\filename.txt", '\\') ==
|
||||||
|
"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\Windows\\Users\\filename.txt", L'\\') ==
|
||||||
|
L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("C:\\", '\\') == "C:\\");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\", L'\\') == L"C:\\");
|
||||||
|
REQUIRE(find_name_from_path("C:\\Windows\\Users/filename.txt", '\\') ==
|
||||||
|
"Users/filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\Windows\\Users/filename.txt", L'\\') ==
|
||||||
|
L"Users/filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("C:/Windows/Users/filename.txt", '/') ==
|
||||||
|
"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:/Windows/Users/filename.txt", L'/') ==
|
||||||
|
L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("/", '/') == "/");
|
||||||
|
REQUIRE(find_name_from_path(L"/", L'/') == L"/");
|
||||||
|
REQUIRE(find_name_from_path("/usr", '/') == "usr");
|
||||||
|
REQUIRE(find_name_from_path(L"/usr", L'/') == L"usr");
|
||||||
|
REQUIRE(find_name_from_path("~/filename.txt", '/') == "filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"~/filename.txt", L'/') == L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("~/.local/share/xenia/filename.txt", '/') ==
|
||||||
|
"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"~/.local/share/xenia/filename.txt", L'/') ==
|
||||||
|
L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("~/.local/share/xenia", '/') == "xenia");
|
||||||
|
REQUIRE(find_name_from_path(L"~/.local/share/xenia", L'/') == L"xenia");
|
||||||
|
REQUIRE(find_name_from_path("~/.local/share/xenia/", '/') == "xenia");
|
||||||
|
REQUIRE(find_name_from_path(L"~/.local/share/xenia/", L'/') == L"xenia");
|
||||||
|
REQUIRE(find_name_from_path("filename.txt", '/') == "filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"filename.txt", L'/') == L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("C:\\New Volume\\filename.txt", '\\') ==
|
||||||
|
"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\New Volume\\filename.txt", L'\\') ==
|
||||||
|
L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("C:\\New Volume\\dir\\filename.txt", '\\') ==
|
||||||
|
"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\New Volume\\dir\\filename.txt", L'\\') ==
|
||||||
|
L"filename.txt");
|
||||||
|
REQUIRE(find_name_from_path("C:\\New Volume\\file name.txt", '\\') ==
|
||||||
|
"file name.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"C:\\New Volume\\file name.txt", L'\\') ==
|
||||||
|
L"file name.txt");
|
||||||
|
REQUIRE(find_name_from_path("/home/xenia/New Volume/file name.txt", '/') ==
|
||||||
|
"file name.txt");
|
||||||
|
REQUIRE(find_name_from_path(L"/home/xenia/New Volume/file name.txt", L'/') ==
|
||||||
|
L"file name.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("find_base_path", "string") {
|
||||||
|
REQUIRE(find_base_path("C:\\Windows\\Users", '\\') == "C:\\Windows");
|
||||||
|
REQUIRE(find_base_path(L"C:\\Windows\\Users", L'\\') == L"C:\\Windows");
|
||||||
|
REQUIRE(find_base_path("C:\\Windows\\Users\\", '\\') == "C:\\Windows");
|
||||||
|
REQUIRE(find_base_path(L"C:\\Windows\\Users\\", L'\\') == L"C:\\Windows");
|
||||||
|
REQUIRE(find_base_path("C:\\Windows", '\\') == "C:\\");
|
||||||
|
REQUIRE(find_base_path(L"C:\\Windows", L'\\') == L"C:\\");
|
||||||
|
REQUIRE(find_base_path("C:\\", '\\') == "C:\\");
|
||||||
|
REQUIRE(find_base_path(L"C:\\", L'\\') == L"C:\\");
|
||||||
|
REQUIRE(find_base_path("C:/Windows/Users", '/') == "C:/Windows");
|
||||||
|
REQUIRE(find_base_path(L"C:/Windows/Users", L'/') == L"C:/Windows");
|
||||||
|
REQUIRE(find_base_path("C:\\Windows/Users", '/') == "C:\\Windows");
|
||||||
|
REQUIRE(find_base_path(L"C:\\Windows/Users", L'/') == L"C:\\Windows");
|
||||||
|
REQUIRE(find_base_path("C:\\Windows/Users", '\\') == "C:\\");
|
||||||
|
REQUIRE(find_base_path(L"C:\\Windows/Users", L'\\') == L"C:\\");
|
||||||
|
REQUIRE(find_base_path("/usr/bin/bash", '/') == "/usr/bin");
|
||||||
|
REQUIRE(find_base_path(L"/usr/bin/bash", L'/') == L"/usr/bin");
|
||||||
|
REQUIRE(find_base_path("/usr/bin", '/') == "/usr");
|
||||||
|
REQUIRE(find_base_path(L"/usr/bin", L'/') == L"/usr");
|
||||||
|
REQUIRE(find_base_path("/usr/bin/", '/') == "/usr");
|
||||||
|
REQUIRE(find_base_path(L"/usr/bin/", L'/') == L"/usr");
|
||||||
|
REQUIRE(find_base_path("/usr", '/') == "/");
|
||||||
|
REQUIRE(find_base_path(L"/usr", L'/') == L"/");
|
||||||
|
REQUIRE(find_base_path("/usr/", '/') == "/");
|
||||||
|
REQUIRE(find_base_path(L"/usr/", L'/') == L"/");
|
||||||
|
REQUIRE(find_base_path("~/filename.txt", '/') == "~");
|
||||||
|
REQUIRE(find_base_path(L"~/filename.txt", L'/') == L"~");
|
||||||
|
REQUIRE(find_base_path("local_dir/subdirectory", '/') == "local_dir");
|
||||||
|
REQUIRE(find_base_path(L"local_dir/subdirectory", L'/') == L"local_dir");
|
||||||
|
REQUIRE(find_base_path("local_dir", '/') == "");
|
||||||
|
REQUIRE(find_base_path(L"local_dir", L'/') == L"");
|
||||||
|
REQUIRE(find_base_path("C:\\New Volume\\filename.txt", '\\') ==
|
||||||
|
"C:\\New Volume");
|
||||||
|
REQUIRE(find_base_path(L"C:\\New Volume\\filename.txt", L'\\') ==
|
||||||
|
L"C:\\New Volume");
|
||||||
|
REQUIRE(find_base_path("C:\\New Volume\\dir\\filename.txt", '\\') ==
|
||||||
|
"C:\\New Volume\\dir");
|
||||||
|
REQUIRE(find_base_path(L"C:\\New Volume\\dir\\filename.txt", L'\\') ==
|
||||||
|
L"C:\\New Volume\\dir");
|
||||||
|
REQUIRE(find_base_path("C:\\New Volume\\file name.txt", '\\') ==
|
||||||
|
"C:\\New Volume");
|
||||||
|
REQUIRE(find_base_path(L"C:\\New Volume\\file name.txt", L'\\') ==
|
||||||
|
L"C:\\New Volume");
|
||||||
|
REQUIRE(find_base_path("/home/xenia/New Volume/file name.txt", '/') ==
|
||||||
|
"/home/xenia/New Volume");
|
||||||
|
REQUIRE(find_base_path(L"/home/xenia/New Volume/file name.txt", L'/') ==
|
||||||
|
L"/home/xenia/New Volume");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("StringBuffer") {
|
TEST_CASE("StringBuffer") {
|
||||||
StringBuffer sb;
|
StringBuffer sb;
|
||||||
uint32_t module_flags = 0x1000000;
|
uint32_t module_flags = 0x1000000;
|
||||||
|
|
|
@ -50,8 +50,8 @@ struct TestCase {
|
||||||
class TestSuite {
|
class TestSuite {
|
||||||
public:
|
public:
|
||||||
TestSuite(const std::wstring& src_file_path) : src_file_path(src_file_path) {
|
TestSuite(const std::wstring& src_file_path) : src_file_path(src_file_path) {
|
||||||
name = src_file_path.substr(src_file_path.find_last_of(xe::kPathSeparator) +
|
name = src_file_path.substr(
|
||||||
1);
|
src_file_path.find_last_of(xe::kPathSeparator<char>) + 1);
|
||||||
name = ReplaceExtension(name, L"");
|
name = ReplaceExtension(name, L"");
|
||||||
map_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".map";
|
map_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".map";
|
||||||
bin_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".bin";
|
bin_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".bin";
|
||||||
|
|
|
@ -48,7 +48,7 @@ bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) {
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
// Setup debug info.
|
// Setup debug info.
|
||||||
auto last_slash = fixed_path.find_last_of(xe::kPathSeparator);
|
auto last_slash = fixed_path.find_last_of(xe::kPathSeparator<wchar_t>);
|
||||||
if (last_slash != std::string::npos) {
|
if (last_slash != std::string::npos) {
|
||||||
name_ = xe::to_string(fixed_path.substr(last_slash + 1));
|
name_ = xe::to_string(fixed_path.substr(last_slash + 1));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -241,7 +241,7 @@ X_STATUS Emulator::TerminateTitle() {
|
||||||
X_STATUS Emulator::LaunchPath(std::wstring path) {
|
X_STATUS Emulator::LaunchPath(std::wstring path) {
|
||||||
// Launch based on file type.
|
// Launch based on file type.
|
||||||
// This is a silly guess based on file extension.
|
// This is a silly guess based on file extension.
|
||||||
auto last_slash = path.find_last_of(xe::kPathSeparator);
|
auto last_slash = path.find_last_of(xe::kPathSeparator<char>);
|
||||||
auto last_dot = path.find_last_of('.');
|
auto last_dot = path.find_last_of('.');
|
||||||
if (last_dot < last_slash) {
|
if (last_dot < last_slash) {
|
||||||
last_dot = std::wstring::npos;
|
last_dot = std::wstring::npos;
|
||||||
|
|
|
@ -350,7 +350,7 @@ void KernelState::LoadKernelModule(object_ref<KernelModule> kernel_module) {
|
||||||
object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
|
object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
|
||||||
bool call_entry) {
|
bool call_entry) {
|
||||||
// Some games try to load relative to launch module, others specify full path.
|
// Some games try to load relative to launch module, others specify full path.
|
||||||
std::string name = xe::find_name_from_path(raw_name);
|
std::string name = xe::find_name_from_path(raw_name, '\\');
|
||||||
std::string path(raw_name);
|
std::string path(raw_name);
|
||||||
if (name == raw_name) {
|
if (name == raw_name) {
|
||||||
assert_not_null(executable_module_);
|
assert_not_null(executable_module_);
|
||||||
|
|
|
@ -21,6 +21,23 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
#if defined(XE_PLATFORM_LINUX)
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#include <memory>
|
||||||
|
std::string demangle(const char* mangled_name) {
|
||||||
|
std::size_t len = 0;
|
||||||
|
int status = 0;
|
||||||
|
std::unique_ptr<char, decltype(&std::free)> ptr(
|
||||||
|
__cxxabiv1::__cxa_demangle(mangled_name, nullptr, &len, &status),
|
||||||
|
&std::free);
|
||||||
|
return ptr ? std::string("class ") + ptr.get() : "";
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::string demangle(const char* name) { return name; }
|
||||||
|
#endif
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ObjectTable::ObjectTable() {}
|
ObjectTable::ObjectTable() {}
|
||||||
|
|
||||||
ObjectTable::~ObjectTable() { Reset(); }
|
ObjectTable::~ObjectTable() { Reset(); }
|
||||||
|
@ -119,7 +136,8 @@ X_STATUS ObjectTable::AddHandle(XObject* object, X_HANDLE* out_handle) {
|
||||||
// Retain so long as the object is in the table.
|
// Retain so long as the object is in the table.
|
||||||
object->Retain();
|
object->Retain();
|
||||||
|
|
||||||
XELOGI("Added handle:%08X for %s", handle, typeid(*object).name());
|
XELOGI("Added handle:%08X for %s", handle,
|
||||||
|
demangle(typeid(*object).name()).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +221,8 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) {
|
||||||
object->handles().erase(handle_entry);
|
object->handles().erase(handle_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
XELOGI("Removed handle:%08X for %s", handle, typeid(*object).name());
|
XELOGI("Removed handle:%08X for %s", handle,
|
||||||
|
demangle(typeid(*object).name()).c_str());
|
||||||
|
|
||||||
// Release now that the object has been removed from the table.
|
// Release now that the object has been removed from the table.
|
||||||
object->Release();
|
object->Release();
|
||||||
|
|
|
@ -77,9 +77,9 @@ std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) {
|
||||||
|
|
||||||
// Package root path:
|
// Package root path:
|
||||||
// content_root/title_id/type_name/
|
// content_root/title_id/type_name/
|
||||||
auto package_root = xe::join_paths(
|
auto package_root =
|
||||||
root_path_, xe::join_paths(title_id_str, content_type_str));
|
xe::join_paths(root_path_, xe::join_paths(title_id, type_name));
|
||||||
return package_root + xe::kWPathSeparator;
|
return package_root + xe::kPathSeparator<wchar_t>;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
|
std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
|
||||||
|
@ -88,13 +88,7 @@ std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
|
||||||
auto package_root = ResolvePackageRoot(data.content_type);
|
auto package_root = ResolvePackageRoot(data.content_type);
|
||||||
auto package_path =
|
auto package_path =
|
||||||
xe::join_paths(package_root, xe::to_wstring(data.file_name));
|
xe::join_paths(package_root, xe::to_wstring(data.file_name));
|
||||||
|
package_path += xe::kPathSeparator<char>;
|
||||||
// Add slash to end of path if this is a folder
|
|
||||||
// (or package doesn't exist, meaning we're creating a new folder)
|
|
||||||
if (!xe::filesystem::PathExists(package_path) ||
|
|
||||||
xe::filesystem::IsFolder(package_path)) {
|
|
||||||
package_path += xe::kPathSeparator;
|
|
||||||
}
|
|
||||||
return package_path;
|
return package_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +308,7 @@ std::wstring ContentManager::ResolveGameUserContentPath() {
|
||||||
root_path_,
|
root_path_,
|
||||||
xe::join_paths(title_id,
|
xe::join_paths(title_id,
|
||||||
xe::join_paths(kGameUserContentDirName, user_name)));
|
xe::join_paths(kGameUserContentDirName, user_name)));
|
||||||
return package_root + xe::kWPathSeparator;
|
return package_root + xe::kPathSeparator<wchar_t>;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xam
|
} // namespace xam
|
||||||
|
|
|
@ -40,13 +40,14 @@ XModule::~XModule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XModule::Matches(const std::string& name) const {
|
bool XModule::Matches(const std::string& name) const {
|
||||||
if (strcasecmp(xe::find_name_from_path(path_).c_str(), name.c_str()) == 0) {
|
auto name_c = name.c_str();
|
||||||
|
if (strcasecmp(xe::find_name_from_path(path_, '\\').c_str(), name_c) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (strcasecmp(name_.c_str(), name.c_str()) == 0) {
|
if (strcasecmp(name_.c_str(), name_c) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (strcasecmp(path_.c_str(), name.c_str()) == 0) {
|
if (strcasecmp(path_.c_str(), name_c) == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -27,7 +27,8 @@ Entry::Entry(Device* device, Entry* parent, const std::string& path)
|
||||||
access_timestamp_(0),
|
access_timestamp_(0),
|
||||||
write_timestamp_(0) {
|
write_timestamp_(0) {
|
||||||
assert_not_null(device);
|
assert_not_null(device);
|
||||||
absolute_path_ = xe::join_paths(device->mount_path(), path);
|
absolute_path_ = xe::join_paths(device->mount_path(),
|
||||||
|
xe::fix_path_separators(path, '\\'), '\\');
|
||||||
name_ = xe::find_name_from_path(path);
|
name_ = xe::find_name_from_path(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ Entry* VirtualFileSystem::ResolvePath(const std::string& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* VirtualFileSystem::ResolveBasePath(const std::string& path) {
|
Entry* VirtualFileSystem::ResolveBasePath(const std::string& path) {
|
||||||
auto base_path = xe::find_base_path(path);
|
auto base_path = xe::find_base_path(path, '\\');
|
||||||
return ResolvePath(base_path);
|
return ResolvePath(base_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ Entry* VirtualFileSystem::CreatePath(const std::string& path,
|
||||||
}
|
}
|
||||||
auto parent_entry = partial_entry;
|
auto parent_entry = partial_entry;
|
||||||
for (size_t i = 1; i < path_parts.size() - 1; ++i) {
|
for (size_t i = 1; i < path_parts.size() - 1; ++i) {
|
||||||
partial_path = xe::join_paths(partial_path, path_parts[i]);
|
partial_path = xe::join_paths(partial_path, path_parts[i], '\\');
|
||||||
auto child_entry = ResolvePath(partial_path);
|
auto child_entry = ResolvePath(partial_path);
|
||||||
if (!child_entry) {
|
if (!child_entry) {
|
||||||
child_entry =
|
child_entry =
|
||||||
|
@ -224,14 +224,14 @@ X_STATUS VirtualFileSystem::OpenFile(const std::string& path,
|
||||||
// If no device or parent, fail.
|
// If no device or parent, fail.
|
||||||
Entry* parent_entry = nullptr;
|
Entry* parent_entry = nullptr;
|
||||||
Entry* entry = nullptr;
|
Entry* entry = nullptr;
|
||||||
if (!xe::find_base_path(path).empty()) {
|
if (!xe::find_base_path(path, '\\').empty()) {
|
||||||
parent_entry = ResolveBasePath(path);
|
parent_entry = ResolveBasePath(path);
|
||||||
if (!parent_entry) {
|
if (!parent_entry) {
|
||||||
*out_action = FileAction::kDoesNotExist;
|
*out_action = FileAction::kDoesNotExist;
|
||||||
return X_STATUS_NO_SUCH_FILE;
|
return X_STATUS_NO_SUCH_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file_name = xe::find_name_from_path(path);
|
auto file_name = xe::find_name_from_path(path, '\\');
|
||||||
entry = parent_entry->GetChild(file_name);
|
entry = parent_entry->GetChild(file_name);
|
||||||
} else {
|
} else {
|
||||||
entry = ResolvePath(path);
|
entry = ResolvePath(path);
|
||||||
|
|
Loading…
Reference in New Issue