diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp
index 0fb067c34..fcbb5aac2 100644
--- a/libraries/libstratosphere/include/stratosphere/fs.hpp
+++ b/libraries/libstratosphere/include/stratosphere/fs.hpp
@@ -29,4 +29,9 @@
#include "fs/fs_mount.hpp"
#include "fs/fs_path_tool.hpp"
#include "fs/fs_path_utils.hpp"
+#include "fs/fs_content_storage.hpp"
+#include "fs/fs_game_card.hpp"
#include "fs/fs_sd_card.hpp"
+#include "fs/fs_save_data_types.hpp"
+#include "fs/fs_save_data_management.hpp"
+#include "fs/fs_system_save_data.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp
new file mode 100644
index 000000000..4a52391e2
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_content_storage.hpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018-2020 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 "fs_common.hpp"
+
+namespace ams::fs {
+
+ enum class ContentStorageId : u32 {
+ System = 0,
+ User = 1,
+ SdCard = 2,
+ };
+
+ constexpr inline const char * const ContentStorageDirectoryName = "Contents";
+
+ const char *GetContentStorageMountName(ContentStorageId id);
+
+ Result MountContentStorage(ContentStorageId id);
+ Result MountContentStorage(const char *name, ContentStorageId id);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp
new file mode 100644
index 000000000..612b7844a
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_game_card.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020 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 "fs_common.hpp"
+
+namespace ams::fs {
+
+ enum class GameCardPartition {
+ Update = 0,
+ Normal = 1,
+ Secure = 2,
+ Logo = 3,
+ };
+
+ enum class GameCardPartitionRaw {
+ NormalReadable,
+ SecureReadable,
+ RootWriteable,
+ };
+
+ enum class GameCardAttribute : u8 {
+ AutoBootFlag = (1 << 0),
+ HistoryEraseFlag = (1 << 1),
+ RepairToolFlag = (1 << 2),
+ DifferentRegionCupToTerraDeviceFlag = (1 << 3),
+ DifferentRegionCupToGlobalDeviceFlag = (1 << 4),
+ };
+
+ using GameCardHandle = u32;
+
+ Result GetGameCardHandle(GameCardHandle *out);
+ Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp
new file mode 100644
index 000000000..9b1eb374f
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2020 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 "fs_common.hpp"
+#include "fs_save_data_types.hpp"
+
+namespace ams::fs {
+
+ Result GetSaveDataFlags(u32 *out, SaveDataId id);
+ Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id);
+ Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp
new file mode 100644
index 000000000..42e6fc466
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2018-2020 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 "fs_common.hpp"
+
+namespace ams::fs {
+
+ using SaveDataId = u64;
+ using SystemSaveDataId = u64;
+ using SystemBcatSaveDataId = SystemSaveDataId;
+
+ enum class SaveDataSpaceId : u8 {
+ System = 0,
+ User = 1,
+ SdSystem = 2,
+ Temporary = 3,
+ SdUser = 4,
+
+ ProperSystem = 100,
+ SafeMode = 101,
+ };
+
+ enum class SaveDataType : u8 {
+ System = 0,
+ Account = 1,
+ Bcat = 2,
+ Device = 3,
+ Temporary = 4,
+ Cache = 5,
+ SystemBcat = 6,
+ };
+
+ enum class SaveDataRank : u8 {
+ Primary = 0,
+ Secondary = 1,
+ };
+
+ struct UserId {
+ u64 data[2];
+ };
+ static_assert(std::is_pod::value);
+
+ constexpr inline bool operator<(const UserId &lhs, const UserId &rhs) {
+ if (lhs.data[0] < rhs.data[0]) {
+ return true;
+ } else if (lhs.data[0] == rhs.data[0] && lhs.data[1] < rhs.data[1]) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ constexpr inline bool operator==(const UserId &lhs, const UserId &rhs) {
+ return lhs.data[0] == rhs.data[0] && lhs.data[1] == rhs.data[1];
+ }
+
+ constexpr inline bool operator!=(const UserId &lhs, const UserId &rhs) {
+ return !(lhs == rhs);
+ }
+
+ constexpr inline SystemSaveDataId InvalidSystemSaveDataId = 0;
+ constexpr inline UserId InvalidUserId = {};
+
+ struct SaveDataCreationInfo {
+ s64 size;
+ s64 journal_size;
+ s64 block_size;
+ u64 owner_id;
+ u32 flags;
+ SaveDataSpaceId space_id;
+ bool pseudo;
+ u8 reserved[0x1A];
+ };
+ static_assert(std::is_pod::value);
+ static_assert(sizeof(SaveDataCreationInfo) == 0x40);
+
+ struct SaveDataAttribute {
+ ncm::ProgramId program_id;
+ UserId user_id;
+ SystemSaveDataId system_save_data_id;
+ SaveDataType type;
+ SaveDataRank rank;
+ u16 index;
+ u8 reserved[0x1C];
+
+ static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index, SaveDataRank rank) {
+ return {
+ .program_id = program_id,
+ .user_id = user_id,
+ .system_save_data_id = system_save_data_id,
+ .type = type,
+ .rank = rank,
+ .index = index,
+ };
+ }
+
+ static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index) {
+ return Make(program_id, type, user_id, system_save_data_id, index, SaveDataRank::Primary);
+ }
+
+ static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id) {
+ return Make(program_id, type, user_id, system_save_data_id, 0, SaveDataRank::Primary);
+ }
+ };
+ static_assert(sizeof(SaveDataAttribute) == 0x40);
+ static_assert(std::is_trivially_destructible::value);
+
+ constexpr inline bool operator<(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) {
+ #define FS_SDA_CHECK_FIELD(FIELD) \
+ if (lhs.FIELD < rhs.FIELD) { \
+ return true; \
+ } else if (lhs.FIELD != rhs.FIELD) { \
+ return false; \
+ }
+
+ FS_SDA_CHECK_FIELD(program_id);
+ FS_SDA_CHECK_FIELD(user_id);
+ FS_SDA_CHECK_FIELD(system_save_data_id);
+ FS_SDA_CHECK_FIELD(index);
+ FS_SDA_CHECK_FIELD(rank);
+ return false;
+
+ #undef FS_SDA_CHECK_FIELD
+ }
+
+ constexpr inline bool operator==(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) {
+ return lhs.program_id == rhs.program_id &&
+ lhs.user_id == rhs.user_id &&
+ lhs.system_save_data_id == rhs.system_save_data_id &&
+ lhs.type == rhs.type &&
+ lhs.rank == rhs.rank &&
+ lhs.index == rhs.index;
+ }
+
+ constexpr inline bool operator!=(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) {
+ return !(lhs == rhs);
+ }
+
+ constexpr inline size_t DefaultSaveDataBlockSize = 16_KB;
+
+ struct SaveDataExtraData {
+ SaveDataAttribute attr;
+ u64 owner_id;
+ s64 timestamp;
+ u32 flags;
+ u8 pad[4];
+ s64 available_size;
+ s64 journal_size;
+ s64 commit_id;
+ u8 unused[0x190];
+ };
+ static_assert(sizeof(SaveDataExtraData) == 0x200);
+ static_assert(std::is_pod::value);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp
new file mode 100644
index 000000000..95093dcde
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_system_save_data.hpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018-2020 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 "fs_common.hpp"
+#include "fs_save_data_types.hpp"
+
+namespace ams::fs {
+
+ void DisableAutoSaveDataCreation();
+
+ Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags);
+ Result CreateSystemSaveData(SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags);
+ Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags);
+
+ Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, s64 size, s64 journal_size, u32 flags);
+ Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags);
+ Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags);
+
+ Result MountSystemSaveData(const char *name, SystemSaveDataId id);
+ Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id);
+
+ Result MountSystemSaveData(const char *name, SystemSaveDataId id, UserId user_id);
+ Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id);
+
+ Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp
index adeec7305..4e743762f 100644
--- a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp
+++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_common_mount_name.hpp
@@ -18,26 +18,33 @@
namespace ams::fs::impl {
/* Delimiting of mount names. */
- constexpr inline const char ReservedMountNamePrefixCharacter = '@';
- constexpr inline const char *MountNameDelimiter = ":/";
+ constexpr inline const char ReservedMountNamePrefixCharacter = '@';
+ constexpr inline const char * const MountNameDelimiter = ":/";
/* Filesystem names. */
- constexpr inline const char *HostRootFileSystemMountName = "@Host";
- constexpr inline const char *SdCardFileSystemMountName = "@Sdcard";
- constexpr inline const char *GameCardFileSystemMountName = "@Gc";
+ constexpr inline const char * const HostRootFileSystemMountName = "@Host";
+ constexpr inline const char * const SdCardFileSystemMountName = "@Sdcard";
+ constexpr inline const char * const GameCardFileSystemMountName = "@Gc";
- constexpr inline size_t GameCardFileSystemMountNameSuffixLength = 1;
- constexpr inline const char *GameCardFileSystemMountNameUpdateSuffix = "U";
- constexpr inline const char *GameCardFileSystemMountNameNormalSuffix = "N";
- constexpr inline const char *GameCardFileSystemMountNameSecureSuffix = "S";
+ constexpr inline size_t GameCardFileSystemMountNameSuffixLength = 1;
+
+ constexpr inline const char * const GameCardFileSystemMountNameUpdateSuffix = "U";
+ constexpr inline const char * const GameCardFileSystemMountNameNormalSuffix = "N";
+ constexpr inline const char * const GameCardFileSystemMountNameSecureSuffix = "S";
/* Built-in storage names. */
- constexpr inline const char *BisCalibrationFilePartitionMountName = "@CalibFile";
- constexpr inline const char *BisSafeModePartitionMountName = "@Safe";
- constexpr inline const char *BisUserPartitionMountName = "@User";
- constexpr inline const char *BisSystemPartitionMountName = "@System";
+ constexpr inline const char * const BisCalibrationFilePartitionMountName = "@CalibFile";
+ constexpr inline const char * const BisSafeModePartitionMountName = "@Safe";
+ constexpr inline const char * const BisUserPartitionMountName = "@User";
+ constexpr inline const char * const BisSystemPartitionMountName = "@System";
+
+ /* Content storage names. */
+ constexpr inline const char * const ContentStorageSystemMountName = "@SystemContent";
+ constexpr inline const char * const ContentStorageUserMountName = "@UserContent";
+ constexpr inline const char * const ContentStorageSdCardMountName = "@SdCardContent";
+
/* Registered update partition. */
- constexpr inline const char *RegisteredUpdatePartitionMountName = "@RegUpdate";
+ constexpr inline const char * const RegisteredUpdatePartitionMountName = "@RegUpdate";
}
diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp
index bb5378b70..23ab2c96e 100644
--- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp
+++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp
@@ -39,7 +39,7 @@ namespace ams::kvdb {
}
AutoBuffer& operator=(AutoBuffer &&rhs) {
- rhs.Swap(*this);
+ AutoBuffer(std::move(rhs)).Swap(*this);
return *this;
}
@@ -70,9 +70,8 @@ namespace ams::kvdb {
/* Allocate a buffer. */
this->buffer = static_cast(std::malloc(size));
- if (this->buffer == nullptr) {
- return ResultAllocationFailed();
- }
+ R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
+
this->size = size;
return ResultSuccess();
}
diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp
index 38811eaec..80ddb5ea3 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp
@@ -17,6 +17,7 @@
#pragma once
#include "ncm/ncm_types.hpp"
+#include "ncm/ncm_auto_buffer.hpp"
#include "ncm/ncm_content_meta.hpp"
#include "ncm/ncm_content_meta_database.hpp"
#include "ncm/ncm_content_storage.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp
new file mode 100644
index 000000000..af2c2b54a
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018-2020 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
+
+namespace ams::ncm {
+
+ class AutoBuffer {
+ NON_COPYABLE(AutoBuffer);
+ private:
+ u8 *buffer;
+ size_t size;
+ public:
+ AutoBuffer() : buffer(nullptr), size(0) { /* ... */ }
+
+ ~AutoBuffer() {
+ this->Reset();
+ }
+
+ AutoBuffer(AutoBuffer &&rhs) {
+ this->buffer = rhs.buffer;
+ this->size = rhs.size;
+ rhs.buffer = nullptr;
+ rhs.size = 0;
+ }
+
+ AutoBuffer& operator=(AutoBuffer &&rhs) {
+ AutoBuffer(std::move(rhs)).Swap(*this);
+ return *this;
+ }
+
+ void Swap(AutoBuffer &rhs) {
+ std::swap(this->buffer, rhs.buffer);
+ std::swap(this->size, rhs.size);
+ }
+
+ void Reset() {
+ if (this->buffer != nullptr) {
+ std::free(this->buffer);
+ this->buffer = nullptr;
+ this->size = 0;
+ }
+ }
+
+ u8 *Get() const {
+ return this->buffer;
+ }
+
+ size_t GetSize() const {
+ return this->size;
+ }
+
+ Result Initialize(size_t size) {
+ /* Check that we're not already initialized. */
+ AMS_ABORT_UNLESS(this->buffer == nullptr);
+
+ /* Allocate a buffer. */
+ this->buffer = static_cast(std::malloc(size));
+ R_UNLESS(this->buffer != nullptr, ResultAllocationFailed());
+
+ this->size = size;
+ return ResultSuccess();
+ }
+
+ Result Initialize(const void *buf, size_t size) {
+ /* Create a new buffer of the right size. */
+ R_TRY(this->Initialize(size));
+
+ /* Copy the input data in. */
+ std::memcpy(this->buffer, buf, size);
+
+ return ResultSuccess();
+ }
+ };
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
index 31fdd73e6..167764a13 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
@@ -55,10 +55,6 @@ namespace ams::ncm {
Unknown = 7,
};
- struct MountName {
- char name[0x10];
- };
-
struct alignas(8) PlaceHolderId {
util::Uuid uuid;
diff --git a/libraries/libstratosphere/source/fs/fs_content_storage.cpp b/libraries/libstratosphere/source/fs/fs_content_storage.cpp
new file mode 100644
index 000000000..59147333b
--- /dev/null
+++ b/libraries/libstratosphere/source/fs/fs_content_storage.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018-2020 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 "fsa/fs_mount_utils.hpp"
+
+namespace ams::fs {
+
+ namespace {
+
+ class ContentStorageCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
+ private:
+ const ContentStorageId id;
+ public:
+ explicit ContentStorageCommonMountNameGenerator(ContentStorageId i) : id(i) { /* ... */ }
+
+ virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
+ /* Determine how much space we need. */
+ const size_t needed_size = strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2;
+ AMS_ABORT_UNLESS(dst_size >= needed_size);
+
+ /* Generate the name. */
+ auto size = std::snprintf(dst, dst_size, "%s:", GetContentStorageMountName(id));
+ AMS_ASSERT(static_cast(size) == needed_size - 1);
+
+ return ResultSuccess();
+ }
+ };
+
+ }
+
+ const char *GetContentStorageMountName(ContentStorageId id) {
+ switch (id) {
+ case ContentStorageId::System: return impl::ContentStorageSystemMountName;
+ case ContentStorageId::User: return impl::ContentStorageUserMountName;
+ case ContentStorageId::SdCard: return impl::ContentStorageSdCardMountName;
+ AMS_UNREACHABLE_DEFAULT_CASE();
+ }
+ }
+
+ Result MountContentStorage(ContentStorageId id) {
+ return MountContentStorage(GetContentStorageMountName(id), id);
+ }
+
+ Result MountContentStorage(const char *name, ContentStorageId id) {
+ /* Validate the mount name. */
+ R_TRY(impl::CheckMountNameAllowingReserved(name));
+
+ /* It can take some time for the system partition to be ready (if it's on the SD card). */
+ /* Thus, we will retry up to 10 times, waiting one second each time. */
+ constexpr size_t MaxRetries = 10;
+ constexpr u64 RetryInterval = 1'000'000'000ul;
+
+ /* Mount the content storage, use libnx bindings. */
+ ::FsFileSystem fs;
+ for (size_t i = 0; i < MaxRetries; i++) {
+ R_TRY_CATCH(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id))) {
+ R_CATCH(fs::ResultSystemPartitionNotReady) {
+ if (i < MaxRetries - 1) {
+ /* TODO: os::SleepThread */
+ svcSleepThread(RetryInterval);
+ } else {
+ return fs::ResultSystemPartitionNotReady();
+ }
+ }
+ } R_END_TRY_CATCH;
+ }
+
+ /* Allocate a new filesystem wrapper. */
+ std::unique_ptr fsa(new RemoteFileSystem(fs));
+ R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA());
+
+ /* Allocate a new mountname generator. */
+ std::unique_ptr generator(new ContentStorageCommonMountNameGenerator(id));
+ R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB());
+
+ /* Register. */
+ return fsa::Register(name, std::move(fsa), std::move(generator));
+ }
+
+}
diff --git a/libraries/libstratosphere/source/fs/fs_game_card.cpp b/libraries/libstratosphere/source/fs/fs_game_card.cpp
new file mode 100644
index 000000000..97c5ddb4a
--- /dev/null
+++ b/libraries/libstratosphere/source/fs/fs_game_card.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018-2020 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 "fsa/fs_mount_utils.hpp"
+
+namespace ams::fs {
+
+ namespace {
+
+ const char *GetGameCardMountNameSuffix(GameCardPartition which) {
+ switch (which) {
+ case GameCardPartition::Update: return impl::GameCardFileSystemMountNameUpdateSuffix;
+ case GameCardPartition::Normal: return impl::GameCardFileSystemMountNameNormalSuffix;
+ case GameCardPartition::Secure: return impl::GameCardFileSystemMountNameSecureSuffix;
+ AMS_UNREACHABLE_DEFAULT_CASE();
+ }
+ }
+
+ class GameCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
+ private:
+ const GameCardHandle handle;
+ const GameCardPartition partition;
+ public:
+ explicit GameCardCommonMountNameGenerator(GameCardHandle h, GameCardPartition p) : handle(h), partition(p) { /* ... */ }
+
+ virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
+ /* Determine how much space we need. */
+ const size_t needed_size = strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + strnlen(GetGameCardMountNameSuffix(this->partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2;
+ AMS_ABORT_UNLESS(dst_size >= needed_size);
+
+ /* Generate the name. */
+ auto size = std::snprintf(dst, dst_size, "%s%s%08x:", impl::GameCardFileSystemMountName, GetGameCardMountNameSuffix(this->partition), this->handle);
+ AMS_ASSERT(static_cast(size) == needed_size - 1);
+
+ return ResultSuccess();
+ }
+ };
+
+ }
+
+ Result GetGameCardHandle(GameCardHandle *out) {
+ /* TODO: fs::DeviceOperator */
+ /* Open a DeviceOperator. */
+ ::FsDeviceOperator d;
+ R_TRY(fsOpenDeviceOperator(std::addressof(d)));
+ ON_SCOPE_EXIT { fsDeviceOperatorClose(std::addressof(d)); };
+
+ /* Get the handle. */
+ static_assert(sizeof(GameCardHandle) == sizeof(::FsGameCardHandle));
+ return fsDeviceOperatorGetGameCardHandle(std::addressof(d), reinterpret_cast<::FsGameCardHandle *>(out));
+ }
+
+ Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition) {
+ /* Validate the mount name. */
+ R_TRY(impl::CheckMountNameAllowingReserved(name));
+
+ /* Open gamecard filesystem. This uses libnx bindings. */
+ ::FsFileSystem fs;
+ const ::FsGameCardHandle _hnd = {handle};
+ R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition)));
+
+ /* Allocate a new filesystem wrapper. */
+ std::unique_ptr fsa(new RemoteFileSystem(fs));
+ R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC());
+
+ /* Allocate a new mountname generator. */
+ std::unique_ptr generator(new GameCardCommonMountNameGenerator(handle, partition));
+ R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD());
+
+ /* Register. */
+ return fsa::Register(name, std::move(fsa), std::move(generator));
+ }
+
+}
diff --git a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp
new file mode 100644
index 000000000..aa6216ebc
--- /dev/null
+++ b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018-2020 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 "fsa/fs_mount_utils.hpp"
+
+namespace ams::fs {
+
+ namespace impl {
+
+ Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataId id) {
+ return fsReadSaveDataFileSystemExtraData(out, sizeof(*out), id);
+ }
+
+ Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataSpaceId space_id, SaveDataId id) {
+ return fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(out, sizeof(*out), static_cast<::FsSaveDataSpaceId>(space_id), id);
+ }
+
+ Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId space_id, SaveDataId id, const SaveDataExtraData &extra_data) {
+ return fsWriteSaveDataFileSystemExtraData(std::addressof(extra_data), sizeof(extra_data), static_cast<::FsSaveDataSpaceId>(space_id), id);
+ }
+
+ }
+
+ void DisableAutoSaveDataCreation() {
+ /* Use libnx binding. */
+ R_ABORT_UNLESS(fsDisableAutoSaveDataCreation());
+ }
+
+ Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) {
+ const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id);
+ const SaveDataCreationInfo info = {
+ .size = size,
+ .journal_size = journal_size,
+ .block_size = DefaultSaveDataBlockSize,
+ .owner_id = owner_id,
+ .flags = flags,
+ .space_id = space_id,
+ .pseudo = false,
+ };
+
+ static_assert(sizeof(SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
+ static_assert(sizeof(SaveDataCreationInfo) == sizeof(::FsSaveDataCreationInfo));
+ return fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast(std::addressof(attribute)), reinterpret_cast(std::addressof(info)));
+ }
+
+ Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags) {
+ return CreateSystemSaveData(SaveDataSpaceId::System, save_id, InvalidUserId, 0, size, journal_size, flags);
+ }
+
+ Result CreateSystemSaveData(SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) {
+ return CreateSystemSaveData(SaveDataSpaceId::System, save_id, InvalidUserId, owner_id, size, journal_size, flags);
+ }
+
+ Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) {
+ return CreateSystemSaveData(space_id, save_id, InvalidUserId, owner_id, size, journal_size, flags);
+ }
+
+ Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, s64 size, s64 journal_size, u32 flags) {
+ return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, 0, size, journal_size, flags);
+ }
+
+ Result CreateSystemSaveData(SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) {
+ return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, owner_id, size, journal_size, flags);
+ }
+
+ Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
+ const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
+
+ /* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */
+ AMS_UNUSED(attribute);
+ AMS_ABORT();
+ }
+
+ Result GetSaveDataFlags(u32 *out, SaveDataId id) {
+ SaveDataExtraData extra_data;
+ R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id));
+
+ *out = extra_data.flags;
+ return ResultSuccess();
+ }
+
+ Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id) {
+ SaveDataExtraData extra_data;
+ R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), space_id, id));
+
+ *out = extra_data.flags;
+ return ResultSuccess();
+ }
+
+ Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags) {
+ SaveDataExtraData extra_data;
+ R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), space_id, id));
+ extra_data.flags = flags;
+ return impl::WriteSaveDataFileSystemExtraData(space_id, id, extra_data);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/fs/fs_sd_card.cpp b/libraries/libstratosphere/source/fs/fs_sd_card.cpp
index 2bdc2cb34..cb20560da 100644
--- a/libraries/libstratosphere/source/fs/fs_sd_card.cpp
+++ b/libraries/libstratosphere/source/fs/fs_sd_card.cpp
@@ -14,10 +14,14 @@
* along with this program. If not, see .
*/
#include
+#include "fsa/fs_mount_utils.hpp"
namespace ams::fs {
Result MountSdCard(const char *name) {
+ /* Validate the mount name. */
+ R_TRY(impl::CheckMountName(name));
+
/* Open the SD card. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
diff --git a/libraries/libstratosphere/source/fs/fs_system_save_data.cpp b/libraries/libstratosphere/source/fs/fs_system_save_data.cpp
new file mode 100644
index 000000000..c4802cbfe
--- /dev/null
+++ b/libraries/libstratosphere/source/fs/fs_system_save_data.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018-2020 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 "fsa/fs_mount_utils.hpp"
+
+namespace ams::fs {
+
+ namespace {
+
+ Result MountSystemSaveDataImpl(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id, SaveDataType type) {
+ /* Validate the mount name. */
+ R_TRY(impl::CheckMountName(name));
+
+ /* Create the attribute. */
+ const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, type, user_id, id);
+ static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
+
+ /* Open the filesystem, use libnx bindings. */
+ ::FsFileSystem fs;
+ R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast(std::addressof(attribute))));
+
+ /* Allocate a new filesystem wrapper. */
+ std::unique_ptr fsa(new RemoteFileSystem(fs));
+ R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA());
+
+ /* Register. */
+ return fsa::Register(name, std::move(fsa));
+ }
+
+ }
+
+ Result MountSystemSaveData(const char *name, SystemSaveDataId id) {
+ return MountSystemSaveData(name, id, InvalidUserId);
+ }
+
+ Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id) {
+ return MountSystemSaveData(name, space_id, id, InvalidUserId);
+ }
+
+ Result MountSystemSaveData(const char *name, SystemSaveDataId id, UserId user_id) {
+ return MountSystemSaveData(name, SaveDataSpaceId::System, id, user_id);
+ }
+
+ Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
+ return MountSystemSaveDataImpl(name, space_id, id, user_id, SaveDataType::System);
+ }
+
+}
diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp
index 02483283c..74ed9aafe 100644
--- a/libraries/libvapours/include/vapours/results/fs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/fs_results.hpp
@@ -47,11 +47,20 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(NotImplemented, 3001);
R_DEFINE_ERROR_RESULT(OutOfRange, 3005);
+ R_DEFINE_ERROR_RESULT(SystemPartitionNotReady, 3100);
+
R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499);
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorA, 3211);
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardA, 3225);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228);
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244);
R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246);
R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321);
R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355);
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);