io: Refactoring with classes (#505)

This commit is contained in:
totlmstr
2019-08-10 16:22:09 -07:00
committed by pent0
parent eb9dea2745
commit 97183ffc1b
32 changed files with 1070 additions and 696 deletions
+3
View File
@@ -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
+1 -1
View File
@@ -23,7 +23,7 @@
#include <gui/imgui_impl_sdl_gl3.h>
#include <host/functions.h>
#include <host/state.h>
#include <io/io.h>
#include <io/vfs.h>
#include <util/fs.h>
#include <util/log.h>
+1 -1
View File
@@ -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()
+5 -2
View File
@@ -23,7 +23,7 @@
#include <host/load_self.h>
#include <host/sfo.h>
#include <io/functions.h>
#include <io/io.h>
#include <io/vfs.h>
#include <kernel/functions.h>
#include <kernel/thread/thread_functions.h>
#include <modules/module_parent.h>
@@ -153,7 +153,6 @@ static bool install_vpk(Ptr<const void> &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<const void> &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;
}
+5
View File
@@ -21,8 +21,13 @@
#include <host/state.h>
#include <util/exit_code.h>
#include <miniz.h>
struct GuiState;
typedef std::shared_ptr<mz_zip_archive> ZipPtr;
typedef std::shared_ptr<mz_zip_reader_extract_iter_state> ZipFilePtr;
inline void delete_zip(mz_zip_archive *zip) {
mz_zip_reader_end(zip);
delete zip;
+14 -7
View File
@@ -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)
@@ -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 <enum.h>
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)
+126
View File
@@ -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 <io/VitaIoDevice.h>
#include <util/fs.h>
#include <string>
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
+1 -1
View File
@@ -17,7 +17,7 @@
#pragma once
#include <io/types.h>
#include <io/util.h>
#include <cassert>
#include <cstring>
+74
View File
@@ -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 <util/fs.h>
#ifdef WIN32
#include <util/string_utils.h>
#endif
#include <dirent.h>
typedef std::shared_ptr<FILE> 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<DIR> 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
+20 -15
View File
@@ -17,33 +17,38 @@
#pragma once
#include <psp2/types.h>
#undef st_atime
#undef st_ctime
#undef st_mtime
#include <psp2/io/dirent.h>
#include <psp2/io/fcntl.h>
#include <psp2/io/stat.h>
#include <io/types.h>
#include <util/fs.h>
#include <miniz.h>
#include <string>
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);
+5 -24
View File
@@ -17,27 +17,8 @@
#pragma once
#include <util/fs.h>
#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 <io/VitaIoDevice.def>
_UKNONWN = -1,
_INVALID = -2,
};
#undef DEVICE
namespace vfs {
using FileBuffer = std::vector<uint8_t>;
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
+65 -32
View File
@@ -17,51 +17,83 @@
#pragma once
#include <miniz.h>
#include <io/filesystem.h>
#include <io/util.h>
#include <io/types.h>
#include <cstdio>
#include <map>
#include <memory>
#ifdef WIN32
struct _WDIR;
#else
#include <dirent.h>
#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<FILE> FilePtr;
typedef std::shared_ptr<mz_zip_archive> ZipPtr;
typedef std::shared_ptr<mz_zip_reader_extract_iter_state> 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<DIR> 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<SceUID, TtyType> TtyFiles;
typedef std::map<SceUID, File> StdFiles;
typedef std::map<SceUID, Directory> DirEntries;
typedef std::map<SceUID, FileStats> StdFiles;
typedef std::map<SceUID, DirStats> 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;
+2 -2
View File
@@ -17,13 +17,13 @@
#pragma once
#include <mem/ptr.h>
#undef st_atime
#undef st_ctime
#undef st_mtime
#include <psp2/io/fcntl.h>
#include <psp2/io/stat.h>
#include <mem/ptr.h>
namespace emu {
struct SceIoDirent {
+124
View File
@@ -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 <psp2/io/dirent.h>
#include <psp2/io/fcntl.h>
#include <psp2/io/stat.h>
#include <util/fs.h>
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;
}
};
+31
View File
@@ -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 <psp2/types.h>
#include <util/fs.h>
class VitaIoDevice;
namespace vfs {
using FileBuffer = std::vector<SceUInt8>;
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
+66
View File
@@ -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 <io/device.h>
#include <util/string_utils.h>
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
+60
View File
@@ -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 <io/filesystem.h>
#include <io/util.h>
#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
+295 -491
View File
@@ -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 <io/device.h>
#include <io/functions.h>
#include <io/io.h>
#include <psp2/io/dirent.h>
#include <psp2/io/stat.h>
#include <io/state.h>
#include <io/types.h>
#include <io/util.h>
#include <io/vfs.h>
#include <rtc/rtc.h>
#include <util/log.h>
#include <util/preprocessor.h>
#include <psp2/io/fcntl.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <dirent.h>
#include <util/string_utils.h>
#else
#include <sys/stat.h>
#include <unistd.h>
@@ -44,13 +39,12 @@
#include <iostream>
#include <iterator>
#include <string>
#include <type_traits>
// ****************************
// * 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 <io/VitaIoDevice.def>
#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 <io/VitaIoDevice.def>
#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<uint32_t>(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<uint8_t>(f), std::istream_iterator<uint8_t>());
@@ -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/<title_id>
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/<title_id>
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/<title_id>
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/<title_id>
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/<title_id>
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/<title_id>
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<TtyType>::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<TtyType>(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<int>(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<int>(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<char *>(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<char const *>(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<int>(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<int>(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<long int>(offset), base);
}
if (ret != 0) {
return IO_ERROR_UNK();
}
if (std_file != io.std_files.end()) {
pos = static_cast<SceOff>(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<SceOff>(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));
}
+61
View File
@@ -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 <io/state.h>
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<long>(offset), base) == 0;
}
SceOff FileStats::tell() const {
if (!wrapped_file)
return -1;
return ftell(wrapped_file.get());
}
+22 -39
View File
@@ -21,6 +21,7 @@
#include <psp2/system_param.h>
#include <host/app_util.h>
#include <io/device.h>
#include <io/functions.h>
#include <cstring>
@@ -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<int>(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<int>(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<SceSystemParamLang>(host.cfg.sys_lang);
const SceSystemParamEnterButtonAssign sys_button = static_cast<SceSystemParamEnterButtonAssign>(host.cfg.sys_button);
const auto sys_lang = static_cast<SceSystemParamLang>(host.cfg.sys_lang);
const auto sys_button = static_cast<SceSystemParamEnterButtonAssign>(host.cfg.sys_button);
switch (paramId) {
case SCE_SYSTEM_PARAM_ID_LANG:
@@ -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<int>(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);
}
@@ -24,7 +24,6 @@
#include <host/functions.h>
#include <host/load_self.h>
#include <io/functions.h>
#include <io/types.h>
#include <kernel/functions.h>
#include <kernel/thread/sync_primitives.h>
#include <kernel/thread/thread_functions.h>
@@ -34,13 +33,8 @@
#include <util/log.h>
#include <psp2/kernel/error.h>
#include <psp2/kernel/threadmgr.h>
#undef st_atime
#undef st_ctime
#undef st_mtime
#include <psp2/io/dirent.h>
#include <psp2/io/stat.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/kernel/threadmgr.h>
#include <algorithm>
#include <cstdlib>
@@ -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<int>(pos);
}
seek_file(fd, static_cast<int>(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<int>(pos);
}
seek_file(fd, static_cast<int>(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<const void> &entry_point, SceKernelModuleInfoPtr &module, HostState &host, const char *export_name, char *path, int &error_val) {
bool load_module(SceUID &mod_id, Ptr<const void> &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<int>(size));
return false;
}
char *data = new char[size];
auto *data = new char[static_cast<int>(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<int>(size));
return false;
}
+1 -1
View File
@@ -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) {
@@ -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) {
+3 -2
View File
@@ -21,7 +21,8 @@
#include <host/import_fn.h>
#include <host/load_self.h>
#include <host/state.h>
#include <io/io.h>
#include <io/device.h>
#include <io/vfs.h>
#include <module/load_module.h>
#include <nids/functions.h>
#include <util/find.h>
@@ -133,7 +134,7 @@ bool load_module(HostState &host, SceSysmoduleModuleId module_id) {
vfs::FileBuffer module_buffer;
Ptr<const void> 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;
+8 -13
View File
@@ -1,5 +1,5 @@
#include <io/device.h>
#include <io/functions.h>
#include <io/state.h>
#include <np/functions.h>
#include <np/state.h>
#include <np/trophy/context.h>
@@ -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;
@@ -20,6 +20,8 @@
#include <np/trophy/trp_parser.h>
#include <util/bytes.h>
#include <cstring>
namespace emu::np::trophy {
static constexpr std::uint32_t NP_TRP_HEADER_MAGIC = 0x004DA2DC;
@@ -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
+8
View File
@@ -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<std::codecvt_utf8_utf16<int16_t>, int16_t> myconv;
+4
View File
@@ -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)
Vendored Submodule
+1