summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/file_sys/archive_savedata.cpp4
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp4
-rw-r--r--src/core/file_sys/errors.h29
-rw-r--r--src/core/file_sys/savedata_archive.cpp283
-rw-r--r--src/core/file_sys/savedata_archive.h43
-rw-r--r--src/core/hle/result.h7
7 files changed, 368 insertions, 4 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ae9e8dcea..63a402ad0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -25,6 +25,7 @@ set(SRCS
file_sys/disk_archive.cpp
file_sys/ivfc_archive.cpp
file_sys/path_parser.cpp
+ file_sys/savedata_archive.cpp
gdbstub/gdbstub.cpp
hle/config_mem.cpp
hle/hle.cpp
@@ -170,6 +171,7 @@ set(HEADERS
file_sys/file_backend.h
file_sys/ivfc_archive.h
file_sys/path_parser.h
+ file_sys/savedata_archive.h
gdbstub/gdbstub.h
hle/config_mem.h
hle/function_wrappers.h
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
index 6711035ec..ecb44a215 100644
--- a/src/core/file_sys/archive_savedata.cpp
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -9,7 +9,7 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/archive_savedata.h"
-#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/savedata_archive.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/fs/archive.h"
@@ -54,7 +54,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P
ErrorSummary::InvalidState, ErrorLevel::Status);
}
- auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point));
+ auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 48ebc0ed4..54e7793e0 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -9,7 +9,7 @@
#include "common/file_util.h"
#include "common/string_util.h"
#include "core/file_sys/archive_systemsavedata.h"
-#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/savedata_archive.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -56,7 +56,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
ErrorSummary::InvalidState, ErrorLevel::Status);
}
- auto archive = std::make_unique<DiskArchive>(fullpath);
+ auto archive = std::make_unique<SaveDataArchive>(fullpath);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
new file mode 100644
index 000000000..da7e82642
--- /dev/null
+++ b/src/core/file_sys/errors.h
@@ -0,0 +1,29 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags,
+ ErrorModule::FS, ErrorSummary::NotSupported,
+ ErrorLevel::Usage);
+const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory,
+ ErrorModule::FS, ErrorSummary::NotSupported,
+ ErrorLevel::Usage);
+const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists,
+ ErrorModule::FS, ErrorSummary::NothingHappened,
+ ErrorLevel::Status);
+const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS,
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpty, ErrorModule::FS,
+ ErrorSummary::Canceled, ErrorLevel::Status);
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp
new file mode 100644
index 000000000..f2e6a06bc
--- /dev/null
+++ b/src/core/file_sys/savedata_archive.cpp
@@ -0,0 +1,283 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/file_util.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"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
+ const Mode& mode) const {
+ 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 && !mode.write_flag) {
+ LOG_ERROR(Service_FS, "Create flag set but write flag not set");
+ 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:
+ if (!mode.create_flag) {
+ LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
+ full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ } else {
+ // Create the file
+ FileUtil::CreateEmptyFile(full_path);
+ }
+ break;
+ }
+
+ FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
+ if (!file.IsOpen()) {
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
+ return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
+}
+
+ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ 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:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "File not found %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ if (FileUtil::Delete(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+}
+
+ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
+ if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) {
+ return RESULT_SUCCESS;
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+template <typename T>
+static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
+ T deleter) {
+ 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 (path_parser.IsRootDirectory())
+ return ERROR_DIRECTORY_NOT_EMPTY;
+
+ 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_PATH_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ }
+
+ if (deleter(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
+ return ERROR_DIRECTORY_NOT_EMPTY;
+}
+
+ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
+ return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
+}
+
+ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
+ return DeleteDirectoryHelper(
+ path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
+}
+
+ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ 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:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ case PathParser::DirectoryFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_FILE_ALREADY_EXISTS;
+ }
+
+ if (size == 0) {
+ FileUtil::CreateEmptyFile(full_path);
+ return RESULT_SUCCESS;
+ }
+
+ FileUtil::IOFile file(full_path, "wb");
+ // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
+ // We do this by seeking to the right size, then writing a single null byte.
+ if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Too large file");
+ return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
+ ErrorLevel::Info);
+}
+
+ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ 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:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ case PathParser::DirectoryFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_DIRECTORY_ALREADY_EXISTS;
+ }
+
+ if (FileUtil::CreateDir(mount_point + path.AsString())) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
+ ErrorLevel::Status);
+}
+
+ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()))
+ return RESULT_SUCCESS;
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
+ const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ 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:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ }
+
+ auto directory = std::make_unique<DiskDirectory>(full_path);
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
+}
+
+u64 SaveDataArchive::GetFreeBytes() const {
+ // TODO: Stubbed to return 1GiB
+ return 1024 * 1024 * 1024;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h
new file mode 100644
index 000000000..2fb6c452a
--- /dev/null
+++ b/src/core/file_sys/savedata_archive.h
@@ -0,0 +1,43 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/file_sys/file_backend.h"
+#include "core/hle/result.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// Archive backend for general save data archive type (SaveData and SystemSaveData)
+class SaveDataArchive : public ArchiveBackend {
+public:
+ SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
+
+ std::string GetName() const override {
+ return "SaveDataArchive: " + mount_point;
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
+ const Mode& mode) const override;
+ ResultCode DeleteFile(const Path& path) const override;
+ ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode CreateFile(const Path& path, u64 size) const override;
+ ResultCode CreateDirectory(const Path& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
+
+protected:
+ std::string mount_point;
+};
+
+} // namespace FileSys
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 7f8d8e00d..8330894f2 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -20,15 +20,22 @@ enum class ErrorDescription : u32 {
OS_InvalidBufferDescriptor = 48,
WrongAddress = 53,
FS_ArchiveNotMounted = 101,
+ FS_FileNotFound = 112,
+ FS_PathNotFound = 113,
FS_NotFound = 120,
+ FS_FileAlreadyExists = 180,
+ FS_DirectoryAlreadyExists = 185,
FS_AlreadyExists = 190,
FS_InvalidOpenFlags = 230,
+ FS_DirectoryNotEmpty = 240,
FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
OutofRangeOrMisalignedAddress =
513, // TODO(purpasmart): Check if this name fits its actual usage
GPU_FirstInitialization = 519,
FS_InvalidPath = 702,
+ FS_UnsupportedOpenFlags = 760,
+ FS_UnexpectedFileOrDirectory = 770,
InvalidSection = 1000,
TooLarge = 1001,
NotAuthorized = 1002,