From 97183ffc1bbc1876afabeabf698e83ba7f9cc75b Mon Sep 17 00:00:00 2001 From: totlmstr Date: Sat, 10 Aug 2019 16:22:09 -0700 Subject: [PATCH] io: Refactoring with classes (#505) --- .gitmodules | 3 + src/emulator/gui/src/gui.cpp | 2 +- src/emulator/host/CMakeLists.txt | 2 +- src/emulator/interface.cpp | 7 +- src/emulator/interface.h | 5 + src/emulator/io/CMakeLists.txt | 21 +- .../io/{VitaIoDevice.def => VitaIoDevice.h} | 46 +- src/emulator/io/include/io/device.h | 126 +++ src/emulator/io/include/io/file.h | 2 +- src/emulator/io/include/io/filesystem.h | 74 ++ src/emulator/io/include/io/functions.h | 35 +- src/emulator/io/include/io/io.h | 29 +- src/emulator/io/include/io/state.h | 97 ++- src/emulator/io/include/io/types.h | 4 +- src/emulator/io/include/io/util.h | 124 +++ src/emulator/io/include/io/vfs.h | 31 + src/emulator/io/src/device.cpp | 66 ++ src/emulator/io/src/filesystem.cpp | 60 ++ src/emulator/io/src/io.cpp | 786 +++++++----------- src/emulator/io/src/state_functions.cpp | 61 ++ .../modules/SceAppUtil/SceAppUtil.cpp | 61 +- .../modules/SceIofilemgr/SceIofilemgr.cpp | 12 +- .../modules/SceLibKernel/SceLibKernel.cpp | 62 +- src/emulator/modules/SceLibc/SceLibc.cpp | 2 +- .../modules/SceProcessmgr/SceProcessmgr.cpp | 6 +- src/emulator/modules/module_parent.cpp | 5 +- src/emulator/np/src/trophy/context.cpp | 21 +- src/emulator/np/src/trophy/trp_parser.cpp | 2 + src/emulator/util/include/util/string_utils.h | 1 + src/emulator/util/src/util.cpp | 8 + src/external/CMakeLists.txt | 4 + src/external/better-enums | 1 + 32 files changed, 1070 insertions(+), 696 deletions(-) rename src/emulator/io/include/io/{VitaIoDevice.def => VitaIoDevice.h} (63%) create mode 100644 src/emulator/io/include/io/device.h create mode 100644 src/emulator/io/include/io/filesystem.h create mode 100644 src/emulator/io/include/io/util.h create mode 100644 src/emulator/io/include/io/vfs.h create mode 100644 src/emulator/io/src/device.cpp create mode 100644 src/emulator/io/src/filesystem.cpp create mode 100644 src/emulator/io/src/state_functions.cpp create mode 160000 src/external/better-enums diff --git a/.gitmodules b/.gitmodules index 1dc83d9be..bea83912b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -69,3 +69,6 @@ [submodule "src/external/vita-toolchain"] path = src/external/vita-toolchain url = https://github.com/vita3k/vita-toolchain +[submodule "src/external/better-enums"] + path = src/external/better-enums + url = https://github.com/aantron/better-enums.git diff --git a/src/emulator/gui/src/gui.cpp b/src/emulator/gui/src/gui.cpp index a3aacaab5..fd0239ca4 100644 --- a/src/emulator/gui/src/gui.cpp +++ b/src/emulator/gui/src/gui.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/emulator/host/CMakeLists.txt b/src/emulator/host/CMakeLists.txt index 62a8c03fe..400fd17df 100644 --- a/src/emulator/host/CMakeLists.txt +++ b/src/emulator/host/CMakeLists.txt @@ -13,7 +13,7 @@ add_library( ) target_include_directories(host PUBLIC include) -target_link_libraries(host PUBLIC audio config ctrl dialog io kernel net nids np renderer sdl2 touch) +target_link_libraries(host PUBLIC audio config ctrl dialog io kernel miniz net nids np renderer sdl2 touch) if(USE_GDBSTUB) target_link_libraries(host PUBLIC gdbstub) endif() diff --git a/src/emulator/interface.cpp b/src/emulator/interface.cpp index e05bf24d7..5d91866e2 100644 --- a/src/emulator/interface.cpp +++ b/src/emulator/interface.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -153,7 +153,6 @@ static bool install_vpk(Ptr &entry_point, HostState &host, GuiState mz_zip_reader_extract_to_file(zip.get(), i, file_output.generic_path().string().c_str(), 0); } } - fs::create_directories(fs::path(host.pref_path) / "ux0/user/00/savedata" / host.io.title_id); LOG_INFO("{} installed succesfully!", host.io.title_id); return true; @@ -243,6 +242,10 @@ static ExitCode load_app_impl(Ptr &entry_point, HostState &host, Gui } else return FileNotFound; + host.io.pref_save_path = host.pref_path + "/ux0/user/00/savedata/" + host.io.title_id + '/'; + if (!fs::exists(host.io.pref_save_path)) + fs::create_directories(host.io.pref_save_path); + return Success; } diff --git a/src/emulator/interface.h b/src/emulator/interface.h index 829406f83..c63a9ad2d 100644 --- a/src/emulator/interface.h +++ b/src/emulator/interface.h @@ -21,8 +21,13 @@ #include #include +#include + struct GuiState; +typedef std::shared_ptr ZipPtr; +typedef std::shared_ptr ZipFilePtr; + inline void delete_zip(mz_zip_archive *zip) { mz_zip_reader_end(zip); delete zip; diff --git a/src/emulator/io/CMakeLists.txt b/src/emulator/io/CMakeLists.txt index 7c7340ccb..c231384f5 100644 --- a/src/emulator/io/CMakeLists.txt +++ b/src/emulator/io/CMakeLists.txt @@ -1,15 +1,22 @@ add_library( io STATIC - src/io.cpp - src/file.cpp - include/io/io.h - include/io/functions.h - include/io/state.h + include/io/device.h include/io/file.h + include/io/filesystem.h + include/io/functions.h + include/io/io.h + include/io/state.h include/io/types.h - include/io/VitaIoDevice.def + include/io/util.h + include/io/vfs.h + include/io/VitaIoDevice.h + src/device.cpp + src/file.cpp + src/filesystem.cpp + src/io.cpp + src/state_functions.cpp ) target_include_directories(io PUBLIC include) -target_link_libraries(io PUBLIC rtc miniz vita-headers dirent mem) +target_link_libraries(io PUBLIC better-enums dirent mem rtc util vita-headers) diff --git a/src/emulator/io/include/io/VitaIoDevice.def b/src/emulator/io/include/io/VitaIoDevice.h similarity index 63% rename from src/emulator/io/include/io/VitaIoDevice.def rename to src/emulator/io/include/io/VitaIoDevice.h index 52c6e3a03..eeaecc1fb 100644 --- a/src/emulator/io/include/io/VitaIoDevice.def +++ b/src/emulator/io/include/io/VitaIoDevice.h @@ -15,26 +15,28 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#ifndef DEVICE - #define DEVICE(x,y) -#endif +#pragma once -DEVICE("addcont0", ADDCONT0) -DEVICE("app0", APP0) -DEVICE("gro0", GRO0) -DEVICE("grw0", GRW0) -DEVICE("os0", OS0) -DEVICE("pd0", PD0) -DEVICE("sa0", SA0) -DEVICE("savedata0", SAVEDATA0) -DEVICE("savedata1", SAVEDATA1) -DEVICE("sd0", SD0) -DEVICE("tm0", TM0) -DEVICE("tty0", TTY0) -DEVICE("tty1", TTY1) -DEVICE("ud0", UD0) -DEVICE("uma0", UMA0) -DEVICE("ur0", UR0) -DEVICE("ux0", UX0) -DEVICE("vd0", VD0) -DEVICE("vs0", VS0) +#include + +BETTER_ENUM(VitaIoDevice, int, + addcont0 = 0, + app0, + gro0, + grw0, + os0, + pd0, + sa0, + savedata0, + savedata1, + sd0, + tm0, + tty0, + tty1, + ud0, + uma0, + ur0, + ux0, + vd0, + vs0, + _INVALID = -1) diff --git a/src/emulator/io/include/io/device.h b/src/emulator/io/include/io/device.h new file mode 100644 index 000000000..693bdd198 --- /dev/null +++ b/src/emulator/io/include/io/device.h @@ -0,0 +1,126 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#include + +#include + +#include + +namespace device { + +/** + * \brief Get a Vita device from a given path. + * \param path The input path to be tested. + * \return The path's root as an enumeration. Otherwise, VitaIoDevice::_INVALID. + */ +inline VitaIoDevice get_device(const std::string &path) { + if (path.empty()) + return VitaIoDevice::_INVALID; + + const auto colon = path.find_first_of(':'); + if (colon == std::string::npos) + return VitaIoDevice::_INVALID; + + const auto p = path.substr(0, colon); + const auto dev = VitaIoDevice::_from_string_nocase_nothrow(p.c_str()); + if (VitaIoDevice::_is_valid(dev)) + return VitaIoDevice::_from_string(p.c_str()); + + return VitaIoDevice::_INVALID; +} + +/** + * \brief Get a valid Vita device as a string. + * \param dev The input Vita device needed. + * \param with_colon Output the string appended with a colon (default: false) + * \return A string version of the Vita device. + */ +inline std::string get_device_string(const VitaIoDevice dev, const bool with_colon = false) { + return with_colon ? std::string(dev._to_string()).append(":") : dev._to_string(); +} + +/** + * \brief Check if the device is a valid output path. + * \param device Input device to be checked. + * \return True if valid, False otherwise. + */ +inline bool is_valid_output_path(const VitaIoDevice device) { + return !(device == VitaIoDevice::savedata0 || device == VitaIoDevice::savedata1 || device == VitaIoDevice::app0 + || device == VitaIoDevice::_INVALID || device == VitaIoDevice::addcont0 || device == VitaIoDevice::tty0 + || device == VitaIoDevice::tty1); +} + +/** + * \brief Check if the device string is valid. + * \param device Input device to be checked. + * \return True if valid, False otherwise. + */ +inline bool is_valid_output_path(const std::string &device) { + return !(device == (+VitaIoDevice::savedata0)._to_string() || device == (+VitaIoDevice::savedata1)._to_string() || device == (+VitaIoDevice::app0)._to_string() + || device == (+VitaIoDevice::_INVALID)._to_string() || device == (+VitaIoDevice::addcont0)._to_string() || device == (+VitaIoDevice::tty0)._to_string() + || device == (+VitaIoDevice::tty1)._to_string()); +} + +/** + * \brief Construct a normalized path (optionally with an extension) to be outputted onto the Vita. + * \param dev The input Vita device. + * \param path The Vita location needed. + * \param ext The extension of the file (optional). + * \return An std::string of a real Vita translated path. + */ +std::string construct_normalized_path(VitaIoDevice dev, const std::string &path, const std::string &ext = ""); + +/** + * \brief Remove a device string from the path, and optionally prepend it with a different string. + * \param path Input path to be modified. + * \param device The device to be removed. + * \param mod_path The new path to prepend the path (optional). + * \return The string without the device, normalized. + */ +std::string remove_device_from_path(const std::string &path, VitaIoDevice device, const std::string &mod_path = ""); + +/** + * \brief Used for removing an extra device from a path. (Note: Not intended to be a permanent solution.) + * \param path The input Vita path. + * \param device The device to be removed. + * \return New path without the duplicate device. + */ +inline std::string remove_duplicate_device(const std::string &path, VitaIoDevice &device) { + auto cur_path = remove_device_from_path(path, device); + if (get_device(cur_path) != VitaIoDevice::_INVALID) { + device = get_device(cur_path); + return cur_path; + } + + return path; +} + +/** + * \brief Construct the emulated Vita path (optionally with an extension). + * \param dev The input Vita device used. + * \param path The path of the file. + * \param base_path The main output for the file. + * \param ext The extension of the file (optional). + * \return A complete Boost.Filesystem path normalized. + */ +inline fs::path construct_emulated_path(const VitaIoDevice dev, const fs::path &path, const std::string &base_path, const std::string &ext = "") { + return fs_utils::construct_file_name(base_path, get_device_string(dev, false), path, ext); +} +} // namespace device diff --git a/src/emulator/io/include/io/file.h b/src/emulator/io/include/io/file.h index 439d47df0..c7cd27b2e 100644 --- a/src/emulator/io/include/io/file.h +++ b/src/emulator/io/include/io/file.h @@ -17,7 +17,7 @@ #pragma once -#include +#include #include #include diff --git a/src/emulator/io/include/io/filesystem.h b/src/emulator/io/include/io/filesystem.h new file mode 100644 index 000000000..2de685951 --- /dev/null +++ b/src/emulator/io/include/io/filesystem.h @@ -0,0 +1,74 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#include + +#ifdef WIN32 +#include +#endif + +#include + +typedef std::shared_ptr FilePtr; + +// For opening Boost.Filesystem files, Boost returns wide strings for Windows, normal strings for other OS +// Dirent and FILE only accept and return wide char strings for Windows, and normal for other OS +#ifdef WIN32 +const wchar_t *translate_open_mode(const int flags); + +inline FilePtr create_shared_file(const fs::path &path, const int open_mode) { + const auto file = _wfopen(path.generic_path().wstring().c_str(), translate_open_mode(open_mode)); + return file ? FilePtr(file, std::fclose) : FilePtr(); +} + +typedef std::shared_ptr<_WDIR> DirPtr; + +inline DirPtr create_shared_dir(const fs::path &path) { + return DirPtr(_wopendir(path.generic_path().wstring().c_str()), _wclosedir); +} + +inline std::string get_file_in_dir(const _wdirent *file) { + return string_utils::wide_to_utf(file->d_name); +} + +inline _wdirent *get_system_dir_ptr(const DirPtr &dir) { + return _wreaddir(dir.get()); +} +#else +const char *translate_open_mode(const int flags); + +inline FilePtr create_shared_file(const fs::path &path, const int open_mode) { + const auto file = fopen(path.generic_path().string().c_str(), translate_open_mode(open_mode)); + return file ? FilePtr(file, std::fclose) : FilePtr(); +} + +typedef std::shared_ptr DirPtr; + +inline DirPtr create_shared_dir(const fs::path &path) { + return DirPtr(opendir(path.generic_path().string().c_str()), closedir); +} + +inline std::string get_file_in_dir(dirent *file) { + return std::string(file->d_name); +} + +inline dirent *get_system_dir_ptr(DirPtr dir) { + return readdir(dir.get()); +} +#endif diff --git a/src/emulator/io/include/io/functions.h b/src/emulator/io/include/io/functions.h index bfe8f7c24..e49ba4a48 100644 --- a/src/emulator/io/include/io/functions.h +++ b/src/emulator/io/include/io/functions.h @@ -17,33 +17,38 @@ #pragma once -#include +#undef st_atime +#undef st_ctime +#undef st_mtime +#include +#include +#include #include + #include -#include #include struct IOState; -struct SceIoStat; -struct SceIoDirent; + +inline SceUID invalid_fd = -1; void init_device_paths(IOState &io); - bool init(IOState &io, const fs::path &base_path, const fs::path &pref_path); -SceUID open_file(IOState &io, const std::string &path_, int flags, const char *pref_path, const char *export_name); + +SceUID open_file(IOState &io, const char *path, const int flags, const std::string &pref_path, const char *export_name); int read_file(void *data, IOState &io, SceUID fd, SceSize size, const char *export_name); int write_file(SceUID fd, const void *data, SceSize size, const IOState &io, const char *export_name); -SceOff seek_file(SceUID fd, SceOff offset, int whence, IOState &io, const char *export_name); +SceOff seek_file(SceUID fd, SceOff offset, SceIoSeekMode whence, IOState &io, const char *export_name); +SceOff tell_file(IOState &io, const SceUID fd, const char *export_name); +int stat_file(IOState &io, const char *file, SceIoStat *statp, const std::string &pref_path, SceUInt64 base_tick, const char *export_name, SceUID fd = invalid_fd); +int stat_file_by_fd(IOState &io, const SceUID fd, SceIoStat *statp, const std::string &pref_path, SceUInt64 base_tick, const char *export_name); int close_file(IOState &io, SceUID fd, const char *export_name); -int create_dir(IOState &io, const char *dir, int mode, const char *pref_path, const char *export_name, const bool recursive = false); -int remove_file(IOState &io, const char *file, const char *pref_path, const char *export_name); -int remove_dir(IOState &io, const char *dir, const char *pref_path, const char *export_name); -int stat_file(IOState &io, const char *file, SceIoStat *stat, const char *pref_path, uint64_t base_tick, const char *export_name); -int stat_file_by_fd(IOState &io, const int fd, SceIoStat *statp, const char *pref_path, uint64_t base_tick, const char *export_name); -SceOff tell_file(IOState &io, SceUID fd, const char *export_name); +int remove_file(IOState &io, const char *file, const std::string &pref_path, const char *export_name); -int open_dir(IOState &io, const char *path, const char *pref_path, const char *export_name); -int read_dir(IOState &io, SceUID fd, emu::SceIoDirent *dent, const char *pref_path, uint64_t base_tick, const char *export_name); +SceUID open_dir(IOState &io, const char *path, const std::string &pref_path, const char *export_name); +SceUID read_dir(IOState &io, SceUID fd, emu::SceIoDirent *dent, const std::string &pref_path, const SceUInt64 base_tick, const char *export_name); +int create_dir(IOState &io, const char *dir, int mode, const std::string &pref_path, const char *export_name, const bool recursive = false); int close_dir(IOState &io, SceUID fd, const char *export_name); +int remove_dir(IOState &io, const char *dir, const std::string &pref_path, const char *export_name); diff --git a/src/emulator/io/include/io/io.h b/src/emulator/io/include/io/io.h index c17d8f43c..573895766 100644 --- a/src/emulator/io/include/io/io.h +++ b/src/emulator/io/include/io/io.h @@ -17,27 +17,8 @@ #pragma once -#include - -#define SCE_ERROR_ERRNO_ENOENT 0x80010002 // Associated file or directory does not exist -#define SCE_ERROR_ERRNO_EEXIST 0x80010011 // File exists -#define SCE_ERROR_ERRNO_EMFILE 0x80010018 // Too many files are open -#define SCE_ERROR_ERRNO_EBADFD 0x80010051 // File descriptor is invalid for this operation - -#define JOIN_DEVICE(p) VitaIoDevice::p -#define DEVICE(path, name) name, -enum class VitaIoDevice { -#include - - _UKNONWN = -1, - _INVALID = -2, -}; -#undef DEVICE - -namespace vfs { -using FileBuffer = std::vector; - -constexpr const char *get_device_string(VitaIoDevice dev, bool with_colon = false); -bool read_file(VitaIoDevice device, FileBuffer &buf, const std::string &pref_path, const fs::path &vfs_file_path); -bool read_app_file(FileBuffer &buf, const std::string &pref_path, const std::string &title_id, const fs::path &vfs_file_path); -} // namespace vfs +constexpr int SCE_ERROR_ERRNO_ENOENT = 0x80010002; // Associated file or directory does not exist +constexpr int SCE_ERROR_ERRNO_EEXIST = 0x80010011; // File exists +constexpr int SCE_ERROR_ERRNO_EMFILE = 0x80010018; // Too many files are open +constexpr int SCE_ERROR_ERRNO_EBADFD = 0x80010051; // File descriptor is invalid for this operation +constexpr int SCE_ERROR_ERRNO_EOPNOTSUPP = 0x8001005F; // Operation not supported diff --git a/src/emulator/io/include/io/state.h b/src/emulator/io/include/io/state.h index 8ccaa9ba9..932305eb7 100644 --- a/src/emulator/io/include/io/state.h +++ b/src/emulator/io/include/io/state.h @@ -17,51 +17,83 @@ #pragma once -#include +#include +#include -#include - -#include #include -#include -#ifdef WIN32 -struct _WDIR; -#else -#include -#endif +// Class for all needed information to access files on Vita3K. +class FileStats : public VitaStats { + // Shared file pointer + FilePtr wrapped_file; -typedef std::shared_ptr FilePtr; -typedef std::shared_ptr ZipPtr; -typedef std::shared_ptr ZipFilePtr; +public: + // Constructor used for files + // Based on https://codereview.stackexchange.com/questions/4679/ + explicit FileStats(const char *vita, const std::string &t, const fs::path &file, const int open) { + wrapped_file = create_shared_file(file, open); -#ifdef _WIN32 -typedef std::shared_ptr<_WDIR> DirPtr; -#else -typedef std::shared_ptr DirPtr; -#endif + file_info.vita_loc = vita; + file_info.translated = t; + file_info.sys_loc = file; + file_info.open_mode = open; + file_info.file_mode = SCE_SO_IFREG | SCE_SO_IROTH; + file_info.access_mode = SCE_S_IFREG; + } -enum TtyType : std::uint8_t { - TTY_IN = 0b01, - TTY_OUT = 0b10, - TTY_INOUT = TTY_IN | TTY_OUT + bool is_regular_file() const { + return file_info.file_mode & SCE_SO_IFREG; + } + + // Check if the file is writable + bool can_write_file() const { + if (!is_regular_file()) + return false; + + return can_write(file_info.open_mode); + } + + // File operations + FILE *get_file_pointer() const { + return wrapped_file.get(); + } + + // File functions + SceOff read(void *input_data, int element_size, SceSize element_count) const; + SceOff write(const void *data, SceSize size, int count) const; + bool seek(SceOff offset, SceIoSeekMode seek_mode) const; + SceOff tell() const; }; -struct IoComponent { - std::string path; -}; +// Class for implementing Directory structure; path names are wide for Windows, normal for else +class DirStats : public VitaStats { + // Shared directory pointer + DirPtr dir_ptr; -struct File : public IoComponent { - FilePtr file_handle; -}; +public: + DirStats(const char *vita, const std::string &t, const fs::path &file, DirPtr ptr) { + dir_ptr = std::move(ptr); -struct Directory : public IoComponent { - DirPtr dir_handle; + file_info.vita_loc = vita; + file_info.translated = t; + file_info.sys_loc = file; + file_info.open_mode = SCE_O_RDONLY; + file_info.file_mode = SCE_SO_IFDIR | SCE_SO_IROTH; + file_info.access_mode = SCE_S_IFDIR | SCE_S_IRUSR; + } + + auto get_dir_ptr() const { + return get_system_dir_ptr(dir_ptr); + } + + bool is_directory() const { + return file_info.file_mode & SCE_SO_IFDIR; + } }; typedef std::map TtyFiles; -typedef std::map StdFiles; -typedef std::map DirEntries; +typedef std::map StdFiles; +typedef std::map DirEntries; struct IOState { struct DevicePaths { @@ -71,6 +103,7 @@ struct IOState { } device_paths; std::string title_id; + std::string pref_save_path; SceUID next_fd = 0; TtyFiles tty_files; StdFiles std_files; diff --git a/src/emulator/io/include/io/types.h b/src/emulator/io/include/io/types.h index 3a879750a..a6b3ef9c8 100644 --- a/src/emulator/io/include/io/types.h +++ b/src/emulator/io/include/io/types.h @@ -17,13 +17,13 @@ #pragma once -#include #undef st_atime #undef st_ctime #undef st_mtime -#include #include +#include + namespace emu { struct SceIoDirent { diff --git a/src/emulator/io/include/io/util.h b/src/emulator/io/include/io/util.h new file mode 100644 index 000000000..9eb70b15e --- /dev/null +++ b/src/emulator/io/include/io/util.h @@ -0,0 +1,124 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#undef st_atime +#undef st_ctime +#undef st_mtime +#include +#include +#include + +#include + +typedef SceUInt8 TtyType; + +inline constexpr TtyType TTY_UNKNOWN = 0b00; +inline constexpr TtyType TTY_IN = 0b01; +inline constexpr TtyType TTY_OUT = 0b10; +inline constexpr TtyType TTY_INOUT = TTY_IN | TTY_OUT; + +struct FileInfo { + // The actual location on the Vita + const char *vita_loc; + // The translated location on the Vita + std::string translated; + // The actual location in the preference path. + fs::path sys_loc; + // One or more SceIoMode flags + int open_mode; + // One or more SceIoFileMode flags + int file_mode; + // One or more SceIoAccessMode flags + int access_mode; + + FileInfo() + : vita_loc(nullptr) + , open_mode(SCE_O_RDONLY) + , file_mode(SCE_SO_IFREG | SCE_SO_IROTH) + , access_mode(SCE_S_IRUSR) {} +}; + +inline bool can_write(const int mode) { + return mode & SCE_O_WRONLY || mode & SCE_O_APPEND || mode & SCE_O_CREAT; +} + +class VitaStats { +protected: + FileInfo file_info; + +public: + VitaStats() { + file_info.vita_loc = nullptr; + } + + VitaStats(const char *vita, const std::string &t, const fs::path &file) { + file_info.vita_loc = vita; + file_info.translated = t; + file_info.sys_loc = file; + } + + const char *get_vita_loc() const { + return file_info.vita_loc; + } + + const std::string &get_translated_path() const { + return file_info.translated; + } + + const fs::path &get_system_location() const { + return file_info.sys_loc; + } + + int get_open_mode() const { + return file_info.open_mode; + } + + int get_file_mode() const { + return file_info.file_mode; + } + + int get_access_mode() const { + return file_info.access_mode; + } + +// Overloaded functions for separate systems +#ifdef WIN32 + const wchar_t *get_char_path() const { + return file_info.sys_loc.generic_path().wstring().c_str(); + } +#else + const char *get_char_path() const { + return file_info.sys_loc.generic_path().string().c_str(); + } +#endif + + // Modify IO parameters + void create_io_perms(const int flags) { + file_info.open_mode = flags; + } + + void add_io_perms(const int flags) { + file_info.open_mode |= flags; + } + + // Reset to only allow reading + void remove_perms() { + file_info.open_mode = SCE_O_RDONLY; + } +}; diff --git a/src/emulator/io/include/io/vfs.h b/src/emulator/io/include/io/vfs.h new file mode 100644 index 000000000..843b3d844 --- /dev/null +++ b/src/emulator/io/include/io/vfs.h @@ -0,0 +1,31 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#include + +#include + +class VitaIoDevice; + +namespace vfs { +using FileBuffer = std::vector; + +bool read_file(VitaIoDevice device, FileBuffer &buf, const std::string &pref_path, const fs::path &vfs_file_path); +bool read_app_file(FileBuffer &buf, const std::string &pref_path, const std::string &title_id, const fs::path &vfs_file_path); +} // namespace vfs diff --git a/src/emulator/io/src/device.cpp b/src/emulator/io/src/device.cpp new file mode 100644 index 000000000..6ea11ed04 --- /dev/null +++ b/src/emulator/io/src/device.cpp @@ -0,0 +1,66 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include + +#include + +namespace device { + +std::string construct_normalized_path(const VitaIoDevice dev, const std::string &path, const std::string &ext) { + const auto device_path = get_device_string(dev, true); + if (path.empty()) { // Wants the device only + const auto ret = device_path + "/"; + return ret; + } + + // Normalize the path + auto normalized = path; + string_utils::replace(normalized, "\\", "/"); + string_utils::replace(normalized, "/./", "/"); + string_utils::replace(normalized, "//", "/"); + // TODO: Handle dot-dot paths + normalized.front() == '/' ? normalized = device_path + normalized : normalized = device_path + '/' + normalized; + + if (!ext.empty()) { + if (fs::path(normalized).has_extension()) { + const auto last_index = normalized.find_last_of('.'); + normalized = normalized.substr(0, last_index + 1) + ext; + } + normalized += '.' + ext; + } + + return normalized; +} + +std::string remove_device_from_path(const std::string &path, const VitaIoDevice device, const std::string &mod_path) { + // Trim the path to include only the substring after the device string + const auto device_length = get_device_string(device, true).length(); + if (device == VitaIoDevice::_INVALID) + return std::string{}; + + auto out = path; + out = out.substr(device_length, out.size()); + if (!mod_path.empty()) + out.front() == '/' ? out = mod_path + out : out = mod_path + '/' + out; + if (out.front() == '/') + out = out.substr(1, out.length()); + + return out; +} + +} // namespace device diff --git a/src/emulator/io/src/filesystem.cpp b/src/emulator/io/src/filesystem.cpp new file mode 100644 index 000000000..8238d7bed --- /dev/null +++ b/src/emulator/io/src/filesystem.cpp @@ -0,0 +1,60 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include +#include + +#ifdef WIN32 +// To open wide files for Boost.Filesystem, we also need the appropriate wide mode flags for Windows, and normal flags for other OS +const wchar_t *translate_open_mode(const int flags) { + if (flags & SCE_O_WRONLY) { + if (flags & SCE_O_RDONLY) { + if (flags & SCE_O_CREAT) { + if (flags & SCE_O_APPEND) { + return L"ab+"; + } + return L"wb+"; + } + return L"rb+"; + } + if (flags & SCE_O_APPEND) { + return L"ab"; + } + return L"wb"; + } + return L"rb"; +} +#else +const char *translate_open_mode(const int flags) { + if (flags & SCE_O_WRONLY) { + if (flags & SCE_O_RDONLY) { + if (flags & SCE_O_CREAT) { + if (flags & SCE_O_APPEND) { + return "ab+"; + } + return "wb+"; + } + return "rb+"; + } + if (flags & SCE_O_APPEND) { + return "ab"; + } + return "wb"; + } + return "rb"; +} +#endif diff --git a/src/emulator/io/src/io.cpp b/src/emulator/io/src/io.cpp index 1767d1a30..708d579d6 100644 --- a/src/emulator/io/src/io.cpp +++ b/src/emulator/io/src/io.cpp @@ -15,26 +15,21 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "rtc/rtc.h" - +#include #include #include - -#include -#include - #include #include +#include +#include + +#include #include #include -#include - #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include -#include -#include #else #include #include @@ -44,13 +39,12 @@ #include #include #include -#include // **************************** // * Utility functions * // **************************** -int io_error_impl(int retval, const char *export_name, const char *func_name) { +static int io_error_impl(const int retval, const char *export_name, const char *func_name) { LOG_WARN("{} ({}) returned {}", func_name, export_name, log_hex(retval)); return retval; } @@ -58,126 +52,15 @@ int io_error_impl(int retval, const char *export_name, const char *func_name) { #define IO_ERROR(retval) io_error_impl(retval, export_name, __func__) #define IO_ERROR_UNK() IO_ERROR(-1) -using namespace vfs; - namespace vfs { -const char * -translate_open_mode(int flags) { - if (flags & SCE_O_WRONLY) { - if (flags & SCE_O_RDONLY) { - if (flags & SCE_O_CREAT) { - if (flags & SCE_O_APPEND) { - return "ab+"; - } else { - return "wb+"; - } - } else { - return "rb+"; - } - } else { - if (flags & SCE_O_APPEND) { - return "ab"; - } else { - return "wb"; - } - } - } else { - return "rb"; - } -} +bool read_file(const VitaIoDevice device, FileBuffer &buf, const std::string &pref_path, const fs::path &vfs_file_path) { + const auto host_file_path = device::construct_emulated_path(device, vfs_file_path, pref_path).generic_path(); -void trim_leading_slash(std::string &path) { - if (path[0] == '.') - path.erase(0, 1); - if (path[0] == '/') - path.erase(0, 1); -} - -constexpr const char * -get_device_string(VitaIoDevice dev, bool with_colon) { - switch (dev) { -#define DEVICE(path, name) \ - case JOIN_DEVICE(name): \ - return with_colon ? UNWRAP(path) ":" : path; -#include -#undef DEVICE - default: - return ""; - } -} - -VitaIoDevice -get_device(const std::string &path) { - // find which IO device the path is associated with - const auto device_end_idx = path.find(":"); - if (device_end_idx == std::string::npos) { - return VitaIoDevice::_INVALID; - } - - std::string device_name(path.substr(0, device_end_idx)); - -#define DEVICE(path, name) \ - if (device_name == path) \ - return JOIN_DEVICE(name); -#include -#undef DEVICE - - LOG_CRITICAL("Unknown device {} used. Report this to developers!", path); - return VitaIoDevice::_UKNONWN; -} - -std::string -get_relative_path(const std::string &full_path, VitaIoDevice device) { - if (device == VitaIoDevice::_UKNONWN) - device = get_device(full_path); - - std::string rel_path(full_path); - - trim_leading_slash(rel_path); - - rel_path.erase(0, std::strlen(get_device_string(device, true))); - return rel_path; -} - -std::string -normalize_path(const std::string &path, VitaIoDevice device) { - if (device == VitaIoDevice::_UKNONWN) - device = get_device(path); - - std::string device_string = get_device_string(device); - std::string normalized_path(device_string + "/"); - uint32_t dev_length = static_cast(device_string.length()); - - if (path.empty()) - return normalized_path; - - if (path[dev_length + 1] == '/') { - // Skip "/" since we already added one - normalized_path.erase(dev_length, 1); - } - - normalized_path += path.substr(dev_length + 1); - return normalized_path; -} - -std::string -to_host_path(const std::string &path, const std::string &pref_path, VitaIoDevice device) { - std::string res = normalize_path(path, device); - return pref_path + res; -} - -// **************************** -// * End of utility functions * -// **************************** - -bool read_file(VitaIoDevice device, FileBuffer &buf, const std::string &pref_path, const fs::path &vfs_file_path) { - const fs::path host_file_path{ fs::path(pref_path) / get_device_string(device) / vfs_file_path }; - - fs::ifstream f(host_file_path, fs::ifstream::binary); - if (!f) { + fs::ifstream f{ host_file_path, fs::ifstream::binary }; + if (!f) return false; - } + f.unsetf(fs::ifstream::skipws); buf.reserve(fs::file_size(host_file_path)); buf.insert(buf.begin(), std::istream_iterator(f), std::istream_iterator()); @@ -185,14 +68,26 @@ bool read_file(VitaIoDevice device, FileBuffer &buf, const std::string &pref_pat } bool read_app_file(FileBuffer &buf, const std::string &pref_path, const std::string &title_id, const fs::path &vfs_file_path) { - return read_file(VitaIoDevice::UX0, buf, pref_path, fs::path("app") / title_id / vfs_file_path); + return read_file(VitaIoDevice::ux0, buf, pref_path, fs::path("app") / title_id / vfs_file_path); } } // namespace vfs +// **************************** +// * End utility functions * +// **************************** + bool init(IOState &io, const fs::path &base_path, const fs::path &pref_path) { - const fs::path ux0{ pref_path / "ux0" }; - const fs::path uma0{ pref_path / "uma0" }; + // Iterate through the entire list of devices and create the subdirectories if they do not exist + for (auto i : VitaIoDevice::_names()) { + if (!device::is_valid_output_path(i)) + continue; + if (!fs::exists(pref_path / i)) + fs::create_directories(pref_path / i); + } + + const fs::path ux0{ pref_path / (+VitaIoDevice::ux0)._to_string() }; + const fs::path uma0{ pref_path / (+VitaIoDevice::uma0)._to_string() }; const fs::path ux0_data{ ux0 / "data" }; const fs::path uma0_data{ uma0 / "data" }; const fs::path ux0_app{ ux0 / "app" }; @@ -200,13 +95,11 @@ bool init(IOState &io, const fs::path &base_path, const fs::path &pref_path) { const fs::path ux0_user00{ ux0_user / "00" }; const fs::path ux0_savedata{ ux0_user00 / "savedata" }; - fs::create_directories(ux0); fs::create_directory(ux0_data); fs::create_directory(ux0_app); fs::create_directory(ux0_user); fs::create_directory(ux0_user00); fs::create_directory(ux0_savedata); - fs::create_directory(uma0); fs::create_directory(uma0_data); fs::create_directory(base_path / "shaderlog"); @@ -215,119 +108,126 @@ bool init(IOState &io, const fs::path &base_path, const fs::path &pref_path) { } void init_device_paths(IOState &io) { - io.device_paths.savedata0 = "ux0:/user/00/savedata/" + io.title_id + "/"; - io.device_paths.app0 = "ux0:/app/" + io.title_id + "/"; - io.device_paths.addcont0 = "ux0:/addcont/" + io.title_id + "/"; + io.device_paths.savedata0 = "user/00/savedata/" + io.title_id; + io.device_paths.app0 = "app/" + io.title_id; + io.device_paths.addcont0 = "addcont/" + io.title_id; } -void translate_path(std::string &path, VitaIoDevice &device, const IOState::DevicePaths &device_paths) { - // Redirect savedata0:/ to ux0:/user/00/savedata/ - if (device == VitaIoDevice::SAVEDATA0) { - const auto relative_path = get_relative_path(path, device); - path = device_paths.savedata0 + relative_path; - device = get_device(path); - } - - // Redirect app0:/ to ux0:/app/ - if (device == VitaIoDevice::APP0) { - const auto relative_path = get_relative_path(path, device); - path = device_paths.app0 + relative_path; - device = get_device(path); - } - - // Redirect addcont0:/ to ux0:/addcont/ - if (device == VitaIoDevice::ADDCONT0) { - const auto relative_path = get_relative_path(path, device); - path = device_paths.addcont0 + relative_path; - device = get_device(path); - } -} - -SceUID open_file(IOState &io, const std::string &path, int flags, const char *pref_path, const char *export_name) { - std::string translated_path(path); - trim_leading_slash(translated_path); - VitaIoDevice device = get_device(translated_path); - - translate_path(translated_path, device, io.device_paths); - - LOG_TRACE("{}: Opening file {} ({})", export_name, path, translated_path); +static std::string translate_path(const char *path, VitaIoDevice &device, const IOState::DevicePaths &device_paths) { + auto relative_path = device::remove_duplicate_device(path, device); switch (device) { - case VitaIoDevice::TTY0: - case VitaIoDevice::TTY1: { + case +VitaIoDevice::savedata0: // Redirect savedata0: to ux0:user/00/savedata/ + case +VitaIoDevice::savedata1: { + relative_path = device::remove_device_from_path(relative_path, device, device_paths.savedata0); + device = VitaIoDevice::ux0; + break; + } + case +VitaIoDevice::app0: { // Redirect app0: to ux0:app/ + relative_path = device::remove_device_from_path(relative_path, device, device_paths.app0); + device = VitaIoDevice::ux0; + break; + } + case +VitaIoDevice::addcont0: { // Redirect addcont0: to ux0:addcont/ + relative_path = device::remove_device_from_path(relative_path, device, device_paths.addcont0); + device = VitaIoDevice::ux0; + break; + } + case +VitaIoDevice::gro0: + case +VitaIoDevice::grw0: + case +VitaIoDevice::os0: + case +VitaIoDevice::pd0: + case +VitaIoDevice::sa0: + case +VitaIoDevice::sd0: + case +VitaIoDevice::tm0: + case +VitaIoDevice::ud0: + case +VitaIoDevice::uma0: + case +VitaIoDevice::ur0: + case +VitaIoDevice::ux0: + case +VitaIoDevice::vd0: + case +VitaIoDevice::vs0: { + relative_path = device::remove_device_from_path(relative_path, device); + break; + } + case +VitaIoDevice::tty0: + case +VitaIoDevice::tty1: { + return std::string{}; + } + default: { + LOG_CRITICAL_IF(relative_path.find(':') != std::string::npos, "Unknown device with path {} used. Report this to the developers!", relative_path); + return std::string{}; + } + } + + // If the path is empty, the request is the device itself + if (relative_path.empty()) + return std::string{}; + + if (relative_path.front() == '/' || relative_path.front() == '\\') + relative_path.erase(0, 1); + + return relative_path; +} + +SceUID open_file(IOState &io, const char *path, const int flags, const std::string &pref_path, const char *export_name) { + auto device = device::get_device(path); + if (device == VitaIoDevice::_INVALID) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + if (device == VitaIoDevice::tty0 || device == VitaIoDevice::tty1) { assert(flags >= 0); - std::underlying_type::type tty_type = 0; + auto tty_type = TTY_UNKNOWN; if (flags & SCE_O_RDONLY) tty_type |= TTY_IN; if (flags & SCE_O_WRONLY) tty_type |= TTY_OUT; - const SceUID fd = io.next_fd++; - io.tty_files.emplace(fd, static_cast(tty_type)); + const auto fd = io.next_fd++; + io.tty_files.emplace(fd, tty_type); + + LOG_TRACE("{}: Opening terminal {}:", export_name, device._to_string()); return fd; } - case VitaIoDevice::SA0: - case VitaIoDevice::UX0: - case VitaIoDevice::UMA0: { - std::string file_path = to_host_path(translated_path, pref_path, device); - const char *const open_mode = translate_open_mode(flags); + const auto translated_path = translate_path(path, device, io.device_paths); + if (translated_path.empty()) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - auto delete_file = [](FILE *file) { - if (file != nullptr) { - fclose(file); - } - }; + const auto system_path = device::construct_emulated_path(device, translated_path, pref_path); -#ifdef WIN32 - const FilePtr file(_wfopen(string_utils::utf_to_wide(file_path).c_str(), string_utils::utf_to_wide(open_mode).c_str()), delete_file); -#else - const FilePtr file(fopen(file_path.c_str(), open_mode), delete_file); -#endif - if (!file) { - return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - } + // Do not allow any new files if they do not have a write flag. + if (!fs::exists(system_path) && !can_write(flags)) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - File file_node; - file_node.path = path; - file_node.file_handle = file; + const auto normalized_path = device::construct_normalized_path(device, translated_path); - const SceUID fd = io.next_fd++; - io.std_files.emplace(fd, file_node); + FileStats f{ path, normalized_path, system_path, flags }; + const auto fd = io.next_fd++; + io.std_files.emplace(fd, f); - return fd; - } - case VitaIoDevice::SAVEDATA1: - case VitaIoDevice::_UKNONWN: { - LOG_ERROR("Unimplemented device {} used", path); - // fall-through default behavior - } - default: { - // TODO: Have a default behavior + LOG_TRACE("{}: Opening file {} ({}), fd: {}", export_name, path, normalized_path, log_hex(fd)); - return IO_ERROR_UNK(); - } - } + return fd; } -int read_file(void *data, IOState &io, SceUID fd, SceSize size, const char *export_name) { +int read_file(void *data, IOState &io, const SceUID fd, const SceSize size, const char *export_name) { assert(data != nullptr); - assert(fd >= 0); assert(size >= 0); - LOG_TRACE("{}: Reading file: fd: {}, size: {}", export_name, log_hex(fd), size); - - const StdFiles::const_iterator file = io.std_files.find(fd); + const auto file = io.std_files.find(fd); if (file != io.std_files.end()) { - return static_cast(fread(data, 1, size, file->second.file_handle.get())); + const auto read = file->second.read(data, 1, size); + LOG_TRACE("{}: Reading {} bytes of fd {}", export_name, read, log_hex(fd)); + return static_cast(read); } - const TtyFiles::const_iterator tty_file = io.tty_files.find(fd); + const auto tty_file = io.tty_files.find(fd); if (tty_file != io.tty_files.end()) { if (tty_file->second == TTY_IN) { std::cin.read(reinterpret_cast(data), size); + LOG_TRACE("{}: Reading terminal fd: {}, size: {}", export_name, log_hex(fd), size); return size; } return IO_ERROR_UNK(); @@ -336,16 +236,16 @@ int read_file(void *data, IOState &io, SceUID fd, SceSize size, const char *expo return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); } -int write_file(SceUID fd, const void *data, SceSize size, const IOState &io, const char *export_name) { +int write_file(SceUID fd, const void *data, const SceSize size, const IOState &io, const char *export_name) { assert(data != nullptr); assert(size >= 0); - if (fd < 0) { - LOG_WARN("Error writing file fd: {}, size: {}", fd, size); + if (fd < 0) { + LOG_WARN("Error writing fd: {}, size: {}", fd, size); return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); } - const TtyFiles::const_iterator tty_file = io.tty_files.find(fd); + const auto tty_file = io.tty_files.find(fd); if (tty_file != io.tty_files.end()) { if (tty_file->second & TTY_OUT) { std::string s(reinterpret_cast(data), size); @@ -357,30 +257,31 @@ int write_file(SceUID fd, const void *data, SceSize size, const IOState &io, con LOG_INFO("*** TTY: {}", s); return size; } - return IO_ERROR_UNK(); } - LOG_TRACE("{}: Writing file: fd: {}, size: {}", export_name, log_hex(fd), size); - - const StdFiles::const_iterator file = io.std_files.find(fd); - if (file != io.std_files.end()) { - return static_cast(fwrite(data, 1, size, file->second.file_handle.get())); + const auto file = io.std_files.find(fd); + if (file->second.can_write_file()) { + const auto written = file->second.write(data, 1, size); + LOG_TRACE("{}: Writing to fd: {}, size: {}", export_name, log_hex(fd), size); + return static_cast(written); } return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); } -SceOff seek_file(SceUID fd, SceOff offset, int whence, IOState &io, const char *export_name) { - assert((whence == SCE_SEEK_SET) || (whence == SCE_SEEK_CUR) || (whence == SCE_SEEK_END)); +SceOff seek_file(const SceUID fd, const SceOff offset, const SceIoSeekMode whence, IOState &io, const char *export_name) { + if (!(whence == SCE_SEEK_SET || whence == SCE_SEEK_CUR || whence == SCE_SEEK_END)) + return IO_ERROR(SCE_ERROR_ERRNO_EOPNOTSUPP); - if (fd < 0) { + if (fd < 0) return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); - } - const StdFiles::const_iterator std_file = io.std_files.find(fd); + const auto file = io.std_files.find(fd); + if (!file->second.seek(offset, whence)) + return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); - auto log_whence = [](int whence) -> const char * { + const auto log_mode = [](const SceIoSeekMode whence) -> const char * { switch (whence) { STR_CASE(SCE_SEEK_SET); STR_CASE(SCE_SEEK_CUR); @@ -390,222 +291,88 @@ SceOff seek_file(SceUID fd, SceOff offset, int whence, IOState &io, const char * } }; - LOG_TRACE("{}: Seeking file: fd: {} at offset: {}, whence: {}", export_name, log_hex(fd), log_hex(offset), log_whence(whence)); + LOG_TRACE("{}: Seeking fd: {}, offset: {}, whence: {}", export_name, log_hex(fd), log_hex(offset), log_mode(whence)); - assert(std_file != io.std_files.end()); - - if (std_file == io.std_files.end()) { - return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); - } - - int base = SEEK_SET; - switch (whence) { - case SCE_SEEK_SET: - base = SEEK_SET; - break; - case SCE_SEEK_CUR: - base = SEEK_CUR; - break; - case SCE_SEEK_END: - base = SEEK_END; - break; - } - - int ret = 0; - SceOff pos = 0; - - if (std_file != io.std_files.end()) { - ret = fseek(std_file->second.file_handle.get(), static_cast(offset), base); - } - - if (ret != 0) { - return IO_ERROR_UNK(); - } - - if (std_file != io.std_files.end()) { - pos = static_cast(ftell(std_file->second.file_handle.get())); - } - return pos; + return file->second.tell(); } -SceOff tell_file(IOState &io, SceUID fd, const char *export_name) { - if (fd < 0) { +SceOff tell_file(IOState &io, const SceUID fd, const char *export_name) { + if (fd < 0) return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); - } - const StdFiles::const_iterator std_file = io.std_files.find(fd); + const auto std_file = io.std_files.find(fd); - return static_cast(ftell(std_file->second.file_handle.get())); + return std_file->second.tell(); } -int close_file(IOState &io, SceUID fd, const char *export_name) { - if (fd < 0) { - return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); - } - - LOG_TRACE("{}: Closing file: fd: {}", export_name, log_hex(fd)); - - io.tty_files.erase(fd); - io.std_files.erase(fd); - - return 0; -} - -int remove_file(IOState &io, const char *file, const char *pref_path, const char *export_name) { - std::string translated_path = file; - trim_leading_slash(translated_path); - VitaIoDevice device = get_device(translated_path); - translate_path(translated_path, device, io.device_paths); - - LOG_TRACE("{}: Removing file {} ({})", export_name, file, translated_path); - - switch (device) { - case VitaIoDevice::SA0: - case VitaIoDevice::UX0: - case VitaIoDevice::UMA0: { - std::string file_path = to_host_path(translated_path, pref_path, device); - fs::remove(file_path); - return 0; - } - default: { - LOG_ERROR("Unimplemented device {} used", file); - return IO_ERROR_UNK(); - } - } -} - -int create_dir(IOState &io, const char *dir, int mode, const char *pref_path, const char *export_name, const bool recursive) { - std::string translated_path = dir; - trim_leading_slash(translated_path); - VitaIoDevice device = get_device(translated_path); - translate_path(translated_path, device, io.device_paths); - - LOG_TRACE("{}: Creating dir {} ({})", export_name, dir, translated_path); - - switch (device) { - case VitaIoDevice::SA0: - case VitaIoDevice::UX0: - case VitaIoDevice::UMA0: { - std::string dir_path = to_host_path(translated_path, pref_path, device); - boost::system::error_code error_code; - - if (recursive) - fs::create_directories(dir_path, error_code); - else - fs::create_directory(dir_path, error_code); - - if (error_code == std::errc::no_such_file_or_directory) { - return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - } else if (error_code == std::errc::file_exists) { - return IO_ERROR(SCE_ERROR_ERRNO_EEXIST); - } - - return 0; - } - default: { - LOG_ERROR("Unimplemented device {} used", dir); - return IO_ERROR_UNK(); - } - } -} - -int remove_dir(IOState &io, const char *dir, const char *pref_path, const char *export_name) { - std::string translated_path = dir; - trim_leading_slash(translated_path); - VitaIoDevice device = get_device(translated_path); - translate_path(translated_path, device, io.device_paths); - - LOG_TRACE("{}: Removing dir {} ({})", export_name, dir, translated_path); - - switch (device) { - case VitaIoDevice::SA0: - case VitaIoDevice::UX0: - case VitaIoDevice::UMA0: { - std::string dir_path = to_host_path(translated_path, pref_path, device); - fs::remove(dir_path); - return 0; - } - default: { - LOG_ERROR("Unimplemented device {} used", dir); - return IO_ERROR_UNK(); - } - } -} - -int stat_file(IOState &io, const char *file, SceIoStat *statp, const char *pref_path, uint64_t base_tick, const char *export_name) { - assert(statp != NULL); +int stat_file(IOState &io, const char *file, SceIoStat *statp, const std::string &pref_path, SceUInt64 base_tick, const char *export_name, + const SceUID fd) { + assert(statp != nullptr); memset(statp, '\0', sizeof(SceIoStat)); - std::string translated_path = file; - trim_leading_slash(translated_path); - VitaIoDevice device = get_device(translated_path); + fs::path file_path = ""; + if (fd == invalid_fd) { + auto device = device::get_device(file); + if (device == VitaIoDevice::_INVALID) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - // read and execute access rights - statp->st_mode = SCE_S_IRUSR | SCE_S_IRGRP | SCE_S_IROTH | SCE_S_IXUSR | SCE_S_IXGRP | SCE_S_IXOTH; + const auto translated_path = translate_path(file, device, io.device_paths); + file_path = device::construct_emulated_path(device, translated_path, pref_path); + if (!fs::exists(file_path)) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + LOG_TRACE("{}: Statting file: {} ({})", export_name, file, device::construct_normalized_path(device, translated_path)); + } else { // We have previously opened and defined the location + const auto fd_file = io.std_files.find(fd); + if (fd_file == io.std_files.end()) + return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); + + file_path = fd_file->second.get_system_location(); + LOG_TRACE("{}: Statting fd: {}", export_name, log_hex(fd)); + + statp->st_attr = fd_file->second.get_file_mode(); + } std::uint64_t last_access_time_ticks; - std::uint64_t last_modification_time_ticks; - std::uint64_t creation_time_ticks{ 0 }; - - translate_path(translated_path, device, io.device_paths); - - LOG_TRACE("{}: Statting file {} ({})", export_name, file, translated_path); - - switch (device) { - case VitaIoDevice::SA0: - case VitaIoDevice::UX0: - case VitaIoDevice::UMA0: { - std::string file_path = to_host_path(translated_path, pref_path, device); + std::uint64_t creation_time_ticks; #ifdef WIN32 - WIN32_FIND_DATAW find_data; - HANDLE handle = FindFirstFileW(string_utils::utf_to_wide(file_path).c_str(), &find_data); - if (handle == INVALID_HANDLE_VALUE) { - return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); - } - FindClose(handle); + WIN32_FIND_DATAW find_data; + const auto handle = FindFirstFileW(file_path.generic_path().wstring().c_str(), &find_data); + if (handle == INVALID_HANDLE_VALUE) + return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); - if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { - statp->st_mode |= SCE_S_IFDIR; - } else { - statp->st_mode |= SCE_S_IFREG; - } + FindClose(handle); - statp->st_size = (std::uint64_t)find_data.nFileSizeHigh << 32 | (std::uint64_t)find_data.nFileSizeLow; - - last_access_time_ticks = convert_filetime(find_data.ftLastAccessTime); - last_modification_time_ticks = convert_filetime(find_data.ftLastWriteTime); - creation_time_ticks = convert_filetime(find_data.ftCreationTime); + last_access_time_ticks = convert_filetime(find_data.ftLastAccessTime); + creation_time_ticks = convert_filetime(find_data.ftCreationTime); #else - struct stat sb; - if (stat(file_path.c_str(), &sb) < 0) { - return IO_ERROR_UNK(); - } + struct stat sb; + if (stat(file_path.generic_path().string().c_str(), &sb) < 0) + return IO_ERROR_UNK(); - if (S_ISREG(sb.st_mode)) { - statp->st_mode |= SCE_S_IFREG; - } else if (S_ISDIR(sb.st_mode)) { - statp->st_mode |= SCE_S_IFDIR; - } - - statp->st_size = sb.st_size; - - last_access_time_ticks = (uint64_t)sb.st_atime * VITA_CLOCKS_PER_SEC; - last_modification_time_ticks = (uint64_t)sb.st_mtime * VITA_CLOCKS_PER_SEC; - creation_time_ticks = (uint64_t)sb.st_ctime * VITA_CLOCKS_PER_SEC; + last_access_time_ticks = (uint64_t)sb.st_atime * VITA_CLOCKS_PER_SEC; + creation_time_ticks = (uint64_t)sb.st_ctime * VITA_CLOCKS_PER_SEC; #undef st_atime #undef st_mtime #undef st_ctime #endif - break; - } - default: { - LOG_ERROR("Unimplemented device {} used", file); - return IO_ERROR_UNK(); - } + + statp->st_mode = SCE_S_IRUSR | SCE_S_IRGRP | SCE_S_IROTH | SCE_S_IXUSR | SCE_S_IXGRP | SCE_S_IXOTH; + + const auto last_modification_time_ticks = fs::last_write_time(file_path); + + if (fs::is_regular_file(file_path)) { + statp->st_size = fs::file_size(file_path); + + if (fd != invalid_fd) + statp->st_attr = SCE_SO_IFREG; } + if (fs::is_directory(file_path) && fd != invalid_fd) + statp->st_attr = SCE_SO_IFDIR; + __RtcTicksToPspTime(&statp->st_atime, last_access_time_ticks); __RtcTicksToPspTime(&statp->st_mtime, last_modification_time_ticks); __RtcTicksToPspTime(&statp->st_ctime, creation_time_ticks); @@ -613,110 +380,147 @@ int stat_file(IOState &io, const char *file, SceIoStat *statp, const char *pref_ return 0; } -int stat_file_by_fd(IOState &io, const int fd, SceIoStat *statp, const char *pref_path, uint64_t base_tick, const char *export_name) { - assert(statp != NULL); +int stat_file_by_fd(IOState &io, const SceUID fd, SceIoStat *statp, const std::string &pref_path, SceUInt64 base_tick, const char *export_name) { + assert(statp != nullptr); memset(statp, '\0', sizeof(SceIoStat)); - if (io.std_files.find(fd) != io.std_files.end()) { - return stat_file(io, io.std_files[fd].path.data(), statp, pref_path, base_tick, export_name); - } else if (io.dir_entries.find(fd) != io.dir_entries.end()) { - return stat_file(io, io.dir_entries[fd].path.data(), statp, pref_path, base_tick, export_name); - } - - return -1; + return stat_file(io, io.std_files.find(fd)->second.get_vita_loc(), statp, pref_path, base_tick, export_name, fd); } -int open_dir(IOState &io, const char *path, const char *pref_path, const char *export_name) { - std::string translated_path = path; - trim_leading_slash(translated_path); - VitaIoDevice device = get_device(translated_path); - translate_path(translated_path, device, io.device_paths); +int close_file(IOState &io, const SceUID fd, const char *export_name) { + if (fd < 0) + return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); - LOG_TRACE("{}: Opening dir {} ({})", export_name, path, translated_path); + LOG_TRACE("{}: Closing file fd: {}", export_name, log_hex(fd)); - std::string dir_path; - switch (device) { - case VitaIoDevice::SA0: - case VitaIoDevice::UX0: - case VitaIoDevice::UMA0: { - dir_path = to_host_path(translated_path, pref_path, device); - break; - } - default: { - LOG_ERROR("Unimplemented device {} used", path); - return IO_ERROR_UNK(); - } - } + io.tty_files.erase(fd); + io.std_files.erase(fd); -#ifdef WIN32 - const DirPtr dir(_wopendir((string_utils::utf_to_wide(dir_path)).c_str()), _wclosedir); -#else - const DirPtr dir(opendir(dir_path.c_str()), [](DIR *dir) { - if (dir != nullptr) { - closedir(dir); - } - }); -#endif - if (!dir) { + return 0; +} + +int remove_file(IOState &io, const char *file, const std::string &pref_path, const char *export_name) { + auto device = device::get_device(file); + if (device == VitaIoDevice::_INVALID) return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - } - Directory dir_node; - dir_node.dir_handle = dir; - dir_node.path = path; + const auto translated_path = translate_path(file, device, io.device_paths); + const auto emulated_path = device::construct_emulated_path(device, translated_path, pref_path); + if (translated_path.empty() || !fs::exists(emulated_path) || fs::is_directory(emulated_path)) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); - const SceUID fd = io.next_fd++; - io.dir_entries.emplace(fd, dir_node); + LOG_TRACE("{}: Removing file {} ({})", export_name, file, device::construct_normalized_path(device, translated_path)); + + return fs::remove(emulated_path); +} + +SceUID open_dir(IOState &io, const char *path, const std::string &pref_path, const char *export_name) { + auto device = device::get_device(path); + const auto translated_path = translate_path(path, device, io.device_paths); + + const auto dir_path = device::construct_emulated_path(device, translated_path, pref_path) / "/"; + if (!fs::exists(dir_path)) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + const DirPtr opened = create_shared_dir(dir_path); + if (!opened) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + const auto normalized = device::construct_normalized_path(device, translated_path); + const DirStats d{ path, normalized, dir_path, opened }; + const auto fd = io.next_fd++; + io.dir_entries.emplace(fd, d); + + LOG_TRACE("{}: Opening dir {} ({}), fd: {}", export_name, path, normalized, log_hex(fd)); return fd; } -int read_dir(IOState &io, SceUID fd, emu::SceIoDirent *dent, const char *pref_path, uint64_t base_tick, const char *export_name) { +SceUID read_dir(IOState &io, const SceUID fd, emu::SceIoDirent *dent, const std::string &pref_path, const SceUInt64 base_tick, const char *export_name) { assert(dent != nullptr); memset(dent->d_name, '\0', sizeof(dent->d_name)); - LOG_TRACE("{}: Reading dir fd: {}", export_name, log_hex(fd)); + const auto dir = io.dir_entries.find(fd); + + // Refuse any fd that is not explicitly a directory + if (!dir->second.is_directory()) + return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); - const DirEntries::const_iterator dir = io.dir_entries.find(fd); if (dir != io.dir_entries.end()) { -#ifdef WIN32 - _wdirent *d = _wreaddir(dir->second.dir_handle.get()); -#else - dirent *d = readdir(dir->second.dir_handle.get()); -#endif - - if (!d) { + const auto d = dir->second.get_dir_ptr(); + if (!d) return 0; - } -#ifdef WIN32 - std::string d_name_utf8 = string_utils::wide_to_utf(d->d_name); + const auto d_name_utf8 = get_file_in_dir(d); strncpy(dent->d_name, d_name_utf8.c_str(), sizeof(dent->d_name)); -#else - strncpy(dent->d_name, d->d_name, sizeof(dent->d_name)); -#endif - if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) { - // Skip . and .. folders - return read_dir(io, fd, dent, pref_path, base_tick, export_name); + + const auto cur_path = dir->second.get_system_location() / d_name_utf8; + if (!(cur_path.filename_is_dot() || cur_path.filename_is_dot_dot())) { + const auto file_path = std::string(dir->second.get_vita_loc()) + '/' + d_name_utf8; + + LOG_TRACE("{}: Reading entry {} of fd: {}", export_name, file_path, log_hex(fd)); + if (fs::is_regular_file(cur_path)) { + if (stat_file(io, file_path.c_str(), &dent->d_stat, pref_path, base_tick, export_name) < 0) + return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); + } + if (fs::is_directory(cur_path)) { + const auto dir_fd = open_dir(io, file_path.c_str(), pref_path, export_name); + return read_dir(io, dir_fd, dent, pref_path, base_tick, export_name); + } } - - std::string filename = dir->second.path + "/" + dent->d_name; - stat_file(io, filename.c_str(), &dent->d_stat, pref_path, base_tick, export_name); - - return 1; + return 1; // move to the next file } return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); } -int close_dir(IOState &io, SceUID fd, const char *export_name) { +int create_dir(IOState &io, const char *dir, int mode, const std::string &pref_path, const char *export_name, const bool recursive) { + auto device = device::get_device(dir); + const auto translated_path = translate_path(dir, device, io.device_paths); + if (translated_path.empty()) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + const auto emulated_path = device::construct_emulated_path(device, translated_path, pref_path); + if (recursive) + return fs::create_directories(emulated_path); + if (fs::exists(emulated_path)) + return IO_ERROR(SCE_ERROR_ERRNO_EEXIST); + if (!fs::exists(emulated_path.parent_path())) // Vita cannot recursively create directories + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + LOG_TRACE("{}: Creating new dir {} ({})", export_name, dir, device::construct_normalized_path(device, translated_path)); + + if (!fs::create_directory(emulated_path)) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + return 0; +} + +int close_dir(IOState &io, const SceUID fd, const char *export_name) { + if (fd < 0) + return IO_ERROR(SCE_ERROR_ERRNO_EMFILE); + const auto erased_entries = io.dir_entries.erase(fd); LOG_TRACE("{}: Closing dir fd: {}", export_name, log_hex(fd)); if (erased_entries == 0) return IO_ERROR(SCE_ERROR_ERRNO_EBADFD); - else - return 0; + + return 0; +} + +int remove_dir(IOState &io, const char *dir, const std::string &pref_path, const char *export_name) { + auto device = device::get_device(dir); + if (device == VitaIoDevice::_INVALID) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + const auto translated_path = translate_path(dir, device, io.device_paths); + if (translated_path.empty()) + return IO_ERROR(SCE_ERROR_ERRNO_ENOENT); + + LOG_TRACE("{}: Removing dir {} ({})", export_name, dir, device::construct_normalized_path(device, translated_path)); + + return fs::remove(device::construct_emulated_path(device, translated_path, pref_path)); } diff --git a/src/emulator/io/src/state_functions.cpp b/src/emulator/io/src/state_functions.cpp new file mode 100644 index 000000000..b93bb083a --- /dev/null +++ b/src/emulator/io/src/state_functions.cpp @@ -0,0 +1,61 @@ +// Vita3K emulator project +// Copyright (C) 2018 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include + +SceOff FileStats::read(void *input_data, const int element_size, const SceSize element_count) const { + if (!wrapped_file) + return -1; + + return fread(input_data, element_size, element_count, wrapped_file.get()); +} + +SceOff FileStats::write(const void *data, const SceSize size, const int count) const { + if (!can_write_file()) + return -1; + + return fwrite(data, size, count, get_file_pointer()); +} + +bool FileStats::seek(const SceOff offset, const SceIoSeekMode seek_mode) const { + if (!wrapped_file) + return false; + + auto base = SEEK_SET; + switch (seek_mode) { + case SCE_SEEK_SET: + base = SEEK_SET; + break; + case SCE_SEEK_CUR: + base = SEEK_CUR; + break; + case SCE_SEEK_END: + base = SEEK_END; + break; + default: + return false; + } + + return fseek(wrapped_file.get(), static_cast(offset), base) == 0; +} + +SceOff FileStats::tell() const { + if (!wrapped_file) + return -1; + + return ftell(wrapped_file.get()); +} diff --git a/src/emulator/modules/SceAppUtil/SceAppUtil.cpp b/src/emulator/modules/SceAppUtil/SceAppUtil.cpp index 89e37e454..ce55ffa08 100644 --- a/src/emulator/modules/SceAppUtil/SceAppUtil.cpp +++ b/src/emulator/modules/SceAppUtil/SceAppUtil.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -149,18 +150,21 @@ EXPORT(int, sceAppUtilResetCookieWebBrowser) { return UNIMPLEMENTED(); } +static std::string construct_savedata0_path(const std::string &data, const char *ext = "") { + return device::construct_normalized_path(VitaIoDevice::savedata0, data, ext); +} + +static std::string construct_slotparam_path(const unsigned int data) { + return construct_savedata0_path("SlotParam_" + std::to_string(data), "bin"); +} + EXPORT(int, sceAppUtilSaveDataDataRemove, emu::SceAppUtilSaveDataFileSlot *slot, emu::SceAppUtilSaveDataRemoveItem *files, unsigned int fileNum, SceAppUtilSaveDataMountPoint *mountPoint) { for (unsigned int i = 0; i < fileNum; i++) { - std::string file_path = "savedata0:/"; - file_path += files[i].dataPath.get(host.mem); - remove_file(host.io, file_path.c_str(), host.pref_path.c_str(), export_name); + remove_file(host.io, construct_savedata0_path(files[i].dataPath.get(host.mem)).c_str(), host.pref_path, export_name); } if (slot && files[0].mode == SCE_APPUTIL_SAVEDATA_DATA_REMOVE_MODE_DEFAULT) { - std::string slot_path = "savedata0:/SlotParam_"; - slot_path += std::to_string(slot->id); - slot_path += ".bin"; - remove_file(host.io, slot_path.c_str(), host.pref_path.c_str(), export_name); + remove_file(host.io, construct_slotparam_path(slot->id).c_str(), host.pref_path, export_name); } return 0; } @@ -169,20 +173,14 @@ EXPORT(int, sceAppUtilSaveDataDataSave, emu::SceAppUtilSaveDataFileSlot *slot, e SceUID fd; for (unsigned int i = 0; i < fileNum; i++) { - std::string file_path = "savedata0:/"; - file_path += files[i].filePath.get(host.mem); + const auto file_path = construct_savedata0_path(files[i].filePath.get(host.mem)); switch (files[i].mode) { - case SCE_APPUTIL_SAVEDATA_DATA_SAVE_MODE_FILE: - fd = open_file(host.io, file_path, SCE_O_WRONLY | SCE_O_CREAT, host.pref_path.c_str(), export_name); - seek_file(fd, static_cast(files[i].offset), SCE_SEEK_SET, host.io, export_name); - write_file(fd, files[i].buf.get(host.mem), files[i].bufSize, host.io, export_name); - close_file(host.io, fd, export_name); - break; case SCE_APPUTIL_SAVEDATA_DATA_SAVE_MODE_DIRECTORY: - create_dir(host.io, file_path.c_str(), 0777, host.pref_path.c_str(), export_name); + create_dir(host.io, file_path.c_str(), 0777, host.pref_path, export_name); break; + case SCE_APPUTIL_SAVEDATA_DATA_SAVE_MODE_FILE: default: - fd = open_file(host.io, file_path, SCE_O_WRONLY | SCE_O_CREAT, host.pref_path.c_str(), export_name); + fd = open_file(host.io, file_path.c_str(), SCE_O_WRONLY | SCE_O_CREAT, host.pref_path, export_name); seek_file(fd, static_cast(files[i].offset), SCE_SEEK_SET, host.io, export_name); write_file(fd, files[i].buf.get(host.mem), files[i].bufSize, host.io, export_name); close_file(host.io, fd, export_name); @@ -191,10 +189,7 @@ EXPORT(int, sceAppUtilSaveDataDataSave, emu::SceAppUtilSaveDataFileSlot *slot, e } if (slot) { - std::string slot_path = "savedata0:/SlotParam_"; - slot_path += std::to_string(slot->id); - slot_path += ".bin"; - fd = open_file(host.io, slot_path, SCE_O_WRONLY | SCE_O_CREAT, host.pref_path.c_str(), export_name); + fd = open_file(host.io, construct_slotparam_path(slot->id).c_str(), SCE_O_WRONLY, host.pref_path, export_name); write_file(fd, &slot->slotParam, sizeof(SceAppUtilSaveDataSlotParam), host.io, export_name); close_file(host.io, fd, export_name); } @@ -211,28 +206,19 @@ EXPORT(int, sceAppUtilSaveDataMount) { } EXPORT(int, sceAppUtilSaveDataSlotCreate, unsigned int slotId, SceAppUtilSaveDataSlotParam *param, SceAppUtilSaveDataMountPoint *mountPoint) { - std::string slot_path = "savedata0:/SlotParam_"; - slot_path += std::to_string(slotId); - slot_path += ".bin"; - SceUID fd = open_file(host.io, slot_path, SCE_O_WRONLY | SCE_O_CREAT, host.pref_path.c_str(), export_name); + const auto fd = open_file(host.io, construct_slotparam_path(slotId).c_str(), SCE_O_WRONLY | SCE_O_CREAT, host.pref_path, export_name); write_file(fd, param, sizeof(SceAppUtilSaveDataSlotParam), host.io, export_name); close_file(host.io, fd, export_name); return 0; } EXPORT(int, sceAppUtilSaveDataSlotDelete, unsigned int slotId, SceAppUtilSaveDataMountPoint *mountPoint) { - std::string slot_path = "savedata0:/SlotParam_"; - slot_path += std::to_string(slotId); - slot_path += ".bin"; - remove_file(host.io, slot_path.c_str(), host.pref_path.c_str(), export_name); + remove_file(host.io, construct_slotparam_path(slotId).c_str(), host.pref_path, export_name); return 0; } EXPORT(int, sceAppUtilSaveDataSlotGetParam, unsigned int slotId, SceAppUtilSaveDataSlotParam *param, SceAppUtilSaveDataMountPoint *mountPoint) { - std::string slot_path = "savedata0:/SlotParam_"; - slot_path += std::to_string(slotId); - slot_path += ".bin"; - SceUID fd = open_file(host.io, slot_path, SCE_O_RDONLY, host.pref_path.c_str(), export_name); + const auto fd = open_file(host.io, construct_slotparam_path(slotId).c_str(), SCE_O_RDONLY, host.pref_path, export_name); if (fd < 0) return RET_ERROR(SCE_APPUTIL_ERROR_SAVEDATA_SLOT_NOT_FOUND); read_file(param, host.io, fd, sizeof(SceAppUtilSaveDataSlotParam), export_name); @@ -245,10 +231,7 @@ EXPORT(int, sceAppUtilSaveDataSlotSearch) { } EXPORT(int, sceAppUtilSaveDataSlotSetParam, unsigned int slotId, SceAppUtilSaveDataSlotParam *param, SceAppUtilSaveDataMountPoint *mountPoint) { - std::string slot_path = "savedata0:/SlotParam_"; - slot_path += std::to_string(slotId); - slot_path += ".bin"; - SceUID fd = open_file(host.io, slot_path, SCE_O_WRONLY, host.pref_path.c_str(), export_name); + const auto fd = open_file(host.io, construct_slotparam_path(slotId).c_str(), SCE_O_WRONLY, host.pref_path, export_name); if (fd < 0) return RET_ERROR(SCE_APPUTIL_ERROR_SAVEDATA_SLOT_NOT_FOUND); write_file(fd, param, sizeof(SceAppUtilSaveDataSlotParam), host.io, export_name); @@ -273,8 +256,8 @@ EXPORT(int, sceAppUtilStoreBrowse) { } EXPORT(int, sceAppUtilSystemParamGetInt, unsigned int paramId, int *value) { - const SceSystemParamLang sys_lang = static_cast(host.cfg.sys_lang); - const SceSystemParamEnterButtonAssign sys_button = static_cast(host.cfg.sys_button); + const auto sys_lang = static_cast(host.cfg.sys_lang); + const auto sys_button = static_cast(host.cfg.sys_button); switch (paramId) { case SCE_SYSTEM_PARAM_ID_LANG: diff --git a/src/emulator/modules/SceIofilemgr/SceIofilemgr.cpp b/src/emulator/modules/SceIofilemgr/SceIofilemgr.cpp index 740cea669..f023bf6bf 100644 --- a/src/emulator/modules/SceIofilemgr/SceIofilemgr.cpp +++ b/src/emulator/modules/SceIofilemgr/SceIofilemgr.cpp @@ -151,7 +151,7 @@ EXPORT(int, sceIoChstatByFdAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoClose, SceUID fd) { +EXPORT(int, sceIoClose, const SceUID fd) { return close_file(host.io, fd, export_name); } @@ -163,7 +163,7 @@ EXPORT(int, sceIoComplete) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoDclose, SceUID fd) { +EXPORT(int, sceIoDclose, const SceUID fd) { return close_dir(host.io, fd, export_name); } @@ -207,11 +207,11 @@ EXPORT(int, sceIoGetstatByFdAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoLseek32, SceUID fd, int offset, int whence) { - return seek_file(fd, offset, whence, host.io, export_name); +EXPORT(int, sceIoLseek32, const SceUID fd, const SceOff offset, const SceIoSeekMode whence) { + return static_cast(seek_file(fd, offset, whence, host.io, export_name)); } -EXPORT(int, sceIoRead, SceUID fd, void *data, SceSize size) { +EXPORT(int, sceIoRead, const SceUID fd, void *data, const SceSize size) { return read_file(data, host.io, fd, size, export_name); } @@ -247,7 +247,7 @@ EXPORT(int, sceIoSyncByFdAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoWrite, SceUID fd, const void *data, SceSize size) { +EXPORT(int, sceIoWrite, const SceUID fd, const void *data, const SceSize size) { return write_file(fd, data, size, host.io, export_name); } diff --git a/src/emulator/modules/SceLibKernel/SceLibKernel.cpp b/src/emulator/modules/SceLibKernel/SceLibKernel.cpp index f8dcff256..0198871bf 100644 --- a/src/emulator/modules/SceLibKernel/SceLibKernel.cpp +++ b/src/emulator/modules/SceLibKernel/SceLibKernel.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -34,13 +33,8 @@ #include #include -#include -#undef st_atime -#undef st_ctime -#undef st_mtime -#include -#include #include +#include #include #include @@ -258,7 +252,7 @@ EXPORT(int, sceClibStrlcpyChk) { EXPORT(int, sceClibStrncasecmp, const char *string1, const char *string2, int count) { #ifdef WIN32 - return strnicmp(string1, string2, count); + return _strnicmp(string1, string2, count); #else return strncasecmp(string1, string2, count); #endif @@ -357,26 +351,26 @@ EXPORT(int, sceIoDevctlAsync) { } EXPORT(int, sceIoDopen, const char *dir) { - return open_dir(host.io, dir, host.pref_path.c_str(), export_name); + return open_dir(host.io, dir, host.pref_path, export_name); } -EXPORT(int, sceIoDread, SceUID fd, emu::SceIoDirent *dir) { +EXPORT(int, sceIoDread, const SceUID fd, emu::SceIoDirent *dir) { if (dir == nullptr) { return RET_ERROR(SCE_KERNEL_ERROR_ILLEGAL_ADDR); } - return read_dir(host.io, fd, dir, host.pref_path.c_str(), host.kernel.base_tick.tick, export_name); + return read_dir(host.io, fd, dir, host.pref_path, host.kernel.base_tick.tick, export_name); } EXPORT(int, sceIoGetstat, const char *file, SceIoStat *stat) { - return stat_file(host.io, file, stat, host.pref_path.c_str(), host.kernel.base_tick.tick, export_name); + return stat_file(host.io, file, stat, host.pref_path, host.kernel.base_tick.tick, export_name); } EXPORT(int, sceIoGetstatAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoGetstatByFd, const int fd, SceIoStat *stat) { - return stat_file_by_fd(host.io, fd, stat, host.pref_path.c_str(), host.kernel.base_tick.tick, export_name); +EXPORT(int, sceIoGetstatByFd, const SceUID fd, SceIoStat *stat) { + return stat_file_by_fd(host.io, fd, stat, host.pref_path, host.kernel.base_tick.tick, export_name); } EXPORT(int, sceIoIoctl) { @@ -387,7 +381,7 @@ EXPORT(int, sceIoIoctlAsync) { return UNIMPLEMENTED(); } -EXPORT(SceOff, sceIoLseek, SceUID fd, SceOff offset, int whence) { +EXPORT(SceOff, sceIoLseek, const SceUID fd, const SceOff offset, const SceIoSeekMode whence) { return seek_file(fd, offset, whence, host.io, export_name); } @@ -395,30 +389,30 @@ EXPORT(int, sceIoLseekAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoMkdir, const char *dir, SceMode mode) { - return create_dir(host.io, dir, mode, host.pref_path.c_str(), export_name); +EXPORT(int, sceIoMkdir, const char *dir, const SceMode mode) { + return create_dir(host.io, dir, mode, host.pref_path, export_name); } EXPORT(int, sceIoMkdirAsync) { return UNIMPLEMENTED(); } -EXPORT(SceUID, sceIoOpen, const char *file, int flags, SceMode mode) { +EXPORT(SceUID, sceIoOpen, const char *file, const int flags, const SceMode mode) { if (file == nullptr) { return RET_ERROR(SCE_ERROR_ERRNO_EINVAL); } LOG_INFO("Opening file: {}", file); - return open_file(host.io, file, flags, host.pref_path.c_str(), export_name); + return open_file(host.io, file, flags, host.pref_path, export_name); } EXPORT(int, sceIoOpenAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoPread, SceUID fd, void *data, SceSize size, SceOff offset) { - SceOff pos = tell_file(host.io, fd, export_name); +EXPORT(int, sceIoPread, const SceUID fd, void *data, const SceSize size, const SceOff offset) { + auto pos = tell_file(host.io, fd, export_name); if (pos < 0) { - return pos; + return static_cast(pos); } seek_file(fd, static_cast(offset), SCE_SEEK_SET, host.io, export_name); const int res = read_file(data, host.io, fd, size, export_name); @@ -430,10 +424,10 @@ EXPORT(int, sceIoPreadAsync) { return UNIMPLEMENTED(); } -EXPORT(int, sceIoPwrite, SceUID fd, const void *data, SceSize size, SceOff offset) { - SceOff pos = tell_file(host.io, fd, export_name); +EXPORT(int, sceIoPwrite, const SceUID fd, const void *data, const SceSize size, const SceOff offset) { + auto pos = tell_file(host.io, fd, export_name); if (pos < 0) { - return pos; + return static_cast(pos); } seek_file(fd, static_cast(offset), SCE_SEEK_SET, host.io, export_name); const int res = write_file(fd, data, size, host.io, export_name); @@ -453,7 +447,7 @@ EXPORT(int, sceIoRemove, const char *path) { if (path == nullptr) { return RET_ERROR(SCE_ERROR_ERRNO_EINVAL); } - return remove_file(host.io, path, host.pref_path.c_str(), export_name); + return remove_file(host.io, path, host.pref_path, export_name); } EXPORT(int, sceIoRemoveAsync) { @@ -472,7 +466,7 @@ EXPORT(int, sceIoRmdir, const char *path) { if (path == nullptr) { return RET_ERROR(SCE_ERROR_ERRNO_EINVAL); } - return remove_dir(host.io, path, host.pref_path.c_str(), export_name); + return remove_dir(host.io, path, host.pref_path, export_name); } EXPORT(int, sceIoRmdirAsync) { @@ -1105,36 +1099,36 @@ EXPORT(int, sceKernelGetTimerTime) { * \param error_val Error value on failure * \return True on success, false on failure */ -bool load_module(SceUID &mod_id, Ptr &entry_point, SceKernelModuleInfoPtr &module, HostState &host, const char *export_name, char *path, int &error_val) { +bool load_module(SceUID &mod_id, Ptr &entry_point, SceKernelModuleInfoPtr &module, HostState &host, const char *export_name, const char *path, int &error_val) { const auto &loaded_modules = host.kernel.loaded_modules; - SceKernelModuleInfoPtrs::const_iterator module_iter = std::find_if(loaded_modules.begin(), loaded_modules.end(), [path](const auto &p) { + auto module_iter = std::find_if(loaded_modules.begin(), loaded_modules.end(), [path](const auto &p) { return std::string(p.second->path) == path; }); if (module_iter == loaded_modules.end()) { // module is not loaded, load it here - SceUID file = open_file(host.io, path, SCE_O_RDONLY, host.pref_path.c_str(), export_name); + const auto file = open_file(host.io, path, SCE_O_RDONLY, host.pref_path, export_name); if (file < 0) { error_val = RET_ERROR(file); return false; } - int size = seek_file(file, 0, SCE_SEEK_END, host.io, export_name); + const auto size = seek_file(file, 0, SCE_SEEK_END, host.io, export_name); if (size < 0) { error_val = RET_ERROR(SCE_ERROR_ERRNO_EINVAL); return false; } if (seek_file(file, 0, SCE_SEEK_SET, host.io, export_name) < 0) { - error_val = RET_ERROR(size); + error_val = RET_ERROR(static_cast(size)); return false; } - char *data = new char[size]; + auto *data = new char[static_cast(size) + 1]; // null-terminated char array if (read_file(data, host.io, file, size, export_name) < 0) { delete[] data; - error_val = RET_ERROR(size); + error_val = RET_ERROR(static_cast(size)); return false; } diff --git a/src/emulator/modules/SceLibc/SceLibc.cpp b/src/emulator/modules/SceLibc/SceLibc.cpp index 5275aa5de..35f406821 100644 --- a/src/emulator/modules/SceLibc/SceLibc.cpp +++ b/src/emulator/modules/SceLibc/SceLibc.cpp @@ -433,7 +433,7 @@ EXPORT(int, fileno) { EXPORT(int, fopen, const char *filename, const char *mode) { LOG_WARN_IF(mode[0] != 'r', "fopen({}, {})", filename, *mode); - return open_file(host.io, filename, SCE_O_RDONLY, host.pref_path.c_str(), export_name); + return open_file(host.io, filename, SCE_O_RDONLY, host.pref_path, export_name); } EXPORT(int, fopen_s) { diff --git a/src/emulator/modules/SceProcessmgr/SceProcessmgr.cpp b/src/emulator/modules/SceProcessmgr/SceProcessmgr.cpp index 5988306af..f61a9269e 100644 --- a/src/emulator/modules/SceProcessmgr/SceProcessmgr.cpp +++ b/src/emulator/modules/SceProcessmgr/SceProcessmgr.cpp @@ -96,15 +96,15 @@ EXPORT(int, sceKernelGetRemoteProcessTime) { } EXPORT(int, sceKernelGetStderr) { - return open_file(host.io, "tty0:", SCE_O_WRONLY, host.pref_path.c_str(), export_name); + return open_file(host.io, "tty0:", SCE_O_WRONLY, host.pref_path, export_name); } EXPORT(int, sceKernelGetStdin) { - return open_file(host.io, "tty0:", SCE_O_RDONLY, host.pref_path.c_str(), export_name); + return open_file(host.io, "tty0:", SCE_O_RDONLY, host.pref_path, export_name); } EXPORT(int, sceKernelGetStdout) { - return open_file(host.io, "tty0:", SCE_O_WRONLY, host.pref_path.c_str(), export_name); + return open_file(host.io, "tty0:", SCE_O_WRONLY, host.pref_path, export_name); } EXPORT(int, sceKernelIsCDialogAvailable) { diff --git a/src/emulator/modules/module_parent.cpp b/src/emulator/modules/module_parent.cpp index e39430bab..5c2c566a7 100644 --- a/src/emulator/modules/module_parent.cpp +++ b/src/emulator/modules/module_parent.cpp @@ -21,7 +21,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -133,7 +134,7 @@ bool load_module(HostState &host, SceSysmoduleModuleId module_id) { vfs::FileBuffer module_buffer; Ptr lib_entry_point; - if (vfs::read_file(VitaIoDevice::VS0, module_buffer, host.pref_path, module_path)) { + if (vfs::read_file(VitaIoDevice::vs0, module_buffer, host.pref_path, module_path)) { SceUID loaded_module_uid = load_self(lib_entry_point, host.kernel, host.mem, module_buffer.data(), module_path, host.cfg); const auto module = host.kernel.loaded_modules[loaded_module_uid]; const auto module_name = module->module_name; diff --git a/src/emulator/np/src/trophy/context.cpp b/src/emulator/np/src/trophy/context.cpp index b1cb77cdd..34df14b93 100644 --- a/src/emulator/np/src/trophy/context.cpp +++ b/src/emulator/np/src/trophy/context.cpp @@ -1,5 +1,5 @@ +#include #include -#include #include #include #include @@ -112,7 +112,7 @@ static constexpr std::uint32_t TROPHY_USR_MAGIC = 0x12D5819A; void Context::save_trophy_progress_file() { // Open the file - const SceUID output = open_file(io, trophy_progress_output_file_path.c_str(), SCE_O_WRONLY, pref_path.c_str(), "save_trophy_progress"); + const SceUID output = open_file(io, trophy_progress_output_file_path.c_str(), SCE_O_WRONLY, pref_path, "save_trophy_progress"); auto write_stuff = [&](const void *data, std::uint32_t amount) -> int { return write_file(output, data, amount, io, "save_trophy_progress_file"); @@ -287,28 +287,23 @@ emu::np::trophy::ContextHandle create_trophy_context(NpState &np, IOState &io, c } } - const std::string trophy_base_dir = fmt::format("app0:sce_sys/trophy/{}_{:0>2d}/", custom_comm->data, - custom_comm->num); - // Initialize the stream - const std::string trophy_file_path = trophy_base_dir + "TROPHY.TRP"; + const auto unique_trophy_folder = fmt::format("{}_{:0>2d}/", custom_comm->data, custom_comm->num); + const auto trophy_file_path = device::construct_normalized_path(VitaIoDevice::app0, "sce_sys/trophy/" + unique_trophy_folder + "TROPHY.TRP"); // Try to open the file - const SceUID trophy_file = open_file(io, trophy_file_path, SCE_O_RDONLY, pref_path.c_str(), "create_trophy_context"); - + const SceUID trophy_file = open_file(io, trophy_file_path.c_str(), SCE_O_RDONLY, pref_path, "create_trophy_context"); if (trophy_file < 0) { TROPHY_RET_ERROR(TROPHY_CONTEXT_FILE_NON_EXIST); } // Try to open the trophy save file. The context will automatically took the default profile to perform trophy // operations on. - std::string trophy_progress_save_file = fmt::format("ux0:/user/00/trophy/data/{}_{:0>2d}/", - custom_comm->data, custom_comm->num); + auto trophy_progress_save_file = device::construct_normalized_path(VitaIoDevice::ux0, "user/00/trophy/data/" + unique_trophy_folder); - create_dir(io, trophy_progress_save_file.c_str(), 0, pref_path.c_str(), "create_trophy_context", true); + create_dir(io, trophy_progress_save_file.c_str(), 0, pref_path, "create_trophy_context", true); trophy_progress_save_file += "TROPUSR.DAT"; - - const SceUID trophy_progress_file_inp = open_file(io, trophy_progress_save_file, SCE_O_RDONLY, pref_path.c_str(), "create_trophy_context"); + const SceUID trophy_progress_file_inp = open_file(io, trophy_progress_save_file.c_str(), SCE_O_RDONLY, pref_path, "create_trophy_context"); emu::np::trophy::Context *new_context = nullptr; diff --git a/src/emulator/np/src/trophy/trp_parser.cpp b/src/emulator/np/src/trophy/trp_parser.cpp index d1cf79ca1..fa7b0e003 100644 --- a/src/emulator/np/src/trophy/trp_parser.cpp +++ b/src/emulator/np/src/trophy/trp_parser.cpp @@ -20,6 +20,8 @@ #include #include +#include + namespace emu::np::trophy { static constexpr std::uint32_t NP_TRP_HEADER_MAGIC = 0x004DA2DC; diff --git a/src/emulator/util/include/util/string_utils.h b/src/emulator/util/include/util/string_utils.h index 295cc9800..625c60696 100644 --- a/src/emulator/util/include/util/string_utils.h +++ b/src/emulator/util/include/util/string_utils.h @@ -12,6 +12,7 @@ std::string wide_to_utf(const std::wstring &str); std::string utf16_to_utf8(const std::u16string &str); std::u16string utf8_to_utf16(const std::string &str); std::string remove_special_chars(std::string str); +void replace(std::string &str, const std::string &in, const std::string &out); std::string toupper(const std::string &s); } // namespace string_utils diff --git a/src/emulator/util/src/util.cpp b/src/emulator/util/src/util.cpp index bd0f2a33a..f5bf41ed6 100644 --- a/src/emulator/util/src/util.cpp +++ b/src/emulator/util/src/util.cpp @@ -149,6 +149,14 @@ std::string remove_special_chars(std::string str) { return str; } +// Based on: https://stackoverflow.com/a/23135441 +// Search and replace "in" with "out" in the given string +void replace(std::string &str, const std::string &in, const std::string &out) { + for (auto start = str.find(in); start != std::string::npos; start = str.find(in)) { + str.replace(str.find(in), in.length(), out); + } +} + #ifdef WIN32 std::string utf16_to_utf8(const std::u16string &str) { std::wstring_convert, int16_t> myconv; diff --git a/src/external/CMakeLists.txt b/src/external/CMakeLists.txt index e1c13b18d..b2a3a922b 100644 --- a/src/external/CMakeLists.txt +++ b/src/external/CMakeLists.txt @@ -63,6 +63,10 @@ add_library(glad STATIC "${CMAKE_CURRENT_SOURCE_DIR}/glad/src/glad.c") target_include_directories(glad PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/glad/include") target_link_libraries(glad PRIVATE ${CMAKE_DL_LIBS}) +add_library(better-enums INTERFACE) +target_include_directories(better-enums INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/better-enums") +target_compile_definitions(better-enums INTERFACE BETTER_ENUMS_STRICT_CONVERSION=1) + add_library(googletest STATIC googletest/googletest/src/gtest_main.cc googletest/googletest/src/gtest-all.cc) target_include_directories(googletest PUBLIC googletest/googletest/include) target_include_directories(googletest PRIVATE googletest/googletest) diff --git a/src/external/better-enums b/src/external/better-enums new file mode 160000 index 000000000..1ea7f04bb --- /dev/null +++ b/src/external/better-enums @@ -0,0 +1 @@ +Subproject commit 1ea7f04bb9afdf7a0c83ba676b9c95b0c4185b4c