From ad9d61c0de7972b1a161cd5ea6fb028217e2b62e Mon Sep 17 00:00:00 2001 From: Adubbz Date: Sat, 14 Mar 2020 19:30:05 +1100 Subject: [PATCH] ncm: begin implementing install task base --- .../include/stratosphere/ncm.hpp | 3 + .../ncm/ncm_content_info_utils.hpp | 26 ++ .../stratosphere/ncm/ncm_content_meta.hpp | 27 ++ .../stratosphere/ncm/ncm_install_task.hpp | 79 +++++ .../ncm/ncm_install_task_data.hpp | 4 + .../stratosphere/ncm/ncm_storage_id_utils.hpp | 74 +++++ .../source/ncm/ncm_content_info_utils.cpp | 47 +++ .../source/ncm/ncm_install_task.cpp | 279 ++++++++++++++++++ .../source/ncm/ncm_storage_id_utils.cpp | 86 ++++++ .../include/vapours/results/ncm_results.hpp | 5 + 10 files changed, 630 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_install_task.cpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index 2a2a7df18..9870f0746 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -21,10 +21,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp new file mode 100644 index 000000000..600848386 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019-2020 Adubbz, 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::ncm { + + constexpr inline s64 MaxClusterSize = 256_KB; + + s64 CalculateRequiredSize(s64 file_size, s64 cluster_size = MaxClusterSize); + s64 CalculateRequiredSizeForExtension(s64 file_size, s64 cluster_size = MaxClusterSize); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp index f23d133eb..820ac073a 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp @@ -206,6 +206,10 @@ namespace ams::ncm { return this->size; } + HeaderType *GetWritableHeader() const { + return reinterpret_cast(this->data); + } + const HeaderType *GetHeader() const { AMS_ABORT_UNLESS(this->is_header_valid); return static_cast(this->data); @@ -291,6 +295,19 @@ namespace ams::ncm { std::optional GetApplicationId() const { return this->GetApplicationId(this->GetKey()); } + + protected: + s64 CalculateContentRequiredSize() const { + s64 required_size = 0; + for (size_t i = 0; i < this->GetContentCount(); i++) { + required_size += CalculateRequiredSize(this->GetContentInfo(i)->info.GetSize()); + } + return required_size; + } + + void SetStorageId(StorageId storage_id) { + this->GetWritableHeader()->storage_id = static_cast(storage_id); + } }; class ContentMetaReader : public ContentMetaAccessor { @@ -320,4 +337,14 @@ namespace ams::ncm { constexpr InstallContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } }; + class InstallContentMetaWriter : public ContentMetaAccessor { + public: + InstallContentMetaWriter(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } + + using ContentMetaAccessor::CalculateSize; + using ContentMetaAccessor::CalculateContentRequiredSize; + using ContentMetaAccessor::GetWritableContentInfo; + using ContentMetaAccessor::SetStorageId; + }; + } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp new file mode 100644 index 000000000..eec63ea5c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019-2020 Adubbz, 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::ncm { + +/* protected: +PrepareContentMeta (both), WritePlaceHolderBuffer, PrepareAgain, Get/Delete InstallContentMetaData, PrepareDependency, PrepareSystemDependency, PrepareContentMetaIfLatest, GetConfig, WriteContentMetaToPlaceHolder, GetInstallStorage, GetSystemUpdateTaskApplyInfo, CanContinue +*/ + struct InstallThroughput { + s64 installed; + TimeSpan elapsed_time; + }; + + class InstallTaskBase { + private: + crypto::Sha256Generator sha256_generator; + StorageId install_storage; + InstallTaskDataBase *data; + InstallProgress progress; + os::Mutex progress_mutex; + u32 config; + os::Mutex cancel_mutex; + bool cancel_requested; + InstallThroughput throughput; + TimeSpan throughput_start_time; + os::Mutex throughput_mutex; + /* ... */ + public: + virtual ~InstallTaskBase() { /* TODO */ }; + private: + Result PrepareImpl(); + protected: + Result Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config); + Result CountInstallContentMetaData(s32 *out_count); + Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index); + public: + /* TODO: Fix access types. */ + bool IsCancelRequested(); + Result Prepare(); + void SetLastResult(Result last_result); + Result GetPreparedPlaceHolderPath(Path *out_path, u64 id, ContentMetaType meta_type, ContentType type); + Result CalculateRequiredSize(size_t *out_size); + void ResetThroughputMeasurement(); + void SetProgressState(InstallProgressState state); + void SetTotalSize(s64 size); + Result PreparePlaceHolder(); + protected: + virtual Result OnPrepareComplete(); + virtual Result PrepareDependency(); + public: + /* TODO: Fix access types. */ + virtual void Cancel(); + virtual void ResetCancel(); + virtual InstallProgress GetProgress(); + virtual Result PrepareInstallContentMetaData() = 0; + void *GetInstallContentMetaInfo; + virtual Result GetLatestVersion(std::optional *out_version, u64 id); + virtual Result CheckInstallable(); + virtual Result OnExecuteComplete(); + void *OnWritePlaceHolder; + void *InstallTicket; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_data.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_data.hpp index 0dde8d3a3..4fe12766a 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_data.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_data.hpp @@ -27,6 +27,10 @@ namespace ams::ncm { InstallContentMetaReader GetReader() const { return InstallContentMetaReader(this->data.get(), this->size); } + + InstallContentMetaWriter GetWriter() const { + return InstallContentMetaWriter(this->data.get(), this->size); + } }; class InstallTaskDataBase { diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp new file mode 100644 index 000000000..c2eaac071 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019-2020 Adubbz, 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::ncm { + + class StorageList { + public: + static constexpr s32 MaxCount = 10; + private: + StorageId ids[MaxCount]; + s32 count; + public: + constexpr StorageList() : ids(), count() { /* ... */ } + + void Push(StorageId storage_id) { + AMS_ABORT_UNLESS(this->count < MaxCount); + + for (s32 i = 0; i < MaxCount; i++) { + if (this->ids[i] == storage_id) { + return; + } + } + + this->ids[this->count++] = storage_id; + } + + s32 Count() const { + return this->count; + } + + StorageId operator[](s32 i) const { + AMS_ABORT_UNLESS(i < this->count); + return this->ids[i]; + } + }; + + constexpr StorageList GetStorageList(StorageId storage_id) { + StorageList list; + switch (storage_id) { + case StorageId::BuiltInSystem: + case StorageId::BuiltInUser: + case StorageId::SdCard: + list.Push(storage_id); + break; + case StorageId::Any: + list.Push(StorageId::SdCard); + list.Push(StorageId::BuiltInUser); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return list; + } + + Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, size_t required_size); + const char *GetStorageIdString(StorageId storage_id); + const char *GetStorageIdStringForPlayReport(StorageId storage_id); + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp new file mode 100644 index 000000000..403ea5c9a --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + namespace { + constexpr inline s64 EncryptionMetadataSize = 16_KB; + constexpr inline s64 ConcatenationFileSizeMax = 4_GB; + + constexpr s64 CalculateAdditionalContentSize(s64 file_size, s64 cluster_size) { + /* Account for the encryption header. */ + s64 size = EncryptionMetadataSize; + + /* Account for the file size splitting costs. */ + size += ((file_size / ConcatenationFileSizeMax) + 1) * cluster_size; + + /* Account for various overhead costs. */ + size += cluster_size * 3; + + return size; + } + + } + + s64 CalculateRequiredSize(s64 file_size, s64 cluster_size) { + return file_size + CalculateAdditionalContentSize(file_size, cluster_size); + } + + s64 CalculateRequiredSizeForExtension(s64 file_size, s64 cluster_size) { + return file_size + ((file_size / ConcatenationFileSizeMax) + 1) * cluster_size; + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_install_task.cpp b/libraries/libstratosphere/source/ncm/ncm_install_task.cpp new file mode 100644 index 000000000..f26085cc9 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_install_task.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + Result InstallTaskBase::OnPrepareComplete() { + return ResultSuccess(); + } + + Result InstallTaskBase::GetLatestVersion(std::optional *out_version, u64 id) { + return ncm::ResultContentMetaNotFound(); + } + + Result InstallTaskBase::CheckInstallable() { + return ResultSuccess(); + } + + Result InstallTaskBase::OnExecuteComplete() { + return ResultSuccess(); + } + + void InstallTaskBase::Cancel() { + std::scoped_lock lk(this->cancel_mutex); + this->cancel_requested = true; + } + + void InstallTaskBase::ResetCancel() { + std::scoped_lock lk(this->cancel_mutex); + this->cancel_requested = false; + } + + bool InstallTaskBase::IsCancelRequested() { + std::scoped_lock lk(this->cancel_mutex); + return this->cancel_requested; + } + + Result InstallTaskBase::Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config) { + R_UNLESS(IsInstallableStorage(install_storage), ncm::ResultUnknownStorage()); + this->install_storage = install_storage; + this->data = data; + this->config = config; + return data->GetProgress(std::addressof(this->progress)); + } + + Result InstallTaskBase::Prepare() { + /* Call the implementation. */ + Result result = this->PrepareImpl(); + + /* Update the last result. */ + this->SetLastResult(result); + return result; + } + + Result InstallTaskBase::PrepareImpl() { + /* Reset the throughput. */ + this->ResetThroughputMeasurement(); + + /* Get the current progress. */ + InstallProgress progress = this->GetProgress(); + + /* Transition from NotPrepared to DataPrepared. */ + if (progress.state == InstallProgressState::NotPrepared) { + R_TRY(this->PrepareInstallContentMetaData()); + R_TRY(this->PrepareDependency()); + R_TRY(this->CheckInstallable()); + this->SetProgressState(InstallProgressState::DataPrepared); + } + + /* Get the current progress. */ + progress = this->GetProgress(); + + /* Transition from DataPrepared to Prepared. */ + if (progress.state == InstallProgressState::DataPrepared) { + R_TRY(this->PreparePlaceHolder()); + this->SetProgressState(InstallProgressState::Prepared); + } + + /* Signal prepare is completed. */ + return this->OnPrepareComplete(); + } + + void InstallTaskBase::SetLastResult(Result last_result) { + std::scoped_lock lk(this->progress_mutex); + this->data->SetLastResult(last_result); + this->progress.SetLastResult(last_result); + } + + Result InstallTaskBase::GetPreparedPlaceHolderPath(Path *out_path, u64 id, ContentMetaType meta_type, ContentType type) { + /* Count the number of content meta entries. */ + s32 count; + R_TRY(this->data->Count(std::addressof(count))); + R_UNLESS(count > 0, ncm::ResultPlaceHolderNotFound()); + + /* Iterate over content meta. */ + for (s32 i = 0; i < count; i++) { + /* Obtain the content meta. */ + InstallContentMeta content_meta; + R_TRY(this->data->Get(std::addressof(content_meta), i)); + const InstallContentMetaReader reader = content_meta.GetReader(); + + /* Ensure content meta matches the key and meta type. */ + if (reader.GetKey().id != id || reader.GetKey().type != meta_type) { + continue; + } + + /* Attempt to obtain a content info for the content type. */ + if (const auto content_info = reader.GetContentInfo(type); content_info != nullptr) { + /* Open the relevant content storage. */ + ContentStorage content_storage; + R_TRY(ncm::OpenContentStorage(&content_storage, content_info->storage_id)); + + /* Get the placeholder path. */ + /* N doesn't bother checking the result. */ + content_storage.GetPlaceHolderPath(out_path, content_info->placeholder_id); + return ResultSuccess(); + } + } + + return ncm::ResultPlaceHolderNotFound(); + } + + Result InstallTaskBase::CountInstallContentMetaData(s32 *out_count) { + return this->data->Count(out_count); + } + + Result InstallTaskBase::GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index) { + return this->data->Get(out_content_meta, index); + } + + Result InstallTaskBase::CalculateRequiredSize(size_t *out_size) { + /* Count the number of content meta entries. */ + s32 count; + R_TRY(this->data->Count(std::addressof(count))); + + size_t required_size = 0; + /* Iterate over each entry. */ + for (s32 i = 0; i < count; i++) { + /* Obtain the content meta. */ + InstallContentMeta content_meta; + R_TRY(this->data->Get(std::addressof(content_meta), i)); + const auto reader = content_meta.GetReader(); + + /* Sum the sizes from the content infos. */ + for (size_t j = 0; j < reader.GetContentCount(); j++) { + const auto *content_info = reader.GetContentInfo(j); + + if (content_info->install_state == InstallState::NotPrepared) { + required_size += ncm::CalculateRequiredSize(content_info->GetSize()); + } + } + } + + *out_size = required_size; + return ResultSuccess(); + } + + void InstallTaskBase::ResetThroughputMeasurement() { + std::scoped_lock lk(this->throughput_mutex); + this->throughput.elapsed_time = TimeSpan(); + this->throughput_start_time = TimeSpan(); + this->throughput.installed = 0; + } + + void InstallTaskBase::SetProgressState(InstallProgressState state) { + std::scoped_lock(this->progress_mutex); + this->data->SetState(state); + this->progress.state = state; + } + + Result InstallTaskBase::PreparePlaceHolder() { + static os::Mutex placeholder_mutex; + size_t total_size = 0; + + /* Count the number of content meta entries. */ + s32 count; + R_TRY(this->data->Count(std::addressof(count))); + + for (s32 i = 0; i < count; i++) { + R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled()); + std::scoped_lock lk(placeholder_mutex); + + InstallContentMeta content_meta; + if (R_SUCCEEDED(this->data->Get(&content_meta, i))) { + auto writer = content_meta.GetWriter(); + StorageId storage_id = static_cast(writer.GetHeader()->storage_id); + + /* Automatically choose a suitable storage id. */ + if (storage_id == StorageId::None) { + R_TRY(ncm::SelectDownloadableStorage(std::addressof(storage_id), storage_id, writer.CalculateContentRequiredSize())); + } + + /* Update the data when we are done. */ + ON_SCOPE_EXIT { this->data->Update(content_meta, i); }; + + /* Open the relevant content storage. */ + ContentStorage content_storage; + R_TRY(ncm::OpenContentStorage(&content_storage, storage_id)); + + /* Update the storage id in the header. */ + writer.SetStorageId(storage_id); + + for (size_t j = 0; j < writer.GetContentCount(); j++) { + R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled()); + auto *content_info = writer.GetWritableContentInfo(j); + + bool has_content; + R_TRY(content_storage.Has(&has_content, content_info->GetId())); + + if (has_content) { + /* Add the size of installed content infos to the total size. */ + if (content_info->install_state == InstallState::Installed) { + total_size += content_info->GetSize(); + } + + /* Update the install state. */ + content_info->install_state = InstallState::AlreadyExists; + } else { + if (content_info->install_state == InstallState::NotPrepared) { + /* Generate a placeholder id. */ + const PlaceHolderId placeholder_id = content_storage.GeneratePlaceHolderId(); + + /* Update the placeholder id in the content info. */ + content_info->placeholder_id = placeholder_id; + + /* Create the placeholder. */ + R_TRY(content_storage.CreatePlaceHolder(placeholder_id, content_info->GetId(), content_info->GetSize())); + + /* Update the install state. */ + content_info->install_state = InstallState::Prepared; + } + + /* Update the storage id for the content info. */ + content_info->storage_id = storage_id; + + /* Add the size of this content info to the total size. */ + total_size += content_info->GetSize(); + } + } + } + } + + this->SetTotalSize(total_size); + return ResultSuccess(); + } + + /* ... */ + + void InstallTaskBase::SetTotalSize(s64 size) { + std::scoped_lock(this->progress_mutex); + this->progress.total_size = size; + } + + /* ... */ + + Result InstallTaskBase::PrepareDependency() { + return ResultSuccess(); + } + + /* ... */ + + InstallProgress InstallTaskBase::GetProgress() { + std::scoped_lock lk(this->progress_mutex); + return this->progress; + } +} diff --git a/libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp new file mode 100644 index 000000000..db37919bd --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + namespace { + + constexpr const char *StorageIdStrings[] = { + "None", + "Host", + "GameCard", + "BuiltInSystem", + "BuiltInUser", + "SdCard" + }; + + constexpr const char *StorageIdStringsForPlayReport[] = { + "None", + "Host", + "Card", + "BuildInSystem", + "BuildInUser", + "SdCard" + }; + + } + + Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, size_t required_size) { + auto list = GetStorageList(storage_id); + for (s32 i = 0; i < list.Count(); i++) { + auto candidate = list[i]; + + /* Open the content meta database. NOTE: This is unused. */ + ContentMetaDatabase content_meta_database; + if (R_FAILED(ncm::OpenContentMetaDatabase(std::addressof(content_meta_database), candidate))) { + continue; + } + + /* Open the content storage. */ + ContentStorage content_storage; + if (R_FAILED(ncm::OpenContentStorage(std::addressof(content_storage), candidate))) { + continue; + } + + /* Get the free space on this storage. */ + s64 free_space_size; + R_TRY(content_storage.GetFreeSpaceSize(std::addressof(free_space_size))); + + /* There must be more free space than is required. */ + if (free_space_size <= required_size) { + continue; + } + + /* Output the storage id. */ + *out_storage_id = storage_id; + return ResultSuccess(); + } + + return ncm::ResultNotEnoughInstallSpace(); + } + + const char *GetStorageIdString(StorageId storage_id) { + u8 id = static_cast(storage_id); + return id > 5 ? "(unknown)" : StorageIdStrings[id]; + } + + const char *GetStorageIdStringForPlayReport(StorageId storage_id) { + u8 id = static_cast(storage_id); + return id > 5 ? "(unknown)" : StorageIdStringsForPlayReport[id]; + } + +} diff --git a/libraries/libvapours/include/vapours/results/ncm_results.hpp b/libraries/libvapours/include/vapours/results/ncm_results.hpp index 494d7b4bd..d597c0d94 100644 --- a/libraries/libvapours/include/vapours/results/ncm_results.hpp +++ b/libraries/libvapours/include/vapours/results/ncm_results.hpp @@ -38,6 +38,7 @@ namespace ams::ncm { R_DEFINE_ERROR_RESULT(InvalidPlaceHolderFile, 170); R_DEFINE_ERROR_RESULT(BufferInsufficient, 180); R_DEFINE_ERROR_RESULT(WriteToReadOnlyContentStorage, 190); + R_DEFINE_ERROR_RESULT(NotEnoughInstallSpace, 200); R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240); R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310); @@ -56,6 +57,10 @@ namespace ams::ncm { R_DEFINE_ERROR_RESULT(SdCardContentMetaDatabaseNotActive, 264); R_DEFINE_ERROR_RESULT(UnknownContentMetaDatabaseNotActive, 268); + R_DEFINE_ERROR_RANGE(InstallTaskCancelled, 290, 299); + R_DEFINE_ERROR_RESULT(CreatePlaceHolderCancelled, 291); + R_DEFINE_ERROR_RESULT(WritePlaceHolderCancelled, 292); + R_DEFINE_ERROR_RANGE(InvalidArgument, 8181, 8191); R_DEFINE_ERROR_RESULT(InvalidOffset, 8182);