diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp
index fcbb5aac2..be40bb263 100644
--- a/libraries/libstratosphere/include/stratosphere/fs.hpp
+++ b/libraries/libstratosphere/include/stratosphere/fs.hpp
@@ -34,4 +34,5 @@
#include "fs/fs_sd_card.hpp"
#include "fs/fs_save_data_types.hpp"
#include "fs/fs_save_data_management.hpp"
+#include "fs/fs_save_data_transaction.hpp"
#include "fs/fs_system_save_data.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp
index 9b1eb374f..a6dbbd8ce 100644
--- a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp
@@ -19,6 +19,9 @@
namespace ams::fs {
+ Result DeleteSaveData(SaveDataId id);
+ Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id);
+
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_transaction.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_transaction.hpp
new file mode 100644
index 000000000..a39762441
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_transaction.hpp
@@ -0,0 +1,24 @@
+/*
+ * 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 CommitSaveData(const char *path);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp
index a47080245..4c4d697a9 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_path.hpp
@@ -39,7 +39,7 @@ namespace ams::ncm {
using PathString = kvdb::BoundedString;
- using MakeContentPathFunc = void (*)(PathString *out, ContentId content_id, const PathString &root);
- using MakePlaceHolderPathFunc = void (*)(PathString *out, PlaceHolderId placeholder_id, const PathString &root);
+ using MakeContentPathFunction = void (*)(PathString *out, ContentId content_id, const char *root_path);
+ using MakePlaceHolderPathFunction = void (*)(PathString *out, PlaceHolderId placeholder_id,const char *root_path);
}
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
index 167764a13..0cd3f52db 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
@@ -154,6 +154,10 @@ namespace ams::ncm {
#undef DEFINE_ENUM_MEMBER
};
+ constexpr inline bool IsUniqueStorage(StorageId id) {
+ return id != StorageId::None && id != StorageId::Any;
+ }
+
/* Program IDs (Formerly: Title IDs). */
struct ProgramId {
svc::ProgramId value;
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp
index bf1d53a7e..d2b05bd96 100644
--- a/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os/os_condvar.hpp
@@ -30,9 +30,7 @@ namespace ams::os {
private:
CondVar cv;
public:
- ConditionVariable() {
- condvarInit(&cv);
- }
+ constexpr ConditionVariable() : cv() { /* ... */ }
ConditionVariableStatus TimedWait(::Mutex *m, u64 timeout) {
if (timeout > 0) {
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp b/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp
index a1f3db374..5fdec6d43 100644
--- a/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os/os_mutex.hpp
@@ -28,13 +28,11 @@ namespace ams::os {
private:
::Mutex m;
private:
- ::Mutex *GetMutex() {
+ constexpr ::Mutex *GetMutex() {
return &this->m;
}
public:
- Mutex() {
- mutexInit(GetMutex());
- }
+ constexpr Mutex() : m() { /* ... */ }
void lock() {
mutexLock(GetMutex());
@@ -65,13 +63,11 @@ namespace ams::os {
private:
::RMutex m;
private:
- ::RMutex *GetMutex() {
+ constexpr ::RMutex *GetMutex() {
return &this->m;
}
public:
- RecursiveMutex() {
- rmutexInit(GetMutex());
- }
+ constexpr RecursiveMutex() : m() { /* ... */ }
void lock() {
rmutexLock(GetMutex());
diff --git a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp
index aa6216ebc..6185029fe 100644
--- a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp
+++ b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp
@@ -76,6 +76,15 @@ namespace ams::fs {
return CreateSystemSaveData(SaveDataSpaceId::System, save_id, user_id, owner_id, size, journal_size, flags);
}
+ Result DeleteSaveData(SaveDataId id) {
+ /* TODO: Libnx binding for DeleteSaveDataFileSystem */
+ AMS_ABORT();
+ }
+
+ Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id) {
+ return fsDeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<::FsSaveDataSpaceId>(space_id), id);
+ }
+
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp
index 2feac310e..23145d840 100644
--- a/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp
+++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_table.hpp
@@ -28,7 +28,7 @@ namespace ams::fs::impl {
FileSystemList fs_list;
os::Mutex mutex;
public:
- MountTable() : fs_list(), mutex() { /* ... */ }
+ constexpr MountTable() : fs_list(), mutex() { /* ... */ }
private:
bool CanAcceptMountName(const char *name);
public:
diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp
index ea736a9b6..d75fff734 100644
--- a/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp
+++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_utils.cpp
@@ -103,7 +103,7 @@ namespace ams::fs::impl {
}
bool IsReservedMountName(const char *name) {
- return name[0] != ReservedMountNamePrefixCharacter;
+ return name[0] == ReservedMountNamePrefixCharacter;
}
Result CheckMountName(const char *name) {
diff --git a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp
index 66c31c545..4022aea4f 100644
--- a/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp
+++ b/libraries/libstratosphere/source/fs/fsa/fs_user_filesystem.cpp
@@ -166,5 +166,24 @@ namespace ams::fs {
return ResultSuccess();
}
+ namespace {
+
+ Result CommitImpl(const char *path) {
+ impl::FileSystemAccessor *accessor;
+ R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
+
+ return accessor->Commit();
+ }
+
+ }
+
+ Result Commit(const char *path) {
+ return CommitImpl(path);
+ }
+
+ Result CommitSaveData(const char *path) {
+ return CommitImpl(path);
+ }
+
}
diff --git a/libraries/libvapours/include/vapours/results/ncm_results.hpp b/libraries/libvapours/include/vapours/results/ncm_results.hpp
index 08175b37c..58d2aef84 100644
--- a/libraries/libvapours/include/vapours/results/ncm_results.hpp
+++ b/libraries/libvapours/include/vapours/results/ncm_results.hpp
@@ -35,7 +35,7 @@ namespace ams::ncm {
R_DEFINE_ERROR_RESULT(InvalidPlaceHolderFile, 170);
R_DEFINE_ERROR_RESULT(BufferInsufficient, 180);
- R_DEFINE_ERROR_RESULT(InvalidContentStorageOperation, 190);
+ R_DEFINE_ERROR_RESULT(WriteToReadOnlyContentStorage, 190);
R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240);
R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310);
diff --git a/stratosphere/boot2/source/boot2_main.cpp b/stratosphere/boot2/source/boot2_main.cpp
index 992e33249..390b87929 100644
--- a/stratosphere/boot2/source/boot2_main.cpp
+++ b/stratosphere/boot2/source/boot2_main.cpp
@@ -79,10 +79,14 @@ void __appInit(void) {
R_ABORT_UNLESS(gpioInitialize());
});
+ /* Mount the SD card. */
+ R_ABORT_UNLESS(fs::MountSdCard("sdmc"));
+
ams::CheckApiVersion();
}
void __appExit(void) {
+ fs::Unmount("sdmc");
gpioExit();
setsysExit();
pmshellExit();
@@ -93,10 +97,6 @@ void __appExit(void) {
int main(int argc, char **argv)
{
- /* Mount the SD card. */
- R_ABORT_UNLESS(fs::MountSdCard("sdmc"));
- ON_SCOPE_EXIT { fs::Unmount("sdmc"); };
-
/* Launch all programs off of SYSTEM/the SD. */
boot2::LaunchPostSdCardBootPrograms();
}
diff --git a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp b/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp
deleted file mode 100644
index 5be23b3ef..000000000
--- a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (c) 2019-2020 Adubbz, 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 "ncm_placeholder_accessor.hpp"
-#include "../ncm_fs.hpp"
-#include "../ncm_utils.hpp"
-#include "../ncm_make_path.hpp"
-#include "../ncm_path_utils.hpp"
-
-namespace ams::ncm::impl {
-
- namespace {
-
- ALWAYS_INLINE Result ConvertNotFoundResult(Result r) {
- R_TRY_CATCH(r) {
- R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
- } R_END_TRY_CATCH;
- return ResultSuccess();
- }
-
- }
-
- Result PlaceHolderAccessor::Open(FILE** out_handle, PlaceHolderId placeholder_id) {
- R_UNLESS(!this->LoadFromCache(out_handle, placeholder_id), ResultSuccess());
- PathString placeholder_path;
- this->MakePath(std::addressof(placeholder_path), placeholder_id);
-
- FILE *f = nullptr;
- R_TRY(fs::OpenFile(&f, placeholder_path, FsOpenMode_Write));
-
- *out_handle = f;
- return ResultSuccess();
- }
-
- bool PlaceHolderAccessor::LoadFromCache(FILE** out_handle, PlaceHolderId placeholder_id) {
- std::scoped_lock lk(this->cache_mutex);
- CacheEntry *entry = this->FindInCache(placeholder_id);
- if (!entry) {
- return false;
- }
- *out_handle = entry->handle;
- entry->id = InvalidPlaceHolderId;
- entry->handle = nullptr;
- return true;
- }
-
- PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::FindInCache(PlaceHolderId placeholder_id) {
- for (size_t i = 0; i < MaxCaches; i++) {
- if (placeholder_id == this->caches[i].id) {
- return &this->caches[i];
- }
- }
- return nullptr;
- }
-
- PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::GetFreeEntry() {
- /* Try to find an already free entry. */
- if (CacheEntry *entry = this->FindInCache(InvalidPlaceHolderId); entry != nullptr) {
- return entry;
- }
-
- /* Get the oldest entry. */
- CacheEntry *entry = &this->caches[0];
- for (size_t i = 1; i < MaxCaches; i++) {
- if (entry->counter > this->caches[i].counter) {
- entry = &this->caches[i];
- }
- }
- this->Invalidate(entry);
- return entry;
- }
-
- void PlaceHolderAccessor::StoreToCache(FILE *handle, PlaceHolderId placeholder_id) {
- std::scoped_lock lk(this->cache_mutex);
- CacheEntry *entry = this->GetFreeEntry();
- entry->id = placeholder_id;
- entry->handle = handle;
- entry->counter = this->cur_counter++;
- }
-
- void PlaceHolderAccessor::Invalidate(CacheEntry *entry) {
- if (entry != nullptr) {
- if (entry->handle != nullptr) {
- fflush(entry->handle);
- fclose(entry->handle);
- entry->handle = nullptr;
- }
- entry->id = InvalidPlaceHolderId;
- }
- }
-
- void PlaceHolderAccessor::Initialize(const char *root, MakePlaceHolderPathFunc path_func, bool delay_flush) {
- this->root_path = PathString(root);
- this->make_placeholder_path_func = path_func;
- this->delay_flush = delay_flush;
- }
-
- unsigned int PlaceHolderAccessor::GetDirectoryDepth() {
- if (this->make_placeholder_path_func == static_cast(path::MakePlaceHolderPathFlat)) {
- return 1;
- } else if (this->make_placeholder_path_func == static_cast(path::MakePlaceHolderPathHashByteLayered)) {
- return 2;
- } else {
- AMS_ABORT();
- }
-
- __builtin_unreachable();
- }
-
- void PlaceHolderAccessor::GetPath(PathString *placeholder_path, PlaceHolderId placeholder_id) {
- std::scoped_lock lock(this->cache_mutex);
- CacheEntry *entry = this->FindInCache(placeholder_id);
- this->Invalidate(entry);
- this->MakePath(placeholder_path, placeholder_id);
- }
-
- Result PlaceHolderAccessor::Create(PlaceHolderId placeholder_id, size_t size) {
- this->EnsureRecursively(placeholder_id);
-
- PathString placeholder_path;
- this->GetPath(std::addressof(placeholder_path), placeholder_id);
-
- R_TRY_CATCH(fsdevCreateFile(placeholder_path, size, FsCreateOption_BigFile)) {
- R_CONVERT(ams::fs::ResultPathAlreadyExists, ncm::ResultPlaceHolderAlreadyExists())
- } R_END_TRY_CATCH;
-
- return ResultSuccess();
- }
-
- Result PlaceHolderAccessor::Delete(PlaceHolderId placeholder_id) {
- PathString placeholder_path;
- this->GetPath(std::addressof(placeholder_path), placeholder_id);
-
- R_UNLESS(std::remove(placeholder_path) == 0, ConvertNotFoundResult(fsdevGetLastResult()));
- return ResultSuccess();
- }
-
- Result PlaceHolderAccessor::Write(PlaceHolderId placeholder_id, size_t offset, const void *buffer, size_t size) {
- FILE *f = nullptr;
-
- R_TRY(ConvertNotFoundResult(this->Open(&f, placeholder_id)));
- ON_SCOPE_EXIT { this->StoreToCache(f, placeholder_id); };
-
- R_TRY(fs::WriteFile(f, offset, buffer, size, this->delay_flush ? ams::fs::WriteOption::Flush : ams::fs::WriteOption::None));
- return ResultSuccess();
- }
-
- Result PlaceHolderAccessor::SetSize(PlaceHolderId placeholder_id, size_t size) {
- PathString placeholder_path;
- this->MakePath(std::addressof(placeholder_path), placeholder_id);
-
- R_UNLESS(truncate(placeholder_path, size) != -1, ConvertNotFoundResult(fsdevGetLastResult()));
- return ResultSuccess();
- }
-
- Result PlaceHolderAccessor::GetSize(bool *found_in_cache, size_t *out_size, PlaceHolderId placeholder_id) {
- FILE *f = NULL;
-
- *found_in_cache = false;
-
- /* Set the scope for the scoped_lock. */
- {
- std::scoped_lock lock(this->cache_mutex);
-
- /* If the placeholder id is invalid, return success early. */
- R_UNLESS(placeholder_id != InvalidPlaceHolderId, ResultSuccess());
-
- CacheEntry *cache_entry = this->FindInCache(placeholder_id);
-
- /* If there is no entry in the cache, return success early. */
- R_UNLESS(cache_entry != nullptr, ResultSuccess());
-
- cache_entry->id = InvalidPlaceHolderId;
- f = cache_entry->handle;
- }
-
- this->StoreToCache(f, placeholder_id);
-
- R_UNLESS(fseek(f, 0L, SEEK_END) == 0, fsdevGetLastResult());
- size_t size = ftell(f);
- R_UNLESS(fseek(f, 0L, SEEK_SET) == 0, fsdevGetLastResult());
-
- *found_in_cache = true;
- *out_size = size;
- return ResultSuccess();
- }
-
- Result PlaceHolderAccessor::EnsureRecursively(PlaceHolderId placeholder_id) {
- PathString placeholder_path;
- this->MakePath(std::addressof(placeholder_path), placeholder_id);
- R_TRY(fs::EnsureParentDirectoryRecursively(placeholder_path));
- return ResultSuccess();
- }
-
- void PlaceHolderAccessor::InvalidateAll() {
- for (auto &entry : this->caches) {
- if (entry.id != InvalidPlaceHolderId) {
- this->Invalidate(&entry);
- }
- }
- }
-
-}
diff --git a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp b/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp
deleted file mode 100644
index 3a656cc87..000000000
--- a/stratosphere/ncm/source/impl/ncm_placeholder_accessor.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2019-2020 Adubbz, 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 "../ncm_path_utils.hpp"
-
-namespace ams::ncm::impl {
-
- class PlaceHolderAccessor {
- private:
- class CacheEntry {
- public:
- PlaceHolderId id;
- FILE *handle;
- u64 counter;
- };
- private:
- static constexpr size_t MaxCaches = 0x2;
-
- std::array caches;
- PathString root_path;
- u64 cur_counter;
- os::Mutex cache_mutex;
- MakePlaceHolderPathFunc make_placeholder_path_func;
- bool delay_flush;
- private:
- Result Open(FILE** out_handle, PlaceHolderId placeholder_id);
- CacheEntry *FindInCache(PlaceHolderId placeholder_id);
- bool LoadFromCache(FILE** out_handle, PlaceHolderId placeholder_id);
- CacheEntry *GetFreeEntry();
- void StoreToCache(FILE *handle, PlaceHolderId placeholder_id);
- void Invalidate(CacheEntry *entry);
- public:
- PlaceHolderAccessor() : cur_counter(0), delay_flush(false) {
- for (size_t i = 0; i < MaxCaches; i++) {
- caches[i].id = InvalidPlaceHolderId;
- }
- }
-
- inline void MakeRootPath(PathString *placeholder_root) {
- path::GetPlaceHolderRootPath(placeholder_root, this->root_path);
- }
-
- inline void MakePath(PathString *placeholder_path, PlaceHolderId placeholder_id) {
- PathString root_path;
- this->MakeRootPath(std::addressof(root_path));
- this->make_placeholder_path_func(placeholder_path, placeholder_id, root_path);
- }
-
- void Initialize(const char *root, MakePlaceHolderPathFunc path_func, bool delay_flush);
- unsigned int GetDirectoryDepth();
- void GetPath(PathString *out_placeholder_path, PlaceHolderId placeholder_id);
- Result Create(PlaceHolderId placeholder_id, size_t size);
- Result Delete(PlaceHolderId placeholder_id);
- Result Write(PlaceHolderId placeholder_id, size_t offset, const void *buffer, size_t size);
- Result SetSize(PlaceHolderId placeholder_id, size_t size);
- Result GetSize(bool *found_in_cache, size_t *out_size, PlaceHolderId placeholder_id);
- Result EnsureRecursively(PlaceHolderId placeholder_id);
- void InvalidateAll();
- };
-
-}
diff --git a/stratosphere/ncm/source/ncm_content_id_utils.cpp b/stratosphere/ncm/source/ncm_content_id_utils.cpp
new file mode 100644
index 000000000..d920a870a
--- /dev/null
+++ b/stratosphere/ncm/source/ncm_content_id_utils.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019-2020 Adubbz, 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 "ncm_content_id_utils.hpp"
+
+namespace ams::ncm {
+
+ namespace {
+
+ void GetStringFromBytes(char *dst, const void *src, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ std::snprintf(dst + 2 * i, 3, "%02x", static_cast(src)[i]);
+ }
+ }
+
+ bool GetBytesFromString(void *dst, size_t dst_size, const char *src, size_t src_size) {
+ if (!util::IsAligned(src_size, 2) || (dst_size * 2 < src_size)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < src_size; i += 2) {
+ char tmp[3];
+ strlcpy(tmp, src + i, sizeof(tmp));
+
+ char *err = nullptr;
+ reinterpret_cast(dst)[i / 2] = static_cast(std::strtoul(tmp, std::addressof(err), 16));
+ if (*err != '\x00') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ }
+
+
+ ContentIdString GetContentIdString(ContentId id) {
+ ContentIdString str;
+
+ GetStringFromContentId(str.data, sizeof(str), id);
+
+ return str;
+ }
+
+ void GetStringFromContentId(char *dst, size_t dst_size, ContentId id) {
+ AMS_ABORT_UNLESS(dst_size > ContentIdStringLength);
+ GetStringFromBytes(dst, std::addressof(id), sizeof(id));
+ }
+
+ std::optional GetContentIdFromString(const char *str, size_t len) {
+ if (len < ContentIdStringLength) {
+ return std::nullopt;
+ }
+
+ ContentId content_id;
+ return GetBytesFromString(std::addressof(content_id), sizeof(content_id), str, ContentIdStringLength) ? std::optional(content_id) : std::nullopt;
+ }
+
+}
diff --git a/stratosphere/ncm/source/ncm_utils.hpp b/stratosphere/ncm/source/ncm_content_id_utils.hpp
similarity index 60%
rename from stratosphere/ncm/source/ncm_utils.hpp
rename to stratosphere/ncm/source/ncm_content_id_utils.hpp
index 2c18a6e93..aaa5608b2 100644
--- a/stratosphere/ncm/source/ncm_utils.hpp
+++ b/stratosphere/ncm/source/ncm_content_id_utils.hpp
@@ -15,16 +15,23 @@
*/
#pragma once
-#include
#include
-#include
namespace ams::ncm {
- void GetStringFromContentId(char *out, ContentId content_id);
- void GetStringFromPlaceHolderId(char *out, PlaceHolderId placeholder_id);
+ constexpr inline size_t ContentIdStringLength = 2 * sizeof(ContentId);
+ constexpr inline size_t RightsIdStringLength = 2 * sizeof(fs::RightsId);
+ constexpr inline size_t TicketFileStringLength = RightsIdStringLength + 4;
+ constexpr inline size_t CertFileStringLength = RightsIdStringLength + 5;
+
+ struct ContentIdString {
+ char data[ContentIdStringLength + 1];
+ };
+
+ ContentIdString GetContentIdString(ContentId id);
+
+ void GetStringFromContentId(char *dst, size_t dst_size, ContentId id);
- Result GetPlaceHolderIdFromDirEntry(PlaceHolderId *out, struct dirent *dir_entry);
std::optional GetContentIdFromString(const char *str, size_t len);
-};
+}
diff --git a/stratosphere/ncm/source/ncm_content_manager_impl.cpp b/stratosphere/ncm/source/ncm_content_manager_impl.cpp
index c70ba4b11..0a6ee634d 100644
--- a/stratosphere/ncm/source/ncm_content_manager_impl.cpp
+++ b/stratosphere/ncm/source/ncm_content_manager_impl.cpp
@@ -25,75 +25,183 @@ namespace ams::ncm {
namespace {
- constexpr u64 BuiltInSystemSaveDataId = 0x8000000000000120;
- constexpr u64 BuiltInSystemSaveDataSize = 0x6c000;
- constexpr u64 BuiltInSystemSaveDataJournalSize = 0x6c000;
- constexpr u32 BuiltInSystemSaveDataFlags = FsSaveDataFlags_KeepAfterResettingSystemSaveData | FsSaveDataFlags_KeepAfterRefurbishment;
+ constexpr fs::SystemSaveDataId BuiltInSystemSaveDataId = 0x8000000000000120;
+ constexpr u64 BuiltInSystemSaveDataSize = 0x6c000;
+ constexpr u64 BuiltInSystemSaveDataJournalSize = 0x6c000;
+ constexpr u32 BuiltInSystemSaveDataFlags = FsSaveDataFlags_KeepAfterResettingSystemSaveData | FsSaveDataFlags_KeepAfterRefurbishment;
constexpr SystemSaveDataInfo BuiltInSystemSystemSaveDataInfo = {
.id = BuiltInSystemSaveDataId,
.size = BuiltInSystemSaveDataSize,
.journal_size = BuiltInSystemSaveDataJournalSize,
.flags = BuiltInSystemSaveDataFlags,
- .space_id = FsSaveDataSpaceId_System
+ .space_id = fs::SaveDataSpaceId::System
};
- constexpr u64 BuiltInUserSaveDataId = 0x8000000000000121;
- constexpr u64 BuiltInUserSaveDataSize = 0x29e000;
- constexpr u64 BuiltInUserSaveDataJournalSize = 0x29e000;
- constexpr u32 BuiltInUserSaveDataFlags = 0;
+ constexpr fs::SystemSaveDataId BuiltInUserSaveDataId = 0x8000000000000121;
+ constexpr u64 BuiltInUserSaveDataSize = 0x29e000;
+ constexpr u64 BuiltInUserSaveDataJournalSize = 0x29e000;
+ constexpr u32 BuiltInUserSaveDataFlags = 0;
constexpr SystemSaveDataInfo BuiltInUserSystemSaveDataInfo = {
.id = BuiltInUserSaveDataId,
.size = BuiltInUserSaveDataSize,
.journal_size = BuiltInUserSaveDataJournalSize,
.flags = BuiltInUserSaveDataFlags,
- .space_id = FsSaveDataSpaceId_System
+ .space_id = fs::SaveDataSpaceId::System
};
- constexpr u64 SdCardSaveDataId = 0x8000000000000124;
- constexpr u64 SdCardSaveDataSize = 0xa08000;
- constexpr u64 SdCardSaveDataJournalSize = 0xa08000;
- constexpr u32 SdCardSaveDataFlags = 0;
+ constexpr fs::SystemSaveDataId SdCardSaveDataId = 0x8000000000000124;
+ constexpr u64 SdCardSaveDataSize = 0xa08000;
+ constexpr u64 SdCardSaveDataJournalSize = 0xa08000;
+ constexpr u32 SdCardSaveDataFlags = 0;
constexpr SystemSaveDataInfo SdCardSystemSaveDataInfo = {
.id = SdCardSaveDataId,
.size = SdCardSaveDataSize,
.journal_size = SdCardSaveDataJournalSize,
.flags = SdCardSaveDataFlags,
- .space_id = FsSaveDataSpaceId_SdSystem,
+ .space_id = fs::SaveDataSpaceId::SdSystem,
};
constexpr size_t MaxBuiltInSystemContentMetaCount = 0x800;
constexpr size_t MaxBuiltInUserContentMetaCount = 0x2000;
constexpr size_t MaxSdCardContentMetaCount = 0x2000;
+ constexpr size_t MaxGameCardContentMetaCount = 0x800;
+ using RootPath = kvdb::BoundedString<32>;
+
+ inline void ReplaceMountName(char *out_path, const char *mount_name, const char *path) {
+ std::strcpy(out_path, mount_name);
+ std::strcat(out_path, std::strchr(path, ':'));
+ }
+
+ Result EnsureBuiltInSystemSaveDataFlags() {
+ u32 cur_flags = 0;
+ R_TRY(fs::GetSaveDataFlags(std::addressof(cur_flags), BuiltInSystemSaveDataId));
+ if (cur_flags != BuiltInSystemSaveDataFlags) {
+ R_TRY(fs::SetSaveDataFlags(BuiltInSystemSaveDataId, fs::SaveDataSpaceId::System, BuiltInSystemSaveDataFlags));
+ }
+ return ResultSuccess();
+ }
+
+ ALWAYS_INLINE Result GetContentStorageNotActiveResult(StorageId storage_id) {
+ switch (storage_id) {
+ case StorageId::GameCard: return ResultGameCardContentStorageNotActive();
+ case StorageId::BuiltInSystem: return ResultNandSystemContentStorageNotActive();
+ case StorageId::BuiltInUser: return ResultNandUserContentStorageNotActive();
+ case StorageId::SdCard: return ResultSdCardContentStorageNotActive();
+ default: return ResultUnknownContentStorageNotActive();
+ }
+ }
+
+ ALWAYS_INLINE Result GetContentMetaDatabaseNotActiveResult(StorageId storage_id) {
+ switch (storage_id) {
+ case StorageId::GameCard: return ResultGameCardContentMetaDatabaseNotActive();
+ case StorageId::BuiltInSystem: return ResultNandSystemContentMetaDatabaseNotActive();
+ case StorageId::BuiltInUser: return ResultNandUserContentMetaDatabaseNotActive();
+ case StorageId::SdCard: return ResultSdCardContentMetaDatabaseNotActive();
+ default: return ResultUnknownContentMetaDatabaseNotActive();
+ }
+ }
}
ContentManagerImpl::~ContentManagerImpl() {
- {
- std::scoped_lock lk(this->mutex);
+ std::scoped_lock lk(this->mutex);
- for (size_t i = 0; i < MaxContentStorageEntries; i++) {
- ContentStorageRoot *entry = &this->content_storage_roots[i];
- this->InactivateContentStorage(entry->storage_id);
+ for (auto &root : this->content_storage_roots) {
+ this->InactivateContentStorage(root.storage_id);
+ }
+
+ for (auto &root : this->content_meta_database_roots) {
+ this->InactivateContentMetaDatabase(root.storage_id);
+ }
+ }
+
+ Result ContentManagerImpl::EnsureAndMountSystemSaveData(const char *mount_name, const SystemSaveDataInfo &info) const {
+ constexpr u64 OwnerId = 0;
+
+ fs::DisableAutoSaveDataCreation();
+
+ R_TRY_CATCH(fs::MountSystemSaveData(mount_name, info.space_id, info.id)) {
+ R_CATCH(fs::ResultTargetNotFound) {
+ R_TRY(fs::CreateSystemSaveData(info.space_id, info.id, OwnerId, info.size, info.journal_size, info.flags));
+ R_TRY(fs::MountSystemSaveData(mount_name, info.space_id, info.id));
}
+ } R_END_TRY_CATCH;
- for (size_t i = 0; i < MaxContentMetaDatabaseEntries; i++) {
- ContentMetaDatabaseEntry *entry = &this->content_meta_entries[i];
- this->InactivateContentMetaDatabase(entry->storage_id);
+ return ResultSuccess();
+ }
+
+ Result ContentManagerImpl::GetContentStorageRoot(ContentStorageRoot **out, StorageId id) {
+ R_UNLESS(IsUniqueStorage(id), ncm::ResultUnknownStorage());
+
+ for (auto &root : this->content_storage_roots) {
+ if (root.storage_id == id) {
+ *out = std::addressof(root);
+ return ResultSuccess();
}
}
- for (size_t i = 0; i < MaxContentMetaDatabaseEntries; i++) {
- ContentMetaDatabaseEntry *entry = &this->content_meta_entries[i];
- entry->kvs.reset();
+ return ncm::ResultUnknownStorage();
+ }
+
+ Result ContentManagerImpl::GetContentMetaDatabaseRoot(ContentMetaDatabaseRoot **out, StorageId id) {
+ R_UNLESS(IsUniqueStorage(id), ncm::ResultUnknownStorage());
+
+ for (auto &root : this->content_meta_database_roots) {
+ if (root.storage_id == id) {
+ *out = std::addressof(root);
+ return ResultSuccess();
+ }
}
- for (size_t i = 0; i < MaxContentStorageEntries; i++) {
- ContentStorageRoot *entry = &this->content_storage_roots[i];
- entry->content_storage = nullptr;
- }
+ return ncm::ResultUnknownStorage();
+ }
+
+
+ Result ContentManagerImpl::InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id) {
+ out->storage_id = storage_id;
+ out->content_storage_id = content_storage_id;
+ out->content_storage = nullptr;
+
+ std::strcpy(out->mount_name, impl::CreateUniqueMountName().str);
+ std::snprintf(out->path, sizeof(out->path), "%s:/", out->mount_name);
+
+ return ResultSuccess();
+ }
+
+ Result ContentManagerImpl::InitializeGameCardContentStorageRoot(ContentStorageRoot *out) {
+ out->storage_id = StorageId::GameCard;
+ out->content_storage = nullptr;
+
+ std::strcpy(out->mount_name, impl::CreateUniqueMountName().str);
+ std::snprintf(out->path, sizeof(out->path), "%s:/", out->mount_name);
+
+ return ResultSuccess();
+ }
+
+ Result ContentManagerImpl::InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas) {
+ out->storage_id = storage_id;
+ out->info = info;
+ out->max_content_metas = max_content_metas;
+ out->content_meta_database = nullptr;
+ out->kvs = std::nullopt;
+
+ std::strcpy(out->mount_name, impl::CreateUniqueMountName().str);
+ out->mount_name[0] = '#';
+ std::snprintf(out->path, sizeof(out->path), "%s:/meta", out->mount_name);
+
+ return ResultSuccess();
+ }
+
+ Result ContentManagerImpl::InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas) {
+ out->storage_id = StorageId::GameCard;
+ out->max_content_metas = max_content_metas;
+ out->content_meta_database = nullptr;
+ out->kvs = std::nullopt;
+
+ return ResultSuccess();
}
Result ContentManagerImpl::Initialize() {
@@ -102,56 +210,52 @@ namespace ams::ncm {
/* Already initialized. */
R_UNLESS(!this->initialized, ResultSuccess());
- for (size_t i = 0; i < MaxContentStorageEntries; i++) {
- ContentStorageRoot *entry = &this->content_storage_roots[i];
- entry->storage_id = StorageId::None;
+ /* Clear storage id for all roots. */
+ for (auto &root : this->content_storage_roots) {
+ root.storage_id = StorageId::None;
}
- for (size_t i = 0; i < MaxContentMetaDatabaseEntries; i++) {
- ContentMetaDatabaseEntry *entry = &this->content_meta_entries[i];
- entry->storage_id = StorageId::None;
+ for (auto &root : this->content_meta_database_roots) {
+ root.storage_id = StorageId::None;
}
/* First, setup the BuiltInSystem storage entry. */
- this->content_storage_roots[this->num_content_storage_entries++].Initialize(StorageId::BuiltInSystem, FsContentStorageId_System);
-
+ R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInSystem, fs::ContentStorageId::System));
if (R_FAILED(this->VerifyContentStorage(StorageId::BuiltInSystem))) {
R_TRY(this->CreateContentStorage(StorageId::BuiltInSystem));
}
-
R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem));
/* Next, the BuiltInSystem content meta entry. */
- R_TRY(this->content_meta_entries[this->num_content_meta_entries++].Initialize(StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount));
+ R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount));
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
- /* TODO: N supports a number of unused modes here, we don't bother implementing them currently. */
+ /* TODO: N supports building the database depending on config (unused on retail). */
}
- u32 current_flags = 0;
- if (hos::GetVersion() >= hos::Version_200 && R_SUCCEEDED(fs::GetSaveDataFlags(¤t_flags, BuiltInSystemSaveDataId)) && current_flags != (FsSaveDataFlags_KeepAfterResettingSystemSaveData | FsSaveDataFlags_KeepAfterRefurbishment)) {
- fs::SetSaveDataFlags(BuiltInSystemSaveDataId, FsSaveDataSpaceId_System, FsSaveDataFlags_KeepAfterResettingSystemSaveData | FsSaveDataFlags_KeepAfterRefurbishment);
+ /* Ensure correct flags on the BuiltInSystem save data. */
+ if (hos::GetVersion() >= hos::Version_200) {
+ R_TRY(EnsureBuiltInSystemSaveDataFlags());
}
R_TRY(this->ActivateContentMetaDatabase(StorageId::BuiltInSystem));
/* Now for BuiltInUser's content storage and content meta entries. */
- this->content_storage_roots[this->num_content_storage_entries++].Initialize(StorageId::BuiltInUser, FsContentStorageId_User);
- R_TRY(this->content_meta_entries[this->num_content_meta_entries++].Initialize(StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount));
+ R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInUser, fs::ContentStorageId::User));
+ R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount));
- /* Beyond this point N no longer appears to bother */
- /* incrementing the count for content storage entries or content meta entries. */
+ /* Beyond this point, N uses hardcoded indices. */
/* Next SdCard's content storage and content meta entries. */
- this->content_storage_roots[2].Initialize(StorageId::SdCard, FsContentStorageId_SdCard);
- R_TRY(this->content_meta_entries[2].Initialize(StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount));
+ R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[2], StorageId::SdCard, fs::ContentStorageId::SdCard));
+ R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount));
/* GameCard's content storage and content meta entries. */
/* N doesn't set a content storage id for game cards, so we'll just use 0 (System). */
- this->content_storage_roots[3].Initialize(StorageId::GameCard, FsContentStorageId_System);
- R_TRY(this->content_meta_entries[3].InitializeGameCard(0x800));
+ R_TRY(this->InitializeGameCardContentStorageRoot(&this->content_storage_roots[3]));
+ R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount));
this->initialized = true;
return ResultSuccess();
@@ -161,13 +265,13 @@ namespace ams::ncm {
std::scoped_lock lk(this->mutex);
ContentStorageRoot *root;
- R_TRY(GetUniqueContentStorageRoot(std::addressof(root), storage_id));
+ R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id));
- R_TRY(fs::MountContentStorage(root->mount_point, root->content_storage_id));
- ON_SCOPE_EXIT { fs::Unmount(root->mount_point); };
+ R_TRY(fs::MountContentStorage(root->mount_name, root->content_storage_id));
+ ON_SCOPE_EXIT { fs::Unmount(root->mount_name); };
- R_TRY(fs::EnsureDirectoryRecursively(root->path));
- R_TRY(fs::EnsureContentAndPlaceHolderRoot(root->path));
+ R_TRY(impl::EnsureDirectoryRecursively(root->path));
+ R_TRY(ContentStorageImpl::InitializeBase(root->path));
return ResultSuccess();
}
@@ -176,17 +280,16 @@ namespace ams::ncm {
std::scoped_lock lk(this->mutex);
R_UNLESS(storage_id != StorageId::GameCard, ncm::ResultUnknownStorage());
- ContentMetaDatabaseEntry *entry;
- R_TRY(GetUniqueContentMetaDatabaseEntry(&entry, storage_id));
- /* N doesn't bother checking the result of this. */
- fsDisableAutoSaveDataCreation();
+ ContentMetaDatabaseRoot *root;
+ R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id));
- R_TRY(EnsureAndMountSystemSaveData(entry->mount_point, entry->save_meta));
- ON_SCOPE_EXIT { fs::Unmount(entry->mount_point); };
+ R_TRY(this->EnsureAndMountSystemSaveData(root->mount_name, root->info));
+ ON_SCOPE_EXIT { fs::Unmount(root->mount_name); };
- R_TRY(fs::EnsureDirectoryRecursively(entry->meta_path));
- R_TRY(fsdevCommitDevice(entry->mount_point));
+ R_TRY(impl::EnsureDirectoryRecursively(root->path));
+
+ R_TRY(fs::CommitSaveData(root->mount_name));
return ResultSuccess();
}
@@ -195,37 +298,38 @@ namespace ams::ncm {
std::scoped_lock lk(this->mutex);
ContentStorageRoot *root;
- R_TRY(GetUniqueContentStorageRoot(std::addressof(root), storage_id));
+ R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id));
- char mount_root[0x80] = {};
- auto mount_name = ncm::fs::CreateUniqueMountName(); /* should this be fs::? should it be ncm::? ncm::impl? */
- ReplaceMountName(mount_root, mount_name.name, root->path);
+ char path[0x80];
+ auto mount_name = impl::CreateUniqueMountName(); /* should this be fs::? should it be ncm::? ncm::impl? */
+ ReplaceMountName(path, mount_name.str, root->path);
- R_TRY(fs::MountContentStorage(mount_name.name, root->content_storage_id));
- ON_SCOPE_EXIT { fs::Unmount(mount_name.name); };
+ R_TRY(fs::MountContentStorage(mount_name.str, root->content_storage_id));
+ ON_SCOPE_EXIT { fs::Unmount(mount_name.str); };
- R_TRY(fs::CheckContentStorageDirectoriesExist(mount_root));
+ R_TRY(ContentStorageImpl::VerifyBase(path));
return ResultSuccess();
}
Result ContentManagerImpl::VerifyContentMetaDatabase(StorageId storage_id) {
+ R_UNLESS(storage_id != StorageId::GameCard, ResultSuccess());
+
std::scoped_lock lk(this->mutex);
- R_UNLESS(storage_id != StorageId::GameCard, ResultSuccess());
- ContentMetaDatabaseEntry *entry;
- R_TRY(GetUniqueContentMetaDatabaseEntry(&entry, storage_id));
+ ContentMetaDatabaseRoot *root;
+ R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id));
- auto mount_guard = SCOPE_GUARD { fs::Unmount(entry->mount_point); };
- if (!entry->content_meta_database) {
- R_TRY(fs::MountSystemSaveData(entry->mount_point, entry->save_meta.space_id, entry->save_meta.id));
+ auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); };
+ if (!root->content_meta_database) {
+ R_TRY(fs::MountSystemSaveData(root->mount_name, root->info.space_id, root->info.id));
} else {
mount_guard.Cancel();
}
- bool has_meta_path = false;
- R_TRY(fs::HasDirectory(&has_meta_path, entry->meta_path));
- R_UNLESS(has_meta_path, ncm::ResultInvalidContentMetaDatabase());
+ bool has_dir = false;
+ R_TRY(impl::HasDirectory(&has_dir, root->path));
+ R_UNLESS(has_dir, ncm::ResultInvalidContentMetaDatabase());
return ResultSuccess();
}
@@ -234,20 +338,18 @@ namespace ams::ncm {
std::scoped_lock lk(this->mutex);
ContentStorageRoot *root;
- R_TRY(GetUniqueContentStorageRoot(std::addressof(root), storage_id));
-
- auto content_storage = root->content_storage;
+ R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id));
if (hos::GetVersion() >= hos::Version_200) {
- R_UNLESS(content_storage, GetContentStorageNotActiveResult(storage_id));
+ R_UNLESS(root->content_storage, GetContentStorageNotActiveResult(storage_id));
} else {
/* 1.0.0 activates content storages as soon as they are opened. */
- if (!content_storage) {
+ if (!root->content_storage) {
R_TRY(this->ActivateContentStorage(storage_id));
- content_storage = root->content_storage;
}
}
+ auto content_storage = root->content_storage;
out.SetValue(std::move(content_storage));
return ResultSuccess();
}
@@ -255,73 +357,41 @@ namespace ams::ncm {
Result ContentManagerImpl::OpenContentMetaDatabase(sf::Out> out, StorageId storage_id) {
std::scoped_lock lk(this->mutex);
- ContentMetaDatabaseEntry *entry;
- R_TRY(GetUniqueContentMetaDatabaseEntry(&entry, storage_id));
+ ContentMetaDatabaseRoot *root;
+ R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id));
- auto content_meta_db = entry->content_meta_database;
if (hos::GetVersion() >= hos::Version_200) {
- R_UNLESS(content_meta_db, GetContentMetaDatabaseNotActiveResult(storage_id));
+ R_UNLESS(root->content_meta_database, GetContentMetaDatabaseNotActiveResult(storage_id));
} else {
/* 1.0.0 activates content meta databases as soon as they are opened. */
- if (!content_meta_db) {
+ if (!root->content_meta_database) {
R_TRY(this->ActivateContentMetaDatabase(storage_id));
- content_meta_db = entry->content_meta_database;
}
}
+ auto content_meta_db = root->content_meta_database;
out.SetValue(std::move(content_meta_db));
return ResultSuccess();
}
Result ContentManagerImpl::CloseContentStorageForcibly(StorageId storage_id) {
- std::scoped_lock lk(this->mutex);
-
- R_UNLESS(storage_id != StorageId::None, ncm::ResultUnknownStorage());
- ContentStorageRoot *root;
- R_TRY(FindContentStorageRoot(std::addressof(root), storage_id));
-
- if (root->content_storage) {
- /* N doesn't bother checking the result of this */
- root->content_storage->DisableForcibly();
- fs::Unmount(root->mount_point);
- root->content_storage = nullptr;
- }
-
- return ResultSuccess();
+ return this->InactivateContentStorage(storage_id);
}
Result ContentManagerImpl::CloseContentMetaDatabaseForcibly(StorageId storage_id) {
- std::scoped_lock lk(this->mutex);
-
- R_UNLESS(storage_id != StorageId::None, ncm::ResultUnknownStorage());
- ContentMetaDatabaseEntry *entry;
- R_TRY(FindContentMetaDatabaseEntry(&entry, storage_id));
-
- auto content_meta_db = entry->content_meta_database;
-
- if (content_meta_db) {
- /* N doesn't bother checking the result of this */
- content_meta_db->DisableForcibly();
-
- if (storage_id != StorageId::GameCard) {
- fs::Unmount(entry->mount_point);
- }
-
- entry->content_meta_database = nullptr;
- entry->kvs = std::nullopt;
- }
-
- return ResultSuccess();
+ return this->InactivateContentMetaDatabase(storage_id);
}
Result ContentManagerImpl::CleanupContentMetaDatabase(StorageId storage_id) {
std::scoped_lock lk(this->mutex);
- ContentMetaDatabaseEntry *entry;
- R_TRY(GetUniqueContentMetaDatabaseEntry(&entry, storage_id));
+ R_TRY(this->InactivateContentMetaDatabase(storage_id));
- R_TRY(fsDeleteSaveDataFileSystemBySaveDataSpaceId(entry->save_meta.space_id, entry->save_meta.id));
+ ContentMetaDatabaseRoot *root;
+ R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id));
+
+ R_TRY(fs::DeleteSaveData(root->info.space_id, root->info.id));
return ResultSuccess();
}
@@ -329,45 +399,40 @@ namespace ams::ncm {
std::scoped_lock lk(this->mutex);
ContentStorageRoot *root;
- R_TRY(GetUniqueContentStorageRoot(std::addressof(root), storage_id));
+ R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id));
- /* Already activated. */
+ /* Check if the storage is already activated. */
R_UNLESS(root->content_storage == nullptr, ResultSuccess());
if (storage_id == StorageId::GameCard) {
- FsGameCardHandle gc_hnd;
- R_TRY(fs::GetGameCardHandle(&gc_hnd));
- R_TRY(fs::MountGameCardPartition(root->mount_point, gc_hnd, FsGameCardPartition_Secure));
+ fs::GameCardHandle handle;
+ R_TRY(fs::GetGameCardHandle(std::addressof(handle)));
+ R_TRY(fs::MountGameCardPartition(root->mount_name, handle, fs::GameCardPartition::Secure));
} else {
- R_TRY(fs::MountContentStorage(root->mount_point, root->content_storage_id));
+ R_TRY(fs::MountContentStorage(root->mount_name, root->content_storage_id));
}
- auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_point); };
+ auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); };
if (storage_id == StorageId::GameCard) {
auto content_storage = std::make_shared();
- R_TRY(content_storage->Initialize(root->path, path::MakeContentPathFlat));
+ R_TRY(content_storage->Initialize(root->path, MakeFlatContentFilePath));
root->content_storage = std::move(content_storage);
} else {
- MakeContentPathFunc content_path_func = nullptr;
- MakePlaceHolderPathFunc placeholder_path_func = nullptr;
- bool delay_flush = false;
auto content_storage = std::make_shared();
switch (storage_id) {
case StorageId::BuiltInSystem:
- content_path_func = path::MakeContentPathFlat;
- placeholder_path_func = path::MakePlaceHolderPathFlat;
+ R_TRY(content_storage->Initialize(root->path, MakeFlatContentFilePath, MakeFlatPlaceHolderFilePath, false, std::addressof(this->rights_id_cache)));
break;
case StorageId::SdCard:
- delay_flush = true;
+ R_TRY(content_storage->Initialize(root->path, MakeSha256HierarchicalContentFilePath_ForFat16KCluster, MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster, true, std::addressof(this->rights_id_cache)));
+ break;
default:
- content_path_func = path::MakeContentPathHashByteLayered;
- placeholder_path_func = path::MakePlaceHolderPathHashByteLayered;
+ R_TRY(content_storage->Initialize(root->path, MakeSha256HierarchicalContentFilePath_ForFat16KCluster, MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster, false, std::addressof(this->rights_id_cache)));
break;
}
- R_TRY(content_storage->Initialize(root->path, content_path_func, placeholder_path_func, delay_flush, &this->rights_id_cache));
root->content_storage = std::move(content_storage);
}
@@ -379,40 +444,42 @@ namespace ams::ncm {
std::scoped_lock lk(this->mutex);
ContentStorageRoot *root;
- R_TRY(GetUniqueContentStorageRoot(std::addressof(root), storage_id));
+ R_TRY(this->GetContentStorageRoot(std::addressof(root), storage_id));
- /* Already inactivated. */
- R_UNLESS(root->content_storage != nullptr, ResultSuccess());
+ if (root->content_storage) {
+ /* N doesn't bother checking the result of this */
+ root->content_storage->DisableForcibly();
+ root->content_storage = nullptr;
+ fs::Unmount(root->mount_name);
+ }
- root->content_storage->DisableForcibly();
- root->content_storage = nullptr;
- fs::Unmount(root->mount_point);
return ResultSuccess();
}
Result ContentManagerImpl::ActivateContentMetaDatabase(StorageId storage_id) {
std::scoped_lock lk(this->mutex);
- ContentMetaDatabaseEntry *entry;
- R_TRY(GetUniqueContentMetaDatabaseEntry(&entry, storage_id));
+ ContentMetaDatabaseRoot *root;
+ R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id));
/* Already activated. */
- R_UNLESS(entry->content_meta_database == nullptr, ResultSuccess());
+ R_UNLESS(root->content_meta_database == nullptr, ResultSuccess());
- /* Make a brand new kvs. N doesn't quite do this, but we will for cleanliness. */
- entry->kvs.emplace();
+ /* Make a new kvs. */
+ root->kvs.emplace();
- if (storage_id != StorageId::GameCard) {
- R_TRY(fs::MountSystemSaveData(entry->mount_point, entry->save_meta.space_id, entry->save_meta.id));
- auto mount_guard = SCOPE_GUARD { fs::Unmount(entry->mount_point); };
- R_TRY(entry->kvs->Initialize(entry->meta_path, entry->max_content_metas));
- R_TRY(entry->kvs->Load());
- entry->content_meta_database = std::make_shared(std::addressof(*entry->kvs), entry->mount_point);
- mount_guard.Cancel();
+ if (storage_id == StorageId::GameCard) {
+ R_TRY(root->kvs->Initialize(root->max_content_metas));
+ root->content_meta_database = std::make_shared(std::addressof(*root->kvs));
} else {
- R_TRY(entry->kvs->Initialize(entry->max_content_metas));
- R_TRY(entry->kvs->Load());
- entry->content_meta_database = std::make_shared(std::addressof(*entry->kvs));
+ R_TRY(fs::MountSystemSaveData(root->mount_name, root->info.space_id, root->info.id));
+ auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); };
+
+ R_TRY(root->kvs->Initialize(root->path, root->max_content_metas));
+ R_TRY(root->kvs->Load());
+
+ root->content_meta_database = std::make_shared(std::addressof(*root->kvs), root->mount_name);
+ mount_guard.Cancel();
}
return ResultSuccess();
@@ -421,18 +488,17 @@ namespace ams::ncm {
Result ContentManagerImpl::InactivateContentMetaDatabase(StorageId storage_id) {
std::scoped_lock lk(this->mutex);
- ContentMetaDatabaseEntry *entry;
- R_TRY(GetUniqueContentMetaDatabaseEntry(&entry, storage_id));
+ ContentMetaDatabaseRoot *root;
+ R_TRY(this->GetContentMetaDatabaseRoot(&root, storage_id));
- /* Already inactivated. */
- if (entry->content_meta_database != nullptr) {
- entry->content_meta_database->DisableForcibly();
- entry->content_meta_database = nullptr;
- /* This should lead to Index's destructor performing cleanup for us. */
- entry->kvs = std::nullopt;
+ if (root->content_meta_database) {
+ /* N doesn't bother checking the result of this */
+ root->content_meta_database->DisableForcibly();
+ root->content_meta_database = nullptr;
+ root->kvs = std::nullopt;
if (storage_id != StorageId::GameCard) {
- fs::Unmount(entry->mount_point);
+ fs::Unmount(root->mount_name);
}
}
diff --git a/stratosphere/ncm/source/ncm_content_manager_impl.hpp b/stratosphere/ncm/source/ncm_content_manager_impl.hpp
index db507e934..1614e5833 100644
--- a/stratosphere/ncm/source/ncm_content_manager_impl.hpp
+++ b/stratosphere/ncm/source/ncm_content_manager_impl.hpp
@@ -16,8 +16,8 @@
#pragma once
#include
-#include "impl/ncm_rights_cache.hpp"
-#include "ncm_fs.hpp"
+#include "ncm_rights_cache.hpp"
+#include "ncm_fs_utils.hpp"
namespace ams::ncm {
@@ -26,177 +26,67 @@ namespace ams::ncm {
u64 size;
u64 journal_size;
u32 flags;
- FsSaveDataSpaceId space_id;
+ fs::SaveDataSpaceId space_id;
};
-
- static_assert(sizeof(SystemSaveDataInfo) == 0x20, "SystemSaveDataInfo definition!");
+ static_assert(std::is_pod::value);
class ContentManagerImpl final : public IContentManager {
private:
- constexpr static size_t MaxContentStorageEntries = 8;
- constexpr static size_t MaxContentMetaDatabaseEntries = 8;
+ constexpr static size_t MaxContentStorageRoots = 8;
+ constexpr static size_t MaxContentMetaDatabaseRoots = 8;
private:
struct ContentStorageRoot {
NON_COPYABLE(ContentStorageRoot);
NON_MOVEABLE(ContentStorageRoot);
- char mount_point[16];
+ char mount_name[fs::MountNameLengthMax + 1];
char path[128];
StorageId storage_id;
- FsContentStorageId content_storage_id;
+ fs::ContentStorageId content_storage_id;
std::shared_ptr content_storage;
- inline ContentStorageRoot() : storage_id(StorageId::None),
- content_storage_id(FsContentStorageId_System), content_storage(nullptr) {
- mount_point[0] = '\0';
- path[0] = '\0';
- }
-
- inline void Initialize(StorageId storage_id, FsContentStorageId content_storage_id) {
- this->storage_id = storage_id;
- this->content_storage_id = content_storage_id;
- this->content_storage = nullptr;
- MountName mount_name = ncm::fs::CreateUniqueMountName();
- std::strcpy(this->mount_point, mount_name.name);
- snprintf(this->path, 0x80, "%s:/", this->mount_point);
- }
+ ContentStorageRoot() { /* ... */ }
};
- struct ContentMetaDatabaseEntry {
- NON_COPYABLE(ContentMetaDatabaseEntry);
- NON_MOVEABLE(ContentMetaDatabaseEntry);
+ struct ContentMetaDatabaseRoot {
+ NON_COPYABLE(ContentMetaDatabaseRoot);
+ NON_MOVEABLE(ContentMetaDatabaseRoot);
- char mount_point[16];
- char meta_path[128];
+ char mount_name[fs::MountNameLengthMax + 1];
+ char path[128];
StorageId storage_id;
- SystemSaveDataInfo save_meta;
+ SystemSaveDataInfo info;
std::shared_ptr content_meta_database;
std::optional> kvs;
u32 max_content_metas;
- inline ContentMetaDatabaseEntry() : storage_id(StorageId::None), save_meta({0}),
- content_meta_database(nullptr), kvs(std::nullopt), max_content_metas(0) {
- mount_point[0] = '\0';
- meta_path[0] = '\0';
- }
-
- Result Initialize(StorageId storage_id, const SystemSaveDataInfo& save_meta, size_t max_content_metas) {
- this->storage_id = storage_id;
- this->max_content_metas = max_content_metas;
- this->save_meta = save_meta;
- this->content_meta_database = nullptr;
- this->kvs = std::nullopt;
- MountName mount_name = ncm::fs::CreateUniqueMountName();
- strcpy(this->mount_point, mount_name.name);
- this->mount_point[0] = '#';
- snprintf(this->meta_path, 0x80, "%s:/meta", this->mount_point);
- return ResultSuccess();
- }
-
- Result InitializeGameCard(size_t max_content_metas) {
- this->storage_id = StorageId::GameCard;
- this->max_content_metas = max_content_metas;
- this->content_meta_database = nullptr;
- this->kvs = std::nullopt;
- return ResultSuccess();
- }
+ ContentMetaDatabaseRoot() { /* ... */ }
};
private:
os::Mutex mutex;
bool initialized = false;
- ContentStorageRoot content_storage_roots[MaxContentStorageEntries];
- ContentMetaDatabaseEntry content_meta_entries[MaxContentMetaDatabaseEntries];
+ ContentStorageRoot content_storage_roots[MaxContentStorageRoots];
+ ContentMetaDatabaseRoot content_meta_database_roots[MaxContentMetaDatabaseRoots];
u32 num_content_storage_entries;
u32 num_content_meta_entries;
- impl::RightsIdCache rights_id_cache;
+ RightsIdCache rights_id_cache;
public:
ContentManagerImpl() { /* ... */ };
~ContentManagerImpl();
public:
Result Initialize();
private:
- constexpr inline bool IsUniqueStorage(StorageId id) {
- return id != StorageId::None && id != StorageId::Any;
- }
+ Result GetContentStorageRoot(ContentStorageRoot **out, StorageId id);
+ Result GetContentMetaDatabaseRoot(ContentMetaDatabaseRoot **out, StorageId id);
- Result FindContentStorageRoot(ContentStorageRoot **out, StorageId storage_id) {
- for (size_t i = 0; i < MaxContentStorageEntries; i++) {
- ContentStorageRoot *root = &this->content_storage_roots[i];
+ Result InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id);
+ Result InitializeGameCardContentStorageRoot(ContentStorageRoot *out);
- if (root->storage_id == storage_id) {
- *out = root;
- return ResultSuccess();
- }
- }
- return ncm::ResultUnknownStorage();
- }
+ Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas);
+ Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas);
- Result GetUniqueContentStorageRoot(ContentStorageRoot **out, StorageId storage_id) {
- R_UNLESS(IsUniqueStorage(storage_id), ncm::ResultUnknownStorage());
- return FindContentStorageRoot(out, storage_id);
- }
+ Result EnsureAndMountSystemSaveData(const char *mount, const SystemSaveDataInfo &info) const;
- Result FindContentMetaDatabaseEntry(ContentMetaDatabaseEntry **out, StorageId storage_id) {
- for (size_t i = 0; i < MaxContentMetaDatabaseEntries; i++) {
- ContentMetaDatabaseEntry *entry = &this->content_meta_entries[i];
-
- if (entry->storage_id == storage_id) {
- *out = entry;
- return ResultSuccess();
- }
- }
- return ncm::ResultUnknownStorage();
- }
-
- Result GetUniqueContentMetaDatabaseEntry(ContentMetaDatabaseEntry **out, StorageId storage_id) {
- R_UNLESS(IsUniqueStorage(storage_id), ncm::ResultUnknownStorage());
- return FindContentMetaDatabaseEntry(out, storage_id);
- }
-
- ALWAYS_INLINE Result GetContentStorageNotActiveResult(StorageId storage_id) {
- switch (storage_id) {
- case StorageId::GameCard:
- return ResultGameCardContentStorageNotActive();
- case StorageId::BuiltInSystem:
- return ResultNandSystemContentStorageNotActive();
- case StorageId::BuiltInUser:
- return ResultNandUserContentStorageNotActive();
- case StorageId::SdCard:
- return ResultSdCardContentStorageNotActive();
- default:
- return ResultUnknownContentStorageNotActive();
- }
- }
-
- ALWAYS_INLINE Result GetContentMetaDatabaseNotActiveResult(StorageId storage_id) {
- switch (storage_id) {
- case StorageId::GameCard:
- return ResultGameCardContentMetaDatabaseNotActive();
- case StorageId::BuiltInSystem:
- return ResultNandSystemContentMetaDatabaseNotActive();
- case StorageId::BuiltInUser:
- return ResultNandUserContentMetaDatabaseNotActive();
- case StorageId::SdCard:
- return ResultSdCardContentMetaDatabaseNotActive();
- default:
- return ResultUnknownContentMetaDatabaseNotActive();
- }
- }
-
- Result EnsureAndMountSystemSaveData(const char *mount_name, const SystemSaveDataInfo &save_meta) {
- R_TRY_CATCH(fs::MountSystemSaveData(mount_name, save_meta.space_id, save_meta.id)) {
- R_CATCH(ams::fs::ResultTargetNotFound) {
- R_TRY(fsCreate_SystemSaveData(save_meta.space_id, save_meta.id, save_meta.size, save_meta.journal_size, save_meta.flags));
- R_TRY(fs::MountSystemSaveData(mount_name, save_meta.space_id, save_meta.id));
- }
- } R_END_TRY_CATCH;
- return ResultSuccess();
- }
-
- inline void ReplaceMountName(char *out_path, const char *mount_name, const char *root_path) {
- strcpy(out_path, mount_name);
- strcat(out_path, strchr(root_path, ':'));
- }
public:
virtual Result CreateContentStorage(StorageId storage_id) override;
virtual Result CreateContentMetaDatabase(StorageId storage_id) override;
diff --git a/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp b/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp
index 3c8989980..e76aad429 100644
--- a/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp
+++ b/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp
@@ -15,7 +15,7 @@
*/
#include "ncm_content_meta_database_impl.hpp"
-#include "ncm_utils.hpp"
+#include "ncm_content_id_utils.hpp"
namespace ams::ncm {
diff --git a/stratosphere/ncm/source/ncm_content_storage_impl.cpp b/stratosphere/ncm/source/ncm_content_storage_impl.cpp
index c474e2528..004ac57eb 100644
--- a/stratosphere/ncm/source/ncm_content_storage_impl.cpp
+++ b/stratosphere/ncm/source/ncm_content_storage_impl.cpp
@@ -15,62 +15,207 @@
*/
#include "ncm_content_storage_impl.hpp"
-#include "ncm_fs.hpp"
+#include "ncm_fs_utils.hpp"
#include "ncm_make_path.hpp"
-#include "ncm_utils.hpp"
+#include "ncm_content_id_utils.hpp"
namespace ams::ncm {
- ContentStorageImpl::~ContentStorageImpl() {
- this->Finalize();
+ namespace {
+
+ constexpr inline const char * const BaseContentDirectory = "/registered";
+
+ void MakeBaseContentDirectoryPath(PathString *out, const char *root_path) {
+ out->SetFormat("%s%s", root_path, BaseContentDirectory);
+ }
+
+ void MakeContentPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) {
+ PathString path;
+ MakeBaseContentDirectoryPath(std::addressof(path), root_path);
+ func(out, id, path);
+ }
+
+ Result EnsureContentDirectory(ContentId id, MakeContentPathFunction func, const char *root_path) {
+ PathString path;
+ MakeContentPath(std::addressof(path), id, func, root_path);
+ return impl::EnsureParentDirectoryRecursively(path);
+ }
+
+ Result DeleteContentFile(ContentId id, MakeContentPathFunction func, const char *root_path) {
+ PathString path;
+ MakeContentPath(std::addressof(path), id, func, root_path);
+
+ R_TRY_CATCH(fs::DeleteFile(path)) {
+ R_CONVERT(fs::ResultPathNotFound, ncm::ResultContentNotFound())
+ } R_END_TRY_CATCH;
+
+ return ResultSuccess();
+ }
+
+ template
+ Result TraverseDirectory(bool *out_should_continue, const char *root_path, int max_level, F f) {
+ R_UNLESS(max_level > 0, ResultSuccess());
+
+ bool retry_dir_read = true;
+ while (retry_dir_read) {
+ retry_dir_read = false;
+
+ fs::DirectoryHandle dir;
+ R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize));
+ ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
+
+ while (true) {
+ fs::DirectoryEntry entry;
+ s64 entry_count;
+ R_TRY(fs::ReadDirectory(std::addressof(entry_count), std::addressof(entry), dir, 1));
+ if (entry_count == 0) {
+ break;
+ }
+
+ PathString current_path;
+ current_path.SetFormat("%s/%s", root_path, entry.name);
+
+ bool should_continue = true;
+ bool should_retry_dir_read = false;
+ R_TRY(f(&should_continue, &should_retry_dir_read, current_path, entry));
+
+ /* If the provided function wishes to terminate immediately, we should respect it. */
+ if (!should_continue) {
+ *out_should_continue = false;
+ return ResultSuccess();
+ }
+
+ if (should_retry_dir_read) {
+ retry_dir_read = true;
+ break;
+ }
+
+ /* If the entry is a directory, recurse. */
+ if (entry.type == fs::DirectoryEntryType_Directory) {
+ R_TRY(TraverseDirectory(std::addressof(should_continue), current_path, max_level - 1, f));
+
+ if (!should_continue) {
+ *out_should_continue = false;
+ return ResultSuccess();
+ }
+ }
+ }
+ }
+
+ return ResultSuccess();
+ }
+
+
+ template
+ Result TraverseDirectory(const char *root_path, int max_level, F f) {
+ bool should_continue = false;
+ return TraverseDirectory(std::addressof(should_continue), root_path, max_level, f);
+ }
+
+ bool IsContentPath(const char *path) {
+ impl::PathView view(path);
+ if (!view.HasSuffix(".nca")) {
+ return false;
+ }
+
+ auto file_name = view.GetFileName();
+ if (file_name.length() != ContentIdStringLength + 4) {
+ return false;
+ }
+
+ for (size_t i = 0; i < ContentIdStringLength; i++) {
+ if (!std::isxdigit(static_cast(file_name[i]))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool IsPlaceHolderPath(const char *path) {
+ return IsContentPath(path);
+ }
+
+ Result CleanDirectoryRecursively(const PathString &path) {
+ if (hos::GetVersion() >= hos::Version_300) {
+ R_TRY(fs::CleanDirectoryRecursively(path));
+ } else {
+ /* CleanDirectoryRecursively didn't exist on < 3.0.0, so we will polyfill it. */
+ /* We'll delete the directory, then recreate it. */
+ R_TRY(fs::DeleteDirectoryRecursively(path));
+ R_TRY(fs::CreateDirectory(path));
+ }
+ return ResultSuccess();
+ }
+
}
- Result ContentStorageImpl::Initialize(const char *path, MakeContentPathFunc content_path_func, MakePlaceHolderPathFunc placeholder_path_func, bool delay_flush, impl::RightsIdCache *rights_id_cache) {
- R_TRY(this->EnsureEnabled());
- R_TRY(fs::CheckContentStorageDirectoriesExist(path));
+ ContentStorageImpl::~ContentStorageImpl() {
+ this->InvalidateFileCache();
+ }
+
+ Result ContentStorageImpl::InitializeBase(const char *root_path) {
+ PathString path;
+
+ MakeBaseContentDirectoryPath(std::addressof(path), root_path);
+ R_TRY(impl::EnsureDirectoryRecursively(path));
+
+ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path);
+ R_TRY(impl::EnsureDirectoryRecursively(path));
- this->root_path = PathString(path);
- this->make_content_path_func = *content_path_func;
- this->placeholder_accessor.Initialize(this->root_path, *placeholder_path_func, delay_flush);
- this->rights_id_cache = rights_id_cache;
return ResultSuccess();
}
- void ContentStorageImpl::Finalize() {
- this->ClearContentCache();
- this->placeholder_accessor.InvalidateAll();
+ Result ContentStorageImpl::CleanupBase(const char *root_path) {
+ PathString path;
+
+ MakeBaseContentDirectoryPath(std::addressof(path), root_path);
+ R_TRY(CleanDirectoryRecursively(path));
+
+ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path);
+ R_TRY(CleanDirectoryRecursively(path));
+
+ return ResultSuccess();
}
- void ContentStorageImpl::ClearContentCache() {
+ Result ContentStorageImpl::VerifyBase(const char *root_path) {
+ PathString path;
+
+ bool has_dir;
+ R_TRY(impl::HasDirectory(std::addressof(has_dir), root_path));
+ R_UNLESS(has_dir, ncm::ResultContentStorageBaseNotFound());
+
+ bool has_registered;
+ MakeBaseContentDirectoryPath(std::addressof(path), root_path);
+ R_TRY(impl::HasDirectory(std::addressof(has_registered), path));
+
+ bool has_placeholder;
+ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path);
+ R_TRY(impl::HasDirectory(std::addressof(has_placeholder), path));
+
+ R_UNLESS(has_registered || has_placeholder, ncm::ResultContentStorageBaseNotFound());
+ R_UNLESS(has_registered, ncm::ResultInvalidContentStorageBase());
+ R_UNLESS(has_placeholder, ncm::ResultInvalidContentStorageBase());
+
+ return ResultSuccess();
+ }
+
+ void ContentStorageImpl::InvalidateFileCache() {
if (this->cached_content_id != InvalidContentId) {
- fclose(this->content_cache_file_handle);
+ fs::CloseFile(this->cached_file_handle);
this->cached_content_id = InvalidContentId;
}
}
- unsigned int ContentStorageImpl::GetContentDirectoryDepth() {
- if (this->make_content_path_func == static_cast(path::MakeContentPathFlat)) {
- return 1;
- } else if (this->make_content_path_func == static_cast(path::MakeContentPathHashByteLayered)) {
- return 2;
- } else if (this->make_content_path_func == static_cast(path::MakeContentPath10BitLayered)) {
- return 2;
- } else if (this->make_content_path_func == static_cast(path::MakeContentPathDualLayered)) {
- return 3;
- }
-
- AMS_ABORT();
- }
-
- Result ContentStorageImpl::OpenCachedContentFile(ContentId content_id) {
+ Result ContentStorageImpl::OpenContentIdFile(ContentId content_id) {
R_UNLESS(this->cached_content_id != content_id, ResultSuccess());
- this->ClearContentCache();
+ this->InvalidateFileCache();
- PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
+ PathString path;
+ MakeContentPath(std::addressof(path), content_id, this->make_content_path_func, this->root_path);
- R_TRY_CATCH(fs::OpenFile(&this->content_cache_file_handle, content_path, FsOpenMode_Read)) {
+ R_TRY_CATCH(fs::OpenFile(&this->cached_file_handle, path, fs::OpenMode_Read)) {
R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultContentNotFound())
} R_END_TRY_CATCH;
@@ -78,6 +223,19 @@ namespace ams::ncm {
return ResultSuccess();
}
+ Result ContentStorageImpl::Initialize(const char *path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache) {
+ R_TRY(this->EnsureEnabled());
+
+ R_TRY(VerifyBase(path));
+
+ this->root_path = PathString(path);
+ this->make_content_path_func = content_path_func;
+ this->placeholder_accessor.Initialize(std::addressof(this->root_path), placeholder_path_func, delay_flush);
+ this->rights_id_cache = rights_id_cache;
+
+ return ResultSuccess();
+ }
+
Result ContentStorageImpl::GeneratePlaceHolderId(sf::Out out) {
R_TRY(this->EnsureEnabled());
out.SetValue({util::GenerateUuid()});
@@ -87,18 +245,15 @@ namespace ams::ncm {
Result ContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) {
R_TRY(this->EnsureEnabled());
- PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
-
- R_TRY(fs::EnsureParentDirectoryRecursively(content_path));
- R_TRY(this->placeholder_accessor.Create(placeholder_id, size));
+ R_TRY(EnsureContentDirectory(content_id, this->make_content_path_func, this->root_path));
+ R_TRY(this->placeholder_accessor.CreatePlaceHolderFile(placeholder_id, size));
return ResultSuccess();
}
Result ContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
- return this->placeholder_accessor.Delete(placeholder_id);
+ return this->placeholder_accessor.DeletePlaceHolderFile(placeholder_id);
}
Result ContentStorageImpl::HasPlaceHolder(sf::Out out, PlaceHolderId placeholder_id) {
@@ -108,7 +263,7 @@ namespace ams::ncm {
this->placeholder_accessor.MakePath(std::addressof(placeholder_path), placeholder_id);
bool has = false;
- R_TRY(fs::HasFile(&has, placeholder_path));
+ R_TRY(impl::HasFile(&has, placeholder_path));
out.SetValue(has);
return ResultSuccess();
@@ -118,25 +273,23 @@ namespace ams::ncm {
/* Offset is too large */
R_UNLESS(offset <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
- R_TRY(this->placeholder_accessor.Write(placeholder_id, offset, data.GetPointer(), data.GetSize()));
+ return this->placeholder_accessor.WritePlaceHolderFile(placeholder_id, offset, data.GetPointer(), data.GetSize());
return ResultSuccess();
}
Result ContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
- this->ClearContentCache();
+ this->InvalidateFileCache();
R_TRY(this->EnsureEnabled());
PathString placeholder_path;
PathString content_path;
this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
- this->GetContentPath(std::addressof(content_path), content_id);
+ MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path);
- if (rename(placeholder_path, content_path) != 0) {
- R_TRY_CATCH(fsdevGetLastResult()) {
- R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
- R_CONVERT(ams::fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists())
- } R_END_TRY_CATCH;
- }
+ R_TRY_CATCH(fs::RenameFile(placeholder_path, content_path)) {
+ R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
+ R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists())
+ } R_END_TRY_CATCH;
return ResultSuccess();
}
@@ -144,28 +297,19 @@ namespace ams::ncm {
Result ContentStorageImpl::Delete(ContentId content_id) {
R_TRY(this->EnsureEnabled());
- this->ClearContentCache();
+ this->InvalidateFileCache();
- PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
-
- if (std::remove(content_path) != 0) {
- R_TRY_CATCH(fsdevGetLastResult()) {
- R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultContentNotFound())
- } R_END_TRY_CATCH;
- }
-
- return ResultSuccess();
+ return DeleteContentFile(content_id, this->make_content_path_func, this->root_path);
}
Result ContentStorageImpl::Has(sf::Out out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
+ MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path);
bool has = false;
- R_TRY(fs::HasFile(&has, content_path));
+ R_TRY(impl::HasFile(&has, content_path));
out.SetValue(has);
return ResultSuccess();
@@ -175,11 +319,12 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled());
PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
+ MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path);
Path common_path;
- R_TRY(fs::ConvertToFsCommonPath(common_path.str, ams::fs::EntryNameLengthMax, content_path));
- out.SetValue(Path::Encode(common_path.str));
+ R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), content_path));
+
+ out.SetValue(common_path);
return ResultSuccess();
}
@@ -190,43 +335,44 @@ namespace ams::ncm {
this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
Path common_path;
- R_TRY(fs::ConvertToFsCommonPath(common_path.str, ams::fs::EntryNameLengthMax, placeholder_path));
- out.SetValue(Path::Encode(common_path.str));
+ R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), placeholder_path));
+
+ out.SetValue(common_path);
return ResultSuccess();
}
Result ContentStorageImpl::CleanupAllPlaceHolder() {
R_TRY(this->EnsureEnabled());
- PathString placeholder_root_path;
this->placeholder_accessor.InvalidateAll();
- this->placeholder_accessor.MakeRootPath(std::addressof(placeholder_root_path));
- /* Nintendo uses CleanDirectoryRecursively which is 3.0.0+.
- We'll just delete the directory and recreate it to support all firmwares. */
- R_TRY(fsdevDeleteDirectoryRecursively(placeholder_root_path));
- R_UNLESS(mkdir(placeholder_root_path, S_IRWXU) != -1, fsdevGetLastResult());
+ PathString placeholder_dir;
+ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(placeholder_dir), this->root_path);
+
+ CleanDirectoryRecursively(placeholder_dir);
+
return ResultSuccess();
}
Result ContentStorageImpl::ListPlaceHolder(sf::Out out_count, const sf::OutArray &out_buf) {
R_TRY(this->EnsureEnabled());
- PathString placeholder_root_path;
- this->placeholder_accessor.MakeRootPath(std::addressof(placeholder_root_path));
- const unsigned int dir_depth = this->placeholder_accessor.GetDirectoryDepth();
+ PathString placeholder_dir;
+ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(placeholder_dir), this->root_path);
+ const size_t max_entries = out_buf.GetSize();
size_t entry_count = 0;
- R_TRY(fs::TraverseDirectory(placeholder_root_path, dir_depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, struct dirent *dir_entry) -> Result {
+ R_TRY(TraverseDirectory(placeholder_dir, placeholder_accessor.GetHierarchicalDirectoryDepth(), [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) -> Result {
*should_continue = true;
*should_retry_dir_read = false;
- if (dir_entry->d_type == DT_REG) {
- R_UNLESS(entry_count <= out_buf.GetSize(), ncm::ResultBufferInsufficient());
+ if (entry.type == fs::DirectoryEntryType_File) {
+ R_UNLESS(entry_count <= max_entries, ncm::ResultBufferInsufficient());
- PlaceHolderId cur_entry_placeholder_id = {0};
- R_TRY(GetPlaceHolderIdFromDirEntry(&cur_entry_placeholder_id, dir_entry));
- out_buf[entry_count++] = cur_entry_placeholder_id;
+ PlaceHolderId placeholder_id;
+ R_TRY(PlaceHolderAccessor::GetPlaceHolderIdFromFileName(std::addressof(placeholder_id), entry.name));
+
+ out_buf[entry_count++] = placeholder_id;
}
return ResultSuccess();
@@ -239,72 +385,64 @@ namespace ams::ncm {
Result ContentStorageImpl::GetContentCount(sf::Out out_count) {
R_TRY(this->EnsureEnabled());
- PathString content_root_path;
- this->GetContentRootPath(std::addressof(content_root_path));
- const unsigned int dir_depth = this->GetContentDirectoryDepth();
- u32 content_count = 0;
+ PathString path;
+ MakeBaseContentDirectoryPath(std::addressof(path), this->root_path);
+ const auto depth = GetHierarchicalContentDirectoryDepth(this->make_content_path_func);
+ size_t count = 0;
- R_TRY(fs::TraverseDirectory(content_root_path, dir_depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, struct dirent *dir_entry) -> Result {
+ R_TRY(TraverseDirectory(path, depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) -> Result {
*should_continue = true;
*should_retry_dir_read = false;
- if (dir_entry->d_type == DT_REG) {
- content_count++;
+ if (entry.type == fs::DirectoryEntryType_File) {
+ count++;
}
return ResultSuccess();
}));
- out_count.SetValue(content_count);
+ out_count.SetValue(static_cast(count));
return ResultSuccess();
}
- Result ContentStorageImpl::ListContentId(sf::Out out_count, const sf::OutArray &out_buf, u32 start_offset) {
- R_UNLESS(start_offset <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
+ Result ContentStorageImpl::ListContentId(sf::Out out_count, const sf::OutArray &out_buf, u32 offset) {
+ R_UNLESS(offset <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
- PathString content_root_path;
- this->GetContentRootPath(std::addressof(content_root_path));
-
- const unsigned int dir_depth = this->GetContentDirectoryDepth();
+ PathString path;
+ MakeBaseContentDirectoryPath(std::addressof(path), this->root_path);
+ const auto depth = GetHierarchicalContentDirectoryDepth(this->make_content_path_func);
size_t entry_count = 0;
- R_TRY(fs::TraverseDirectory(content_root_path, dir_depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, struct dirent *dir_entry) {
+ R_TRY(TraverseDirectory(path, depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) {
*should_retry_dir_read = false;
*should_continue = true;
- if (dir_entry->d_type == DT_REG) {
- /* Skip entries until we reach the start offset. */
- if (start_offset > 0) {
- start_offset--;
- return ResultSuccess();
- }
+ /* We have nothing to do if not working with a file. */
+ if (entry.type != fs::DirectoryEntryType_File) {
+ return ResultSuccess();
+ }
- /* We don't necessarily expect to be able to completely fill the output buffer. */
- if (entry_count > out_buf.GetSize()) {
- *should_continue = false;
- return ResultSuccess();
- }
+ /* Skip entries until we reach the start offset. */
+ if (offset > 0) {
+ --offset;
+ return ResultSuccess();
+ }
- size_t name_len = strlen(dir_entry->d_name);
- std::optional content_id = GetContentIdFromString(dir_entry->d_name, name_len);
-
- /* Skip to the next entry if the id was invalid. */
- if (!content_id) {
- return ResultSuccess();
- }
+ /* We don't necessarily expect to be able to completely fill the output buffer. */
+ if (entry_count >= out_buf.GetSize()) {
+ *should_continue = false;
+ return ResultSuccess();
+ }
+ auto content_id = GetContentIdFromString(entry.name, std::strlen(entry.name));
+ if (content_id) {
out_buf[entry_count++] = *content_id;
}
return ResultSuccess();
}));
- for (size_t i = 0; i < entry_count; i++) {
- char content_name[sizeof(ContentId)*2+1] = {0};
- GetStringFromContentId(content_name, out_buf[i]);
- }
-
out_count.SetValue(static_cast(entry_count));
return ResultSuccess();
}
@@ -313,17 +451,22 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled());
PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
- struct stat st;
+ MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path);
- R_UNLESS(stat(content_path, &st) != -1, fsdevGetLastResult());
- out_size.SetValue(st.st_size);
+ fs::FileHandle file;
+ R_TRY(fs::OpenFile(std::addressof(file), content_path, fs::OpenMode_Read));
+ ON_SCOPE_EXIT { fs::CloseFile(file); };
+
+ s64 file_size;
+ R_TRY(fs::GetFileSize(std::addressof(file_size), file));
+
+ out_size.SetValue(file_size);
return ResultSuccess();
}
Result ContentStorageImpl::DisableForcibly() {
this->disabled = true;
- this->ClearContentCache();
+ this->InvalidateFileCache();
this->placeholder_accessor.InvalidateAll();
return ResultSuccess();
}
@@ -331,31 +474,27 @@ namespace ams::ncm {
Result ContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
R_TRY(this->EnsureEnabled());
- PathString old_content_path;
- PathString new_content_path;
+ this->InvalidateFileCache();
+
+ R_TRY(EnsureContentDirectory(new_content_id, this->make_content_path_func, this->root_path));
+ R_TRY(this->placeholder_accessor.EnsurePlaceHolderDirectory(placeholder_id));
+
PathString placeholder_path;
-
- this->ClearContentCache();
-
- /* Ensure the new content path is ready. */
- this->GetContentPath(std::addressof(new_content_path), new_content_id);
- R_TRY(fs::EnsureParentDirectoryRecursively(new_content_path));
-
- R_TRY(this->placeholder_accessor.EnsureRecursively(placeholder_id));
+ PathString content_path;
this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
- if (rename(old_content_path, placeholder_path) != 0) {
- R_TRY_CATCH(fsdevGetLastResult()) {
- R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
- R_CONVERT(ams::fs::ResultPathAlreadyExists, ncm::ResultPlaceHolderAlreadyExists())
- } R_END_TRY_CATCH;
- }
+ MakeContentPath(std::addressof(content_path), old_content_id, this->make_content_path_func, this->root_path);
+
+ R_TRY_CATCH(fs::RenameFile(content_path, placeholder_path)) {
+ R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
+ R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists())
+ } R_END_TRY_CATCH;
return ResultSuccess();
}
Result ContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, u64 size) {
R_TRY(this->EnsureEnabled());
- R_TRY(this->placeholder_accessor.SetSize(placeholder_id, size));
+ R_TRY(this->placeholder_accessor.SetPlaceHolderFileSize(placeholder_id, size));
return ResultSuccess();
}
@@ -365,10 +504,11 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled());
PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
+ MakeContentPath(std::addressof(content_path), content_id, this->make_content_path_func, this->root_path);
- R_TRY(this->OpenCachedContentFile(content_id));
- R_TRY(fs::ReadFile(this->content_cache_file_handle, offset, buf.GetPointer(), buf.GetSize()));
+ R_TRY(this->OpenContentIdFile(content_id));
+
+ R_TRY(fs::ReadFile(this->cached_file_handle, offset, buf.GetPointer(), buf.GetSize()));
return ResultSuccess();
}
@@ -383,17 +523,10 @@ namespace ams::ncm {
Result ContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out out_rights_id, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
- PathString placeholder_path;
- this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
+ Path path;
+ R_TRY(this->GetPlaceHolderPath(std::addressof(path), placeholder_id));
- Path common_path;
- R_TRY(fs::ConvertToFsCommonPath(common_path.str, ams::fs::EntryNameLengthMax, placeholder_path));
-
- ncm::RightsId rights_id;
- R_TRY(GetRightsId(&rights_id, common_path));
- out_rights_id.SetValue(rights_id);
-
- return ResultSuccess();
+ return GetRightsId(out_rights_id.GetPointer(), path);
}
Result ContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out out_rights_id, ContentId content_id) {
@@ -410,17 +543,13 @@ namespace ams::ncm {
return ResultSuccess();
}
- PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
-
- Path common_path;
- R_TRY(fs::ConvertToFsCommonPath(common_path.str, ams::fs::EntryNameLengthMax, content_path));
+ Path path;
+ R_TRY(this->GetPath(std::addressof(path), content_id));
ncm::RightsId rights_id;
- R_TRY(GetRightsId(&rights_id, common_path));
+ R_TRY(GetRightsId(std::addressof(rights_id), path));
this->rights_id_cache->Store(content_id, rights_id);
- /* Set output. */
out_rights_id.SetValue(rights_id);
return ResultSuccess();
}
@@ -430,39 +559,33 @@ namespace ams::ncm {
R_UNLESS(offset <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
- bool is_development = false;
+ AMS_ABORT_UNLESS(spl::IsDevelopmentHardware());
- AMS_ABORT_UNLESS(R_SUCCEEDED(splIsDevelopment(&is_development)));
- AMS_ABORT_UNLESS(is_development);
+ this->InvalidateFileCache();
- this->ClearContentCache();
+ PathString path;
+ MakeContentPath(std::addressof(path), content_id, this->make_content_path_func, this->root_path);
- PathString content_path;
- this->GetContentPath(std::addressof(content_path), content_id);
+ fs::FileHandle file;
+ R_TRY(fs::OpenFile(std::addressof(file), path.Get(), fs::OpenMode_Write));
+ ON_SCOPE_EXIT { fs::CloseFile(file); };
- FILE *f = nullptr;
- R_TRY(fs::OpenFile(&f, content_path, FsOpenMode_Write));
-
- ON_SCOPE_EXIT {
- fclose(f);
- };
-
- R_TRY(fs::WriteFile(f, offset, data.GetPointer(), data.GetSize(), ams::fs::WriteOption::Flush));
+ R_TRY(fs::WriteFile(file, offset, data.GetPointer(), data.GetSize(), fs::WriteOption::Flush));
return ResultSuccess();
}
Result ContentStorageImpl::GetFreeSpaceSize(sf::Out out_size) {
- struct statvfs st = {0};
- R_UNLESS(statvfs(this->root_path, &st) != -1, fsdevGetLastResult());
- out_size.SetValue(st.f_bfree);
+ s64 size;
+ R_TRY(fs::GetFreeSpaceSize(std::addressof(size), this->root_path));
+ out_size.SetValue(size);
return ResultSuccess();
}
Result ContentStorageImpl::GetTotalSpaceSize(sf::Out out_size) {
- struct statvfs st = {0};
- R_UNLESS(statvfs(this->root_path, &st) != -1, fsdevGetLastResult());
- out_size.SetValue(st.f_blocks);
+ s64 size;
+ R_TRY(fs::GetTotalSpaceSize(std::addressof(size), this->root_path));
+ out_size.SetValue(size);
return ResultSuccess();
}
@@ -474,37 +597,39 @@ namespace ams::ncm {
Result ContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out out_size, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
- bool found_in_cache = false;
- size_t size = 0;
-
- R_TRY(this->placeholder_accessor.GetSize(&found_in_cache, &size, placeholder_id));
-
- if (found_in_cache) {
- out_size.SetValue(size);
+ bool found = false;
+ s64 file_size = 0;
+ R_TRY(this->placeholder_accessor.TryGetPlaceHolderFileSize(std::addressof(found), std::addressof(file_size), placeholder_id));
+ if (found) {
+ out_size.SetValue(file_size);
return ResultSuccess();
}
PathString placeholder_path;
- struct stat st;
-
this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
- R_UNLESS(stat(placeholder_path, &st) != -1, fsdevGetLastResult());
- out_size.SetValue(st.st_size);
+ fs::FileHandle file;
+ R_TRY(fs::OpenFile(std::addressof(file), placeholder_path, fs::OpenMode_Read));
+ ON_SCOPE_EXIT { fs::CloseFile(file); };
+
+ R_TRY(fs::GetFileSize(std::addressof(file_size), file));
+
+ out_size.SetValue(file_size);
return ResultSuccess();
}
Result ContentStorageImpl::RepairInvalidFileAttribute() {
- PathString content_root_path;
- this->GetContentRootPath(std::addressof(content_root_path));
- unsigned int dir_depth = this->GetContentDirectoryDepth();
- auto fix_file_attributes = [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, struct dirent *dir_entry) {
+ /* Callback for TraverseDirectory */
+ using PathChecker = bool (*)(const char *);
+ PathChecker path_checker = nullptr;
+
+ auto fix_file_attributes = [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) {
*should_retry_dir_read = false;
*should_continue = true;
- if (dir_entry->d_type == DT_DIR) {
- if (path::IsNcaPath(current_path)) {
- if (R_SUCCEEDED(fsdevSetConcatenationFileAttribute(current_path))) {
+ if (entry.type == fs::DirectoryEntryType_Directory) {
+ if (path_checker(current_path)) {
+ if (R_SUCCEEDED(fs::SetConcatenationFileAttribute(current_path))) {
*should_retry_dir_read = true;
}
}
@@ -513,14 +638,24 @@ namespace ams::ncm {
return ResultSuccess();
};
- R_TRY(fs::TraverseDirectory(content_root_path, dir_depth, fix_file_attributes));
+ /* Fix Content */
+ {
+ path_checker = IsContentPath;
+ PathString path;
+ MakeBaseContentDirectoryPath(std::addressof(path), this->root_path);
- PathString placeholder_root_path;
+ R_TRY(TraverseDirectory(path, GetHierarchicalContentDirectoryDepth(this->make_content_path_func), fix_file_attributes));
+ }
+
+ /* Fix placeholder. */
this->placeholder_accessor.InvalidateAll();
- this->placeholder_accessor.MakeRootPath(std::addressof(placeholder_root_path));
- dir_depth = this->placeholder_accessor.GetDirectoryDepth();
+ {
+ path_checker = IsPlaceHolderPath;
+ PathString path;
+ PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), this->root_path);
- R_TRY(fs::TraverseDirectory(placeholder_root_path, dir_depth, fix_file_attributes));
+ R_TRY(TraverseDirectory(path, GetHierarchicalContentDirectoryDepth(this->make_content_path_func), fix_file_attributes));
+ }
return ResultSuccess();
}
@@ -536,7 +671,7 @@ namespace ams::ncm {
this->placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
Path common_path;
- R_TRY(fs::ConvertToFsCommonPath(common_path.str, ams::fs::EntryNameLengthMax, placeholder_path));
+ R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), placeholder_path));
ncm::RightsId rights_id;
R_TRY(GetRightsId(&rights_id, common_path));
@@ -544,7 +679,6 @@ namespace ams::ncm {
/* Set output. */
out_rights_id.SetValue(rights_id);
-
return ResultSuccess();
}
diff --git a/stratosphere/ncm/source/ncm_content_storage_impl.hpp b/stratosphere/ncm/source/ncm_content_storage_impl.hpp
index c7c3afbb6..582d7807d 100644
--- a/stratosphere/ncm/source/ncm_content_storage_impl.hpp
+++ b/stratosphere/ncm/source/ncm_content_storage_impl.hpp
@@ -18,38 +18,30 @@
#include
#include
-#include "impl/ncm_placeholder_accessor.hpp"
-#include "impl/ncm_rights_cache.hpp"
+#include "ncm_placeholder_accessor.hpp"
+#include "ncm_rights_cache.hpp"
#include "ncm_content_storage_impl_base.hpp"
-#include "ncm_path_utils.hpp"
+#include "ncm_fs_utils.hpp"
namespace ams::ncm {
class ContentStorageImpl : public ContentStorageImplBase {
protected:
- impl::PlaceHolderAccessor placeholder_accessor;
+ PlaceHolderAccessor placeholder_accessor;
ContentId cached_content_id;
- FILE *content_cache_file_handle;
- impl::RightsIdCache *rights_id_cache;
+ fs::FileHandle cached_file_handle;
+ RightsIdCache *rights_id_cache;
+ public:
+ static Result InitializeBase(const char *root_path);
+ static Result CleanupBase(const char *root_path);
+ static Result VerifyBase(const char *root_path);
public:
~ContentStorageImpl();
- Result Initialize(const char *root_path, MakeContentPathFunc content_path_func, MakePlaceHolderPathFunc placeholder_path_func, bool delay_flush, impl::RightsIdCache *rights_id_cache);
- void Finalize();
+ Result Initialize(const char *root_path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache);
private:
- void ClearContentCache();
- unsigned int GetContentDirectoryDepth();
- Result OpenCachedContentFile(ContentId content_id);
-
- inline void GetContentRootPath(PathString *content_root) {
- path::GetContentRootPath(content_root, this->root_path);
- }
-
- inline void GetContentPath(PathString *content_path, ContentId content_id) {
- PathString root_path;
- this->GetContentRootPath(std::addressof(root_path));
- this->make_content_path_func(content_path, content_id, root_path);
- }
+ Result OpenContentIdFile(ContentId content_id);
+ void InvalidateFileCache();
public:
virtual Result GeneratePlaceHolderId(sf::Out out) override;
virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, u64 size) override;
diff --git a/stratosphere/ncm/source/ncm_content_storage_impl_base.hpp b/stratosphere/ncm/source/ncm_content_storage_impl_base.hpp
index 03f59caef..4e92ecd55 100644
--- a/stratosphere/ncm/source/ncm_content_storage_impl_base.hpp
+++ b/stratosphere/ncm/source/ncm_content_storage_impl_base.hpp
@@ -24,21 +24,21 @@ namespace ams::ncm {
NON_MOVEABLE(ContentStorageImplBase);
protected:
PathString root_path;
- MakeContentPathFunc make_content_path_func;
+ MakeContentPathFunction make_content_path_func;
bool disabled;
protected:
ContentStorageImplBase() { /* ... */ }
protected:
- Result EnsureEnabled() {
+ Result EnsureEnabled() const {
R_UNLESS(!this->disabled, ncm::ResultInvalidContentStorage());
return ResultSuccess();
}
static Result GetRightsId(ncm::RightsId *out_rights_id, const Path &path) {
if (hos::GetVersion() >= hos::Version_300) {
- R_TRY(ams::fs::GetRightsId(std::addressof(out_rights_id->id), std::addressof(out_rights_id->key_generation), path.str));
+ R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), std::addressof(out_rights_id->key_generation), path.str));
} else {
- R_TRY(ams::fs::GetRightsId(std::addressof(out_rights_id->id), path.str));
+ R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), path.str));
out_rights_id->key_generation = 0;
}
return ResultSuccess();
diff --git a/stratosphere/ncm/source/ncm_fs.cpp b/stratosphere/ncm/source/ncm_fs.cpp
deleted file mode 100644
index def06f20e..000000000
--- a/stratosphere/ncm/source/ncm_fs.cpp
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (c) 2019-2020 Adubbz, 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