diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.cpp
index c2914af1f..e0d1fae4e 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.cpp
@@ -54,4 +54,31 @@ namespace ams::sprofile::srv {
return fs::WriteFile(file, 0, src, size, fs::WriteOption::Flush);
}
+ Result MoveFile(const char *src_path, const char *dst_path) {
+ /* Require that the source path is a file. */
+ {
+ fs::DirectoryEntryType type;
+ R_ABORT_UNLESS(fs::GetEntryType(std::addressof(type), src_path));
+ AMS_ABORT_UNLESS(type == fs::DirectoryEntryType_File);
+ }
+
+ /* Delete the destination file. */
+ R_TRY_CATCH(fs::DeleteFile(dst_path)) {
+ R_CATCH(fs::ResultPathNotFound) { /* It's okay if the dst path doesn't exist. */ }
+ } R_END_TRY_CATCH;
+
+ /* Move the source file to the destination file. */
+ R_TRY(fs::RenameFile(src_path, dst_path));
+
+ return ResultSuccess();
+ }
+
+ Result EnsureDirectory(const char *path) {
+ R_TRY_CATCH(fs::CreateDirectory(path)) {
+ R_CATCH(fs::ResultPathAlreadyExists) { /* It's okay if the directory already exists. */ }
+ } R_END_TRY_CATCH;
+
+ return ResultSuccess();
+ }
+
}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.hpp
index 19bb8229c..8e420bf17 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.hpp
@@ -20,6 +20,8 @@ namespace ams::sprofile::srv {
Result ReadFile(const char *path, void *dst, size_t size, s64 offset);
Result WriteFile(const char *path, const void *src, size_t size);
- Result MoveFile(const char *dst_path, const char *src_path);
+ Result MoveFile(const char *src_path, const char *dst_path);
+
+ Result EnsureDirectory(const char *path);
}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_controller_for_debug_impl.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_controller_for_debug_impl.cpp
new file mode 100644
index 000000000..dec66ad3d
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_controller_for_debug_impl.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "sprofile_srv_profile_manager.hpp"
+#include "sprofile_srv_profile_controller_for_debug_impl.hpp"
+
+namespace ams::sprofile::srv {
+
+ Result ProfileControllerForDebugImpl::Reset() {
+ return m_manager->ResetSaveData();
+ }
+
+ Result ProfileControllerForDebugImpl::GetRaw(sf::Out out_type, sf::Out out_value, sprofile::Identifier profile, sprofile::Identifier key) {
+ return m_manager->GetRaw(out_type.GetPointer(), out_value.GetPointer(), profile, key);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_controller_for_debug_impl.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_controller_for_debug_impl.hpp
new file mode 100644
index 000000000..faa975f76
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_controller_for_debug_impl.hpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "sprofile_srv_i_profile_controller_for_debug.hpp"
+
+namespace ams::sprofile::srv {
+
+ class ProfileManager;
+
+ class ProfileControllerForDebugImpl {
+ private:
+ ProfileManager *m_manager;
+ public:
+ ProfileControllerForDebugImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
+ public:
+ Result Reset();
+ Result GetRaw(sf::Out out_type, sf::Out out_value, sprofile::Identifier profile, sprofile::Identifier key);
+ };
+ static_assert(IsIProfileControllerForDebug);
+
+}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp
new file mode 100644
index 000000000..fc1d3b07e
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer.hpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "sprofile_srv_types.hpp"
+
+namespace ams::sprofile::srv {
+
+ class ProfileImporter {
+ private:
+ bool m_committed;
+ bool m_imported_metadata;
+ int m_importing_count;
+ util::optional m_metadata;
+ Identifier m_importing_profiles[50];
+ util::BitFlagSet<50> m_imported_profiles;
+ Identifier m_revision_key;
+ public:
+ ProfileImporter(const util::optional &meta) : m_committed(false), m_imported_metadata(false), m_importing_count(0), m_metadata(util::nullopt), m_imported_profiles(), m_revision_key() {
+ if (meta.has_value()) {
+ m_metadata = *meta;
+ }
+ }
+ public:
+ 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) {
+ const auto &entry = m_metadata->entries[i];
+ if (entry.identifier_0 == id0 && entry.identifier_1 == id1) {
+ return true;
+ }
+ }
+ }
+
+ /* We don't have the desired profile. */
+ return false;
+ }
+
+ bool CanImportMetadata() {
+ /* We can only import metadata if we haven't already imported metadata. */
+ return !m_imported_metadata;
+ }
+
+ void ImportMetadata(const ProfileMetadata &meta) {
+ /* Set that we've imported metadata. */
+ m_imported_metadata = true;
+
+ /* Import the service revision key. */
+ m_revision_key = meta.revision_key;
+
+ /* Import all profiles. */
+ for (auto i = 0u; i < meta.num_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;
+ }
+ }
+ }
+
+ bool CanImportProfile(Identifier profile) {
+ /* Require that we imported metadata. */
+ if (m_imported_metadata) {
+ /* Find the specified profile. */
+ for (auto i = 0; i < m_importing_count; ++i) {
+ if (m_importing_profiles[i] == profile) {
+ /* Require the profile not already be imported. */
+ return !m_imported_profiles[i];
+ }
+ }
+ }
+
+ /* We can't import the desired profile. */
+ return false;
+ }
+
+ void OnImportProfile(Identifier profile) {
+ /* Set the profile as imported. */
+ for (auto i = 0; i < m_importing_count; ++i) {
+ if (m_importing_profiles[i] == profile) {
+ m_imported_profiles[i] = true;
+ break;
+ }
+ }
+ }
+
+ bool CanCommit() {
+ /* We can't commit if we've already committed. */
+ if (m_committed) {
+ return false;
+ }
+
+ /* We need metadata in order to commit. */
+ if (!m_imported_metadata) {
+ return false;
+ }
+
+ /* We need to have imported everything we intended to import. */
+ return m_imported_profiles.PopCount() == m_importing_count;
+ }
+
+ int GetImportingCount() const { return m_importing_count; }
+ Identifier GetImportingProfile(int i) const { return m_importing_profiles[i]; }
+
+ Identifier GetRevisionKey() const { return m_revision_key; }
+ };
+
+}
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
new file mode 100644
index 000000000..d13185e52
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "sprofile_srv_profile_manager.hpp"
+#include "sprofile_srv_profile_importer_impl.hpp"
+
+namespace ams::sprofile::srv {
+
+ Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
+ return m_manager->ImportProfile(data);
+ }
+
+ Result ProfileImporterImpl::Commit() {
+ return m_manager->Commit();
+ }
+
+ Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
+ return m_manager->ImportMetadata(data);
+ }
+
+}
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
new file mode 100644
index 000000000..b47ffb246
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_importer_impl.hpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "sprofile_srv_i_profile_importer.hpp"
+
+namespace ams::sprofile::srv {
+
+ class ProfileManager;
+
+ class ProfileImporterImpl {
+ private:
+ ProfileManager *m_manager;
+ public:
+ ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
+ public:
+ Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
+ Result Commit();
+ Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
+ };
+ static_assert(IsIProfileImporter);
+
+}
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 9ae304218..acde764ad 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp
@@ -21,6 +21,9 @@ namespace ams::sprofile::srv {
namespace {
+ constexpr const char PrimaryDirectoryName[] = "primary";
+ constexpr const char TemporaryDirectoryName[] = "temp";
+
Result CreateSaveData(const ProfileManager::SaveDataInfo &save_data_info) {
R_TRY_CATCH(fs::CreateSystemSaveData(save_data_info.id, save_data_info.size, save_data_info.journal_size, save_data_info.flags)) {
R_CATCH(fs::ResultPathAlreadyExists) { /* Nintendo accepts already-existing savedata here. */ }
@@ -28,11 +31,70 @@ namespace ams::sprofile::srv {
return ResultSuccess();
}
+ void SafePrint(char *dst, size_t dst_size, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
+
+ void SafePrint(char *dst, size_t dst_size, const char *fmt, ...) {
+ std::va_list vl;
+ va_start(vl, fmt);
+ const size_t len = util::TVSNPrintf(dst, dst_size, fmt, vl);
+ va_end(vl);
+
+ AMS_ABORT_UNLESS(len < dst_size);
+ }
+
+ void CreateMetadataPathImpl(char *dst, size_t dst_size, const char *mount, const char *dir) {
+ SafePrint(dst, dst_size, "%s:/%s/metadata", mount, dir);
+ }
+
+ void CreatePrimaryMetadataPath(char *dst, size_t dst_size, const char *mount) {
+ CreateMetadataPathImpl(dst, dst_size, mount, PrimaryDirectoryName);
+ }
+
+ void CreateTemporaryMetadataPath(char *dst, size_t dst_size, const char *mount) {
+ CreateMetadataPathImpl(dst, dst_size, mount, TemporaryDirectoryName);
+ }
+
+ void CreateProfilePathImpl(char *dst, size_t dst_size, const char *mount, const char *dir, const Identifier &id) {
+ SafePrint(dst, dst_size, "%s:/%s/profiles/%02x%02x%02x%02x%02x%02x%02x", mount, dir, id.data[0], id.data[1], id.data[2], id.data[3], id.data[4], id.data[5], id.data[6]);
+ }
+
+ void CreatePrimaryProfilePath(char *dst, size_t dst_size, const char *mount, const Identifier &id) {
+ CreateProfilePathImpl(dst, dst_size, mount, PrimaryDirectoryName, id);
+ }
+
+ void CreateTemporaryProfilePath(char *dst, size_t dst_size, const char *mount, const Identifier &id) {
+ CreateProfilePathImpl(dst, dst_size, mount, TemporaryDirectoryName, id);
+ }
+
+ void CreateDirectoryPathImpl(char *dst, size_t dst_size, const char *mount, const char *dir) {
+ SafePrint(dst, dst_size, "%s:/%s", mount, dir);
+ }
+
+ void CreatePrimaryDirectoryPath(char *dst, size_t dst_size, const char *mount) {
+ CreateDirectoryPathImpl(dst, dst_size, mount, PrimaryDirectoryName);
+ }
+
+ void CreateTemporaryDirectoryPath(char *dst, size_t dst_size, const char *mount) {
+ CreateDirectoryPathImpl(dst, dst_size, mount, TemporaryDirectoryName);
+ }
+
+ void CreateProfileDirectoryPathImpl(char *dst, size_t dst_size, const char *mount, const char *dir) {
+ SafePrint(dst, dst_size, "%s:/%s/profiles", mount, dir);
+ }
+
+ void CreatePrimaryProfileDirectoryPath(char *dst, size_t dst_size, const char *mount) {
+ CreateProfileDirectoryPathImpl(dst, dst_size, mount, PrimaryDirectoryName);
+ }
+
+ void CreateTemporaryProfileDirectoryPath(char *dst, size_t dst_size, const char *mount) {
+ CreateProfileDirectoryPathImpl(dst, dst_size, mount, TemporaryDirectoryName);
+ }
+
}
ProfileManager::ProfileManager(const SaveDataInfo &save_data_info)
- : m_save_data_info(save_data_info), m_save_file_mounted(false),
- m_profile_metadata(util::nullopt), m_update_observer_manager()
+ : m_save_data_info(save_data_info), m_save_file_mounted(false), m_profile_importer(util::nullopt),
+ m_profile_metadata(util::nullopt), m_service_profile(util::nullopt), m_update_observer_manager()
{
/* ... */
}
@@ -45,9 +107,6 @@ namespace ams::sprofile::srv {
/* Ensure the savedata exists. */
if (R_SUCCEEDED(CreateSaveData(m_save_data_info))) {
m_save_file_mounted = R_SUCCEEDED(fs::MountSystemSaveData(m_save_data_info.mount_name, m_save_data_info.id));
-
- /* TODO: Remove this after implementation, as it's for debugging. */
- R_ABORT_UNLESS(fs::MountSdCard("sprof-dbg"));
}
}
@@ -67,7 +126,7 @@ namespace ams::sprofile::srv {
/* Unload profile. */
m_profile_metadata = util::nullopt;
- /* TODO m_service_profile = util::nullopt; */
+ m_service_profile = util::nullopt;
/* Create the save data. */
R_TRY(CreateSaveData(m_save_data_info));
@@ -79,6 +138,124 @@ namespace ams::sprofile::srv {
return result;
}
+ Result ProfileManager::OpenProfileImporter() {
+ /* Acquire locks. */
+ std::scoped_lock lk1(m_profile_metadata_mutex);
+ std::scoped_lock lk2(m_profile_importer_mutex);
+
+ /* Check that we don't already have an importer. */
+ R_UNLESS(!m_profile_importer.has_value(), sprofile::ResultInvalidState());
+
+ /* Create importer. */
+ m_profile_importer.emplace(m_profile_metadata);
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
+ /* Acquire locks. */
+ std::scoped_lock lk1(m_profile_importer_mutex);
+ std::scoped_lock lk2(m_fs_mutex);
+
+ /* 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());
+
+ /* Succeed if we already have the profile. */
+ R_SUCCEED_IF(m_profile_importer->HasProfile(data.identifier_0, data.identifier_1));
+
+ /* Check that we're importing the profile. */
+ R_UNLESS(m_profile_importer->CanImportProfile(data.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)));
+
+ /* Set profile imported. */
+ m_profile_importer->OnImportProfile(data.identifier_0);
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::Commit() {
+ /* Acquire locks. */
+ std::scoped_lock lk1(m_service_profile_mutex);
+ std::scoped_lock lk2(m_profile_metadata_mutex);
+ std::scoped_lock lk3(m_profile_importer_mutex);
+ std::scoped_lock lk4(m_general_mutex);
+ std::scoped_lock lk5(m_fs_mutex);
+
+ /* Check that we have an importer. */
+ R_UNLESS(m_profile_importer.has_value(), sprofile::ResultInvalidState());
+
+ /* Commit, and if we fail remount our save. */
+ {
+ /* Setup guard in case we fail. */
+ auto remount_guard = SCOPE_GUARD {
+ if (m_profile_importer.has_value()) {
+ /* Unmount save file. */
+ fs::Unmount(m_save_data_info.mount_name);
+ m_save_file_mounted = false;
+
+ /* Re-mount save file. */
+ R_ABORT_UNLESS(fs::MountSystemSaveData(m_save_data_info.mount_name, m_save_data_info.id));
+ m_save_file_mounted = true;
+
+ /* Reset our importer. */
+ m_profile_importer = util::nullopt;
+ }
+ };
+
+ /* Check that we can commit the importer. */
+ R_UNLESS(m_profile_importer->CanCommit(), sprofile::ResultInvalidState());
+
+ /* Commit. */
+ R_TRY(this->CommitImpl());
+
+ /* Commit the save file. */
+ R_TRY(fs::CommitSaveData(m_save_data_info.mount_name));
+
+ /* We successfully committed. */
+ remount_guard.Cancel();
+ }
+
+ /* NOTE: Here nintendo generates an "sprofile_update_profile" sreport with the new and old revision keys. */
+
+ /* Handle tasks for when we've committed (including notifying update observers). */
+ this->OnCommitted();
+
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
+ /* Acquire locks. */
+ std::scoped_lock lk1(m_profile_importer_mutex);
+ std::scoped_lock lk2(m_fs_mutex);
+
+ /* Check that we can import metadata. */
+ 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());
+
+ /* Create temporary directories. */
+ R_TRY(this->EnsureTemporaryDirectories());
+
+ /* 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)));
+
+ /* Import the metadata. */
+ m_profile_importer->ImportMetadata(data.metadata);
+ return ResultSuccess();
+ }
+
Result ProfileManager::LoadPrimaryMetadata(ProfileMetadata *out) {
/* Acquire locks. */
std::scoped_lock lk1(m_profile_metadata_mutex);
@@ -92,7 +269,7 @@ namespace ams::sprofile::srv {
/* Read profile metadata. */
char path[0x30];
- AMS_ABORT_UNLESS(util::TSNPrintf(path, sizeof(path), "%s:/%s/metadata", m_save_data_info.mount_name, "primary") < static_cast(sizeof(path)));
+ CreatePrimaryMetadataPath(path, sizeof(path), m_save_data_info.mount_name);
R_TRY(ReadFile(path, std::addressof(*m_profile_metadata), sizeof(*m_profile_metadata), 0));
/* We read the metadata successfully. */
@@ -104,4 +281,196 @@ namespace ams::sprofile::srv {
return ResultSuccess();
}
+ Result ProfileManager::LoadProfile(Identifier profile) {
+ /* Check if we already have the profile. */
+ if (m_service_profile.has_value()) {
+ R_SUCCEED_IF(m_service_profile->name == profile);
+ }
+
+ /* If we fail past this point, we want to have no profile. */
+ auto prof_guard = SCOPE_GUARD { m_service_profile = util::nullopt; };
+
+ /* Create profile path. */
+ char path[0x30];
+ CreatePrimaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, profile);
+
+ /* Load the profile. */
+ m_service_profile = {};
+ R_TRY(ReadFile(path, std::addressof(m_service_profile->data), sizeof(m_service_profile->data), 0));
+
+ /* We succeeded. */
+ prof_guard.Cancel();
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::GetDataEntry(ProfileDataEntry *out, Identifier profile, Identifier key) {
+ /* Acquire locks. */
+ std::scoped_lock lk1(m_service_profile_mutex);
+ 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();
+ }
+ }
+
+ return sprofile::ResultKeyNotFound();
+ }
+
+ Result ProfileManager::GetSigned64(s64 *out, Identifier profile, Identifier key) {
+ /* Get the data entry. */
+ ProfileDataEntry entry;
+ R_TRY(this->GetDataEntry(std::addressof(entry), profile, key));
+
+ /* Check the type. */
+ R_UNLESS(entry.type == ValueType_S64, sprofile::ResultInvalidDataType());
+
+ /* Set the output value. */
+ *out = entry.value_s64;
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::GetUnsigned64(u64 *out, Identifier profile, Identifier key) {
+ /* Get the data entry. */
+ ProfileDataEntry entry;
+ R_TRY(this->GetDataEntry(std::addressof(entry), profile, key));
+
+ /* Check the type. */
+ R_UNLESS(entry.type == ValueType_U64, sprofile::ResultInvalidDataType());
+
+ /* Set the output value. */
+ *out = entry.value_u64;
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::GetSigned32(s32 *out, Identifier profile, Identifier key) {
+ /* Get the data entry. */
+ ProfileDataEntry entry;
+ R_TRY(this->GetDataEntry(std::addressof(entry), profile, key));
+
+ /* Check the type. */
+ R_UNLESS(entry.type == ValueType_S32, sprofile::ResultInvalidDataType());
+
+ /* Set the output value. */
+ *out = entry.value_s32;
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::GetUnsigned32(u32 *out, Identifier profile, Identifier key) {
+ /* Get the data entry. */
+ ProfileDataEntry entry;
+ R_TRY(this->GetDataEntry(std::addressof(entry), profile, key));
+
+ /* Check the type. */
+ R_UNLESS(entry.type == ValueType_U32, sprofile::ResultInvalidDataType());
+
+ /* Set the output value. */
+ *out = entry.value_u32;
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::GetByte(u8 *out, Identifier profile, Identifier key) {
+ /* Get the data entry. */
+ ProfileDataEntry entry;
+ R_TRY(this->GetDataEntry(std::addressof(entry), profile, key));
+
+ /* Check the type. */
+ R_UNLESS(entry.type == ValueType_Byte, sprofile::ResultInvalidDataType());
+
+ /* Set the output value. */
+ *out = entry.value_u8;
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::GetRaw(u8 *out_type, u64 *out_value, Identifier profile, Identifier key) {
+ /* Get the data entry. */
+ ProfileDataEntry entry;
+ R_TRY(this->GetDataEntry(std::addressof(entry), profile, key));
+
+ /* Set the output type and value. */
+ *out_type = entry.type;
+ *out_value = entry.value_u64;
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::CommitImpl() {
+ /* Ensure primary directories. */
+ R_TRY(this->EnsurePrimaryDirectories());
+
+ /* Declare re-usable paths. */
+ char tmp_path[0x30];
+ char pri_path[0x30];
+
+ /* Move the metadata. */
+ {
+ CreateTemporaryMetadataPath(tmp_path, sizeof(tmp_path), m_save_data_info.mount_name);
+ CreatePrimaryMetadataPath(pri_path, sizeof(pri_path), m_save_data_info.mount_name);
+ R_TRY(MoveFile(tmp_path, pri_path));
+ }
+
+ /* Move all profiles. */
+ for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
+ const auto id = m_profile_importer->GetImportingProfile(i);
+
+ CreateTemporaryProfilePath(tmp_path, sizeof(tmp_path), m_save_data_info.mount_name, id);
+ CreatePrimaryProfilePath(pri_path, sizeof(pri_path), m_save_data_info.mount_name, id);
+ R_TRY(MoveFile(tmp_path, pri_path));
+ }
+
+ return ResultSuccess();
+ }
+
+ void ProfileManager::OnCommitted() {
+ /* 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) {
+ if (m_service_profile->name == m_profile_importer->GetImportingProfile(i)) {
+ m_service_profile = util::nullopt;
+ break;
+ }
+ }
+ }
+
+ /* TODO: Here, Nintendo sets the erpt ServiceProfileRevisionKey to the current revision key. */
+
+ /* Reset profile metadata. */
+ m_profile_metadata = util::nullopt;
+
+ /* Invoke any listeners. */
+ for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
+ m_update_observer_manager.OnUpdate(m_profile_importer->GetImportingProfile(i));
+ }
+ }
+
+ Result ProfileManager::EnsurePrimaryDirectories() {
+ /* Ensure the primary directories. */
+ char path[0x30];
+
+ CreatePrimaryDirectoryPath(path, sizeof(path), m_save_data_info.mount_name);
+ R_TRY(EnsureDirectory(path));
+
+ CreatePrimaryProfileDirectoryPath(path, sizeof(path), m_save_data_info.mount_name);
+ R_TRY(EnsureDirectory(path));
+
+ return ResultSuccess();
+ }
+
+ Result ProfileManager::EnsureTemporaryDirectories() {
+ /* Ensure the temporary directories. */
+ char path[0x30];
+
+ CreateTemporaryDirectoryPath(path, sizeof(path), m_save_data_info.mount_name);
+ R_TRY(EnsureDirectory(path));
+
+ CreateTemporaryProfileDirectoryPath(path, sizeof(path), m_save_data_info.mount_name);
+ R_TRY(EnsureDirectory(path));
+
+ return ResultSuccess();
+ }
+
}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.hpp
index 1f2fe7dee..92b4ea75b 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.hpp
@@ -17,6 +17,7 @@
#include
#include "sprofile_srv_types.hpp"
#include "sprofile_srv_profile_update_observer_impl.hpp"
+#include "sprofile_srv_profile_importer.hpp"
namespace ams::sprofile::srv {
@@ -35,11 +36,11 @@ namespace ams::sprofile::srv {
os::SdkMutex m_fs_mutex{};
SaveDataInfo m_save_data_info;
bool m_save_file_mounted;
- /* TODO: util::optional m_profile_importer; */
+ util::optional m_profile_importer;
os::SdkMutex m_profile_importer_mutex{};
util::optional m_profile_metadata;
os::SdkMutex m_profile_metadata_mutex{};
- /* TODO: util::optional m_service_profile; */
+ util::optional m_service_profile;
os::SdkMutex m_service_profile_mutex{};
ProfileUpdateObserverManager m_update_observer_manager;
public:
@@ -48,11 +49,33 @@ namespace ams::sprofile::srv {
void InitializeSaveData();
Result ResetSaveData();
+ Result OpenProfileImporter();
+ Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
+ Result Commit();
+ Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
+
Result LoadPrimaryMetadata(ProfileMetadata *out);
+ Result GetSigned64(s64 *out, Identifier profile, Identifier key);
+ Result GetUnsigned64(u64 *out, Identifier profile, Identifier key);
+ Result GetSigned32(s32 *out, Identifier profile, Identifier key);
+ Result GetUnsigned32(u32 *out, Identifier profile, Identifier key);
+ Result GetByte(u8 *out, Identifier profile, Identifier key);
+
+ Result GetRaw(u8 *out_type, u64 *out_value, Identifier profile, Identifier key);
+
ProfileUpdateObserverManager &GetUpdateObserverManager() { return m_update_observer_manager; }
private:
- /* TODO */
+ Result CommitImpl();
+
+ void OnCommitted();
+
+ Result GetDataEntry(ProfileDataEntry *out, Identifier profile, Identifier key);
+
+ Result LoadProfile(Identifier profile);
+
+ Result EnsurePrimaryDirectories();
+ Result EnsureTemporaryDirectories();
};
}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_reader_impl.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_reader_impl.cpp
new file mode 100644
index 000000000..c602535bc
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_reader_impl.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "sprofile_srv_profile_manager.hpp"
+#include "sprofile_srv_profile_reader_impl.hpp"
+
+namespace ams::sprofile::srv {
+
+ Result ProfileReaderImpl::GetSigned64(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key) {
+ return m_manager->GetSigned64(out.GetPointer(), profile, key);
+ }
+
+ Result ProfileReaderImpl::GetUnsigned64(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key) {
+ return m_manager->GetUnsigned64(out.GetPointer(), profile, key);
+ }
+
+ Result ProfileReaderImpl::GetSigned32(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key) {
+ return m_manager->GetSigned32(out.GetPointer(), profile, key);
+ }
+
+ Result ProfileReaderImpl::GetUnsigned32(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key) {
+ return m_manager->GetUnsigned32(out.GetPointer(), profile, key);
+ }
+
+ Result ProfileReaderImpl::GetByte(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key) {
+ return m_manager->GetByte(out.GetPointer(), profile, key);
+ }
+
+}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_reader_impl.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_reader_impl.hpp
new file mode 100644
index 000000000..884a1224f
--- /dev/null
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_reader_impl.hpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "sprofile_srv_i_profile_reader.hpp"
+
+namespace ams::sprofile::srv {
+
+ class ProfileManager;
+
+ class ProfileReaderImpl {
+ private:
+ ProfileManager *m_manager;
+ public:
+ ProfileReaderImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
+ public:
+ Result GetSigned64(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key);
+ Result GetUnsigned64(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key);
+ Result GetSigned32(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key);
+ Result GetUnsigned32(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key);
+ Result GetByte(sf::Out out, sprofile::Identifier profile, sprofile::Identifier key);
+ };
+ static_assert(IsIProfileReader);
+
+}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.cpp
index e5b4a66ee..34d7cd8f5 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.cpp
@@ -22,51 +22,11 @@ namespace ams::sprofile::srv {
namespace {
class AutoUnregisterObserver : public ProfileUpdateObserverImpl {
- public:
- static constexpr auto MaxProfiles = 4;
private:
- Identifier m_profiles[MaxProfiles];
- int m_profile_count;
- os::SdkMutex m_mutex;
ProfileUpdateObserverManager *m_manager;
public:
- AutoUnregisterObserver(ProfileUpdateObserverManager *manager) : m_profile_count(0), m_mutex(), m_manager(manager) { /* ... */ }
+ AutoUnregisterObserver(ProfileUpdateObserverManager *manager) : m_manager(manager) { /* ... */ }
virtual ~AutoUnregisterObserver() { m_manager->CloseObserver(this); }
- public:
- Result Listen(Identifier profile) {
- /* Lock ourselves. */
- std::scoped_lock lk(m_mutex);
-
- /* Check if we can listen. */
- R_UNLESS(m_profile_count < MaxProfiles, sprofile::ResultMaxListeners());
-
- /* Check if we're already listening. */
- for (auto i = 0; i < m_profile_count; ++i) {
- R_UNLESS(m_profiles[i] != profile, sprofile::ResultAlreadyListening());
- }
-
- /* Add the profile. */
- m_profiles[m_profile_count++] = profile;
- return ResultSuccess();
- }
-
- Result Unlisten(Identifier profile) {
- /* Check that we're listening. */
- for (auto i = 0; i < m_profile_count; ++i) {
- if (m_profiles[i] == profile) {
- m_profiles[i] = m_profiles[--m_profile_count];
- AMS_ABORT_UNLESS(m_profile_count >= 0);
- return ResultSuccess();
- }
- }
-
- return sprofile::ResultNotListening();
- }
-
- Result GetEventHandle(sf::OutCopyHandle out) {
- out.SetValue(this->GetEvent().GetReadableHandle());
- return ResultSuccess();
- }
};
static_assert(sprofile::IsIProfileUpdateObserver);
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.hpp
index 6a1810630..fd6f9a707 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_update_observer_impl.hpp
@@ -20,15 +20,65 @@
namespace ams::sprofile::srv {
class ProfileUpdateObserverImpl {
+ public:
+ static constexpr auto MaxProfiles = 4;
private:
os::SystemEvent m_event;
+ Identifier m_profiles[MaxProfiles];
+ int m_profile_count;
+ os::SdkMutex m_mutex;
public:
- ProfileUpdateObserverImpl() : m_event(os::EventClearMode_ManualClear, true) { /* ... */ }
+ ProfileUpdateObserverImpl() : m_event(os::EventClearMode_ManualClear, true), m_profile_count(0), m_mutex() { /* ... */ }
virtual ~ProfileUpdateObserverImpl() { /* ... */ }
public:
os::SystemEvent &GetEvent() { return m_event; }
const os::SystemEvent &GetEvent() const { return m_event; }
+ public:
+ Result Listen(Identifier profile) {
+ /* Lock ourselves. */
+ std::scoped_lock lk(m_mutex);
+
+ /* Check if we can listen. */
+ R_UNLESS(m_profile_count < MaxProfiles, sprofile::ResultMaxListeners());
+
+ /* Check if we're already listening. */
+ for (auto i = 0; i < m_profile_count; ++i) {
+ R_UNLESS(m_profiles[i] != profile, sprofile::ResultAlreadyListening());
+ }
+
+ /* Add the profile. */
+ m_profiles[m_profile_count++] = profile;
+ return ResultSuccess();
+ }
+
+ Result Unlisten(Identifier profile) {
+ /* Check that we're listening. */
+ for (auto i = 0; i < m_profile_count; ++i) {
+ if (m_profiles[i] == profile) {
+ m_profiles[i] = m_profiles[--m_profile_count];
+ AMS_ABORT_UNLESS(m_profile_count >= 0);
+ return ResultSuccess();
+ }
+ }
+
+ return sprofile::ResultNotListening();
+ }
+
+ Result GetEventHandle(sf::OutCopyHandle out) {
+ out.SetValue(m_event.GetReadableHandle());
+ return ResultSuccess();
+ }
+ public:
+ void OnUpdate(Identifier profile) {
+ for (auto i = 0; i < m_profile_count; ++i) {
+ if (m_profiles[i] == profile) {
+ m_event.Signal();
+ break;
+ }
+ }
+ }
};
+ static_assert(sprofile::IsIProfileUpdateObserver);
class ProfileUpdateObserverManager {
public:
@@ -42,6 +92,14 @@ namespace ams::sprofile::srv {
public:
Result OpenObserver(sf::Out> &out, MemoryResource *memory_resource);
void CloseObserver(ProfileUpdateObserverImpl *observer);
+
+ void OnUpdate(Identifier profile) {
+ std::scoped_lock lk(m_mutex);
+
+ for (auto i = 0; i < m_observer_count; ++i) {
+ m_observers[i]->OnUpdate(profile);
+ }
+ }
};
}
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 567cb7f2c..af9991dc4 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
@@ -16,12 +16,22 @@
#include
#include "sprofile_srv_profile_manager.hpp"
#include "sprofile_srv_service_for_bg_agent.hpp"
+#include "sprofile_srv_profile_importer_impl.hpp"
#include "sprofile_srv_fs_utils.hpp"
namespace ams::sprofile::srv {
Result ServiceForBgAgent::OpenProfileImporter(sf::Out> out) {
- AMS_ABORT("TODO: OpenProfileImporter");
+ /* Allocate an object. */
+ auto obj = sf::ObjectFactory::CreateSharedEmplaced(m_memory_resource, m_profile_manager);
+ R_UNLESS(obj != nullptr, sprofile::ResultAllocationFailed());
+
+ /* Confirm that we can begin an import. */
+ R_TRY(m_profile_manager->OpenProfileImporter());
+
+ /* Return the object. */
+ *out = std::move(obj);
+ return ResultSuccess();
}
Result ServiceForBgAgent::ReadMetadata(sf::Out out_count, const sf::OutArray &out, const sprofile::srv::ReadMetadataArgument &arg) {
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_system_process.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_system_process.cpp
index f0844e4f9..9313c3156 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_system_process.cpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_service_for_system_process.cpp
@@ -16,12 +16,19 @@
#include
#include "sprofile_srv_profile_manager.hpp"
#include "sprofile_srv_service_for_system_process.hpp"
+#include "sprofile_srv_profile_reader_impl.hpp"
+#include "sprofile_srv_profile_controller_for_debug_impl.hpp"
namespace ams::sprofile::srv {
Result ServiceForSystemProcess::OpenProfileReader(sf::Out> out) {
- /* TODO */
- AMS_ABORT("TODO: OpenProfileReader");
+ /* Allocate an object. */
+ auto obj = sf::ObjectFactory::CreateSharedEmplaced(m_memory_resource, m_profile_manager);
+ R_UNLESS(obj != nullptr, sprofile::ResultAllocationFailed());
+
+ /* Return the object. */
+ *out = std::move(obj);
+ return ResultSuccess();
}
Result ServiceForSystemProcess::OpenProfileUpdateObserver(sf::Out> out) {
@@ -29,8 +36,16 @@ namespace ams::sprofile::srv {
}
Result ServiceForSystemProcess::OpenProfileControllerForDebug(sf::Out> out) {
- /* TODO */
- AMS_ABORT("TODO: OpenProfileControllerForDebug");
+ /* Require debug mode in order to open a debug controller. */
+ R_UNLESS(settings::fwdbg::IsDebugModeEnabled(), sprofile::ResultNotPermitted());
+
+ /* Allocate an object. */
+ auto obj = sf::ObjectFactory::CreateSharedEmplaced(m_memory_resource, m_profile_manager);
+ R_UNLESS(obj != nullptr, sprofile::ResultAllocationFailed());
+
+ /* Return the object. */
+ *out = std::move(obj);
+ return ResultSuccess();
}
}
diff --git a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp
index 364739280..291c83bc5 100644
--- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp
+++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp
@@ -78,6 +78,13 @@ namespace ams::sprofile::srv {
static_assert(util::is_pod::value);
static_assert(sizeof(ProfileData) == 0x4000);
+ struct ServiceProfile {
+ Identifier name;
+ ProfileData data;
+ };
+ static_assert(util::is_pod::value);
+ static_assert(sizeof(ServiceProfile) == 0x4008);
+
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
Identifier identifier_0;
Identifier identifier_1;
diff --git a/libraries/libvapours/include/vapours/results/sprofile_results.hpp b/libraries/libvapours/include/vapours/results/sprofile_results.hpp
index fa8abd29d..03ba5ce70 100644
--- a/libraries/libvapours/include/vapours/results/sprofile_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sprofile_results.hpp
@@ -24,13 +24,19 @@ namespace ams::sprofile {
R_DEFINE_ERROR_RESULT(InvalidArgument, 100);
R_DEFINE_ERROR_RESULT(InvalidState, 101);
+ R_DEFINE_ERROR_RESULT(NotPermitted, 303);
+
R_DEFINE_ERROR_RESULT(AllocationFailed, 401);
+ R_DEFINE_ERROR_RESULT(KeyNotFound, 600);
+ R_DEFINE_ERROR_RESULT(InvalidDataType, 601);
+
R_DEFINE_ERROR_RESULT(MaxListeners, 620);
R_DEFINE_ERROR_RESULT(AlreadyListening, 621);
R_DEFINE_ERROR_RESULT(NotListening, 622);
R_DEFINE_ERROR_RESULT(MaxObservers, 623);
R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
+ R_DEFINE_ERROR_RESULT(InvalidDataVersion, 3230);
}
diff --git a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp
index 4ccf07ccd..6c3447193 100644
--- a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp
+++ b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp
@@ -76,7 +76,7 @@ namespace ams::util {
constexpr int PopCount(const Storage arr[]) {
int count = 0;
for (size_t i = 0; i < Count; i++) {
- count += PopCount(arr[i]);
+ count += ::ams::util::PopCount(arr[i]);
}
return count;
}