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/math.h"
|
||||
#include "xenia/base/platform_linux.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace xe {
|
|||
namespace filesystem {
|
||||
|
||||
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::vector<std::string::size_type> path_breaks;
|
||||
|
@ -82,7 +82,7 @@ std::string CanonicalizePath(const std::string& original_path) {
|
|||
pos = pos_n;
|
||||
}
|
||||
|
||||
// Remove trailing seperator.
|
||||
// Remove trailing separator.
|
||||
if (!path.empty() && path.back() == path_sep) {
|
||||
path.erase(path.size() - 1);
|
||||
}
|
||||
|
@ -97,8 +97,8 @@ std::string CanonicalizePath(const std::string& original_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);
|
||||
auto fixed_path = xe::fix_path_separators(path);
|
||||
auto base_path = xe::find_base_path(fixed_path);
|
||||
if (!base_path.empty() && !PathExists(base_path)) {
|
||||
return CreateFolder(base_path);
|
||||
} else {
|
||||
|
|
|
@ -9,20 +9,19 @@
|
|||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <ftw.h>
|
||||
#include <libgen.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace xe {
|
||||
|
@ -45,7 +44,7 @@ std::wstring GetUserFolder() {
|
|||
char* dataHome = std::getenv("XDG_DATA_HOME");
|
||||
|
||||
// if XDG_DATA_HOME not set, fallback to HOME directory
|
||||
if (dataHome == NULL) {
|
||||
if (dataHome == nullptr) {
|
||||
dataHome = std::getenv("HOME");
|
||||
} else {
|
||||
std::string home(dataHome);
|
||||
|
@ -53,17 +52,17 @@ std::wstring GetUserFolder() {
|
|||
}
|
||||
|
||||
// if HOME not set, fall back to this
|
||||
if (dataHome == NULL) {
|
||||
if (dataHome == nullptr) {
|
||||
struct passwd pw1;
|
||||
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);
|
||||
assert(&pw1 == pw); // sanity check
|
||||
assert_true(&pw1 == pw); // sanity check
|
||||
dataHome = pw->pw_dir;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -88,18 +87,16 @@ static int removeCallback(const char* fpath, const struct stat* sb,
|
|||
|
||||
bool DeleteFolder(const std::wstring& path) {
|
||||
return nftw(xe::to_string(path).c_str(), removeCallback, 64,
|
||||
FTW_DEPTH | FTW_PHYS) == 0
|
||||
? true
|
||||
: false;
|
||||
FTW_DEPTH | FTW_PHYS) == 0;
|
||||
}
|
||||
|
||||
static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) {
|
||||
// Linux uses number of seconds since 1/1/1970, and Windows uses
|
||||
static uint64_t convertUnixtimeToWinFiletime(const timespec& unixtime) {
|
||||
// Linux uses number of nanoseconds since 1/1/1970, and Windows uses
|
||||
// number of nanoseconds since 1/1/1601
|
||||
// so we convert linux time to nanoseconds and then add the number of
|
||||
// nanoseconds from 1601 to 1970
|
||||
// so we add the number of nanoseconds from 1601 to 1970
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -121,7 +118,7 @@ bool CreateFile(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 {
|
||||
|
@ -136,16 +133,16 @@ class PosixFileHandle : public FileHandle {
|
|||
size_t* out_bytes_read) override {
|
||||
ssize_t out = pread(handle_, buffer, buffer_length, file_offset);
|
||||
*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,
|
||||
size_t* out_bytes_written) override {
|
||||
ssize_t out = pwrite(handle_, buffer, buffer_length, file_offset);
|
||||
*out_bytes_written = out;
|
||||
return out >= 0 ? true : false;
|
||||
return out >= 0;
|
||||
}
|
||||
bool SetLength(size_t length) override {
|
||||
return ftruncate(handle_, length) >= 0 ? true : false;
|
||||
return ftruncate(handle_, length) >= 0;
|
||||
}
|
||||
void Flush() override { fsync(handle_); }
|
||||
|
||||
|
@ -155,7 +152,7 @@ class PosixFileHandle : public FileHandle {
|
|||
|
||||
std::unique_ptr<FileHandle> FileHandle::OpenExisting(std::wstring path,
|
||||
uint32_t desired_access) {
|
||||
int open_access;
|
||||
int open_access = 0;
|
||||
if (desired_access & FileAccess::kGenericRead) {
|
||||
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 (S_ISDIR(st.st_mode)) {
|
||||
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 {
|
||||
out_info->type = FileInfo::Type::kFile;
|
||||
out_info->total_size = st.st_size;
|
||||
}
|
||||
out_info->create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime);
|
||||
out_info->access_timestamp = convertUnixtimeToWinFiletime(st.st_atime);
|
||||
out_info->write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime);
|
||||
out_info->name = find_name_from_path(path);
|
||||
out_info->path = find_base_path(path);
|
||||
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 false;
|
||||
|
@ -210,20 +212,27 @@ std::vector<FileInfo> ListFiles(const std::wstring& path) {
|
|||
}
|
||||
|
||||
while (auto ent = readdir(dir)) {
|
||||
if (std::strcmp(ent->d_name, ".") == 0 ||
|
||||
std::strcmp(ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FileInfo info;
|
||||
|
||||
info.name = xe::to_wstring(ent->d_name);
|
||||
struct stat st;
|
||||
stat((xe::to_string(path) + xe::to_string(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);
|
||||
auto full_path = xe::to_string(xe::join_paths(path, info.name));
|
||||
auto ret = stat(full_path.c_str(), &st);
|
||||
assert_zero(ret);
|
||||
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) {
|
||||
info.type = FileInfo::Type::kDirectory;
|
||||
info.total_size = 0;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ bool PathExists(const std::wstring& path) {
|
|||
bool CreateFolder(const std::wstring& path) {
|
||||
size_t pos = 0;
|
||||
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);
|
||||
} while (pos != std::string::npos);
|
||||
return PathExists(path);
|
||||
|
|
|
@ -29,13 +29,14 @@
|
|||
|
||||
// For MessageBox:
|
||||
// TODO(benvanik): generic API? logging_win.cc?
|
||||
// TODO(dougvj): For now I just inlined a small difference for linux
|
||||
#if XE_PLATFORM_WIN32
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
|
||||
DEFINE_string(
|
||||
log_file, "",
|
||||
"Logs are written to the given file (specify stdout for command line)",
|
||||
DEFINE_string(log_file, "",
|
||||
"Logs are written to the given file (specify stdout/stderr "
|
||||
"for command line)",
|
||||
"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.",
|
||||
|
@ -63,6 +64,8 @@ class Logger {
|
|||
} else {
|
||||
if (cvars::log_file == "stdout") {
|
||||
file_ = stdout;
|
||||
} else if (cvars::log_file == "stderr") {
|
||||
file_ = stderr;
|
||||
} else {
|
||||
auto file_path = xe::to_wstring(cvars::log_file);
|
||||
xe::filesystem::CreateParentFolder(file_path);
|
||||
|
@ -330,6 +333,8 @@ void FatalError(const char* fmt, ...) {
|
|||
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
|
||||
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
|
||||
}
|
||||
#elif XE_PLATFORM_LINUX
|
||||
fprintf(stderr, fmt, args);
|
||||
#endif // WIN32
|
||||
ShutdownLogging();
|
||||
std::exit(1);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "build/version.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/main.h"
|
||||
|
||||
|
@ -33,6 +34,10 @@ extern "C" int main(int argc, char** argv) {
|
|||
// Initialize logging. Needs parsed FLAGS.
|
||||
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.
|
||||
int result = entry_info.entry_point(args);
|
||||
|
||||
|
|
|
@ -93,14 +93,16 @@
|
|||
namespace xe {
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
const char kPathSeparator = '\\';
|
||||
const wchar_t kWPathSeparator = L'\\';
|
||||
template <typename T>
|
||||
const T kPathSeparator = T('\\');
|
||||
const size_t kMaxPath = 260; // _MAX_PATH
|
||||
#else
|
||||
const char kPathSeparator = '/';
|
||||
const wchar_t kWPathSeparator = L'/';
|
||||
template <typename T>
|
||||
const T kPathSeparator = T('/');
|
||||
const size_t kMaxPath = 1024; // PATH_MAX
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
template <typename T>
|
||||
const T kAllPathSeparators[3] = {T('\\'), T('/'), '\0'};
|
||||
|
||||
// Launches a web browser to the given URL.
|
||||
void LaunchBrowser(const wchar_t* url);
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/platform_linux.h"
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include "xenia/base/string.h"
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ std::wstring to_wstring(const std::string& source) {
|
|||
#endif // XE_PLATFORM_LINUX
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string format_string(const char* format, va_list args) {
|
||||
if (!format) {
|
||||
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) {
|
||||
if (!format) {
|
||||
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) {
|
||||
#if XE_PLATFORM_WIN32
|
||||
wchar_t buffer[kMaxPath];
|
||||
_wfullpath(buffer, path.c_str(), sizeof(buffer) / sizeof(wchar_t));
|
||||
return buffer;
|
||||
#else
|
||||
char buffer[kMaxPath];
|
||||
realpath(xe::to_string(path).c_str(), buffer);
|
||||
return xe::to_wstring(buffer);
|
||||
return xe::to_wstring(to_absolute_path(xe::to_string(path)));
|
||||
#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()) {
|
||||
|
|
|
@ -23,16 +23,10 @@ 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, ...) {
|
||||
template <typename T>
|
||||
std::basic_string<T> format_string(const T* format, va_list args);
|
||||
template <typename T>
|
||||
inline std::basic_string<T> format_string(const T* format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
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);
|
||||
|
||||
// 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.
|
||||
std::vector<std::string> split_path(const std::string& path);
|
||||
std::vector<std::wstring> split_path(const std::wstring& path);
|
||||
template <typename T>
|
||||
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.
|
||||
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);
|
||||
template <typename T>
|
||||
std::basic_string<T> join_paths(const std::basic_string<T>& left,
|
||||
const std::basic_string<T>& right,
|
||||
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
|
||||
// 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);
|
||||
template <typename T>
|
||||
std::basic_string<T> fix_path_separators(const std::basic_string<T>& source,
|
||||
T new_sep = xe::kPathSeparator<T>) {
|
||||
// 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.
|
||||
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);
|
||||
template <typename T>
|
||||
std::basic_string<T> find_name_from_path(const std::basic_string<T>& path,
|
||||
T sep = xe::kPathSeparator<T>) {
|
||||
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.
|
||||
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);
|
||||
template <typename T>
|
||||
std::basic_string<T> find_base_path(const std::basic_string<T>& path,
|
||||
T sep = xe::kPathSeparator<T>) {
|
||||
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.
|
||||
// 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",
|
||||
},
|
||||
})
|
||||
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 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") {
|
||||
StringBuffer sb;
|
||||
uint32_t module_flags = 0x1000000;
|
||||
|
|
|
@ -50,8 +50,8 @@ struct TestCase {
|
|||
class TestSuite {
|
||||
public:
|
||||
TestSuite(const std::wstring& src_file_path) : src_file_path(src_file_path) {
|
||||
name = src_file_path.substr(src_file_path.find_last_of(xe::kPathSeparator) +
|
||||
1);
|
||||
name = src_file_path.substr(
|
||||
src_file_path.find_last_of(xe::kPathSeparator<char>) + 1);
|
||||
name = ReplaceExtension(name, L"");
|
||||
map_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".map";
|
||||
bin_file_path = xe::to_wstring(cvars::test_bin_path) + name + L".bin";
|
||||
|
|
|
@ -48,7 +48,7 @@ bool RawModule::LoadFile(uint32_t base_address, const std::wstring& path) {
|
|||
fclose(file);
|
||||
|
||||
// Setup debug info.
|
||||
auto last_slash = fixed_path.find_last_of(xe::kPathSeparator);
|
||||
auto last_slash = fixed_path.find_last_of(xe::kPathSeparator<wchar_t>);
|
||||
if (last_slash != std::string::npos) {
|
||||
name_ = xe::to_string(fixed_path.substr(last_slash + 1));
|
||||
} else {
|
||||
|
|
|
@ -241,7 +241,7 @@ X_STATUS Emulator::TerminateTitle() {
|
|||
X_STATUS Emulator::LaunchPath(std::wstring path) {
|
||||
// Launch based on file type.
|
||||
// This is a silly guess based on file extension.
|
||||
auto last_slash = path.find_last_of(xe::kPathSeparator);
|
||||
auto last_slash = path.find_last_of(xe::kPathSeparator<char>);
|
||||
auto last_dot = path.find_last_of('.');
|
||||
if (last_dot < last_slash) {
|
||||
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,
|
||||
bool call_entry) {
|
||||
// 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);
|
||||
if (name == raw_name) {
|
||||
assert_not_null(executable_module_);
|
||||
|
|
|
@ -21,6 +21,23 @@ namespace xe {
|
|||
namespace kernel {
|
||||
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() { 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.
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
object->Release();
|
||||
|
|
|
@ -77,9 +77,9 @@ std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) {
|
|||
|
||||
// Package root path:
|
||||
// content_root/title_id/type_name/
|
||||
auto package_root = xe::join_paths(
|
||||
root_path_, xe::join_paths(title_id_str, content_type_str));
|
||||
return package_root + xe::kWPathSeparator;
|
||||
auto package_root =
|
||||
xe::join_paths(root_path_, xe::join_paths(title_id, type_name));
|
||||
return package_root + xe::kPathSeparator<wchar_t>;
|
||||
}
|
||||
|
||||
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_path =
|
||||
xe::join_paths(package_root, xe::to_wstring(data.file_name));
|
||||
|
||||
// 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;
|
||||
}
|
||||
package_path += xe::kPathSeparator<char>;
|
||||
return package_path;
|
||||
}
|
||||
|
||||
|
@ -314,7 +308,7 @@ std::wstring ContentManager::ResolveGameUserContentPath() {
|
|||
root_path_,
|
||||
xe::join_paths(title_id,
|
||||
xe::join_paths(kGameUserContentDirName, user_name)));
|
||||
return package_root + xe::kWPathSeparator;
|
||||
return package_root + xe::kPathSeparator<wchar_t>;
|
||||
}
|
||||
|
||||
} // namespace xam
|
||||
|
|
|
@ -40,13 +40,14 @@ XModule::~XModule() {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (strcasecmp(name_.c_str(), name.c_str()) == 0) {
|
||||
if (strcasecmp(name_.c_str(), name_c) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp(path_.c_str(), name.c_str()) == 0) {
|
||||
if (strcasecmp(path_.c_str(), name_c) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -27,7 +27,8 @@ Entry::Entry(Device* device, Entry* parent, const std::string& path)
|
|||
access_timestamp_(0),
|
||||
write_timestamp_(0) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ Entry* VirtualFileSystem::ResolvePath(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);
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ Entry* VirtualFileSystem::CreatePath(const std::string& path,
|
|||
}
|
||||
auto parent_entry = partial_entry;
|
||||
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);
|
||||
if (!child_entry) {
|
||||
child_entry =
|
||||
|
@ -224,14 +224,14 @@ X_STATUS VirtualFileSystem::OpenFile(const std::string& path,
|
|||
// If no device or parent, fail.
|
||||
Entry* parent_entry = nullptr;
|
||||
Entry* entry = nullptr;
|
||||
if (!xe::find_base_path(path).empty()) {
|
||||
if (!xe::find_base_path(path, '\\').empty()) {
|
||||
parent_entry = ResolveBasePath(path);
|
||||
if (!parent_entry) {
|
||||
*out_action = FileAction::kDoesNotExist;
|
||||
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);
|
||||
} else {
|
||||
entry = ResolvePath(path);
|
||||
|
|
Loading…
Reference in New Issue