mirror of
https://github.com/Vita3K/Vita3K.git
synced 2026-07-11 01:34:23 +02:00
io: Refactoring with classes (#505)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
+24
-22
@@ -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)
|
||||
@@ -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
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <io/types.h>
|
||||
#include <io/util.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Vendored
+4
@@ -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)
|
||||
|
||||
+1
Submodule src/external/better-enums added at 1ea7f04bb9
Reference in New Issue
Block a user