From 0e81eac9d1f260a842c230b027efc5b2fcae9a8f Mon Sep 17 00:00:00 2001
From: Michael Scire <SciresM@gmail.com>
Date: Tue, 26 Oct 2021 00:51:44 -0700
Subject: [PATCH] sprof: update for 13.1.0 (format version 1)

---
 .../source/sprofile/srv/sprofile_srv_api.cpp  |  6 +-
 .../srv/sprofile_srv_i_profile_importer.hpp   |  6 +-
 .../srv/sprofile_srv_i_profile_reader.hpp     |  2 +-
 .../sprofile_srv_i_service_for_bg_agent.hpp   |  8 +-
 .../srv/sprofile_srv_profile_importer.hpp     |  4 +-
 .../sprofile_srv_profile_importer_impl.cpp    |  8 +-
 .../sprofile_srv_profile_importer_impl.hpp    |  4 +-
 .../srv/sprofile_srv_profile_manager.cpp      | 72 ++++++++++-----
 .../srv/sprofile_srv_service_for_bg_agent.cpp |  2 +-
 .../srv/sprofile_srv_service_for_bg_agent.hpp |  2 +-
 .../sprofile/srv/sprofile_srv_types.hpp       | 88 ++++++++++---------
 .../vapours/results/sprofile_results.hpp      |  2 +
 12 files changed, 119 insertions(+), 85 deletions(-)

diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_api.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_api.cpp
index 216ae2b53..f08498037 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_api.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_api.cpp
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
         constexpr const sm::ServiceName ServiceNameForSystemProcess = sm::ServiceName::Encode("sprof:sp");
 
         constexpr inline size_t BgAgentSessionCountMax       = 2;
-        constexpr inline size_t SystemProcessSessionCountMax = 5;
+        constexpr inline size_t SystemProcessSessionCountMax = 10;
 
         constexpr inline size_t SessionCountMax = BgAgentSessionCountMax + SystemProcessSessionCountMax;
 
@@ -42,8 +42,8 @@ namespace ams::sprofile::srv {
 
         struct ServerManagerOptions {
             static constexpr size_t PointerBufferSize   = 0x0;
-            static constexpr size_t MaxDomains          = SessionCountMax; /* NOTE: Official is 3 */
-            static constexpr size_t MaxDomainObjects    = 16;              /* NOTE: Official is 8 */
+            static constexpr size_t MaxDomains          = SessionCountMax; /* NOTE: Official is 9 */
+            static constexpr size_t MaxDomainObjects    = 16;              /* NOTE: Official is 14 */
             static constexpr bool CanDeferInvokeRequest = false;
             static constexpr bool CanManageMitmServers  = false;
         };
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_importer.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_importer.hpp
index 4858abc95..556ebfe45 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_importer.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_importer.hpp
@@ -18,8 +18,8 @@
 #include "sprofile_srv_types.hpp"
 
 #define AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO(C, H) \
-    AMS_SF_METHOD_INFO(C, H, 0, Result, ImportProfile,  (const sprofile::srv::ProfileDataForImportData &data),         (data)) \
-    AMS_SF_METHOD_INFO(C, H, 1, Result, Commit,         (),                                                            ())     \
-    AMS_SF_METHOD_INFO(C, H, 0, Result, ImportMetadata, (const sprofile::srv::ProfileMetadataForImportMetadata &data), (data)) \
+    AMS_SF_METHOD_INFO(C, H, 0, Result, ImportProfile,  (const sprofile::srv::ProfileDataForImportData &import),         (import)) \
+    AMS_SF_METHOD_INFO(C, H, 1, Result, Commit,         (),                                                              ())     \
+    AMS_SF_METHOD_INFO(C, H, 2, Result, ImportMetadata, (const sprofile::srv::ProfileMetadataForImportMetadata &import), (import)) \
 
 AMS_SF_DEFINE_INTERFACE(ams::sprofile, IProfileImporter, AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO)
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_reader.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_reader.hpp
index 76d8310ef..ca1bc7153 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_reader.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_profile_reader.hpp
@@ -21,6 +21,6 @@
     AMS_SF_METHOD_INFO(C, H, 1, Result, GetUnsigned64, (sf::Out<u64> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key)) \
     AMS_SF_METHOD_INFO(C, H, 2, Result, GetSigned32,   (sf::Out<s32> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key)) \
     AMS_SF_METHOD_INFO(C, H, 3, Result, GetUnsigned32, (sf::Out<u32> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key)) \
-    AMS_SF_METHOD_INFO(C, H, 3, Result, GetByte,       (sf::Out<u8> out, sprofile::Identifier profile, sprofile::Identifier key),  (out, profile, key))
+    AMS_SF_METHOD_INFO(C, H, 4, Result, GetByte,       (sf::Out<u8> out, sprofile::Identifier profile, sprofile::Identifier key),  (out, profile, key))
 
 AMS_SF_DEFINE_INTERFACE(ams::sprofile, IProfileReader, AMS_SPROFILE_I_PROFILE_READER_INTERFACE_INFO)
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_service_for_bg_agent.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_service_for_bg_agent.hpp
index e90cabe9d..5e9612d91 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_service_for_bg_agent.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_i_service_for_bg_agent.hpp
@@ -18,9 +18,9 @@
 #include "sprofile_srv_i_profile_importer.hpp"
 
 #define AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO(C, H) \
-    AMS_SF_METHOD_INFO(C, H, 100,  Result, OpenProfileImporter, (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out),                                                                 (out)) \
-    AMS_SF_METHOD_INFO(C, H, 200,  Result, ReadMetadata,        (sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg), (out_count, out, arg)) \
-    AMS_SF_METHOD_INFO(C, H, 201,  Result, IsUpdateNeeded,      (sf::Out<bool> out, sprofile::Identifier revision_key),                                                                              (out, revision_key)) \
-    AMS_SF_METHOD_INFO(C, H, 2000, Result, Reset,               (),                                                                                                                                  ())
+    AMS_SF_METHOD_INFO(C, H, 100,  Result, OpenProfileImporter,  (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out),                                                                                    (out)) \
+    AMS_SF_METHOD_INFO(C, H, 200,  Result, GetMetadataEntryData, (sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileMetadataEntryData> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg), (out_count, out, arg)) \
+    AMS_SF_METHOD_INFO(C, H, 201,  Result, IsUpdateNeeded,       (sf::Out<bool> out, sprofile::Identifier revision_key),                                                                                                 (out, revision_key)) \
+    AMS_SF_METHOD_INFO(C, H, 2000, Result, Reset,                (),                                                                                                                                                     ())
 
 AMS_SF_DEFINE_INTERFACE(ams::sprofile, ISprofileServiceForBgAgent, AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO)
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp
index fe91c19a2..6ed8ae2b6 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp
@@ -38,7 +38,7 @@ namespace ams::sprofile::srv {
             bool HasProfile(Identifier id0, Identifier id1) {
                 /* Require that we have metadata. */
                 if (m_metadata.has_value()) {
-                    for (auto i = 0u; i < m_metadata->num_entries; ++i) {
+                    for (auto i = 0u; i < std::min<size_t>(m_metadata->num_entries, util::size(m_metadata->entries)); ++i) {
                         const auto &entry = m_metadata->entries[i];
                         if (entry.identifier_0 == id0 && entry.identifier_1 == id1) {
                             return true;
@@ -63,7 +63,7 @@ namespace ams::sprofile::srv {
                 m_revision_key = meta.revision_key;
 
                 /* Import all profiles. */
-                for (auto i = 0u; i < meta.num_entries; ++i) {
+                for (auto i = 0u; i < std::min<size_t>(meta.num_entries, util::size(meta.entries)); ++i) {
                     const auto &import_entry = meta.entries[i];
                     if (!this->HasProfile(import_entry.identifier_0, import_entry.identifier_1)) {
                         m_importing_profiles[m_importing_count++] = import_entry.identifier_0;
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.cpp
index 80bfe3070..8535c5604 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.cpp
@@ -19,16 +19,16 @@
 
 namespace ams::sprofile::srv {
 
-    Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
-        return m_manager->ImportProfile(data);
+    Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
+        return m_manager->ImportProfile(import);
     }
 
     Result ProfileImporterImpl::Commit() {
         return m_manager->Commit();
     }
 
-    Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
-        return m_manager->ImportMetadata(data);
+    Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import) {
+        return m_manager->ImportMetadata(import);
     }
 
 }
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.hpp
index 9e961c788..6ffea3a6c 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.hpp
@@ -27,9 +27,9 @@ namespace ams::sprofile::srv {
         public:
             ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
         public:
-            Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
+            Result ImportProfile(const sprofile::srv::ProfileDataForImportData &import);
             Result Commit();
-            Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
+            Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import);
     };
     static_assert(IsIProfileImporter<ProfileImporterImpl>);
 
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp
index a9579b9f5..899bf3318 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp
@@ -151,7 +151,7 @@ namespace ams::sprofile::srv {
         return ResultSuccess();
     }
 
-    Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
+    Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
         /* Acquire locks. */
         std::scoped_lock lk1(m_profile_importer_mutex);
         std::scoped_lock lk2(m_fs_mutex);
@@ -159,25 +159,37 @@ namespace ams::sprofile::srv {
         /* Check that we have an importer. */
         R_UNLESS(m_profile_importer.has_value(), sprofile::ResultInvalidState());
 
-        /* Check that the metadata we're importing is valid. */
-        R_UNLESS(data.data.version == ProfileDataVersion, sprofile::ResultInvalidDataVersion());
+        /* Check that the metadata we're importing is a valid version. */
+        R_UNLESS(IsValidProfileFormatVersion(import.header.version), sprofile::ResultInvalidDataVersion());
+
+        /* Check that the metadata we're importing has a valid hash. */
+        {
+            crypto::Md5Generator md5;
+            md5.Update(std::addressof(import.header), sizeof(import.header));
+            md5.Update(std::addressof(import.data), sizeof(import.data) - sizeof(import.data.entries[0]) * (util::size(import.data.entries) - std::min<size_t>(import.data.num_entries, util::size(import.data.entries))));
+
+            u8 hash[crypto::Md5Generator::HashSize];
+            md5.GetHash(hash, sizeof(hash));
+
+            R_UNLESS(crypto::IsSameBytes(hash, import.hash, sizeof(hash)), sprofile::ResultInvalidDataHash());
+        }
 
         /* Succeed if we already have the profile. */
-        R_SUCCEED_IF(m_profile_importer->HasProfile(data.identifier_0, data.identifier_1));
+        R_SUCCEED_IF(m_profile_importer->HasProfile(import.header.identifier_0, import.header.identifier_1));
 
         /* Check that we're importing the profile. */
-        R_UNLESS(m_profile_importer->CanImportProfile(data.identifier_0), sprofile::ResultInvalidState());
+        R_UNLESS(m_profile_importer->CanImportProfile(import.header.identifier_0), sprofile::ResultInvalidState());
 
         /* Create temporary directories. */
         R_TRY(this->EnsureTemporaryDirectories());
 
         /* Create profile. */
         char path[0x30];
-        CreateTemporaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, data.identifier_0);
-        R_TRY(WriteFile(path, std::addressof(data.data), sizeof(data.data)));
+        CreateTemporaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, import.header.identifier_0);
+        R_TRY(WriteFile(path, std::addressof(import.data), sizeof(import.data)));
 
         /* Set profile imported. */
-        m_profile_importer->OnImportProfile(data.identifier_0);
+        m_profile_importer->OnImportProfile(import.header.identifier_0);
         return ResultSuccess();
     }
 
@@ -231,7 +243,7 @@ namespace ams::sprofile::srv {
         return ResultSuccess();
     }
 
-    Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
+    Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import) {
         /* Acquire locks. */
         std::scoped_lock lk1(m_profile_importer_mutex);
         std::scoped_lock lk2(m_fs_mutex);
@@ -240,8 +252,21 @@ namespace ams::sprofile::srv {
         R_UNLESS(m_profile_importer.has_value(),          sprofile::ResultInvalidState());
         R_UNLESS(m_profile_importer->CanImportMetadata(), sprofile::ResultInvalidState());
 
-        /* Check that the metadata we're importing is valid. */
-        R_UNLESS(data.metadata.version == ProfileMetadataVersion, sprofile::ResultInvalidMetadataVersion());
+        /* Check that the metadata we're importing is a valid version. */
+        R_UNLESS(IsValidProfileFormatVersion(import.header.version), sprofile::ResultInvalidMetadataVersion());
+
+        /* Check that the metadata we're importing has a valid hash. */
+        {
+            crypto::Md5Generator md5;
+            md5.Update(std::addressof(import.header), sizeof(import.header));
+            md5.Update(std::addressof(import.metadata), sizeof(import.metadata));
+            md5.Update(std::addressof(import.entries), sizeof(import.entries[0]) * std::min<size_t>(import.metadata.num_entries, util::size(import.metadata.entries)));
+
+            u8 hash[crypto::Md5Generator::HashSize];
+            md5.GetHash(hash, sizeof(hash));
+
+            R_UNLESS(crypto::IsSameBytes(hash, import.hash, sizeof(hash)), sprofile::ResultInvalidMetadataHash());
+        }
 
         /* Create temporary directories. */
         R_TRY(this->EnsureTemporaryDirectories());
@@ -249,10 +274,10 @@ namespace ams::sprofile::srv {
         /* Create metadata. */
         char path[0x30];
         CreateTemporaryMetadataPath(path, sizeof(path), m_save_data_info.mount_name);
-        R_TRY(WriteFile(path, std::addressof(data.metadata), sizeof(data.metadata)));
+        R_TRY(WriteFile(path, std::addressof(import.metadata), sizeof(import.metadata)));
 
         /* Import the metadata. */
-        m_profile_importer->ImportMetadata(data.metadata);
+        m_profile_importer->ImportMetadata(import.metadata);
         return ResultSuccess();
     }
 
@@ -309,13 +334,13 @@ namespace ams::sprofile::srv {
         std::scoped_lock lk2(m_general_mutex);
 
         /* Load the desired profile. */
-        R_TRY(this->LoadProfile(profile));
-
-        /* Find the specified key. */
-        for (auto i = 0u; i < m_service_profile->data.num_entries; ++i) {
-            if (m_service_profile->data.entries[i].key == key) {
-                *out = m_service_profile->data.entries[i];
-                return ResultSuccess();
+        if (R_SUCCEEDED(this->LoadProfile(profile))) {
+            /* Find the specified key. */
+            for (auto i = 0u; i < std::min<size_t>(m_service_profile->data.num_entries, util::size(m_service_profile->data.entries)); ++i) {
+                if (m_service_profile->data.entries[i].key == key) {
+                    *out = m_service_profile->data.entries[i];
+                    return ResultSuccess();
+                }
             }
         }
 
@@ -426,6 +451,8 @@ namespace ams::sprofile::srv {
     }
 
     void ProfileManager::OnCommitted() {
+        /* TODO: Here, Nintendo sets the erpt ServiceProfileRevisionKey to the current revision key. */
+
         /* If we need to, invalidate the loaded service profile. */
         if (m_service_profile.has_value()) {
             for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
@@ -436,8 +463,6 @@ namespace ams::sprofile::srv {
             }
         }
 
-        /* TODO: Here, Nintendo sets the erpt ServiceProfileRevisionKey to the current revision key. */
-
         /* Reset profile metadata. */
         m_profile_metadata = util::nullopt;
 
@@ -445,6 +470,9 @@ namespace ams::sprofile::srv {
         for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
             m_update_observer_manager.OnUpdate(m_profile_importer->GetImportingProfile(i));
         }
+
+        /* Reset profile importer. */
+        m_profile_importer = util::nullopt;
     }
 
     Result ProfileManager::EnsurePrimaryDirectories() {
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.cpp
index 2c8bb0d44..62653d13a 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.cpp
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
         return ResultSuccess();
     }
 
-    Result ServiceForBgAgent::ReadMetadata(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg) {
+    Result ServiceForBgAgent::GetMetadataEntryData(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileMetadataEntryData> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg) {
         /* Check size. */
         R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
 
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.hpp
index 986223d66..d7508a250 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_bg_agent.hpp
@@ -29,7 +29,7 @@ namespace ams::sprofile::srv {
             constexpr ServiceForBgAgent(MemoryResource *mr, ProfileManager *pm) : m_memory_resource(mr), m_profile_manager(pm) { /* ... */ }
         public:
             Result OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out);
-            Result ReadMetadata(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg);
+            Result GetMetadataEntryData(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileMetadataEntryData> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg);
             Result IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key);
             Result Reset();
     };
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp
index 421ec2b69..43c9e28f0 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp
@@ -18,34 +18,11 @@
 
 namespace ams::sprofile::srv {
 
-    struct ProfileMetadataEntry {
-        Identifier identifier_0;
-        Identifier identifier_1;
-        u8 unk_0E[0x32];
-    };
-    static_assert(util::is_pod<ProfileMetadataEntry>::value);
-    static_assert(sizeof(ProfileMetadataEntry) == 0x40);
+    constexpr inline const u32 ProfileFormatVersion = 1;
 
-    constexpr inline const u32 ProfileMetadataVersion = 0;
-
-    struct ProfileMetadata {
-        u32 version;
-        u32 num_entries;
-        Identifier revision_key;
-        u8 unk_10[0x30];
-        ProfileMetadataEntry entries[50];
-    };
-    static_assert(util::is_pod<ProfileMetadata>::value);
-    static_assert(sizeof(ProfileMetadata) == 0xCC0);
-
-    struct ProfileMetadataForImportMetadata : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
-        ProfileMetadata metadata;
-        u8 unk[0x8000 - sizeof(metadata)];
-    };
-    static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
-    static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000);
-
-    constexpr inline const u32 ProfileDataVersion = 0;
+    constexpr inline bool IsValidProfileFormatVersion(u32 version) {
+        return version == ProfileFormatVersion;
+    }
 
     enum ValueType : u8 {
         ValueType_Byte = 0,
@@ -70,9 +47,9 @@ namespace ams::sprofile::srv {
     static_assert(sizeof(ProfileDataEntry) == 0x10);
 
     struct ProfileData {
-        u32 version;
         u32 num_entries;
-        u8 unk_08[0x28];
+        u8 unk_04[0x0C];
+        u8 unk_10[0x20];
         ProfileDataEntry entries[(0x4000 - 0x30) / sizeof(ProfileDataEntry)];
     };
     static_assert(util::is_pod<ProfileData>::value);
@@ -86,28 +63,55 @@ namespace ams::sprofile::srv {
     static_assert(sizeof(ServiceProfile) == 0x4008);
 
     struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
-        Identifier identifier_0;
-        Identifier identifier_1;
-        u8 unk_0E[2];
+        struct {
+            Identifier identifier_0;
+            Identifier identifier_1;
+            u8 unk_0E[2];
+            u32 version;
+            u8 unk_14[0x1C];
+        } header;
+        u8 hash[crypto::Md5Generator::HashSize];
         ProfileData data;
-        u8 unk_4010[0x4400 - 0x4010];
+        u8 unk_4040[0x4400 - 0x4040];
     };
     static_assert(util::is_pod<ProfileDataForImportData>::value);
     static_assert(sizeof(ProfileDataForImportData) == 0x4400);
 
-    struct ReadMetadataEntry {
+    struct ProfileMetadataEntry {
+        Identifier identifier_0;
+        Identifier identifier_1;
+        u8 unk_0E[0x32];
+    };
+    static_assert(util::is_pod<ProfileMetadataEntry>::value);
+    static_assert(sizeof(ProfileMetadataEntry) == 0x40);
+
+    struct ProfileMetadataEntryData : public sf::PrefersMapAliasTransferMode {
         u8 unk[0x100];
     };
-    static_assert(util::is_pod<ReadMetadataEntry>::value);
-    static_assert(sizeof(ReadMetadataEntry) == 0x100);
+    static_assert(util::is_pod<ProfileMetadataEntryData>::value);
+    static_assert(sizeof(ProfileMetadataEntryData) == 0x100);
 
-    struct ReadMetadataArgument : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
-        ProfileMetadata metadata;
-        ReadMetadataEntry entries[(0x8000 - sizeof(metadata)) / sizeof(ReadMetadataEntry)];
-        u8 unk_7FC0[0x40];
+    struct ProfileMetadata {
+        u32 num_entries;
+        u32 unk_04;
+        Identifier revision_key;
+        u8 unk_10[0x30];
+        ProfileMetadataEntry entries[50];
     };
-    static_assert(util::is_pod<ReadMetadataArgument>::value);
-    static_assert(sizeof(ReadMetadataArgument) == 0x8000);
+    static_assert(util::is_pod<ProfileMetadata>::value);
+    static_assert(sizeof(ProfileMetadata) == 0xCC0);
 
+    struct ProfileMetadataForImportMetadata : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
+        struct {
+            u32 version;
+            u8 unk_04[0x1C];
+        } header;
+        u8 hash[crypto::Md5Generator::HashSize];
+        ProfileMetadata metadata;
+        ProfileMetadataEntryData entries[50];
+        u8 unk[0x8000 - 0x3EF0];
+    };
+    static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
+    static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000);
 
 }
diff --git a/libraries/libvapours/include/vapours/results/sprofile_results.hpp b/libraries/libvapours/include/vapours/results/sprofile_results.hpp
index dafa9dad7..8772bdb0c 100644
--- a/libraries/libvapours/include/vapours/results/sprofile_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sprofile_results.hpp
@@ -37,6 +37,8 @@ namespace ams::sprofile {
     R_DEFINE_ERROR_RESULT(MaxObservers,     623);
 
     R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
+    R_DEFINE_ERROR_RESULT(InvalidMetadataHash,    3211);
     R_DEFINE_ERROR_RESULT(InvalidDataVersion,     3230);
+    R_DEFINE_ERROR_RESULT(InvalidDataHash,        3231);
 
 }