Merge remote-tracking branch 'bwr/linux_filesystem' into canary

This commit is contained in:
Prism Tutaj 2019-09-07 20:58:20 -05:00
commit 3bbc8431ad
23 changed files with 451 additions and 113 deletions

View File

@ -11,7 +11,6 @@
#include "xenia/base/assert.h"
#include "xenia/base/math.h"
#include "xenia/base/platform_linux.h"
namespace xe {

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -7,8 +7,7 @@
******************************************************************************
*/
#include "xenia/base/platform_linux.h"
#include <stdlib.h>
#include <cstdlib>
#include <string>
#include "xenia/base/string.h"

View File

@ -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()) {

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1 @@
Test file to test the xe::filesystem::GetInfo function on both Windows and Linux

View File

@ -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;

View File

@ -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";

View File

@ -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 {

View File

@ -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;

View File

@ -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_);

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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);