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 6f3a11a95..c2914af1f 100644 --- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.cpp +++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_fs_utils.cpp @@ -18,6 +18,24 @@ namespace ams::sprofile::srv { + Result ReadFile(const char *path, void *dst, size_t size, s64 offset) { + /* Open the file. */ + fs::FileHandle file; + R_TRY_CATCH(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read)) { + R_CATCH_RETHROW(fs::ResultPathNotFound) /* It's okay if the file doesn't exist. */ + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + /* Read the file. */ + size_t read_size; + R_TRY(fs::ReadFile(std::addressof(read_size), file, offset, dst, size)); + + /* Check the size was correct. */ + AMS_ABORT_UNLESS(size == read_size); + + return ResultSuccess(); + } + Result WriteFile(const char *path, const void *src, size_t size) { /* Create the file. */ R_TRY_CATCH(fs::CreateFile(path, size)) { 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 a2b77b6da..addce9b30 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 @@ -15,11 +15,11 @@ */ #pragma once #include +#include "sprofile_srv_types.hpp" -/* TODO: sf::LargeData types, not buffers */ #define AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, ImportProfile, (const sf::InBuffer &data), (data)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, Commit, (), ()) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, ImportMetadata, (const sf::InBuffer &data), (data)) \ + 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_DEFINE_INTERFACE(ams::sprofile, IProfileImporter, AMS_SPROFILE_I_PROFILE_IMPORTER_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 e26d91308..ac7c1184d 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 @@ -17,11 +17,10 @@ #include #include "sprofile_srv_i_profile_importer.hpp" -/* TODO: sf::LargeData types, not buffers */ #define AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 100, Result, OpenProfileImporter, (sf::Out> out), (out)) \ - AMS_SF_METHOD_INFO(C, H, 200, Result, ReadMetadata, (sf::Out out_count, const sf::OutBuffer &out_buf, const sf::InBuffer &meta), (out_count, out_buf, meta)) \ - AMS_SF_METHOD_INFO(C, H, 201, Result, IsUpdateNeeded, (sf::Out 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> out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 200, Result, ReadMetadata, (sf::Out out_count, const sf::OutArray &out, const sprofile::srv::ReadMetadataArgument &arg), (out_count, out, arg)) \ + AMS_SF_METHOD_INFO(C, H, 201, Result, IsUpdateNeeded, (sf::Out 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_manager.cpp b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp index 684ce7715..9ae304218 100644 --- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp +++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.cpp @@ -15,6 +15,7 @@ */ #include #include "sprofile_srv_profile_manager.hpp" +#include "sprofile_srv_fs_utils.hpp" namespace ams::sprofile::srv { @@ -30,8 +31,8 @@ namespace ams::sprofile::srv { } ProfileManager::ProfileManager(const SaveDataInfo &save_data_info) - : m_general_mutex(), m_fs_mutex(), m_save_data_info(save_data_info), m_save_file_mounted(false), - m_update_observer_manager() + : m_save_data_info(save_data_info), m_save_file_mounted(false), + m_profile_metadata(util::nullopt), m_update_observer_manager() { /* ... */ } @@ -50,4 +51,57 @@ namespace ams::sprofile::srv { } } + Result ProfileManager::ResetSaveData() { + /* Acquire locks. */ + std::scoped_lock lk1(m_service_profile_mutex); + std::scoped_lock lk2(m_profile_metadata_mutex); + std::scoped_lock lk3(m_general_mutex); + std::scoped_lock lk4(m_fs_mutex); + + /* Unmount save file. */ + fs::Unmount(m_save_data_info.mount_name); + m_save_file_mounted = false; + + /* Delete save file. */ + R_TRY(fs::DeleteSystemSaveData(fs::SaveDataSpaceId::System, m_save_data_info.id, fs::InvalidUserId)); + + /* Unload profile. */ + m_profile_metadata = util::nullopt; + /* TODO m_service_profile = util::nullopt; */ + + /* Create the save data. */ + R_TRY(CreateSaveData(m_save_data_info)); + + /* Try to mount the save file. */ + const auto result = fs::MountSystemSaveData(m_save_data_info.mount_name, m_save_data_info.id); + m_save_file_mounted = R_SUCCEEDED(result); + + return result; + } + + Result ProfileManager::LoadPrimaryMetadata(ProfileMetadata *out) { + /* Acquire locks. */ + std::scoped_lock lk1(m_profile_metadata_mutex); + std::scoped_lock lk2(m_general_mutex); + + /* If we don't have metadata, load it. */ + if (!m_profile_metadata.has_value()) { + /* Emplace our metadata. */ + m_profile_metadata.emplace(); + auto meta_guard = SCOPE_GUARD { m_profile_metadata = util::nullopt; }; + + /* 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))); + R_TRY(ReadFile(path, std::addressof(*m_profile_metadata), sizeof(*m_profile_metadata), 0)); + + /* We read the metadata successfully. */ + meta_guard.Cancel(); + } + + /* Set the output. */ + *out = *m_profile_metadata; + 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 c800134c1..1f2fe7dee 100644 --- a/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.hpp +++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_profile_manager.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "sprofile_srv_types.hpp" #include "sprofile_srv_profile_update_observer_impl.hpp" namespace ams::sprofile::srv { @@ -30,18 +31,24 @@ namespace ams::sprofile::srv { }; private: private: - os::SdkMutex m_general_mutex; - os::SdkMutex m_fs_mutex; + os::SdkMutex m_general_mutex{}; + os::SdkMutex m_fs_mutex{}; SaveDataInfo m_save_data_info; bool m_save_file_mounted; /* TODO: util::optional m_profile_importer; */ - /* TODO: util::optional m_profile_metadata; */ + os::SdkMutex m_profile_importer_mutex{}; + util::optional m_profile_metadata; + os::SdkMutex m_profile_metadata_mutex{}; /* TODO: util::optional m_service_profile; */ + os::SdkMutex m_service_profile_mutex{}; ProfileUpdateObserverManager m_update_observer_manager; public: ProfileManager(const SaveDataInfo &save_data_info); public: void InitializeSaveData(); + Result ResetSaveData(); + + Result LoadPrimaryMetadata(ProfileMetadata *out); ProfileUpdateObserverManager &GetUpdateObserverManager() { return m_update_observer_manager; } private: 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 cd9a19ad1..567cb7f2c 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 @@ -24,17 +24,58 @@ namespace ams::sprofile::srv { AMS_ABORT("TODO: OpenProfileImporter"); } - Result ServiceForBgAgent::ReadMetadata(sf::Out out_count, const sf::OutBuffer &out_buf, const sf::InBuffer &meta) { - WriteFile("sprof-dbg:/sprof/meta.bin", meta.GetPointer(), meta.GetSize()); - AMS_ABORT("TODO: ReadMetadata"); + Result ServiceForBgAgent::ReadMetadata(sf::Out out_count, const sf::OutArray &out, const sprofile::srv::ReadMetadataArgument &arg) { + /* Check size. */ + R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument()); + + /* Load primary metadata. */ + sprofile::srv::ProfileMetadata primary_metadata; + R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) { + R_CATCH(fs::ResultPathNotFound) { + /* If we have no metadata, we can't get any entries. */ + *out_count = 0; + return ResultSuccess(); + } + } R_END_TRY_CATCH; + + /* Copy matching entries. */ + u32 count = 0; + for (u32 i = 0; i < arg.metadata.num_entries; ++i) { + const auto &arg_entry = arg.metadata.entries[i]; + + for (u32 j = 0; j < primary_metadata.num_entries; ++j) { + const auto &pri_entry = primary_metadata.entries[j]; + + if (pri_entry.identifier_0 == arg_entry.identifier_0 && pri_entry.identifier_1 == arg_entry.identifier_1) { + out[count++] = arg.entries[i]; + break; + } + } + } + + /* Set output count. */ + *out_count = count; + return ResultSuccess(); + } Result ServiceForBgAgent::IsUpdateNeeded(sf::Out out, Identifier revision_key) { - WriteFile("sprof-dbg:/sprof/revision_key.bin", std::addressof(revision_key), sizeof(revision_key)); - AMS_ABORT("TODO: IsUpdateNeeded"); + /* Load primary metadata. */ + bool loaded_metadata = true; + sprofile::srv::ProfileMetadata primary_metadata; + R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) { + R_CATCH(fs::ResultPathNotFound) { + /* If we have no metadata, we don't have a revision key. */ + loaded_metadata = false; + } + } R_END_TRY_CATCH; + + /* Determine if update is needed. */ + *out = !(loaded_metadata && revision_key == primary_metadata.revision_key); + return ResultSuccess(); } Result ServiceForBgAgent::Reset() { - AMS_ABORT("TODO: Reset"); + return m_profile_manager->ResetSaveData(); } } 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 54f78230a..9eb7d3ea3 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> out); - Result ReadMetadata(sf::Out out_count, const sf::OutBuffer &out_buf, const sf::InBuffer &meta); + Result ReadMetadata(sf::Out out_count, const sf::OutArray &out, const sprofile::srv::ReadMetadataArgument &arg); Result IsUpdateNeeded(sf::Out 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 new file mode 100644 index 000000000..364739280 --- /dev/null +++ b/libraries/libstratosphere/source/sprofile/srv/sprofile_srv_types.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::sprofile::srv { + + struct ProfileMetadataEntry { + Identifier identifier_0; + Identifier identifier_1; + u8 unk_0E[0x32]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(ProfileMetadataEntry) == 0x40); + + 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::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::value); + static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000); + + constexpr inline const u32 ProfileDataVersion = 0; + + enum ValueType : u8 { + ValueType_Byte = 0, + ValueType_U32 = 1, + ValueType_S32 = 2, + ValueType_U64 = 3, + ValueType_S64 = 4, + }; + + struct ProfileDataEntry { + Identifier key; + ValueType type; + union { + s64 value_s64; + u64 value_u64; + s32 value_s32; + u32 value_u32; + u8 value_u8; + }; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(ProfileDataEntry) == 0x10); + + struct ProfileData { + u32 version; + u32 num_entries; + u8 unk_08[0x28]; + ProfileDataEntry entries[(0x4000 - 0x30) / sizeof(ProfileDataEntry)]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(ProfileData) == 0x4000); + + struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode { + Identifier identifier_0; + Identifier identifier_1; + u8 unk_0E[2]; + ProfileData data; + u8 unk_4010[0x4400 - 0x4010]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(ProfileDataForImportData) == 0x4400); + + struct ReadMetadataEntry { + u8 unk[0x100]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(ReadMetadataEntry) == 0x100); + + struct ReadMetadataArgument : public sf::LargeData, public sf::PrefersMapAliasTransferMode { + ProfileMetadata metadata; + ReadMetadataEntry entries[(0x8000 - sizeof(metadata)) / sizeof(ReadMetadataEntry)]; + u8 unk_7FC0[0x40]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(ReadMetadataArgument) == 0x8000); + + +} diff --git a/libraries/libvapours/include/vapours/results/sprofile_results.hpp b/libraries/libvapours/include/vapours/results/sprofile_results.hpp index 81eb78ab1..fa8abd29d 100644 --- a/libraries/libvapours/include/vapours/results/sprofile_results.hpp +++ b/libraries/libvapours/include/vapours/results/sprofile_results.hpp @@ -21,6 +21,9 @@ namespace ams::sprofile { R_DEFINE_NAMESPACE_RESULT_MODULE(246); + R_DEFINE_ERROR_RESULT(InvalidArgument, 100); + R_DEFINE_ERROR_RESULT(InvalidState, 101); + R_DEFINE_ERROR_RESULT(AllocationFailed, 401); R_DEFINE_ERROR_RESULT(MaxListeners, 620); @@ -28,4 +31,6 @@ namespace ams::sprofile { R_DEFINE_ERROR_RESULT(NotListening, 622); R_DEFINE_ERROR_RESULT(MaxObservers, 623); + R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210); + }