diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/file_sys/archive_extsavedata.cpp | 115 | ||||
-rw-r--r-- | src/core/hle/result.h | 1 |
2 files changed, 115 insertions, 1 deletions
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index e1d29efd3..e1c4931ec 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -11,6 +11,9 @@ #include "common/string_util.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/disk_archive.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/path_parser.h" +#include "core/file_sys/savedata_archive.h" #include "core/hle/service/fs/archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -18,6 +21,116 @@ namespace FileSys { +/** + * A modified version of DiskFile for fixed-size file used by ExtSaveData + * The file size can't be changed by SetSize or Write. + */ +class FixSizeDiskFile : public DiskFile { +public: + FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode) : DiskFile(std::move(file), mode) { + size = GetSize(); + } + + bool SetSize(u64 size) const override { + return false; + } + + ResultVal<size_t> Write(u64 offset, size_t length, bool flush, + const u8* buffer) const override { + if (offset > size) { + return ResultCode(ErrorDescription::FS_WriteBeyondEnd, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } else if (offset == size) { + return MakeResult<size_t>(0); + } + + if (offset + length > size) { + length = size - offset; + } + + return DiskFile::Write(offset, length, flush, buffer); + } + +private: + u64 size{}; +}; + +/** + * Archive backend for general extsave data archive type. + * The behaviour of ExtSaveDataArchive is almost the same as SaveDataArchive, except for + * - file size can't be changed once created (thus creating zero-size file and openning with create + * flag are prohibited); + * - always open a file with read+write permission. + */ +class ExtSaveDataArchive : public SaveDataArchive { +public: + ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {} + + std::string GetName() const override { + return "ExtSaveDataArchive: " + mount_point; + } + + ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, + const Mode& mode) const override { + LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); + + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + if (mode.hex == 0) { + LOG_ERROR(Service_FS, "Empty open mode"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + + if (mode.create_flag) { + LOG_ERROR(Service_FS, "Create flag is not supported"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::DirectoryFound: + LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + case PathParser::NotFound: + LOG_ERROR(Service_FS, "%s not found", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } + + FileUtil::IOFile file(full_path, "r+b"); + if (!file.IsOpen()) { + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } + + Mode rwmode; + rwmode.write_flag.Assign(1); + rwmode.read_flag.Assign(1); + auto disk_file = std::make_unique<FixSizeDiskFile>(std::move(file), rwmode); + return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); + } + + ResultCode CreateFile(const Path& path, u64 size) const override { + if (size == 0) { + LOG_ERROR(Service_FS, "Zero-size file is not supported"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + return SaveDataArchive::CreateFile(path, size); + } +}; + std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { std::vector<u8> vec_data = path.AsBinary(); const u32* data = reinterpret_cast<const u32*>(vec_data.data()); @@ -84,7 +197,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons ErrorSummary::InvalidState, ErrorLevel::Status); } } - auto archive = std::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<ExtSaveDataArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 8330894f2..a355f970a 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -34,6 +34,7 @@ enum class ErrorDescription : u32 { 513, // TODO(purpasmart): Check if this name fits its actual usage GPU_FirstInitialization = 519, FS_InvalidPath = 702, + FS_WriteBeyondEnd = 705, FS_UnsupportedOpenFlags = 760, FS_UnexpectedFileOrDirectory = 770, InvalidSection = 1000, |