diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp
new file mode 100644
index 000000000..52f0a71ca
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+
+#include "../utils.hpp"
+#include "fs_directory_redirection_filesystem.hpp"
+#include "fs_path_utils.hpp"
+
+static char *GetNormalizedDirectory(const char *dir_prefix) {
+ /* Normalize the path. */
+ char normal_path[FS_MAX_PATH + 1];
+ size_t normal_path_len;
+ Result rc = FsPathUtils::Normalize(normal_path, sizeof(normal_path), dir_prefix, &normal_path_len);
+ if (R_FAILED(rc)) {
+ /* N calls svcBreak here. */
+ std::abort();
+ }
+
+ /* Ensure terminating '/' */
+ if (normal_path[normal_path_len-1] != '/') {
+ if (normal_path_len + 2 > sizeof(normal_path)) {
+ std::abort();
+ }
+
+ strncat(normal_path, "/", 2);
+ normal_path[sizeof(normal_path)-1] = 0;
+ normal_path_len++;
+ }
+
+ char *output = static_cast(malloc(normal_path_len + 1));
+ if (output == nullptr) {
+ std::abort();
+ /* TODO: Result error code? */
+ }
+
+ std::strncpy(output, normal_path, normal_path_len + 1);
+ output[normal_path_len] = 0;
+ return output;
+}
+
+Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) {
+ if (strnlen(before, FS_MAX_PATH) >= FS_MAX_PATH || strnlen(after, FS_MAX_PATH) >= FS_MAX_PATH) {
+ return ResultFsTooLongPath;
+ }
+
+ this->before_dir = GetNormalizedDirectory(before);
+ this->after_dir = GetNormalizedDirectory(after);
+ this->before_dir_len = strlen(this->before_dir);
+ this->after_dir_len = strlen(this->after_dir);
+
+ return ResultSuccess;
+}
+
+Result DirectoryRedirectionFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
+ FsPath tmp_rel_path;
+ Result rc = FsPathUtils::Normalize(tmp_rel_path.str, sizeof(tmp_rel_path), relative_path, nullptr);
+ if (R_FAILED(rc)) {
+ return rc;
+ }
+
+ if (std::strncmp(tmp_rel_path.str, this->before_dir, this->before_dir_len) == 0) {
+ if (this->after_dir_len + strnlen(tmp_rel_path.str, FS_MAX_PATH) - this->before_dir_len > out_size) {
+ return ResultFsTooLongPath;
+ }
+
+ /* Copy after path. */
+ std::strncpy(out, this->after_dir, out_size);
+ out[out_size - 1] = 0;
+
+ /* Normalize it. */
+ return FsPathUtils::Normalize(out + this->after_dir_len - 1, out_size - (this->after_dir_len - 1), relative_path + this->before_dir_len - 1, nullptr);
+ } else if (std::memcmp(tmp_rel_path.str, this->before_dir, this->before_dir_len - 1) == 0) {
+ /* Handling raw directory. */
+ if (this->after_dir_len + 1 > out_size) {
+ return ResultFsTooLongPath;
+ }
+ std::strncpy(out, this->after_dir, out_size);
+ out[out_size - 1] = 0;
+ return ResultSuccess;
+ } else {
+ return FsPathUtils::Normalize(out, out_size, relative_path, nullptr);
+ }
+}
+
+Result DirectoryRedirectionFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->CreateFile(full_path, size, flags);
+}
+
+Result DirectoryRedirectionFileSystem::DeleteFileImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->DeleteFile(full_path);
+}
+
+Result DirectoryRedirectionFileSystem::CreateDirectoryImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->CreateDirectory(full_path);
+}
+
+Result DirectoryRedirectionFileSystem::DeleteDirectoryImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->DeleteDirectory(full_path);
+}
+
+Result DirectoryRedirectionFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->DeleteDirectoryRecursively(full_path);
+}
+
+Result DirectoryRedirectionFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
+ Result rc;
+ FsPath full_old_path, full_new_path;
+
+ if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) {
+ return rc;
+ }
+
+ return this->base_fs->RenameFile(full_old_path, full_new_path);
+}
+
+Result DirectoryRedirectionFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
+ Result rc;
+ FsPath full_old_path, full_new_path;
+
+ if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) {
+ return rc;
+ }
+
+ return this->base_fs->RenameDirectory(full_old_path, full_new_path);
+}
+
+Result DirectoryRedirectionFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->GetEntryType(out, full_path);
+}
+
+Result DirectoryRedirectionFileSystem::OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->OpenFile(out_file, full_path, mode);
+}
+
+Result DirectoryRedirectionFileSystem::OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->OpenDirectory(out_dir, full_path, mode);
+}
+
+Result DirectoryRedirectionFileSystem::CommitImpl() {
+ return this->base_fs->Commit();
+}
+
+Result DirectoryRedirectionFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->GetFreeSpaceSize(out, full_path);
+}
+
+Result DirectoryRedirectionFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->GetTotalSpaceSize(out, full_path);
+}
+
+Result DirectoryRedirectionFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->CleanDirectoryRecursively(full_path);
+}
+
+Result DirectoryRedirectionFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->GetFileTimeStampRaw(out, full_path);
+}
+
+Result DirectoryRedirectionFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
+}
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp
new file mode 100644
index 000000000..95d7b342b
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include
+
+#include "fs_ifilesystem.hpp"
+#include "fs_path_utils.hpp"
+
+class DirectoryRedirectionFileSystem : public IFileSystem {
+ private:
+ std::shared_ptr base_fs;
+ char *before_dir = nullptr;
+ size_t before_dir_len = 0;
+ char *after_dir = nullptr;
+ size_t after_dir_len = 0;
+
+ public:
+ DirectoryRedirectionFileSystem(IFileSystem *fs, const char *before, const char *after) : base_fs(fs) {
+ Result rc = this->Initialize(before, after);
+ if (R_FAILED(rc)) {
+ fatalSimple(rc);
+ }
+ }
+
+ DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after) : base_fs(fs) {
+ Result rc = this->Initialize(before, after);
+ if (R_FAILED(rc)) {
+ fatalSimple(rc);
+ }
+ }
+
+
+ virtual ~DirectoryRedirectionFileSystem() {
+ if (this->before_dir != nullptr) {
+ free(this->before_dir);
+ }
+ if (this->after_dir != nullptr) {
+ free(this->after_dir);
+ }
+ }
+
+ private:
+ Result Initialize(const char *before, const char *after);
+ protected:
+ Result GetFullPath(char *out, size_t out_size, const char *relative_path);
+ Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
+ return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
+ }
+
+ public:
+ virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
+ virtual Result DeleteFileImpl(const FsPath &path) override;
+ virtual Result CreateDirectoryImpl(const FsPath &path) override;
+ virtual Result DeleteDirectoryImpl(const FsPath &path) override;
+ virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
+ virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
+ virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
+ virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
+ virtual Result OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) override;
+ virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
+ virtual Result CommitImpl() override;
+ virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
+ virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
+ virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
+ virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
+ virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
+};
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
index 3ce545193..991d4113f 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
@@ -31,6 +31,7 @@
#include "fs_dir_utils.hpp"
#include "fs_save_utils.hpp"
#include "fs_subdirectory_filesystem.hpp"
+#include "fs_directory_redirection_filesystem.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "../debug.hpp"
@@ -161,6 +162,43 @@ Result FsMitmService::OpenFileSystemWithId(OutOpenHblWebContentFileSystem(out_fs);
}
+Result FsMitmService::OpenSdCardFileSystem(Out> out_fs) {
+ /* We only care about redirecting this for NS/Emunand. */
+ if (!IsEmunand()) {
+ return ResultAtmosphereMitmShouldForwardToSession;
+ }
+ if (this->title_id != TitleId_Ns) {
+ return ResultAtmosphereMitmShouldForwardToSession;
+ }
+
+ std::shared_ptr fs = nullptr;
+ u32 out_domain_id = 0;
+ Result rc = ResultSuccess;
+
+ ON_SCOPE_EXIT {
+ if (R_SUCCEEDED(rc)) {
+ out_fs.SetValue(std::move(fs));
+ if (out_fs.IsDomain()) {
+ out_fs.ChangeObjectId(out_domain_id);
+ }
+ }
+ };
+
+ /* Mount the SD card. */
+ FsFileSystem sd_fs;
+ if (R_FAILED((rc = fsMountSdcard(&sd_fs)))) {
+ return rc;
+ }
+
+ std::shared_ptr redir_fs = std::make_shared(new ProxyFileSystem(sd_fs), "/Nintendo", "/Emu/0000" /* TODO: Real Path */);
+ fs = std::make_shared(redir_fs);
+ if (out_fs.IsDomain()) {
+ out_domain_id = sd_fs.s.object_id;
+ }
+
+ return rc;
+}
+
Result FsMitmService::OpenSaveDataFileSystem(Out> out_fs, u8 space_id, FsSave save_struct) {
bool should_redirect_saves = false;
const bool has_redirect_save_flags = Utils::HasFlag(this->title_id, "redirect_save");
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp
index 1e818aba2..54f8b6ff6 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp
@@ -29,6 +29,8 @@ enum FspSrvCmd : u32 {
FspSrvCmd_OpenFileSystemWithPatch = 7,
FspSrvCmd_OpenFileSystemWithId = 8,
+ FspSrvCmd_OpenSdCardFileSystem = 18,
+
FspSrvCmd_OpenSaveDataFileSystem = 51,
FspSrvCmd_OpenBisStorage = 12,
@@ -75,6 +77,7 @@ class FsMitmService : public IMitmServiceObject {
/* Overridden commands. */
Result OpenFileSystemWithPatch(Out> out, u64 title_id, u32 filesystem_type);
Result OpenFileSystemWithId(Out> out, InPointer path, u64 title_id, u32 filesystem_type);
+ Result OpenSdCardFileSystem(Out> out);
Result OpenSaveDataFileSystem(Out> out, u8 space_id, FsSave save_struct);
Result OpenBisStorage(Out> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(Out> out);
@@ -84,6 +87,7 @@ class FsMitmService : public IMitmServiceObject {
/* TODO MakeServiceCommandMeta(), */
MakeServiceCommandMeta(),
MakeServiceCommandMeta(),
+ MakeServiceCommandMeta(),
MakeServiceCommandMeta(),
MakeServiceCommandMeta(),
MakeServiceCommandMeta(),
diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere
index 8f2328975..44f52b445 160000
--- a/stratosphere/libstratosphere
+++ b/stratosphere/libstratosphere
@@ -1 +1 @@
-Subproject commit 8f2328975a66519275ada907cbc0000377ce5ede
+Subproject commit 44f52b445e9154a18fc0e99b9efc6be78b859495