diff --git a/libraries/libstratosphere/include/stratosphere/fssystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem.hpp
index de899e024..1a7c20e6d 100644
--- a/libraries/libstratosphere/include/stratosphere/fssystem.hpp
+++ b/libraries/libstratosphere/include/stratosphere/fssystem.hpp
@@ -17,6 +17,7 @@
#pragma once
#include "fssystem/fssystem_utility.hpp"
#include "fssystem/fssystem_external_code.hpp"
+#include "fssystem/fssystem_partition_file_system_meta.hpp"
#include "fssystem/fssystem_path_tool.hpp"
#include "fssystem/fssystem_subdirectory_filesystem.hpp"
#include "fssystem/fssystem_directory_redirection_filesystem.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp
new file mode 100644
index 000000000..ee8e89472
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp
@@ -0,0 +1,82 @@
+/*
+ * 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
+#include
+
+namespace ams::fssystem {
+
+ namespace impl {
+
+ struct PartitionFileSystemFormat {
+ #pragma pack(push, 1)
+ struct PartitionEntry {
+ u64 offset;
+ u64 size;
+ u32 name_offset;
+ u32 reserved;
+ };
+ static_assert(std::is_pod::value);
+ #pragma pack(pop)
+
+ static constexpr char VersionSignature[] = { 'P', 'F', 'S', '0' };
+
+ static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
+ static constexpr size_t FileDataAlignmentSize = 0x20;
+ };
+
+ }
+
+ template
+ class PartitionFileSystemMetaCore : public fs::impl::Newable {
+ public:
+ static constexpr size_t EntryNameLengthMax = Format::EntryNameLengthMax;
+ static constexpr size_t FileDataAlignmentSize = Format::FileDataAlignmentSize;
+
+ /* Forward declare header. */
+ struct PartitionFileSystemHeader;
+
+ using PartitionEntry = typename Format::PartitionEntry;
+ using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed;
+ protected:
+ bool initialized;
+ PartitionFileSystemHeader *header;
+ PartitionEntry *entries;
+ char *name_table;
+ size_t meta_data_size;
+ MemoryResource *allocator;
+ char *buffer;
+ public:
+ PartitionFileSystemMetaCore() { /* ... */ }
+ ~PartitionFileSystemMetaCore();
+
+ Result Initialize(fs::IStorage *storage, MemoryResource *allocator);
+ Result Initialize(fs::IStorage *storage, void *header, size_t header_size);
+
+ const PartitionEntry *GetEntry(s32 index) const;
+ s32 GetEntryCount() const;
+ s32 GetEntryIndex(const char *name) const;
+ const char *GetEntryName(s32 index) const;
+ size_t GetHeaderSize() const;
+ size_t GetMetaDataSize() const;
+ Result QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) const;
+ protected:
+ void DeallocateBuffer();
+ };
+
+ using PartitionFileSystemMeta = PartitionFileSystemMetaCore;
+
+}
diff --git a/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp
new file mode 100644
index 000000000..4e7aeb87a
--- /dev/null
+++ b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::fssystem {
+
+ template
+ struct PartitionFileSystemMetaCore::PartitionFileSystemHeader {
+ u32 signature;
+ s32 entry_count;
+ u32 name_table_size;
+ u32 reserved;
+ };
+
+ template
+ PartitionFileSystemMetaCore::~PartitionFileSystemMetaCore() {
+ this->DeallocateBuffer();
+ }
+
+ template
+ Result PartitionFileSystemMetaCore::Initialize(fs::IStorage *storage, MemoryResource *allocator) {
+ /* Determine the meta data size. */
+ R_TRY(this->QueryMetaDataSize(std::addressof(this->meta_data_size), storage));
+
+ /* Deallocate any old meta buffer and allocate a new one. */
+ this->DeallocateBuffer();
+ this->allocator = allocator;
+ this->buffer = reinterpret_cast(this->allocator->Allocate(this->meta_data_size));
+ R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaCore());
+
+ /* Perform regular initialization. */
+ return this->Initialize(storage, this->buffer, this->meta_data_size);
+ }
+
+ template
+ Result PartitionFileSystemMetaCore::Initialize(fs::IStorage *storage, void *meta, size_t meta_size) {
+ /* Validate size for header. */
+ R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader), fs::ResultInvalidSize());
+
+ /* Read the header. */
+ R_TRY(storage->Read(0, meta, sizeof(PartitionFileSystemHeader)));
+
+ /* Set and validate the header. */
+ this->header = reinterpret_cast(meta);
+ R_UNLESS(crypto::IsSameBytes(this->header, Format::VersionSignature, sizeof(Format::VersionSignature)), ResultSignatureVerificationFailed());
+
+ /* Setup entries and name table. */
+ const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
+ this->entries = reinterpret_cast(reinterpret_cast(meta) + sizeof(PartitionFileSystemHeader));
+ this->name_table = reinterpret_cast(meta) + sizeof(PartitionFileSystemHeader) + entries_size;
+
+ /* Validate size for header + entries + name table. */
+ R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSize());
+
+ /* Read entries and name table. */
+ R_TRY(storage->Read(sizeof(PartitionFileSystemHeader), this->entries, entries_size + this->header->name_table_size));
+
+ /* Mark as initialized. */
+ this->initialized = true;
+ return ResultSuccess();
+ }
+
+ template
+ void PartitionFileSystemMetaCore::DeallocateBuffer() {
+ if (this->buffer != nullptr) {
+ AMS_ABORT_UNLESS(this->allocator != nullptr);
+ this->allocator->Deallocate(this->buffer, this->meta_data_size);
+ this->buffer = nullptr;
+ }
+ }
+
+ template
+ const typename Format::PartitionEntry *PartitionFileSystemMetaCore::GetEntry(s32 index) const {
+ if (this->initialized && index >= 0 && index < this->header->entry_count) {
+ return std::addressof(this->entries[index]);
+ }
+ return nullptr;
+ }
+
+ template
+ s32 PartitionFileSystemMetaCore::GetEntryCount() const {
+ if (this->initialized) {
+ return this->header->entry_count;
+ }
+ return 0;
+ }
+
+ template
+ s32 PartitionFileSystemMetaCore::GetEntryIndex(const char *name) const {
+ if (this->initialized) {
+ for (s32 i = 0; i < this->header->entry_count; i++) {
+ const auto &entry = this->entries[i];
+
+ /* Name offset is invalid. */
+ if (entry.name_offset >= this->header->name_table_size) {
+ return 0;
+ }
+
+ /* Compare to input name. */
+ const s32 max_count = this->header->name_table_size - entry.name_offset;
+ if (std::strncmp(std::addressof(this->name_table[entry.name_offset]), name, max_count) == 0) {
+ return i;
+ }
+ }
+
+ /* Not found. */
+ return -1;
+ }
+
+ return 0;
+ }
+
+ template
+ const char *PartitionFileSystemMetaCore::GetEntryName(s32 index) const {
+ if (this->initialized && index < this->header->entry_count) {
+ return std::addressof(this->name_table[this->GetEntry(index)->name_offset]);
+ }
+ return nullptr;
+ }
+
+ template
+ size_t PartitionFileSystemMetaCore::GetHeaderSize() const {
+ return sizeof(PartitionFileSystemHeader);
+ }
+
+ template
+ size_t PartitionFileSystemMetaCore::GetMetaDataSize() const {
+ return this->meta_data_size;
+ }
+
+ template
+ Result PartitionFileSystemMetaCore::QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) const {
+ AMS_ABORT_UNLESS(allocator != nullptr);
+
+ /* Read and validate the header. */
+ PartitionFileSystemHeader header;
+ R_TRY(storage->Read(0, std::addressof(header), sizeof(PartitionFileSystemHeader)));
+ R_UNLESS(crypto::IsSameBytes(std::addressof(header), Format::VersionSignature, sizeof(Format::VersionSignature)), ResultSignatureVerificationFailed());
+
+ /* Output size. */
+ *out_size = sizeof(PartitionFileSystemHeader) + header.entry_count * sizeof(typename Format::PartitionEntry) + header.name_table_size;
+ return ResultSuccess();
+ }
+
+ template class PartitionFileSystemMetaCore;
+
+}
diff --git a/libraries/libvapours/include/vapours.hpp b/libraries/libvapours/include/vapours.hpp
index d51797e3a..9da48965b 100644
--- a/libraries/libvapours/include/vapours.hpp
+++ b/libraries/libvapours/include/vapours.hpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
diff --git a/libraries/libvapours/include/vapours/allocator.hpp b/libraries/libvapours/include/vapours/allocator.hpp
new file mode 100644
index 000000000..446706b56
--- /dev/null
+++ b/libraries/libvapours/include/vapours/allocator.hpp
@@ -0,0 +1,58 @@
+/*
+ * 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
+#include
+
+namespace ams {
+
+ constexpr inline size_t DefaultAlignment = alignof(max_align_t);
+
+ using AllocateFunction = void *(*)(size_t);
+ using AllocateFunctionWithUserData = void *(*)(size_t, void *);
+ using AlignedAllocateFunction = void *(*)(size_t, size_t);
+ using AlignedAllocateFunctionWithUserData = void *(*)(size_t, size_t, void *);
+ using DeallocateFunction = void (*)(void *, size_t);
+ using FreeFunction = void (*)(void *);
+ using FreeFunctionWithUserData = void (*)(void *, void *);
+
+ class MemoryResource {
+ public:
+ ALWAYS_INLINE void *allocate(size_t size, size_t alignment = DefaultAlignment) {
+ return this->AllocateImpl(size, alignment);
+ }
+ ALWAYS_INLINE void deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) {
+ this->DeallocateImpl(buffer, size, alignment);
+ }
+ ALWAYS_INLINE bool is_equal(const MemoryResource &resource) const {
+ return this->IsEqualImpl(resource);
+ }
+ ALWAYS_INLINE void *Allocate(size_t size, size_t alignment = DefaultAlignment) {
+ return this->AllocateImpl(size, alignment);
+ }
+ ALWAYS_INLINE void Deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) {
+ this->DeallocateImpl(buffer, size, alignment);
+ }
+ ALWAYS_INLINE bool IsEqual(const MemoryResource &resource) const {
+ return this->IsEqualImpl(resource);
+ }
+ protected:
+ virtual void *AllocateImpl(size_t size, size_t alignment) = 0;
+ virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) = 0;
+ virtual bool IsEqualImpl(const MemoryResource &resource) const = 0;
+ };
+
+}
diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp
index 2f5d74fd6..76f77445d 100644
--- a/libraries/libvapours/include/vapours/results/fs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/fs_results.hpp
@@ -75,6 +75,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249);
R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321);
+ R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaCore, 3350);
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352);
R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355);
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);