mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
sprofile: fully reimplement sprof:bg + sprof:sp
This commit is contained in:
parent
ae54ec5981
commit
89541c8042
18 changed files with 872 additions and 59 deletions
|
@ -54,4 +54,31 @@ namespace ams::sprofile::srv {
|
||||||
return fs::WriteFile(file, 0, src, size, fs::WriteOption::Flush);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace ams::sprofile::srv {
|
||||||
|
|
||||||
Result ReadFile(const char *path, void *dst, size_t size, s64 offset);
|
Result ReadFile(const char *path, void *dst, size_t size, s64 offset);
|
||||||
Result WriteFile(const char *path, const void *src, size_t size);
|
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);
|
||||||
|
|
||||||
}
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<u8> out_type, sf::Out<u64> out_value, sprofile::Identifier profile, sprofile::Identifier key) {
|
||||||
|
return m_manager->GetRaw(out_type.GetPointer(), out_value.GetPointer(), profile, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<u8> out_type, sf::Out<u64> out_value, sprofile::Identifier profile, sprofile::Identifier key);
|
||||||
|
};
|
||||||
|
static_assert(IsIProfileControllerForDebug<ProfileControllerForDebugImpl>);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<ProfileMetadata> m_metadata;
|
||||||
|
Identifier m_importing_profiles[50];
|
||||||
|
util::BitFlagSet<50> m_imported_profiles;
|
||||||
|
Identifier m_revision_key;
|
||||||
|
public:
|
||||||
|
ProfileImporter(const util::optional<ProfileMetadata> &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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<ProfileImporterImpl>);
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,9 @@ namespace ams::sprofile::srv {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char PrimaryDirectoryName[] = "primary";
|
||||||
|
constexpr const char TemporaryDirectoryName[] = "temp";
|
||||||
|
|
||||||
Result CreateSaveData(const ProfileManager::SaveDataInfo &save_data_info) {
|
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_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. */ }
|
R_CATCH(fs::ResultPathAlreadyExists) { /* Nintendo accepts already-existing savedata here. */ }
|
||||||
|
@ -28,11 +31,70 @@ namespace ams::sprofile::srv {
|
||||||
return ResultSuccess();
|
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)
|
ProfileManager::ProfileManager(const SaveDataInfo &save_data_info)
|
||||||
: m_save_data_info(save_data_info), m_save_file_mounted(false),
|
: m_save_data_info(save_data_info), m_save_file_mounted(false), m_profile_importer(util::nullopt),
|
||||||
m_profile_metadata(util::nullopt), m_update_observer_manager()
|
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. */
|
/* Ensure the savedata exists. */
|
||||||
if (R_SUCCEEDED(CreateSaveData(m_save_data_info))) {
|
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));
|
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. */
|
/* Unload profile. */
|
||||||
m_profile_metadata = util::nullopt;
|
m_profile_metadata = util::nullopt;
|
||||||
/* TODO m_service_profile = util::nullopt; */
|
m_service_profile = util::nullopt;
|
||||||
|
|
||||||
/* Create the save data. */
|
/* Create the save data. */
|
||||||
R_TRY(CreateSaveData(m_save_data_info));
|
R_TRY(CreateSaveData(m_save_data_info));
|
||||||
|
@ -79,6 +138,124 @@ namespace ams::sprofile::srv {
|
||||||
return result;
|
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) {
|
Result ProfileManager::LoadPrimaryMetadata(ProfileMetadata *out) {
|
||||||
/* Acquire locks. */
|
/* Acquire locks. */
|
||||||
std::scoped_lock lk1(m_profile_metadata_mutex);
|
std::scoped_lock lk1(m_profile_metadata_mutex);
|
||||||
|
@ -92,7 +269,7 @@ namespace ams::sprofile::srv {
|
||||||
|
|
||||||
/* Read profile metadata. */
|
/* Read profile metadata. */
|
||||||
char path[0x30];
|
char path[0x30];
|
||||||
AMS_ABORT_UNLESS(util::TSNPrintf(path, sizeof(path), "%s:/%s/metadata", m_save_data_info.mount_name, "primary") < static_cast<int>(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));
|
R_TRY(ReadFile(path, std::addressof(*m_profile_metadata), sizeof(*m_profile_metadata), 0));
|
||||||
|
|
||||||
/* We read the metadata successfully. */
|
/* We read the metadata successfully. */
|
||||||
|
@ -104,4 +281,196 @@ namespace ams::sprofile::srv {
|
||||||
return ResultSuccess();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "sprofile_srv_types.hpp"
|
#include "sprofile_srv_types.hpp"
|
||||||
#include "sprofile_srv_profile_update_observer_impl.hpp"
|
#include "sprofile_srv_profile_update_observer_impl.hpp"
|
||||||
|
#include "sprofile_srv_profile_importer.hpp"
|
||||||
|
|
||||||
namespace ams::sprofile::srv {
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
|
@ -35,11 +36,11 @@ namespace ams::sprofile::srv {
|
||||||
os::SdkMutex m_fs_mutex{};
|
os::SdkMutex m_fs_mutex{};
|
||||||
SaveDataInfo m_save_data_info;
|
SaveDataInfo m_save_data_info;
|
||||||
bool m_save_file_mounted;
|
bool m_save_file_mounted;
|
||||||
/* TODO: util::optional<ProfileImporter> m_profile_importer; */
|
util::optional<ProfileImporter> m_profile_importer;
|
||||||
os::SdkMutex m_profile_importer_mutex{};
|
os::SdkMutex m_profile_importer_mutex{};
|
||||||
util::optional<ProfileMetadata> m_profile_metadata;
|
util::optional<ProfileMetadata> m_profile_metadata;
|
||||||
os::SdkMutex m_profile_metadata_mutex{};
|
os::SdkMutex m_profile_metadata_mutex{};
|
||||||
/* TODO: util::optional<ServiceProfile> m_service_profile; */
|
util::optional<ServiceProfile> m_service_profile;
|
||||||
os::SdkMutex m_service_profile_mutex{};
|
os::SdkMutex m_service_profile_mutex{};
|
||||||
ProfileUpdateObserverManager m_update_observer_manager;
|
ProfileUpdateObserverManager m_update_observer_manager;
|
||||||
public:
|
public:
|
||||||
|
@ -48,11 +49,33 @@ namespace ams::sprofile::srv {
|
||||||
void InitializeSaveData();
|
void InitializeSaveData();
|
||||||
Result ResetSaveData();
|
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 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; }
|
ProfileUpdateObserverManager &GetUpdateObserverManager() { return m_update_observer_manager; }
|
||||||
private:
|
private:
|
||||||
/* TODO */
|
Result CommitImpl();
|
||||||
|
|
||||||
|
void OnCommitted();
|
||||||
|
|
||||||
|
Result GetDataEntry(ProfileDataEntry *out, Identifier profile, Identifier key);
|
||||||
|
|
||||||
|
Result LoadProfile(Identifier profile);
|
||||||
|
|
||||||
|
Result EnsurePrimaryDirectories();
|
||||||
|
Result EnsureTemporaryDirectories();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sprofile_srv_profile_manager.hpp"
|
||||||
|
#include "sprofile_srv_profile_reader_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
|
Result ProfileReaderImpl::GetSigned64(sf::Out<s64> out, sprofile::Identifier profile, sprofile::Identifier key) {
|
||||||
|
return m_manager->GetSigned64(out.GetPointer(), profile, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ProfileReaderImpl::GetUnsigned64(sf::Out<u64> out, sprofile::Identifier profile, sprofile::Identifier key) {
|
||||||
|
return m_manager->GetUnsigned64(out.GetPointer(), profile, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ProfileReaderImpl::GetSigned32(sf::Out<s32> out, sprofile::Identifier profile, sprofile::Identifier key) {
|
||||||
|
return m_manager->GetSigned32(out.GetPointer(), profile, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ProfileReaderImpl::GetUnsigned32(sf::Out<u32> out, sprofile::Identifier profile, sprofile::Identifier key) {
|
||||||
|
return m_manager->GetUnsigned32(out.GetPointer(), profile, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ProfileReaderImpl::GetByte(sf::Out<u8> out, sprofile::Identifier profile, sprofile::Identifier key) {
|
||||||
|
return m_manager->GetByte(out.GetPointer(), profile, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<s64> out, sprofile::Identifier profile, sprofile::Identifier key);
|
||||||
|
Result GetUnsigned64(sf::Out<u64> out, sprofile::Identifier profile, sprofile::Identifier key);
|
||||||
|
Result GetSigned32(sf::Out<s32> out, sprofile::Identifier profile, sprofile::Identifier key);
|
||||||
|
Result GetUnsigned32(sf::Out<u32> out, sprofile::Identifier profile, sprofile::Identifier key);
|
||||||
|
Result GetByte(sf::Out<u8> out, sprofile::Identifier profile, sprofile::Identifier key);
|
||||||
|
};
|
||||||
|
static_assert(IsIProfileReader<ProfileReaderImpl>);
|
||||||
|
|
||||||
|
}
|
|
@ -22,51 +22,11 @@ namespace ams::sprofile::srv {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class AutoUnregisterObserver : public ProfileUpdateObserverImpl {
|
class AutoUnregisterObserver : public ProfileUpdateObserverImpl {
|
||||||
public:
|
|
||||||
static constexpr auto MaxProfiles = 4;
|
|
||||||
private:
|
private:
|
||||||
Identifier m_profiles[MaxProfiles];
|
|
||||||
int m_profile_count;
|
|
||||||
os::SdkMutex m_mutex;
|
|
||||||
ProfileUpdateObserverManager *m_manager;
|
ProfileUpdateObserverManager *m_manager;
|
||||||
public:
|
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); }
|
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<AutoUnregisterObserver>);
|
static_assert(sprofile::IsIProfileUpdateObserver<AutoUnregisterObserver>);
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,65 @@
|
||||||
namespace ams::sprofile::srv {
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
class ProfileUpdateObserverImpl {
|
class ProfileUpdateObserverImpl {
|
||||||
|
public:
|
||||||
|
static constexpr auto MaxProfiles = 4;
|
||||||
private:
|
private:
|
||||||
os::SystemEvent m_event;
|
os::SystemEvent m_event;
|
||||||
|
Identifier m_profiles[MaxProfiles];
|
||||||
|
int m_profile_count;
|
||||||
|
os::SdkMutex m_mutex;
|
||||||
public:
|
public:
|
||||||
ProfileUpdateObserverImpl() : m_event(os::EventClearMode_ManualClear, true) { /* ... */ }
|
ProfileUpdateObserverImpl() : m_event(os::EventClearMode_ManualClear, true), m_profile_count(0), m_mutex() { /* ... */ }
|
||||||
virtual ~ProfileUpdateObserverImpl() { /* ... */ }
|
virtual ~ProfileUpdateObserverImpl() { /* ... */ }
|
||||||
public:
|
public:
|
||||||
os::SystemEvent &GetEvent() { return m_event; }
|
os::SystemEvent &GetEvent() { return m_event; }
|
||||||
const os::SystemEvent &GetEvent() const { 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<ProfileUpdateObserverImpl>);
|
||||||
|
|
||||||
class ProfileUpdateObserverManager {
|
class ProfileUpdateObserverManager {
|
||||||
public:
|
public:
|
||||||
|
@ -42,6 +92,14 @@ namespace ams::sprofile::srv {
|
||||||
public:
|
public:
|
||||||
Result OpenObserver(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileUpdateObserver>> &out, MemoryResource *memory_resource);
|
Result OpenObserver(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileUpdateObserver>> &out, MemoryResource *memory_resource);
|
||||||
void CloseObserver(ProfileUpdateObserverImpl *observer);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,22 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "sprofile_srv_profile_manager.hpp"
|
#include "sprofile_srv_profile_manager.hpp"
|
||||||
#include "sprofile_srv_service_for_bg_agent.hpp"
|
#include "sprofile_srv_service_for_bg_agent.hpp"
|
||||||
|
#include "sprofile_srv_profile_importer_impl.hpp"
|
||||||
#include "sprofile_srv_fs_utils.hpp"
|
#include "sprofile_srv_fs_utils.hpp"
|
||||||
|
|
||||||
namespace ams::sprofile::srv {
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
Result ServiceForBgAgent::OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out) {
|
Result ServiceForBgAgent::OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out) {
|
||||||
AMS_ABORT("TODO: OpenProfileImporter");
|
/* Allocate an object. */
|
||||||
|
auto obj = sf::ObjectFactory<sf::MemoryResourceAllocationPolicy>::CreateSharedEmplaced<IProfileImporter, ProfileImporterImpl>(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<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg) {
|
Result ServiceForBgAgent::ReadMetadata(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg) {
|
||||||
|
|
|
@ -16,12 +16,19 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "sprofile_srv_profile_manager.hpp"
|
#include "sprofile_srv_profile_manager.hpp"
|
||||||
#include "sprofile_srv_service_for_system_process.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 {
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
Result ServiceForSystemProcess::OpenProfileReader(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileReader>> out) {
|
Result ServiceForSystemProcess::OpenProfileReader(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileReader>> out) {
|
||||||
/* TODO */
|
/* Allocate an object. */
|
||||||
AMS_ABORT("TODO: OpenProfileReader");
|
auto obj = sf::ObjectFactory<sf::MemoryResourceAllocationPolicy>::CreateSharedEmplaced<IProfileReader, ProfileReaderImpl>(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<sf::SharedPointer<::ams::sprofile::IProfileUpdateObserver>> out) {
|
Result ServiceForSystemProcess::OpenProfileUpdateObserver(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileUpdateObserver>> out) {
|
||||||
|
@ -29,8 +36,16 @@ namespace ams::sprofile::srv {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceForSystemProcess::OpenProfileControllerForDebug(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileControllerForDebug>> out) {
|
Result ServiceForSystemProcess::OpenProfileControllerForDebug(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileControllerForDebug>> out) {
|
||||||
/* TODO */
|
/* Require debug mode in order to open a debug controller. */
|
||||||
AMS_ABORT("TODO: OpenProfileControllerForDebug");
|
R_UNLESS(settings::fwdbg::IsDebugModeEnabled(), sprofile::ResultNotPermitted());
|
||||||
|
|
||||||
|
/* Allocate an object. */
|
||||||
|
auto obj = sf::ObjectFactory<sf::MemoryResourceAllocationPolicy>::CreateSharedEmplaced<IProfileControllerForDebug, ProfileControllerForDebugImpl>(m_memory_resource, m_profile_manager);
|
||||||
|
R_UNLESS(obj != nullptr, sprofile::ResultAllocationFailed());
|
||||||
|
|
||||||
|
/* Return the object. */
|
||||||
|
*out = std::move(obj);
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,13 @@ namespace ams::sprofile::srv {
|
||||||
static_assert(util::is_pod<ProfileData>::value);
|
static_assert(util::is_pod<ProfileData>::value);
|
||||||
static_assert(sizeof(ProfileData) == 0x4000);
|
static_assert(sizeof(ProfileData) == 0x4000);
|
||||||
|
|
||||||
|
struct ServiceProfile {
|
||||||
|
Identifier name;
|
||||||
|
ProfileData data;
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<ServiceProfile>::value);
|
||||||
|
static_assert(sizeof(ServiceProfile) == 0x4008);
|
||||||
|
|
||||||
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||||
Identifier identifier_0;
|
Identifier identifier_0;
|
||||||
Identifier identifier_1;
|
Identifier identifier_1;
|
||||||
|
|
|
@ -24,13 +24,19 @@ namespace ams::sprofile {
|
||||||
R_DEFINE_ERROR_RESULT(InvalidArgument, 100);
|
R_DEFINE_ERROR_RESULT(InvalidArgument, 100);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidState, 101);
|
R_DEFINE_ERROR_RESULT(InvalidState, 101);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(NotPermitted, 303);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(AllocationFailed, 401);
|
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(MaxListeners, 620);
|
||||||
R_DEFINE_ERROR_RESULT(AlreadyListening, 621);
|
R_DEFINE_ERROR_RESULT(AlreadyListening, 621);
|
||||||
R_DEFINE_ERROR_RESULT(NotListening, 622);
|
R_DEFINE_ERROR_RESULT(NotListening, 622);
|
||||||
R_DEFINE_ERROR_RESULT(MaxObservers, 623);
|
R_DEFINE_ERROR_RESULT(MaxObservers, 623);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
|
R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidDataVersion, 3230);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace ams::util {
|
||||||
constexpr int PopCount(const Storage arr[]) {
|
constexpr int PopCount(const Storage arr[]) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (size_t i = 0; i < Count; i++) {
|
for (size_t i = 0; i < Count; i++) {
|
||||||
count += PopCount(arr[i]);
|
count += ::ams::util::PopCount(arr[i]);
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue