summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/file_sys/archive_backend.h28
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp115
-rw-r--r--src/core/file_sys/archive_ncch.cpp (renamed from src/core/file_sys/archive_savedatacheck.cpp)22
-rw-r--r--src/core/file_sys/archive_ncch.h (renamed from src/core/file_sys/archive_savedatacheck.h)8
-rw-r--r--src/core/file_sys/archive_savedata.cpp4
-rw-r--r--src/core/file_sys/archive_sdmc.cpp279
-rw-r--r--src/core/file_sys/archive_sdmc.h26
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.cpp70
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.h57
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp4
-rw-r--r--src/core/file_sys/directory_backend.h6
-rw-r--r--src/core/file_sys/disk_archive.cpp150
-rw-r--r--src/core/file_sys/disk_archive.h43
-rw-r--r--src/core/file_sys/errors.h40
-rw-r--r--src/core/file_sys/file_backend.h6
-rw-r--r--src/core/file_sys/ivfc_archive.cpp31
-rw-r--r--src/core/file_sys/ivfc_archive.h20
-rw-r--r--src/core/file_sys/path_parser.cpp98
-rw-r--r--src/core/file_sys/path_parser.h61
-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/applets/erreula.cpp6
-rw-r--r--src/core/hle/applets/mii_selector.cpp6
-rw-r--r--src/core/hle/applets/swkbd.cpp6
-rw-r--r--src/core/hle/kernel/event.cpp5
-rw-r--r--src/core/hle/kernel/timer.cpp5
-rw-r--r--src/core/hle/result.h9
-rw-r--r--src/core/hle/service/apt/apt.cpp11
-rw-r--r--src/core/hle/service/apt/apt.h34
-rw-r--r--src/core/hle/service/apt/apt_a.cpp2
-rw-r--r--src/core/hle/service/apt/apt_u.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp19
-rw-r--r--src/core/hle/service/err_f.cpp316
-rw-r--r--src/core/hle/service/fs/archive.cpp58
-rw-r--r--src/core/hle/service/fs/archive.h2
-rw-r--r--src/core/hle/service/mic_u.cpp322
-rw-r--r--src/core/hle/service/mic_u.h1
-rw-r--r--src/core/hle/service/ptm/ptm.cpp2
-rw-r--r--src/core/hw/gpu.cpp21
-rw-r--r--src/core/hw/gpu.h1
-rw-r--r--src/core/memory.cpp14
-rw-r--r--src/core/memory.h9
-rw-r--r--src/core/settings.h1
45 files changed, 1773 insertions, 488 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4a9c6fd2f..299f1f261 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -17,13 +17,16 @@ set(SRCS
core_timing.cpp
file_sys/archive_backend.cpp
file_sys/archive_extsavedata.cpp
+ file_sys/archive_ncch.cpp
file_sys/archive_romfs.cpp
file_sys/archive_savedata.cpp
- file_sys/archive_savedatacheck.cpp
file_sys/archive_sdmc.cpp
+ file_sys/archive_sdmcwriteonly.cpp
file_sys/archive_systemsavedata.cpp
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
@@ -159,15 +162,18 @@ set(HEADERS
core_timing.h
file_sys/archive_backend.h
file_sys/archive_extsavedata.h
+ file_sys/archive_ncch.h
file_sys/archive_romfs.h
file_sys/archive_savedata.h
- file_sys/archive_savedatacheck.h
file_sys/archive_sdmc.h
+ file_sys/archive_sdmcwriteonly.h
file_sys/archive_systemsavedata.h
file_sys/directory_backend.h
file_sys/disk_archive.h
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/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index b4444c869..ca8a94ee9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -52,6 +52,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks(ARMul_State* interpeter_state) {
user_callbacks.MemoryWrite16 = &Memory::Write16;
user_callbacks.MemoryWrite32 = &Memory::Write32;
user_callbacks.MemoryWrite64 = &Memory::Write64;
+ user_callbacks.page_table = Memory::GetCurrentPageTablePointers();
return user_callbacks;
}
@@ -130,9 +131,9 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit);
- jit->Run(static_cast<unsigned>(num_instructions));
+ unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
- AddTicks(num_instructions);
+ AddTicks(ticks_executed);
}
void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) {
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index 06b8f2ed7..58f6c150c 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -87,7 +87,7 @@ public:
* @return Opened file, or error code
*/
virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
- const Mode mode) const = 0;
+ const Mode& mode) const = 0;
/**
* Delete a file specified by its path
@@ -100,53 +100,53 @@ public:
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
- * @return Whether rename succeeded
+ * @return Result of the operation
*/
- virtual bool RenameFile(const Path& src_path, const Path& dest_path) const = 0;
+ virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be deleted
+ * @return Result of the operation
*/
- virtual bool DeleteDirectory(const Path& path) const = 0;
+ virtual ResultCode DeleteDirectory(const Path& path) const = 0;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
- * @return Whether the directory could be deleted
+ * @return Result of the operation
*/
- virtual bool DeleteDirectoryRecursively(const Path& path) const = 0;
+ virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
- * @return File creation result code
+ * @return Result of the operation
*/
virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be created
+ * @return Result of the operation
*/
- virtual bool CreateDirectory(const Path& path) const = 0;
+ virtual ResultCode CreateDirectory(const Path& path) const = 0;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
- * @return Whether rename succeeded
+ * @return Result of the operation
*/
- virtual bool RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
+ virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
- * @return Opened directory, or nullptr
+ * @return Opened directory, or error code
*/
- virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
+ virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
/**
* Get the free space
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/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_ncch.cpp
index 6c4542b7d..6f1aadfc3 100644
--- a/src/core/file_sys/archive_savedatacheck.cpp
+++ b/src/core/file_sys/archive_ncch.cpp
@@ -9,7 +9,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/file_sys/archive_savedatacheck.h"
+#include "core/file_sys/archive_ncch.h"
#include "core/file_sys/ivfc_archive.h"
#include "core/hle/service/fs/archive.h"
@@ -18,22 +18,22 @@
namespace FileSys {
-static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) {
+static std::string GetNCCHContainerPath(const std::string& nand_directory) {
return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str());
}
-static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) {
+static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) {
return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(),
high, low);
}
-ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory)
- : mount_point(GetSaveDataCheckContainerPath(nand_directory)) {}
+ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
+ : mount_point(GetNCCHContainerPath(nand_directory)) {}
-ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(const Path& path) {
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) {
auto vec = path.AsBinary();
const u32* data = reinterpret_cast<u32*>(vec.data());
- std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]);
+ std::string file_path = GetNCCHPath(mount_point, data[1], data[0]);
auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb");
if (!file->IsOpen()) {
@@ -45,15 +45,15 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path,
- const FileSys::ArchiveFormatInfo& format_info) {
- LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive.");
+ResultCode ArchiveFactory_NCCH::Format(const Path& path,
+ const FileSys::ArchiveFormatInfo& format_info) {
+ LOG_ERROR(Service_FS, "Attempted to format a NCCH archive.");
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
ErrorLevel::Permanent);
}
-ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const {
+ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path) const {
// TODO(Subv): Implement
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
return ResultCode(-1);
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_ncch.h
index e9cafbed9..66b8ce75d 100644
--- a/src/core/file_sys/archive_savedatacheck.h
+++ b/src/core/file_sys/archive_ncch.h
@@ -14,13 +14,13 @@
namespace FileSys {
-/// File system interface to the SaveDataCheck archive
-class ArchiveFactory_SaveDataCheck final : public ArchiveFactory {
+/// File system interface to the NCCH archive
+class ArchiveFactory_NCCH final : public ArchiveFactory {
public:
- ArchiveFactory_SaveDataCheck(const std::string& mount_point);
+ ArchiveFactory_NCCH(const std::string& mount_point);
std::string GetName() const override {
- return "SaveDataCheck";
+ return "NCCH";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
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_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index bcb03ed36..333dfb92e 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,6 +8,8 @@
#include "common/logging/log.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/path_parser.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,281 @@
namespace FileSys {
+ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
+ const Mode& mode) const {
+ Mode modified_mode;
+ modified_mode.hex = mode.hex;
+
+ // SDMC archive always opens a file with at least read permission
+ modified_mode.read_flag.Assign(1);
+
+ return OpenFileBase(path, modified_mode);
+}
+
+ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(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_INVALID_OPEN_FLAGS;
+ }
+
+ if (mode.create_flag && !mode.write_flag) {
+ LOG_ERROR(Service_FS, "Create flag set but write flag not set");
+ return ERROR_INVALID_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_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ 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_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_NOT_FOUND;
+ }
+
+ auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
+ return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
+}
+
+ResultCode SDMCArchive::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_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ }
+
+ if (FileUtil::Delete(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+}
+
+ResultCode SDMCArchive::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_NOT_FOUND;
+
+ 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_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_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_SDMC;
+ }
+
+ if (deleter(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+}
+
+ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
+ return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
+}
+
+ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
+ return DeleteDirectoryHelper(
+ path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
+}
+
+ResultCode SDMCArchive::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_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_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 SDMCArchive::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_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_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 SDMCArchive::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>> SDMCArchive::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_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ }
+
+ auto directory = std::make_unique<DiskDirectory>(full_path);
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
+}
+
+u64 SDMCArchive::GetFreeBytes() const {
+ // TODO: Stubbed to return 1GiB
+ return 1024 * 1024 * 1024;
+}
+
ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
: sdmc_directory(sdmc_directory) {
LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
@@ -35,7 +312,7 @@ bool ArchiveFactory_SDMC::Initialize() {
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
- auto archive = std::make_unique<DiskArchive>(sdmc_directory);
+ auto archive = std::make_unique<SDMCArchive>(sdmc_directory);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 88e855351..9d99b110c 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -14,6 +14,32 @@
namespace FileSys {
+/// Archive backend for SDMC archive
+class SDMCArchive : public ArchiveBackend {
+public:
+ SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
+
+ std::string GetName() const override {
+ return "SDMCArchive: " + 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:
+ ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
+ std::string mount_point;
+};
+
/// File system interface to the SDMC archive
class ArchiveFactory_SDMC final : public ArchiveFactory {
public:
diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp
new file mode 100644
index 000000000..2aafc9b1d
--- /dev/null
+++ b/src/core/file_sys/archive_sdmcwriteonly.cpp
@@ -0,0 +1,70 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "common/file_util.h"
+#include "core/file_sys/archive_sdmcwriteonly.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/file_backend.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
+ const Mode& mode) const {
+ if (mode.read_flag) {
+ LOG_ERROR(Service_FS, "Read flag is not supported");
+ return ERROR_INVALID_READ_FLAG;
+ }
+ return SDMCArchive::OpenFileBase(path, mode);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(
+ const Path& path) const {
+ LOG_ERROR(Service_FS, "Not supported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+}
+
+ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
+ : sdmc_directory(mount_point) {
+ LOG_INFO(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
+}
+
+bool ArchiveFactory_SDMCWriteOnly::Initialize() {
+ if (!Settings::values.use_virtual_sd) {
+ LOG_WARNING(Service_FS, "SDMC disabled by config.");
+ return false;
+ }
+
+ if (!FileUtil::CreateFullPath(sdmc_directory)) {
+ LOG_ERROR(Service_FS, "Unable to create SDMC path.");
+ return false;
+ }
+
+ return true;
+}
+
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path) {
+ auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory);
+ return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
+}
+
+ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path,
+ const FileSys::ArchiveFormatInfo& format_info) {
+ // TODO(wwylele): hwtest this
+ LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive.");
+ return ResultCode(-1);
+}
+
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path) const {
+ // TODO(Subv): Implement
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ return ResultCode(-1);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h
new file mode 100644
index 000000000..ed977485a
--- /dev/null
+++ b/src/core/file_sys/archive_sdmcwriteonly.h
@@ -0,0 +1,57 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/file_sys/archive_sdmc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/**
+ * Archive backend for SDMC write-only archive.
+ * The behaviour of SDMCWriteOnlyArchive is almost the same as SDMCArchive, except for
+ * - OpenDirectory is unsupported;
+ * - OpenFile with read flag is unsupported.
+ */
+class SDMCWriteOnlyArchive : public SDMCArchive {
+public:
+ SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {}
+
+ std::string GetName() const override {
+ return "SDMCWriteOnlyArchive: " + mount_point;
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
+ const Mode& mode) const override;
+
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
+};
+
+/// File system interface to the SDMC write-only archive
+class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory {
+public:
+ ArchiveFactory_SDMCWriteOnly(const std::string& mount_point);
+
+ /**
+ * Initialize the archive.
+ * @return true if it initialized successfully
+ */
+ bool Initialize();
+
+ std::string GetName() const override {
+ return "SDMCWriteOnly";
+ }
+
+ ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string sdmc_directory;
+};
+
+} // namespace FileSys
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/directory_backend.h b/src/core/file_sys/directory_backend.h
index b55e382ef..0c93f2074 100644
--- a/src/core/file_sys/directory_backend.h
+++ b/src/core/file_sys/directory_backend.h
@@ -41,12 +41,6 @@ public:
virtual ~DirectoryBackend() {}
/**
- * Open the directory
- * @return true if the directory opened correctly
- */
- virtual bool Open() = 0;
-
- /**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index 2f05af361..a243d9a13 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -15,144 +15,8 @@
namespace FileSys {
-ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path,
- const Mode mode) const {
- LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
- auto file = std::make_unique<DiskFile>(*this, path, mode);
- ResultCode result = file->Open();
- if (result.IsError())
- return result;
- return MakeResult<std::unique_ptr<FileBackend>>(std::move(file));
-}
-
-ResultCode DiskArchive::DeleteFile(const Path& path) const {
- std::string file_path = mount_point + path.AsString();
-
- if (FileUtil::IsDirectory(file_path))
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-
- if (!FileUtil::Exists(file_path))
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
- ErrorLevel::Status);
-
- if (FileUtil::Delete(file_path))
- return RESULT_SUCCESS;
-
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-}
-
-bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
- return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString());
-}
-
-bool DiskArchive::DeleteDirectory(const Path& path) const {
- return FileUtil::DeleteDir(mount_point + path.AsString());
-}
-
-bool DiskArchive::DeleteDirectoryRecursively(const Path& path) const {
- return FileUtil::DeleteDirRecursively(mount_point + path.AsString());
-}
-
-ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const {
- std::string full_path = mount_point + path.AsString();
-
- if (FileUtil::IsDirectory(full_path))
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-
- if (FileUtil::Exists(full_path))
- return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS,
- ErrorSummary::NothingHappened, ErrorLevel::Status);
-
- 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;
-
- return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
- ErrorLevel::Info);
-}
-
-bool DiskArchive::CreateDirectory(const Path& path) const {
- return FileUtil::CreateDir(mount_point + path.AsString());
-}
-
-bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
- return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString());
-}
-
-std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
- LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
- auto directory = std::make_unique<DiskDirectory>(*this, path);
- if (!directory->Open())
- return nullptr;
- return std::move(directory);
-}
-
-u64 DiskArchive::GetFreeBytes() const {
- // TODO: Stubbed to return 1GiB
- return 1024 * 1024 * 1024;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../etc/passwd can give the emulated program your users list.
- this->path = archive.mount_point + path.AsString();
- this->mode.hex = mode.hex;
-}
-
-ResultCode DiskFile::Open() {
- if (FileUtil::IsDirectory(path))
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-
- // Specifying only the Create flag is invalid
- if (mode.create_flag && !mode.read_flag && !mode.write_flag) {
- return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
- ErrorSummary::Canceled, ErrorLevel::Status);
- }
-
- if (!FileUtil::Exists(path)) {
- if (!mode.create_flag) {
- LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
- path.c_str());
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
- ErrorSummary::NotFound, ErrorLevel::Status);
- } else {
- // Create the file
- FileUtil::CreateEmptyFile(path);
- }
- }
-
- std::string mode_string = "";
- if (mode.write_flag)
- mode_string += "r+"; // Files opened with Write access can be read from
- else if (mode.read_flag)
- mode_string += "r";
-
- // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
- mode_string += "b";
-
- file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
- if (file->IsOpen())
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
- ErrorLevel::Status);
-}
-
ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
- if (!mode.read_flag && !mode.write_flag)
+ if (!mode.read_flag)
return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
ErrorSummary::Canceled, ErrorLevel::Status);
@@ -189,21 +53,11 @@ bool DiskFile::Close() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
-DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../usr/bin can give the emulated program your installed programs.
- this->path = archive.mount_point + path.AsString();
-}
-
-bool DiskDirectory::Open() {
- if (!FileUtil::IsDirectory(path))
- return false;
+DiskDirectory::DiskDirectory(const std::string& path) : directory() {
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
directory.size = size;
directory.isDirectory = true;
children_iterator = directory.children.begin();
- return true;
}
u32 DiskDirectory::Read(const u32 count, Entry* entries) {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index 59ebb2002..eb9166df6 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -20,43 +20,13 @@
namespace FileSys {
-/**
- * Helper which implements a backend accessing the host machine's filesystem.
- * This should be subclassed by concrete archive types, which will provide the
- * base directory on the host filesystem and override any required functionality.
- */
-class DiskArchive : public ArchiveBackend {
-public:
- DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
-
- virtual std::string GetName() const override {
- return "DiskArchive: " + mount_point;
- }
-
- ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
- const Mode mode) const override;
- ResultCode DeleteFile(const Path& path) const override;
- bool RenameFile(const Path& src_path, const Path& dest_path) const override;
- bool DeleteDirectory(const Path& path) const override;
- bool DeleteDirectoryRecursively(const Path& path) const override;
- ResultCode CreateFile(const Path& path, u64 size) const override;
- bool CreateDirectory(const Path& path) const override;
- bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
- std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
- u64 GetFreeBytes() const override;
-
-protected:
- friend class DiskFile;
- friend class DiskDirectory;
-
- std::string mount_point;
-};
-
class DiskFile : public FileBackend {
public:
- DiskFile(const DiskArchive& archive, const Path& path, const Mode mode);
+ DiskFile(FileUtil::IOFile&& file_, const Mode& mode_)
+ : file(new FileUtil::IOFile(std::move(file_))) {
+ mode.hex = mode_.hex;
+ }
- ResultCode Open() override;
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
@@ -68,20 +38,18 @@ public:
}
protected:
- std::string path;
Mode mode;
std::unique_ptr<FileUtil::IOFile> file;
};
class DiskDirectory : public DirectoryBackend {
public:
- DiskDirectory(const DiskArchive& archive, const Path& path);
+ DiskDirectory(const std::string& path);
~DiskDirectory() override {
Close();
}
- bool Open() override;
u32 Read(const u32 count, Entry* entries) override;
bool Close() const override {
@@ -89,7 +57,6 @@ public:
}
protected:
- std::string path;
u32 total_entries_in_directory;
FileUtil::FSTEntry directory;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
new file mode 100644
index 000000000..fd1b07df0
--- /dev/null
+++ b/src/core/file_sys/errors.h
@@ -0,0 +1,40 @@
+// 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_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
+ ErrorSummary::Canceled, ErrorLevel::Status);
+const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, 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_NOT_FOUND(ErrorDescription::FS_NotFound, 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_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile,
+ ErrorModule::FS, ErrorSummary::Canceled,
+ ErrorLevel::Status);
+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_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, 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/file_backend.h b/src/core/file_sys/file_backend.h
index ed997537f..5e7c2bab4 100644
--- a/src/core/file_sys/file_backend.h
+++ b/src/core/file_sys/file_backend.h
@@ -19,12 +19,6 @@ public:
virtual ~FileBackend() {}
/**
- * Open the file
- * @return Result of the file operation
- */
- virtual ResultCode Open() = 0;
-
- /**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp
index af59d296d..2735d2e3c 100644
--- a/src/core/file_sys/ivfc_archive.cpp
+++ b/src/core/file_sys/ivfc_archive.cpp
@@ -18,7 +18,7 @@ std::string IVFCArchive::GetName() const {
}
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
- const Mode mode) const {
+ const Mode& mode) const {
return MakeResult<std::unique_ptr<FileBackend>>(
std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
}
@@ -31,22 +31,25 @@ ResultCode IVFCArchive::DeleteFile(const Path& path) const {
ErrorLevel::Status);
}
-bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
+ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-bool IVFCArchive::DeleteDirectory(const Path& path) const {
+ResultCode IVFCArchive::DeleteDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-bool IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
+ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
@@ -57,20 +60,22 @@ ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
ErrorLevel::Permanent);
}
-bool IVFCArchive::CreateDirectory(const Path& path) const {
+ResultCode IVFCArchive::CreateDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const {
- return std::make_unique<IVFCDirectory>();
+ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>());
}
u64 IVFCArchive::GetFreeBytes() const {
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h
index 2fbb3a568..e6fbdfb1f 100644
--- a/src/core/file_sys/ivfc_archive.h
+++ b/src/core/file_sys/ivfc_archive.h
@@ -33,15 +33,15 @@ public:
std::string GetName() const override;
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
- const Mode mode) const override;
+ const Mode& mode) const override;
ResultCode DeleteFile(const Path& path) const override;
- bool RenameFile(const Path& src_path, const Path& dest_path) const override;
- bool DeleteDirectory(const Path& path) const override;
- bool DeleteDirectoryRecursively(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;
- bool CreateDirectory(const Path& path) const override;
- bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
- std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) 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:
@@ -55,9 +55,6 @@ public:
IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
- ResultCode Open() override {
- return RESULT_SUCCESS;
- }
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
@@ -75,9 +72,6 @@ private:
class IVFCDirectory : public DirectoryBackend {
public:
- bool Open() override {
- return false;
- }
u32 Read(const u32 count, Entry* entries) override {
return 0;
}
diff --git a/src/core/file_sys/path_parser.cpp b/src/core/file_sys/path_parser.cpp
new file mode 100644
index 000000000..5a89b02b8
--- /dev/null
+++ b/src/core/file_sys/path_parser.cpp
@@ -0,0 +1,98 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <set>
+#include "common/file_util.h"
+#include "common/string_util.h"
+#include "core/file_sys/path_parser.h"
+
+namespace FileSys {
+
+PathParser::PathParser(const Path& path) {
+ if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) {
+ is_valid = false;
+ return;
+ }
+
+ auto path_string = path.AsString();
+ if (path_string.size() == 0 || path_string[0] != '/') {
+ is_valid = false;
+ return;
+ }
+
+ // Filter out invalid characters for the host system.
+ // Although some of these characters are valid on 3DS, they are unlikely to be used by games.
+ if (std::find_if(path_string.begin(), path_string.end(), [](char c) {
+ static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'};
+ return invalid_chars.find(c) != invalid_chars.end();
+ }) != path_string.end()) {
+ is_valid = false;
+ return;
+ }
+
+ Common::SplitString(path_string, '/', path_sequence);
+
+ auto begin = path_sequence.begin();
+ auto end = path_sequence.end();
+ end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; });
+ path_sequence = std::vector<std::string>(begin, end);
+
+ // checks if the path is out of bounds.
+ int level = 0;
+ for (auto& node : path_sequence) {
+ if (node == "..") {
+ --level;
+ if (level < 0) {
+ is_valid = false;
+ return;
+ }
+ } else {
+ ++level;
+ }
+ }
+
+ is_valid = true;
+ is_root = level == 0;
+}
+
+PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const {
+ auto path = mount_point;
+ if (!FileUtil::IsDirectory(path))
+ return InvalidMountPoint;
+ if (path_sequence.empty()) {
+ return DirectoryFound;
+ }
+
+ for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) {
+ if (path.back() != '/')
+ path += '/';
+ path += *iter;
+
+ if (!FileUtil::Exists(path))
+ return PathNotFound;
+ if (FileUtil::IsDirectory(path))
+ continue;
+ return FileInPath;
+ }
+
+ path += "/" + path_sequence.back();
+ if (!FileUtil::Exists(path))
+ return NotFound;
+ if (FileUtil::IsDirectory(path))
+ return DirectoryFound;
+ return FileFound;
+}
+
+std::string PathParser::BuildHostPath(const std::string& mount_point) const {
+ std::string path = mount_point;
+ for (auto& node : path_sequence) {
+ if (path.back() != '/')
+ path += '/';
+ path += node;
+ }
+ return path;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h
new file mode 100644
index 000000000..990802579
--- /dev/null
+++ b/src/core/file_sys/path_parser.h
@@ -0,0 +1,61 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include "core/file_sys/archive_backend.h"
+
+namespace FileSys {
+
+/**
+ * A helper class parsing and verifying a string-type Path.
+ * Every archives with a sub file system should use this class to parse the path argument and check
+ * the status of the file / directory in question on the host file system.
+ */
+class PathParser {
+public:
+ PathParser(const Path& path);
+
+ /**
+ * Checks if the Path is valid.
+ * This function should be called once a PathParser is constructed.
+ * A Path is valid if:
+ * - it is a string path (with type LowPathType::Char or LowPathType::Wchar),
+ * - it starts with "/" (this seems a hard requirement in real 3DS),
+ * - it doesn't contain invalid characters, and
+ * - it doesn't go out of the root directory using "..".
+ */
+ bool IsValid() const {
+ return is_valid;
+ }
+
+ /// Checks if the Path represents the root directory.
+ bool IsRootDirectory() const {
+ return is_root;
+ }
+
+ enum HostStatus {
+ InvalidMountPoint,
+ PathNotFound, // "/a/b/c" when "a" doesn't exist
+ FileInPath, // "/a/b/c" when "a" is a file
+ FileFound, // "/a/b/c" when "c" is a file
+ DirectoryFound, // "/a/b/c" when "c" is a directory
+ NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist
+ };
+
+ /// Checks the status of the specified file / directory by the Path on the host file system.
+ HostStatus GetHostStatus(const std::string& mount_point) const;
+
+ /// Builds a full path on the host file system.
+ std::string BuildHostPath(const std::string& mount_point) const;
+
+private:
+ std::vector<std::string> path_sequence;
+ bool is_valid{};
+ bool is_root{};
+};
+
+} // 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/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index 14964427b..e1379ac4d 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -10,7 +10,7 @@ namespace HLE {
namespace Applets {
ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
- if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
+ if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
UNIMPLEMENTED();
// TODO(Subv): Find the right error code
@@ -36,7 +36,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
+ result.signal = static_cast<u32>(Service::APT::SignalType::Response);
result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
@@ -57,7 +57,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame
Service::APT::MessageParameter message;
message.buffer.resize(parameter.buffer.size());
std::fill(message.buffer.begin(), message.buffer.end(), 0);
- message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
+ message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
Service::APT::SendParameter(message);
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 53a8683a4..3455b9201 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -19,7 +19,7 @@ namespace HLE {
namespace Applets {
ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
- if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
+ if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
UNIMPLEMENTED();
// TODO(Subv): Find the right error code
@@ -44,7 +44,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
+ result.signal = static_cast<u32>(Service::APT::SignalType::Response);
result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
@@ -73,7 +73,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
Service::APT::MessageParameter message;
message.buffer.resize(sizeof(MiiResult));
std::memcpy(message.buffer.data(), &result, message.buffer.size());
- message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
+ message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
Service::APT::SendParameter(message);
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 06ddf538b..1e21337f5 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -22,7 +22,7 @@ namespace HLE {
namespace Applets {
ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
- if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
+ if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
UNIMPLEMENTED();
// TODO(Subv): Find the right error code
@@ -47,7 +47,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
- result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
+ result.signal = static_cast<u32>(Service::APT::SignalType::Response);
result.buffer.clear();
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
result.sender_id = static_cast<u32>(id);
@@ -108,7 +108,7 @@ void SoftwareKeyboard::Finalize() {
Service::APT::MessageParameter message;
message.buffer.resize(sizeof(SoftwareKeyboardConfig));
std::memcpy(message.buffer.data(), &config, message.buffer.size());
- message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
+ message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
message.sender_id = static_cast<u32>(id);
Service::APT::SendParameter(message);
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 1489c7002..3e116e3df 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -22,6 +22,11 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
evt->reset_type = reset_type;
evt->name = std::move(name);
+ if (reset_type == ResetType::Pulse) {
+ LOG_ERROR(Kernel, "Unimplemented event reset type Pulse");
+ UNIMPLEMENTED();
+ }
+
return evt;
}
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index a9f98223c..eac181f4e 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -31,6 +31,11 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
timer->interval_delay = 0;
timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom();
+ if (reset_type == ResetType::Pulse) {
+ LOG_ERROR(Kernel, "Unimplemented timer reset type Pulse");
+ UNIMPLEMENTED();
+ }
+
return timer;
}
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 7f8d8e00d..f7356f9d8 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -20,15 +20,24 @@ 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_InvalidReadFlag = 700,
FS_InvalidPath = 702,
+ FS_WriteBeyondEnd = 705,
+ FS_UnsupportedOpenFlags = 760,
+ FS_UnexpectedFileOrDirectory = 770,
InvalidSection = 1000,
TooLarge = 1001,
NotAuthorized = 1002,
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index c4bd65986..31e5e07b2 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -396,6 +396,15 @@ void StartLibraryApplet(Service::Interface* self) {
cmd_buff[1] = applet->Start(parameter).raw;
}
+void CancelLibraryApplet(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 exiting = cmd_buff[1] & 0xFF;
+
+ cmd_buff[1] = 1; // TODO: Find the return code meaning
+
+ LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting);
+}
+
void SetScreenCapPostPermission(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -523,7 +532,7 @@ void Init() {
notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification");
parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start");
- next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted);
+ next_parameter.signal = static_cast<u32>(SignalType::Wakeup);
next_parameter.destination_id = 0x300;
}
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index a118cda1f..44dbd8757 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -46,12 +46,23 @@ static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has i
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
- AppJustStarted = 0x1,
- LibAppJustStarted = 0x2,
- LibAppFinished = 0x3,
- LibAppClosed = 0xA,
- ReturningToApp = 0xB,
- ExitingApp = 0xC,
+ Wakeup = 0x1,
+ Request = 0x2,
+ Response = 0x3,
+ Exit = 0x4,
+ Message = 0x5,
+ HomeButtonSingle = 0x6,
+ HomeButtonDouble = 0x7,
+ DspSleep = 0x8,
+ DspWakeup = 0x9,
+ WakeupByExit = 0xA,
+ WakeupByPause = 0xB,
+ WakeupByCancel = 0xC,
+ WakeupByCancelAll = 0xD,
+ WakeupByPowerButtonClick = 0xE,
+ WakeupToJumpHome = 0xF,
+ RequestForSysApplet = 0x10,
+ WakeupToLaunchApplication = 0x11,
};
/// App Id's used by APT functions
@@ -381,6 +392,17 @@ void PreloadLibraryApplet(Service::Interface* self);
void StartLibraryApplet(Service::Interface* self);
/**
+ * APT::CancelLibraryApplet service function
+ * Inputs:
+ * 0 : Command header [0x003B0040]
+ * 1 : u8, Application exiting (0 = not exiting, 1 = exiting)
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
+void CancelLibraryApplet(Service::Interface* self);
+
+/**
* APT::GetStartupArgument service function
* Inputs:
* 1 : Parameter Size (capped to 0x300)
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index f27ad91b7..a7a0c8a41 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -25,7 +25,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
- {0x003B0040, nullptr, "CancelLibraryApplet?"},
+ {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
{0x003E0080, nullptr, "ReplySleepQuery"},
{0x00430040, NotifyToWait, "NotifyToWait?"},
{0x00440000, GetSharedFont, "GetSharedFont?"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index d6ad42e21..a731c39f6 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -67,7 +67,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00380040, nullptr, "PreloadResidentApplet"},
{0x00390040, nullptr, "PrepareToStartResidentApplet"},
{0x003A0044, nullptr, "StartResidentApplet"},
- {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
{0x003C0042, nullptr, "SendDspSleep"},
{0x003D0042, nullptr, "SendDspWakeUp"},
{0x003E0080, nullptr, "ReplySleepQuery"},
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 24eee6903..d554c3f54 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -45,7 +45,8 @@ static_assert(sizeof(SaveFileConfig) == 0x455C,
enum ConfigBlockID {
StereoCameraSettingsBlockID = 0x00050005,
SoundOutputModeBlockID = 0x00070001,
- ConsoleUniqueIDBlockID = 0x00090001,
+ ConsoleUniqueID1BlockID = 0x00090000,
+ ConsoleUniqueID2BlockID = 0x00090001,
UsernameBlockID = 0x000A0000,
BirthdayBlockID = 0x000A0001,
LanguageBlockID = 0x000A0002,
@@ -359,7 +360,7 @@ ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* da
}
ResultCode DeleteConfigNANDSaveFile() {
- FileSys::Path path("config");
+ FileSys::Path path("/config");
return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path);
}
@@ -368,7 +369,7 @@ ResultCode UpdateConfigNANDSavegame() {
mode.write_flag.Assign(1);
mode.create_flag.Assign(1);
- FileSys::Path path("config");
+ FileSys::Path path("/config");
auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);
ASSERT_MSG(config_result.Succeeded(), "could not open file");
@@ -382,8 +383,9 @@ ResultCode UpdateConfigNANDSavegame() {
ResultCode FormatConfig() {
ResultCode res = DeleteConfigNANDSaveFile();
// The delete command fails if the file doesn't exist, so we have to check that too
- if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound)
+ if (!res.IsSuccess() && res.description != ErrorDescription::FS_FileNotFound) {
return res;
+ }
// Delete the old data
cfg_config_file_buffer.fill(0);
// Create the header
@@ -409,7 +411,12 @@ ResultCode FormatConfig() {
if (!res.IsSuccess())
return res;
- res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE,
+ res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE,
+ &CONSOLE_UNIQUE_ID);
+ if (!res.IsSuccess())
+ return res;
+
+ res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE,
&CONSOLE_UNIQUE_ID);
if (!res.IsSuccess())
return res;
@@ -504,7 +511,7 @@ ResultCode LoadConfigNANDSaveFile() {
cfg_system_save_data_archive = *archive_result;
- FileSys::Path config_path("config");
+ FileSys::Path config_path("/config");
FileSys::Mode open_mode = {};
open_mode.read_flag.Assign(1);
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 3ca4f98de..9905757c7 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -2,9 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <chrono>
+#include <iomanip>
+#include <sstream>
+
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "core/hle/result.h"
#include "core/hle/service/err_f.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,12 +18,45 @@
namespace ERR_F {
-enum {
- ErrSpecifier0 = 0,
- ErrSpecifier1 = 1,
- ErrSpecifier3 = 3,
- ErrSpecifier4 = 4,
+enum class FatalErrType : u32 {
+ Generic = 0,
+ Corrupted = 1,
+ CardRemoved = 2,
+ Exception = 3,
+ ResultFailure = 4,
+ Logged = 5,
+};
+
+enum class ExceptionType : u32 {
+ PrefetchAbort = 0,
+ DataAbort = 1,
+ Undefined = 2,
+ VectorFP = 3,
+};
+
+struct ExceptionInfo {
+ u8 exception_type;
+ INSERT_PADDING_BYTES(3);
+ u32 sr;
+ u32 ar;
+ u32 fpexc;
+ u32 fpinst;
+ u32 fpinst2;
+};
+static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size");
+
+struct ExceptionContext final {
+ std::array<u32, 16> arm_regs;
+ u32 cpsr;
};
+static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size");
+
+struct ExceptionData {
+ ExceptionInfo exception_info;
+ ExceptionContext exception_context;
+ INSERT_PADDING_WORDS(1);
+};
+static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size");
// This is used instead of ResultCode from result.h
// because we can't have non-trivial data members in unions.
@@ -30,150 +69,191 @@ union RSL {
BitField<27, 5, u32> level;
};
-union ErrInfo {
- u8 specifier;
-
- struct {
- u8 specifier; // 0x0
- u8 rev_high; // 0x1
- u16 rev_low; // 0x2
- RSL result_code; // 0x4
- u32 address; // 0x8
- INSERT_PADDING_BYTES(4); // 0xC
- u32 pid_low; // 0x10
- u32 pid_high; // 0x14
- u32 aid_low; // 0x18
- u32 aid_high; // 0x1C
- } errtype1;
-
- struct {
- u8 specifier; // 0x0
- u8 rev_high; // 0x1
- u16 rev_low; // 0x2
- INSERT_PADDING_BYTES(0xC); // 0x4
- u32 pid_low; // 0x10
- u32 pid_high; // 0x14
- u32 aid_low; // 0x18
- u32 aid_high; // 0x1C
- u8 error_type; // 0x20
- INSERT_PADDING_BYTES(3); // 0x21
- u32 fault_status_reg; // 0x24
- u32 fault_addr; // 0x28
- u32 fpexc; // 0x2C
- u32 finst; // 0x30
- u32 finst2; // 0x34
- INSERT_PADDING_BYTES(0x34); // 0x38
- u32 sp; // 0x6C
- u32 pc; // 0x70
- u32 lr; // 0x74
- u32 cpsr; // 0x78
- } errtype3;
-
- struct {
- u8 specifier; // 0x0
- u8 rev_high; // 0x1
- u16 rev_low; // 0x2
- RSL result_code; // 0x4
- INSERT_PADDING_BYTES(8); // 0x8
- u32 pid_low; // 0x10
- u32 pid_high; // 0x14
- u32 aid_low; // 0x18
- u32 aid_high; // 0x1C
- char debug_string1[0x2E]; // 0x20
- char debug_string2[0x2E]; // 0x4E
- } errtype4;
+struct ErrInfo {
+ struct ErrInfoCommon {
+ u8 specifier; // 0x0
+ u8 rev_high; // 0x1
+ u16 rev_low; // 0x2
+ RSL result_code; // 0x4
+ u32 pc_address; // 0x8
+ u32 pid; // 0xC
+ u32 title_id_low; // 0x10
+ u32 title_id_high; // 0x14
+ u32 app_title_id_low; // 0x18
+ u32 app_title_id_high; // 0x1C
+ } errinfo_common;
+ static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size");
+
+ union {
+ struct {
+ char data[0x60]; // 0x20
+ } generic;
+
+ struct {
+ ExceptionData exception_data; // 0x20
+ } exception;
+
+ struct {
+ char message[0x60]; // 0x20
+ } result_failure;
+ };
};
-enum { PrefetchAbort = 0, DataAbort = 1, UndefInstr = 2, VectorFP = 3 };
+static std::string GetErrType(u8 type_code) {
+ switch (static_cast<FatalErrType>(type_code)) {
+ case FatalErrType::Generic:
+ return "Generic";
+ case FatalErrType::Corrupted:
+ return "Corrupted";
+ case FatalErrType::CardRemoved:
+ return "CardRemoved";
+ case FatalErrType::Exception:
+ return "Exception";
+ case FatalErrType::ResultFailure:
+ return "ResultFailure";
+ case FatalErrType::Logged:
+ return "Logged";
+ default:
+ return "Unknown Error Type";
+ }
+}
-static std::string GetErrInfo3Type(u8 type_code) {
- switch (type_code) {
- case PrefetchAbort:
+static std::string GetExceptionType(u8 type_code) {
+ switch (static_cast<ExceptionType>(type_code)) {
+ case ExceptionType::PrefetchAbort:
return "Prefetch Abort";
- case DataAbort:
+ case ExceptionType::DataAbort:
return "Data Abort";
- case UndefInstr:
- return "Undefined Instruction";
- case VectorFP:
- return "Vector Floating Point";
+ case ExceptionType::Undefined:
+ return "Undefined Exception";
+ case ExceptionType::VectorFP:
+ return "Vector Floating Point Exception";
default:
- return "unknown";
+ return "Unknown Exception Type";
}
}
+static std::string GetCurrentSystemTime() {
+ auto now = std::chrono::system_clock::now();
+ auto time = std::chrono::system_clock::to_time_t(now);
+
+ std::stringstream time_stream;
+ time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S");
+ return time_stream.str();
+}
+
+static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) {
+ LOG_CRITICAL(Service_ERR, "PID: 0x%08X", errinfo_common.pid);
+ LOG_CRITICAL(Service_ERR, "REV: 0x%08X_0x%08X", errinfo_common.rev_high,
+ errinfo_common.rev_low);
+ LOG_CRITICAL(Service_ERR, "TID: 0x%08X_0x%08X", errinfo_common.title_id_high,
+ errinfo_common.title_id_low);
+ LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errinfo_common.app_title_id_high,
+ errinfo_common.app_title_id_low);
+ LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errinfo_common.pc_address);
+
+ LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errinfo_common.result_code.raw);
+ LOG_CRITICAL(Service_ERR, " Level: %u", errinfo_common.result_code.level.Value());
+ LOG_CRITICAL(Service_ERR, " Summary: %u", errinfo_common.result_code.summary.Value());
+ LOG_CRITICAL(Service_ERR, " Module: %u", errinfo_common.result_code.module.Value());
+ LOG_CRITICAL(Service_ERR, " Desc: %u", errinfo_common.result_code.description.Value());
+}
+
+/* ThrowFatalError function
+ * Inputs:
+ * 0 : Header code [0x00010800]
+ * 1-32 : FatalErrInfo
+ * Outputs:
+ * 0 : Header code
+ * 1 : Result code
+ */
static void ThrowFatalError(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- LOG_CRITICAL(Service_ERR, "Fatal error!");
+ LOG_CRITICAL(Service_ERR, "Fatal error");
const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]);
+ LOG_CRITICAL(Service_ERR, "Fatal error type: %s",
+ GetErrType(errinfo->errinfo_common.specifier).c_str());
- switch (errinfo->specifier) {
- case ErrSpecifier0:
- case ErrSpecifier1: {
- const auto& errtype = errinfo->errtype1;
- LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
- LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
- LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
- LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address);
-
- LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw);
- LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value());
- LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value());
- LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value());
- LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value());
+ // Generic Info
+ LogGenericInfo(errinfo->errinfo_common);
+
+ switch (static_cast<FatalErrType>(errinfo->errinfo_common.specifier)) {
+ case FatalErrType::Generic:
+ case FatalErrType::Corrupted:
+ case FatalErrType::CardRemoved:
+ case FatalErrType::Logged: {
+ LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
break;
}
+ case FatalErrType::Exception: {
+ const auto& errtype = errinfo->exception;
+
+ // Register Info
+ LOG_CRITICAL(Service_ERR, "ARM Registers:");
+ for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size();
+ ++index) {
+ if (index < 13) {
+ LOG_DEBUG(Service_ERR, "r%u=0x%08X", index,
+ errtype.exception_data.exception_context.arm_regs.at(index));
+ } else if (index == 13) {
+ LOG_CRITICAL(Service_ERR, "SP=0x%08X",
+ errtype.exception_data.exception_context.arm_regs.at(index));
+ } else if (index == 14) {
+ LOG_CRITICAL(Service_ERR, "LR=0x%08X",
+ errtype.exception_data.exception_context.arm_regs.at(index));
+ } else if (index == 15) {
+ LOG_CRITICAL(Service_ERR, "PC=0x%08X",
+ errtype.exception_data.exception_context.arm_regs.at(index));
+ }
+ }
+ LOG_CRITICAL(Service_ERR, "CPSR=0x%08X", errtype.exception_data.exception_context.cpsr);
- case ErrSpecifier3: {
- const auto& errtype = errinfo->errtype3;
- LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
- LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
- LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
- LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str());
-
- LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc);
- LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr);
- LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp);
- LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr);
-
- switch (errtype.error_type) {
- case PrefetchAbort:
- case DataAbort:
- LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr);
- LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg);
+ // Exception Info
+ LOG_CRITICAL(
+ Service_ERR, "EXCEPTION TYPE: %s",
+ GetExceptionType(errtype.exception_data.exception_info.exception_type).c_str());
+ switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) {
+ case ExceptionType::PrefetchAbort:
+ LOG_CRITICAL(Service_ERR, "IFSR: 0x%08X", errtype.exception_data.exception_info.sr);
+ LOG_CRITICAL(Service_ERR, "r15: 0x%08X", errtype.exception_data.exception_info.ar);
+ case ExceptionType::DataAbort:
+ LOG_CRITICAL(Service_ERR, "DFSR: 0x%08X", errtype.exception_data.exception_info.sr);
+ LOG_CRITICAL(Service_ERR, "DFAR: 0x%08X", errtype.exception_data.exception_info.ar);
break;
- case VectorFP:
- LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc);
- LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst);
- LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2);
+ case ExceptionType::VectorFP:
+ LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X",
+ errtype.exception_data.exception_info.fpinst);
+ LOG_CRITICAL(Service_ERR, "FINST: 0x%08X",
+ errtype.exception_data.exception_info.fpinst);
+ LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X",
+ errtype.exception_data.exception_info.fpinst2);
break;
}
+ LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
break;
}
- case ErrSpecifier4: {
- const auto& errtype = errinfo->errtype4;
- LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
- LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
- LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
+ case FatalErrType::ResultFailure: {
+ const auto& errtype = errinfo->result_failure;
- LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw);
- LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value());
- LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value());
- LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value());
- LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value());
-
- LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1);
- LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2);
+ // Failure Message
+ LOG_CRITICAL(Service_ERR, "Failure Message: %s", errtype.message);
+ LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
break;
}
- }
- cmd_buff[1] = 0; // No error
+ } // switch FatalErrType
+
+ cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
}
const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
{0x00010800, ThrowFatalError, "ThrowFatalError"},
+ {0x00020042, nullptr, "SetUserString"},
+ // clang-format on
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 7f9696bfb..4c29784e8 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -15,9 +15,10 @@
#include "common/logging/log.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/archive_extsavedata.h"
+#include "core/file_sys/archive_ncch.h"
#include "core/file_sys/archive_savedata.h"
-#include "core/file_sys/archive_savedatacheck.h"
#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/archive_sdmcwriteonly.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/file_backend.h"
@@ -338,17 +339,11 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle,
return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) {
- if (src_archive->RenameFile(src_path, dest_path))
- return RESULT_SUCCESS;
+ return src_archive->RenameFile(src_path, dest_path);
} else {
// TODO: Implement renaming across archives
return UnimplementedFunction(ErrorModule::FS);
}
-
- // 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);
}
ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
@@ -356,10 +351,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- if (archive->DeleteDirectory(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->DeleteDirectory(path);
}
ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
@@ -368,10 +360,7 @@ ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- if (archive->DeleteDirectoryRecursively(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->DeleteDirectoryRecursively(path);
}
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path,
@@ -388,10 +377,7 @@ ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- if (archive->CreateDirectory(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->CreateDirectory(path);
}
ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
@@ -404,17 +390,11 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) {
- if (src_archive->RenameDirectory(src_path, dest_path))
- return RESULT_SUCCESS;
+ return src_archive->RenameDirectory(src_path, dest_path);
} else {
// TODO: Implement renaming across archives
return UnimplementedFunction(ErrorModule::FS);
}
-
- // 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<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle,
@@ -423,13 +403,11 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
- if (backend == nullptr) {
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
- ErrorLevel::Permanent);
- }
+ auto backend = archive->OpenDirectory(path);
+ if (backend.Failed())
+ return backend.Code();
- auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path));
+ auto directory = Kernel::SharedPtr<Directory>(new Directory(backend.MoveFrom(), path));
return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory));
}
@@ -549,6 +527,13 @@ void RegisterArchiveTypes() {
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s",
sdmc_directory.c_str());
+ auto sdmcwo_factory = std::make_unique<FileSys::ArchiveFactory_SDMCWriteOnly>(sdmc_directory);
+ if (sdmcwo_factory->Initialize())
+ RegisterArchiveType(std::move(sdmcwo_factory), ArchiveIdCode::SDMCWriteOnly);
+ else
+ LOG_ERROR(Service_FS, "Can't instantiate SDMCWriteOnly archive with path %s",
+ sdmc_directory.c_str());
+
// Create the SaveData archive
auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory);
RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData);
@@ -569,10 +554,9 @@ void RegisterArchiveTypes() {
LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s",
sharedextsavedata_factory->GetMountPoint().c_str());
- // Create the SaveDataCheck archive, basically a small variation of the RomFS archive
- auto savedatacheck_factory =
- std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory);
- RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck);
+ // Create the NCCH archive, basically a small variation of the RomFS archive
+ auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_NCCH>(nand_directory);
+ RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::NCCH);
auto systemsavedata_factory =
std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 41a76285c..21ed9717b 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -33,7 +33,7 @@ enum class ArchiveIdCode : u32 {
SystemSaveData = 0x00000008,
SDMC = 0x00000009,
SDMCWriteOnly = 0x0000000A,
- SaveDataCheck = 0x2345678A,
+ NCCH = 0x2345678A,
};
/// Media types for the archives
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index edd1ea97b..563341504 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/mic_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -9,23 +12,298 @@
namespace MIC_U {
+enum class Encoding : u8 {
+ PCM8 = 0,
+ PCM16 = 1,
+ PCM8Signed = 2,
+ PCM16Signed = 3,
+};
+
+enum class SampleRate : u8 {
+ SampleRate32730 = 0,
+ SampleRate16360 = 1,
+ SampleRate10910 = 2,
+ SampleRate8180 = 3
+};
+
+static Kernel::SharedPtr<Kernel::Event> buffer_full_event;
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
+static u8 mic_gain = 0;
+static bool mic_power = false;
+static bool is_sampling = false;
+static bool allow_shell_closed;
+static bool clamp = false;
+static Encoding encoding;
+static SampleRate sample_rate;
+static s32 audio_buffer_offset;
+static u32 audio_buffer_size;
+static bool audio_buffer_loop;
+
+/**
+ * MIC::MapSharedMem service function
+ * Inputs:
+ * 0 : Header Code[0x00010042]
+ * 1 : Shared-mem size
+ * 2 : CopyHandleDesc
+ * 3 : Shared-mem handle
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void MapSharedMem(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 size = cmd_buff[1];
+ Handle mem_handle = cmd_buff[3];
+ shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle);
+ if (shared_memory) {
+ shared_memory->name = "MIC_U:shared_memory";
+ }
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle);
+}
+
+/**
+ * MIC::UnmapSharedMem service function
+ * Inputs:
+ * 0 : Header Code[0x00020000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void UnmapSharedMem(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "called");
+}
+
+/**
+ * MIC::StartSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00030140]
+ * 1 : Encoding
+ * 2 : SampleRate
+ * 3 : Base offset for audio data in sharedmem
+ * 4 : Size of the audio data in sharedmem
+ * 5 : Loop at end of buffer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void StartSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF);
+ sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF);
+ audio_buffer_offset = cmd_buff[3];
+ audio_buffer_size = cmd_buff[4];
+ audio_buffer_loop = static_cast<bool>(cmd_buff[5] & 0xFF);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ is_sampling = true;
+ LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, "
+ "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u",
+ encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop);
+}
+
+/**
+ * MIC::AdjustSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00040040]
+ * 1 : SampleRate
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void AdjustSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate);
+}
+
+/**
+ * MIC::StopSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00050000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void StopSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ is_sampling = false;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::IsSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00060000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : 0 = sampling, non-zero = sampling
+ */
+static void IsSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = is_sampling;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::GetBufferFullEvent service function
+ * Inputs:
+ * 0 : Header Code[0x00070000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Event handle
+ */
+static void GetBufferFullEvent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom();
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetGain service function
+ * Inputs:
+ * 0 : Header Code[0x00080040]
+ * 1 : Gain
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetGain(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ mic_gain = cmd_buff[1] & 0xFF;
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain);
+}
+
+/**
+ * MIC::GetGain service function
+ * Inputs:
+ * 0 : Header Code[0x00090000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Gain
+ */
+static void GetGain(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = mic_gain;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetPower service function
+ * Inputs:
+ * 0 : Header Code[0x000A0040]
+ * 1 : Power (0 = off, 1 = on)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetPower(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ mic_power = static_cast<bool>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power);
+}
+
+/**
+ * MIC::GetPower service function
+ * Inputs:
+ * 0 : Header Code[0x000B0000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Power
+ */
+static void GetPower(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = mic_power;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetIirFilterMic service function
+ * Inputs:
+ * 0 : Header Code[0x000C0042]
+ * 1 : Size
+ * 2 : (Size << 4) | 0xA
+ * 3 : Pointer to IIR Filter Data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetIirFilterMic(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 size = cmd_buff[1];
+ VAddr buffer = cmd_buff[3];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer);
+}
+
+/**
+ * MIC::SetClamp service function
+ * Inputs:
+ * 0 : Header Code[0x000D0040]
+ * 1 : Clamp (0 = don't clamp, non-zero = clamp)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetClamp(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ clamp = static_cast<bool>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp);
+}
+
+/**
+ * MIC::GetClamp service function
+ * Inputs:
+ * 0 : Header Code[0x000E0000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Clamp (0 = don't clamp, non-zero = clamp)
+ */
+static void GetClamp(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = clamp;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetAllowShellClosed service function
+ * Inputs:
+ * 0 : Header Code[0x000D0040]
+ * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetAllowShellClosed(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed);
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010042, nullptr, "MapSharedMem"},
- {0x00020000, nullptr, "UnmapSharedMem"},
- {0x00030140, nullptr, "Initialize"},
- {0x00040040, nullptr, "AdjustSampling"},
- {0x00050000, nullptr, "StopSampling"},
- {0x00060000, nullptr, "IsSampling"},
- {0x00070000, nullptr, "GetEventHandle"},
- {0x00080040, nullptr, "SetGain"},
- {0x00090000, nullptr, "GetGain"},
- {0x000A0040, nullptr, "SetPower"},
- {0x000B0000, nullptr, "GetPower"},
- {0x000C0042, nullptr, "size"},
- {0x000D0040, nullptr, "SetClamp"},
- {0x000E0000, nullptr, "GetClamp"},
- {0x000F0040, nullptr, "SetAllowShellClosed"},
- {0x00100040, nullptr, "unknown_input2"},
+ {0x00010042, MapSharedMem, "MapSharedMem"},
+ {0x00020000, UnmapSharedMem, "UnmapSharedMem"},
+ {0x00030140, StartSampling, "StartSampling"},
+ {0x00040040, AdjustSampling, "AdjustSampling"},
+ {0x00050000, StopSampling, "StopSampling"},
+ {0x00060000, IsSampling, "IsSampling"},
+ {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"},
+ {0x00080040, SetGain, "SetGain"},
+ {0x00090000, GetGain, "GetGain"},
+ {0x000A0040, SetPower, "SetPower"},
+ {0x000B0000, GetPower, "GetPower"},
+ {0x000C0042, SetIirFilterMic, "SetIirFilterMic"},
+ {0x000D0040, SetClamp, "SetClamp"},
+ {0x000E0000, GetClamp, "GetClamp"},
+ {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"},
+ {0x00100040, nullptr, "SetClientSDKVersion"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,6 +311,18 @@ const Interface::FunctionInfo FunctionTable[] = {
Interface::Interface() {
Register(FunctionTable);
+ shared_memory = nullptr;
+ buffer_full_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event");
+ mic_gain = 0;
+ mic_power = false;
+ is_sampling = false;
+ clamp = false;
+}
+
+Interface::~Interface() {
+ shared_memory = nullptr;
+ buffer_full_event = nullptr;
}
} // namespace
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index dc795d14c..1cff7390e 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -16,6 +16,7 @@ namespace MIC_U {
class Interface : public Service::Interface {
public:
Interface();
+ ~Interface();
std::string GetPortName() const override {
return "mic:u";
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 6e6b63329..cc859c14c 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -128,7 +128,7 @@ void Init() {
Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
- FileSys::Path gamecoin_path("gamecoin.dat");
+ FileSys::Path gamecoin_path("/gamecoin.dat");
FileSys::Mode open_mode = {};
open_mode.write_flag.Assign(1);
open_mode.create_flag.Assign(1);
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 28cb97d8e..45dedea68 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -29,16 +29,12 @@ namespace GPU {
Regs g_regs;
-/// True if the current frame was skipped
-bool g_skip_frame;
/// 268MHz CPU clocks / 60Hz frames per second
const u64 frame_ticks = 268123480ull / 60;
/// Event id for CoreTiming
static int vblank_event;
/// Total number of frames drawn
static u64 frame_count;
-/// True if the last frame was skipped
-static bool last_skip_frame;
template <typename T>
inline void Read(T& var, const u32 raw_addr) {
@@ -519,20 +515,7 @@ template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
static void VBlankCallback(u64 userdata, int cycles_late) {
frame_count++;
- last_skip_frame = g_skip_frame;
- g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
-
- // Swap buffers based on the frameskip mode, which is a little bit tricky. When
- // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
- // So, we should only swap frames if the last frame was rendered. The rules are:
- // - If frameskip == 0 (disabled), always swap buffers
- // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
- // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
- if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) &&
- last_skip_frame != g_skip_frame) ||
- Settings::values.frame_skip == 0) {
- VideoCore::g_renderer->SwapBuffers();
- }
+ VideoCore::g_renderer->SwapBuffers();
// Signal to GSP that GPU interrupt has occurred
// TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
@@ -579,8 +562,6 @@ void Init() {
framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8);
framebuffer_sub.active_fb = 0;
- last_skip_frame = false;
- g_skip_frame = false;
frame_count = 0;
vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 32ddc5697..d53381216 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -316,7 +316,6 @@ ASSERT_REG_POSITION(command_processor_config, 0x00638);
static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set");
extern Regs g_regs;
-extern bool g_skip_frame;
template <typename T>
void Read(T& var, const u32 addr);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 64c388374..65e4bba85 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -45,13 +45,11 @@ struct SpecialRegion {
* requires an indexed fetch and a check for NULL.
*/
struct PageTable {
- static const size_t NUM_ENTRIES = 1 << (32 - PAGE_BITS);
-
/**
* Array of memory pointers backing each page. An entry can only be non-null if the
* corresponding entry in the `attributes` array is of type `Memory`.
*/
- std::array<u8*, NUM_ENTRIES> pointers;
+ std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
/**
* Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
@@ -63,13 +61,13 @@ struct PageTable {
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
* the corresponding entry in `pointers` MUST be set to null.
*/
- std::array<PageType, NUM_ENTRIES> attributes;
+ std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
/**
* Indicates the number of externally cached resources touching a page that should be
* flushed before the memory is accessed
*/
- std::array<u8, NUM_ENTRIES> cached_res_count;
+ std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
};
/// Singular page table used for the singleton process
@@ -77,6 +75,10 @@ static PageTable main_page_table;
/// Currently active page table
static PageTable* current_page_table = &main_page_table;
+std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
+ return &current_page_table->pointers;
+}
+
static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
@@ -84,7 +86,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
u32 end = base + size;
while (base != end) {
- ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base);
+ ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
// Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
// null here
diff --git a/src/core/memory.h b/src/core/memory.h
index 8fd3080ff..903b58a22 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -4,6 +4,7 @@
#pragma once
+#include <array>
#include <cstddef>
#include <string>
#include "common/common_types.h"
@@ -17,6 +18,7 @@ namespace Memory {
const u32 PAGE_SIZE = 0x1000;
const u32 PAGE_MASK = PAGE_SIZE - 1;
const int PAGE_BITS = 12;
+const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
/// Physical memory regions as seen from the ARM11
enum : PAddr {
@@ -166,4 +168,11 @@ void RasterizerFlushRegion(PAddr start, u32 size);
* Flushes and invalidates any externally cached rasterizer resources touching the given region.
*/
void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size);
+
+/**
+ * Dynarmic has an optimization to memory accesses when the pointer to the page exists that
+ * can be used by setting up the current page table as a callback. This function is used to
+ * retrieve the current page table for that purpose.
+ */
+std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
}
diff --git a/src/core/settings.h b/src/core/settings.h
index e931953d7..7470fdbeb 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -78,7 +78,6 @@ struct Values {
// Core
bool use_cpu_jit;
- int frame_skip;
// Data Storage
bool use_virtual_sd;