From 221c9739cbc1666a9d5a46ac7165f30f5c6d044d Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 20 Jan 2019 21:28:17 -0500 Subject: [PATCH 01/15] [string] Add base string tests --- src/xenia/base/testing/string_test.cc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/xenia/base/testing/string_test.cc diff --git a/src/xenia/base/testing/string_test.cc b/src/xenia/base/testing/string_test.cc new file mode 100644 index 000000000..fbdc2be0b --- /dev/null +++ b/src/xenia/base/testing/string_test.cc @@ -0,0 +1,25 @@ +/** +****************************************************************************** +* 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/string.h" + +#include "third_party/catch/include/catch.hpp" + +namespace xe { +namespace base { +namespace test { + +TEST_CASE("find_base_path") { + // TODO(bwrsandman): + REQUIRE(false); +} + +} // namespace test +} // namespace base +} // namespace xe From 738eb6564321d4cd38ce536bd6228d6755fb3c80 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 20 Jan 2019 19:39:40 -0500 Subject: [PATCH 02/15] [string] Combine string and wstring in template Merge kPathSeparator and kWPathSeparator to template variable. Generalize /, and \ in kAllPathSeparators. --- src/xenia/base/filesystem.cc | 8 +- src/xenia/base/filesystem_win.cc | 2 +- src/xenia/base/platform.h | 10 +- src/xenia/base/string.cc | 28 +-- src/xenia/base/string.h | 173 +++++++++++++++--- src/xenia/cpu/ppc/testing/ppc_testing_main.cc | 4 +- src/xenia/cpu/raw_module.cc | 2 +- src/xenia/emulator.cc | 2 +- src/xenia/kernel/kernel_state.cc | 2 +- src/xenia/kernel/xam/content_manager.cc | 6 +- src/xenia/kernel/xmodule.cc | 7 +- src/xenia/vfs/entry.cc | 2 +- 12 files changed, 185 insertions(+), 61 deletions(-) diff --git a/src/xenia/base/filesystem.cc b/src/xenia/base/filesystem.cc index a24bbaf85..f7b5f3751 100644 --- a/src/xenia/base/filesystem.cc +++ b/src/xenia/base/filesystem.cc @@ -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 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 { diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc index 2028a2d9a..fb2e9caf4 100644 --- a/src/xenia/base/filesystem_win.cc +++ b/src/xenia/base/filesystem_win.cc @@ -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, pos + 1); CreateDirectoryW(path.substr(0, pos).c_str(), nullptr); } while (pos != std::string::npos); return PathExists(path); diff --git a/src/xenia/base/platform.h b/src/xenia/base/platform.h index 54caae98f..063f80ba1 100644 --- a/src/xenia/base/platform.h +++ b/src/xenia/base/platform.h @@ -93,14 +93,16 @@ namespace xe { #if XE_PLATFORM_WIN32 -const char kPathSeparator = '\\'; -const wchar_t kWPathSeparator = L'\\'; +template +const T kPathSeparator = T('\\'); const size_t kMaxPath = 260; // _MAX_PATH #else -const char kPathSeparator = '/'; -const wchar_t kWPathSeparator = L'/'; +template +const T kPathSeparator = T('/'); const size_t kMaxPath = 1024; // PATH_MAX #endif // XE_PLATFORM_WIN32 +template +const T kAllPathSeparators[3] = {T('\\'), T('/'), '\0'}; // Launches a web browser to the given URL. void LaunchBrowser(const wchar_t* url); diff --git a/src/xenia/base/string.cc b/src/xenia/base/string.cc index 804127c6d..96ed6d235 100644 --- a/src/xenia/base/string.cc +++ b/src/xenia/base/string.cc @@ -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 split_path(const std::string& path) { - return split_string(path, "\\/"); -} - -std::vector 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()) { diff --git a/src/xenia/base/string.h b/src/xenia/base/string.h index 1ce7dafeb..574b1453f 100644 --- a/src/xenia/base/string.h +++ b/src/xenia/base/string.h @@ -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 +std::basic_string format_string(const T* format, va_list args); +template +inline std::basic_string format_string(const T* format, ...) { va_list va; va_start(va, format); auto result = format_string(format, va); @@ -52,36 +46,157 @@ 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 +std::basic_string to_absolute_path(const std::basic_string& path); +template +inline std::basic_string to_absolute_path(const T* path) { + return to_absolute_path(std::basic_string(path)); +} // Splits the given path on any valid path separator and returns all parts. -std::vector split_path(const std::string& path); -std::vector split_path(const std::wstring& path); +template +std::vector> split_path(const std::basic_string& path) { + std::vector> parts; + size_t n = 0; + size_t last = 0; + while ((n = path.find_first_of(kAllPathSeparators, last)) != + std::basic_string::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 +inline std::vector> split_path(const T* path) { + return split_path(std::basic_string(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 +std::basic_string join_paths(const std::basic_string& left, + const std::basic_string& right, + T sep = xe::kPathSeparator) { + 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 +inline std::basic_string join_paths(const std::basic_string& left, + const T* right, + T sep = xe::kPathSeparator) { + return join_paths(left, std::basic_string(right), sep); +} +template +inline std::basic_string join_paths(const T* left, + const std::basic_string& right, + T sep = xe::kPathSeparator) { + return join_paths(std::basic_string(left), right, sep); +} +template +inline std::basic_string join_paths(const T* left, const T* right, + T sep = xe::kPathSeparator) { + return join_paths(std::basic_string(left), std::basic_string(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 +std::basic_string fix_path_separators(const std::basic_string& source, + T new_sep = xe::kPathSeparator) { + // Swap all separators to new_sep. + T old_sep = new_sep == kAllPathSeparators[0] ? kAllPathSeparators[1] + : kAllPathSeparators[0]; + typename std::basic_string::size_type pos = 0; + std::basic_string dest = source; + while ((pos = source.find_first_of(old_sep, pos)) != + std::basic_string::npos) { + dest[pos] = new_sep; + ++pos; + } + // Replace redundant separators. + pos = 0; + while ((pos = dest.find_first_of(new_sep, pos)) != + std::basic_string::npos) { + if (pos < dest.size() - 1) { + if (dest[pos + 1] == new_sep) { + dest.erase(pos + 1, 1); + } + } + ++pos; + } + return dest; +} + +template +inline static std::basic_string fix_path_separators( + const T* source, T new_sep = xe::kPathSeparator) { + return fix_path_separators(std::basic_string(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 +std::basic_string find_name_from_path(const std::basic_string& path, + T sep = xe::kPathSeparator) { + std::basic_string name(path); + if (!path.empty()) { + typename std::basic_string::size_type from(std::basic_string::npos); + if (path.back() == sep) { + from = path.size() - 2; + } + auto pos(path.find_last_of(sep, from)); + if (pos != std::basic_string::npos) { + if (from == std::basic_string::npos) { + name = path.substr(pos + 1); + } else { + auto len(from - pos); + name = path.substr(pos + 1, len); + } + } + } + return name; +} +template +inline std::basic_string find_name_from_path(const T* path, + T sep = xe::kPathSeparator) { + return find_name_from_path(std::basic_string(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 +std::basic_string find_base_path(const std::basic_string& path, + T sep = xe::kPathSeparator) { + auto last_slash = path.find_last_of(sep); + if (last_slash == std::basic_string::npos) { + return path; + } else if (last_slash == path.length() - 1) { + auto prev_slash = path.find_last_of(sep, last_slash - 1); + if (prev_slash == std::basic_string::npos) { + return std::basic_string(); + } else { + return path.substr(0, prev_slash + 1); + } + } else { + return path.substr(0, last_slash + 1); + } +} +template +inline std::basic_string find_base_path(const T* path, + T sep = xe::kPathSeparator) { + return find_base_path(std::basic_string(path), sep); +} // Tests a match against a case-insensitive fuzzy filter. // Returns the score of the match or 0 if none. diff --git a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc index 17304124f..2af2df56f 100644 --- a/src/xenia/cpu/ppc/testing/ppc_testing_main.cc +++ b/src/xenia/cpu/ppc/testing/ppc_testing_main.cc @@ -49,8 +49,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) + 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"; diff --git a/src/xenia/cpu/raw_module.cc b/src/xenia/cpu/raw_module.cc index ae51067c7..2d2cf1abf 100644 --- a/src/xenia/cpu/raw_module.cc +++ b/src/xenia/cpu/raw_module.cc @@ -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); if (last_slash != std::string::npos) { name_ = xe::to_string(fixed_path.substr(last_slash + 1)); } else { diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index b1d976c9e..fe596838a 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -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); auto last_dot = path.find_last_of('.'); if (last_dot < last_slash) { last_dot = std::wstring::npos; diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 57097148e..5506290b7 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -353,7 +353,7 @@ void KernelState::LoadKernelModule(object_ref kernel_module) { object_ref 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_); diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 2ca34f716..d14f08308 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -85,7 +85,7 @@ std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { // content_root/title_id/type_name/ auto package_root = xe::join_paths(root_path_, xe::join_paths(title_id, type_name)); - return package_root + xe::kWPathSeparator; + return package_root + xe::kPathSeparator; } std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) { @@ -94,7 +94,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)); - package_path += xe::kPathSeparator; + package_path += xe::kPathSeparator; return package_path; } @@ -265,7 +265,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; } } // namespace xam diff --git a/src/xenia/kernel/xmodule.cc b/src/xenia/kernel/xmodule.cc index 0d734bad4..22a4c87fa 100644 --- a/src/xenia/kernel/xmodule.cc +++ b/src/xenia/kernel/xmodule.cc @@ -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; diff --git a/src/xenia/vfs/entry.cc b/src/xenia/vfs/entry.cc index d07795520..80fd11bfe 100644 --- a/src/xenia/vfs/entry.cc +++ b/src/xenia/vfs/entry.cc @@ -27,7 +27,7 @@ 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(), path, '\\'); name_ = xe::find_name_from_path(path); } From 6cde4f9e12b14b677cbc6531eca66483204c0cd0 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 20 Jan 2019 21:34:59 -0500 Subject: [PATCH 03/15] [logging] Fix log folder creation from same directory Expand find_base_path to support unix-style and local files. Remove trailing path separators from find_base_path output. Fix cases of local files such as `find_base_path("local_file")` Fix logger creating directories such as xenia.log/ when run from a local directory such as with ./build/bin/xenia under linux creating a directory called xenia.log/ and then being unable to create a log of the same name. Add tests to validate find_base_path. --- src/xenia/base/string.h | 21 ++++- src/xenia/base/testing/string_test.cc | 107 +++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 7 deletions(-) diff --git a/src/xenia/base/string.h b/src/xenia/base/string.h index 574b1453f..197982df9 100644 --- a/src/xenia/base/string.h +++ b/src/xenia/base/string.h @@ -154,10 +154,13 @@ std::basic_string find_name_from_path(const std::basic_string& path, if (!path.empty()) { typename std::basic_string::size_type from(std::basic_string::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::npos) { + if (pos != std::basic_string::npos && pos != from) { if (from == std::basic_string::npos) { name = path.substr(pos + 1); } else { @@ -178,18 +181,28 @@ inline std::basic_string find_name_from_path(const T* path, template std::basic_string find_base_path(const std::basic_string& path, T sep = xe::kPathSeparator) { + auto path_start = path.find_first_of(T(':')); + if (path_start == std::basic_string::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::npos) { - return path; + return std::basic_string(); + } 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::npos) { return std::basic_string(); + } else if (prev_slash == path_start) { + return path.substr(0, path_start + 1); } else { - return path.substr(0, prev_slash + 1); + return path.substr(0, prev_slash); } } else { - return path.substr(0, last_slash + 1); + return path.substr(0, last_slash); } } template diff --git a/src/xenia/base/testing/string_test.cc b/src/xenia/base/testing/string_test.cc index fbdc2be0b..df0698bb1 100644 --- a/src/xenia/base/testing/string_test.cc +++ b/src/xenia/base/testing/string_test.cc @@ -15,9 +15,110 @@ namespace xe { namespace base { namespace test { -TEST_CASE("find_base_path") { - // TODO(bwrsandman): - REQUIRE(false); +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"); } } // namespace test From 2970b84bc1663d04c37d47ddd4f75a6ed8f89afc Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 27 Jan 2019 10:10:04 -0500 Subject: [PATCH 04/15] [string] Remove reuse of va_list in AppendVarargs va_list are not guarenteed to maintain their values after being used. With clang on Linux, args is undefined after fetching length and will print "(null)". Copy args into another va_list before getting length to prevent this. Add tests. --- src/xenia/base/string_buffer.cc | 5 ++++- src/xenia/base/testing/string_test.cc | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/xenia/base/string_buffer.cc b/src/xenia/base/string_buffer.cc index f7b4c8889..ac8a924d6 100644 --- a/src/xenia/base/string_buffer.cc +++ b/src/xenia/base/string_buffer.cc @@ -64,7 +64,10 @@ void StringBuffer::AppendFormat(const char* format, ...) { } void StringBuffer::AppendVarargs(const char* format, va_list args) { - int length = vsnprintf(nullptr, 0, format, args); + va_list size_args; + va_copy(size_args, args); // arg is indeterminate after the return so copy it + int length = vsnprintf(nullptr, 0, format, size_args); + va_end(size_args); Grow(length + 1); vsnprintf(buffer_ + buffer_offset_, buffer_capacity_, format, args); buffer_offset_ += length; diff --git a/src/xenia/base/testing/string_test.cc b/src/xenia/base/testing/string_test.cc index df0698bb1..4c1e2d164 100644 --- a/src/xenia/base/testing/string_test.cc +++ b/src/xenia/base/testing/string_test.cc @@ -8,6 +8,7 @@ */ #include "xenia/base/string.h" +#include "xenia/base/string_buffer.h" #include "third_party/catch/include/catch.hpp" @@ -121,6 +122,19 @@ TEST_CASE("find_base_path", "string") { L"/home/xenia/New Volume"); } +TEST_CASE("StringBuffer") { + StringBuffer sb; + uint32_t module_flags = 0x1000000; + + std::string path_(R"(\Device\Cdrom0\default.xex)"); + sb.AppendFormat("Module %s:\n", path_.c_str()); + REQUIRE(sb.to_string() == "Module \\Device\\Cdrom0\\default.xex:\n"); + sb.AppendFormat(" Module Flags: %.8X\n", module_flags); + REQUIRE( + sb.to_string() == + "Module \\Device\\Cdrom0\\default.xex:\n Module Flags: 01000000\n"); +} + } // namespace test } // namespace base } // namespace xe From e092c8c1875f5c0227a46becc8e1f0e94bd129b1 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 15 Jul 2017 17:43:44 -0600 Subject: [PATCH 05/15] base: logging: Add Linux support, add stderr output support --- src/xenia/base/logging.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/xenia/base/logging.cc b/src/xenia/base/logging.cc index 238969513..a113557ad 100644 --- a/src/xenia/base/logging.cc +++ b/src/xenia/base/logging.cc @@ -29,14 +29,15 @@ // 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)", - "Logging"); +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.", "Logging"); @@ -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); From 959ad9f0cc2abf793706a2ac9fb49872073281df Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Sat, 15 Jul 2017 17:44:37 -0600 Subject: [PATCH 06/15] base: main_posix: Print build version and date, like the windows main --- src/xenia/base/main_posix.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xenia/base/main_posix.cc b/src/xenia/base/main_posix.cc index 1a56b57c2..25c2d67c4 100644 --- a/src/xenia/base/main_posix.cc +++ b/src/xenia/base/main_posix.cc @@ -7,6 +7,7 @@ ****************************************************************************** */ +#include "build/version.h" #include "xenia/base/cvar.h" #include "xenia/base/main.h" @@ -32,6 +33,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); From 0ac572a28bcaae2ffd71aad544711cefc80674df Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 3 Feb 2019 09:58:57 -0500 Subject: [PATCH 07/15] [fs linux] Fix ListFiles Skip recursive "." and ".." paths. Join paths instead of concatenating them. Assert stat return code. --- src/xenia/base/filesystem_posix.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index caf8219ad..777dbd507 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -19,10 +19,11 @@ #include #include #include -#include #include #include #include +#include +#include #include namespace xe { @@ -210,11 +211,18 @@ std::vector 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); + 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_ctime); info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime); info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime); @@ -223,7 +231,7 @@ std::vector ListFiles(const std::wstring& path) { info.total_size = 0; } else { info.type = FileInfo::Type::kFile; - info.total_size = st.st_size; + info.total_size = static_cast(st.st_size); } result.push_back(info); } From e22cc86067875489ec3adc4814d4e940b6fcc337 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 3 Feb 2019 10:15:15 -0500 Subject: [PATCH 08/15] [fs linux] Fix sep of absolute paths of entries --- src/xenia/vfs/entry.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/xenia/vfs/entry.cc b/src/xenia/vfs/entry.cc index 80fd11bfe..4762f6bd8 100644 --- a/src/xenia/vfs/entry.cc +++ b/src/xenia/vfs/entry.cc @@ -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); } From 6e5f1fd610f09701b8d4b2cd9c6534f4533737ba Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Mon, 15 Jul 2019 18:39:29 -0400 Subject: [PATCH 09/15] [kernel] Add demangling of type_id for linux --- src/xenia/kernel/util/object_table.cc | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/xenia/kernel/util/object_table.cc b/src/xenia/kernel/util/object_table.cc index 20c1a9ceb..e43b82067 100644 --- a/src/xenia/kernel/util/object_table.cc +++ b/src/xenia/kernel/util/object_table.cc @@ -21,6 +21,23 @@ namespace xe { namespace kernel { namespace util { +namespace { +#if defined(XE_PLATFORM_LINUX) +#include +#include +std::string demangle(const char* mangled_name) { + std::size_t len = 0; + int status = 0; + std::unique_ptr 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(); From 546b21819b3b88b107bb72b01a87529d1e44e12f Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Mon, 15 Jul 2019 21:00:34 -0400 Subject: [PATCH 10/15] [fs linux] Specify NT style step --- src/xenia/vfs/virtual_file_system.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index aae42a1f0..fc7e7cdde 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -128,7 +128,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); } @@ -146,7 +146,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 = @@ -196,14 +196,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); From 6a3fe3eba79cd6d742b574d21d6286cc69381e32 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Tue, 16 Jul 2019 15:57:03 -0400 Subject: [PATCH 11/15] [fs linux] Zero-init access flag in OpenExisting --- src/xenia/base/filesystem_posix.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 777dbd507..7607fa432 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -156,7 +156,7 @@ class PosixFileHandle : public FileHandle { std::unique_ptr 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; } From c1ad9032437174dd16f4ba91d54e70d58809582c Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Fri, 23 Aug 2019 08:16:08 +0200 Subject: [PATCH 12/15] [fs linux] Cleanup headers Remove unused platform_linux reference. Replace C-style header uses. Replace C-style NULL with nullptr. Replace stdlib assert with xenia assert. Fix spelling --- src/xenia/base/exception_handler_linux.cc | 1 - src/xenia/base/filesystem_posix.cc | 8 ++++---- src/xenia/base/platform_linux.cc | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/xenia/base/exception_handler_linux.cc b/src/xenia/base/exception_handler_linux.cc index bc656a15d..040e23dd7 100644 --- a/src/xenia/base/exception_handler_linux.cc +++ b/src/xenia/base/exception_handler_linux.cc @@ -11,7 +11,6 @@ #include "xenia/base/assert.h" #include "xenia/base/math.h" -#include "xenia/base/platform_linux.h" namespace xe { diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 7607fa432..6b55b146b 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -46,7 +46,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); @@ -54,12 +54,12 @@ 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; } diff --git a/src/xenia/base/platform_linux.cc b/src/xenia/base/platform_linux.cc index aae0c7e21..26093a8b5 100644 --- a/src/xenia/base/platform_linux.cc +++ b/src/xenia/base/platform_linux.cc @@ -7,8 +7,7 @@ ****************************************************************************** */ -#include "xenia/base/platform_linux.h" -#include +#include #include #include "xenia/base/string.h" From 10ea3faa8fc831b7efb75d5f2586c9848b3be730 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Fri, 23 Aug 2019 08:18:14 +0200 Subject: [PATCH 13/15] [fs linux] Remove extra '/' from user folder Use join_paths instead of string concatenation. --- src/xenia/base/filesystem_posix.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 6b55b146b..65fd0c653 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -64,7 +64,7 @@ std::wstring GetUserFolder() { } 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) { From ee0b7904edbcfa09f8d4962f0fe218953cfc1d26 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sat, 24 Aug 2019 19:13:53 +0200 Subject: [PATCH 14/15] [fs linux]: Add more info to GetInfo Fill in name, path and total_size. Correct convertUnixtimeToWinFiletime which was using deprecated stat function without nanoseconds. It now includes nanoseconds. Add unit test for file and folder info. --- src/xenia/base/filesystem_posix.cc | 33 +++++++------ src/xenia/base/testing/filesystem_test.cc | 56 +++++++++++++++++++++++ src/xenia/base/testing/premake5.lua | 7 +++ src/xenia/base/testing/res/test_file | 1 + 4 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 src/xenia/base/testing/filesystem_test.cc create mode 100644 src/xenia/base/testing/res/test_file diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 65fd0c653..61d8709c9 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -9,19 +9,17 @@ #include "xenia/base/assert.h" #include "xenia/base/filesystem.h" -#include "xenia/base/logging.h" #include "xenia/base/string.h" -#include #include #include #include -#include #include -#include #include #include #include +#include +#include #include #include #include @@ -94,13 +92,13 @@ bool DeleteFolder(const std::wstring& path) { : false; } -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; } @@ -191,12 +189,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; @@ -223,9 +226,9 @@ std::vector ListFiles(const std::wstring& path) { 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_ctime); - info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime); - 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) { info.type = FileInfo::Type::kDirectory; info.total_size = 0; diff --git a/src/xenia/base/testing/filesystem_test.cc b/src/xenia/base/testing/filesystem_test.cc new file mode 100644 index 000000000..a6450e48d --- /dev/null +++ b/src/xenia/base/testing/filesystem_test.cc @@ -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 diff --git a/src/xenia/base/testing/premake5.lua b/src/xenia/base/testing/premake5.lua index 9570a76f5..dbeb8aed4 100644 --- a/src/xenia/base/testing/premake5.lua +++ b/src/xenia/base/testing/premake5.lua @@ -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) \ No newline at end of file diff --git a/src/xenia/base/testing/res/test_file b/src/xenia/base/testing/res/test_file new file mode 100644 index 000000000..c94444439 --- /dev/null +++ b/src/xenia/base/testing/res/test_file @@ -0,0 +1 @@ +Test file to test the xe::filesystem::GetInfo function on both Windows and Linux From b085837f3be26256a05ee7677e555a118ea99d6f Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sat, 24 Aug 2019 22:33:30 +0200 Subject: [PATCH 15/15] [fs linux]: Simplify terniary expressions to bool --- src/xenia/base/filesystem_posix.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 61d8709c9..830088483 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -87,9 +87,7 @@ 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(const timespec& unixtime) { @@ -120,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 { @@ -135,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_); }