From b8e2c968249853f97f535b779d35977cd088d759 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 31 Mar 2020 19:39:35 -0700 Subject: [PATCH] ncm: cleanup client code --- .../include/stratosphere/fs/fs_rights_id.hpp | 4 +- .../stratosphere/kvdb/kvdb_auto_buffer.hpp | 2 +- .../include/stratosphere/ncm.hpp | 4 +- .../stratosphere/ncm/ncm_auto_buffer.hpp | 2 +- .../stratosphere/ncm/ncm_content_id.hpp | 8 +- .../ncm/ncm_content_info_data.hpp | 1 - .../ncm/ncm_content_info_utils.hpp | 5 + .../stratosphere/ncm/ncm_content_meta.hpp | 96 +- .../ncm/ncm_content_meta_extended_data.hpp | 384 ++++++ .../stratosphere/ncm/ncm_content_meta_key.hpp | 6 +- .../ncm/ncm_firmware_variation.hpp | 16 +- .../stratosphere/ncm/ncm_install_progress.hpp | 10 +- .../ncm/ncm_install_task_base.hpp | 157 +-- .../ncm/ncm_install_task_data.hpp | 28 +- ...l_progress_state.hpp => ncm_max_count.hpp} | 12 +- .../ncm/ncm_package_install_task.hpp | 3 - .../ncm/ncm_package_install_task_base.hpp | 6 +- .../ncm/ncm_package_system_update_task.hpp | 3 +- .../stratosphere/ncm/ncm_placeholder_id.hpp | 8 +- .../stratosphere/ncm/ncm_storage_id.hpp | 5 + ...age_id_utils.hpp => ncm_storage_utils.hpp} | 6 +- .../ncm_submission_package_install_task.hpp | 2 +- .../nim/nim_network_install_manager_api.hpp | 2 +- .../stratosphere/os/os_managed_handle.hpp | 4 +- .../stratosphere/sm/sm_scoped_holder.hpp | 4 +- .../source/lr/lr_location_redirector.cpp | 2 +- .../source/lr/lr_registered_data.hpp | 14 +- .../source/ncm/ncm_content_info_utils.cpp | 48 +- .../source/ncm/ncm_content_manager_impl.cpp | 13 +- .../source/ncm/ncm_content_meta.cpp | 103 +- .../ncm/ncm_content_meta_database_impl.cpp | 2 +- .../ncm/ncm_content_meta_database_impl.hpp | 2 +- .../source/ncm/ncm_content_meta_utils.cpp | 20 +- .../source/ncm/ncm_install_task_base.cpp | 1048 +++++++++-------- .../source/ncm/ncm_install_task_data.cpp | 52 +- .../ncm/ncm_package_install_task_base.cpp | 25 +- .../ncm/ncm_package_system_update_task.cpp | 61 +- ...age_id_utils.cpp => ncm_storage_utils.cpp} | 99 +- .../nim/nim_network_install_manager_api.cpp | 2 +- .../include/vapours/results/ncm_results.hpp | 1 + 40 files changed, 1395 insertions(+), 875 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_extended_data.hpp rename libraries/libstratosphere/include/stratosphere/ncm/{ncm_install_progress_state.hpp => ncm_max_count.hpp} (74%) rename libraries/libstratosphere/include/stratosphere/ncm/{ncm_storage_id_utils.hpp => ncm_storage_utils.hpp} (92%) rename libraries/libstratosphere/source/ncm/{ncm_storage_id_utils.cpp => ncm_storage_utils.cpp} (54%) diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp index 9d03387de..e13bca7bf 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp @@ -25,7 +25,7 @@ namespace ams::fs { static_assert(sizeof(RightsId) == 0x10); static_assert(std::is_pod::value); - inline bool operator==(const RightsId &lhs, const RightsId& rhs) { + inline bool operator==(const RightsId &lhs, const RightsId &rhs) { return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(RightsId)) == 0; } @@ -33,7 +33,7 @@ namespace ams::fs { return !(lhs == rhs); } - inline bool operator<(const RightsId &lhs, const RightsId& rhs) { + inline bool operator<(const RightsId &lhs, const RightsId &rhs) { return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(RightsId)) < 0; } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp index f2fdee47a..261fbe28d 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_auto_buffer.hpp @@ -37,7 +37,7 @@ namespace ams::kvdb { rhs.size = 0; } - AutoBuffer& operator=(AutoBuffer &&rhs) { + AutoBuffer &operator=(AutoBuffer &&rhs) { AutoBuffer(std::move(rhs)).Swap(*this); return *this; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index c89f5b907..a43a872fc 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -17,12 +17,14 @@ #pragma once #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -35,5 +37,5 @@ #include #include #include -#include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp index 4b71a3479..095343b5e 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_auto_buffer.hpp @@ -37,7 +37,7 @@ namespace ams::ncm { rhs.size = 0; } - AutoBuffer& operator=(AutoBuffer &&rhs) { + AutoBuffer &operator=(AutoBuffer &&rhs) { AutoBuffer(std::move(rhs)).Swap(*this); return *this; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp index 2c727c843..be0ef1e65 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_id.hpp @@ -21,19 +21,19 @@ namespace ams::ncm { struct alignas(4) ContentId { util::Uuid uuid; - bool operator==(const ContentId& other) const { + bool operator==(const ContentId &other) const { return this->uuid == other.uuid; } - bool operator!=(const ContentId& other) const { + bool operator!=(const ContentId &other) const { return this->uuid != other.uuid; } - bool operator==(const util::Uuid& other) const { + bool operator==(const util::Uuid &other) const { return this->uuid == other; } - bool operator!=(const util::Uuid& other) const { + bool operator!=(const util::Uuid &other) const { return this->uuid != other; } }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp index 2922ed015..4fea848f5 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp @@ -62,7 +62,6 @@ namespace ams::ncm { StorageId storage_id; bool is_temporary; bool is_sha256_calculated; - u8 reserved[2]; s64 written; constexpr const ContentId &GetId() const { diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp index 600848386..2f5bb3594 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_utils.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include namespace ams::ncm { @@ -23,4 +24,8 @@ namespace ams::ncm { s64 CalculateRequiredSize(s64 file_size, s64 cluster_size = MaxClusterSize); s64 CalculateRequiredSizeForExtension(s64 file_size, s64 cluster_size = MaxClusterSize); + class ContentMetaDatabase; + + Result EstimateRequiredSize(s64 *out_size, const ContentMetaKey &key, ContentMetaDatabase *db); + } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp index 71da4e557..a307da0f6 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp @@ -112,11 +112,6 @@ namespace ams::ncm { u32 extended_data_size; }; - struct SystemUpdateMetaExtendedDataHeader { - u32 unk; // Always seems to be set to 2 - u32 firmware_variation_count; - }; - template class ContentMetaAccessor { public: @@ -227,6 +222,7 @@ namespace ams::ncm { } HeaderType *GetWritableHeader() const { + AMS_ABORT_UNLESS(this->is_header_valid); return reinterpret_cast(this->data); } @@ -339,13 +335,8 @@ namespace ams::ncm { size_t CalculateConvertContentMetaSize() const; void ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta); - Result CalculateConvertFragmentOnlyInstallContentMetaSize(size_t *out_size, u32 source_version) { - AMS_ABORT("Not implemented"); - }; - - Result ConvertToFragmentOnlyInstallContentMeta(void *dst, size_t size, const InstallContentInfo &content_info, u32 source_version) { - AMS_ABORT("Not implemented"); - } + Result CalculateConvertFragmentOnlyInstallContentMetaSize(size_t *out_size, u32 source_version) const; + Result ConvertToFragmentOnlyInstallContentMeta(void *dst, size_t size, const InstallContentInfo &content_info, u32 source_version); size_t CountDeltaFragments() const; @@ -358,6 +349,10 @@ namespace ams::ncm { public: constexpr InstallContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } + using ContentMetaAccessor::CalculateSize; + using ContentMetaAccessor::CalculateContentRequiredSize; + using ContentMetaAccessor::GetStorageId; + size_t CalculateConvertSize() const; void ConvertToContentMeta(void *dst, size_t size) const; @@ -373,81 +368,4 @@ namespace ams::ncm { using ContentMetaAccessor::SetStorageId; }; - class SystemUpdateMetaExtendedDataReaderWriterBase { - private: - void *data; - const size_t size; - bool is_header_valid; - protected: - constexpr SystemUpdateMetaExtendedDataReaderWriterBase(const void *d, size_t sz) : data(const_cast(d)), size(sz), is_header_valid(true) { /* ... */ } - constexpr SystemUpdateMetaExtendedDataReaderWriterBase(void *d, size_t sz) : data(d), size(sz), is_header_valid(false) { /* ... */ } - - uintptr_t GetHeaderAddress() const { - return reinterpret_cast(this->data); - } - - uintptr_t GetFirmwarVariationIdStartAddress() const { - return this->GetHeaderAddress() + sizeof(SystemUpdateMetaExtendedDataHeader); - } - - uintptr_t GetFirmwareVariationIdAddress(size_t i) const { - return this->GetFirmwarVariationIdStartAddress() + i * sizeof(FirmwareVariationId); - } - - uintptr_t GetFirmwareVariationInfoStartAddress() const { - return this->GetFirmwareVariationIdAddress(this->GetFirmwareVariationCount()); - } - - uintptr_t GetFirmwareVariationInfoAddress(size_t i) const { - return this->GetFirmwarVariationIdStartAddress() + i * sizeof(FirmwareVariationInfo); - } - - uintptr_t GetContentMetaInfoStartAddress() const { - return this->GetFirmwareVariationInfoAddress(this->GetFirmwareVariationCount()); - } - - uintptr_t GetContentMetaInfoAddress(size_t i) const { - return this->GetContentMetaInfoStartAddress() + i * sizeof(ContentMetaInfo); - } - - public: - const SystemUpdateMetaExtendedDataHeader *GetHeader() const { - AMS_ABORT_UNLESS(this->is_header_valid); - return reinterpret_cast(this->GetHeaderAddress()); - } - - size_t GetFirmwareVariationCount() const { - return this->GetHeader()->firmware_variation_count; - } - - const FirmwareVariationId *GetFirmwareVariationId(size_t i) const { - AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount()); - - return reinterpret_cast(this->GetFirmwareVariationIdAddress(i)); - } - - const FirmwareVariationInfo *GetFirmwareVariationInfo(size_t i) const { - AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount()); - - return reinterpret_cast(this->GetFirmwareVariationInfoAddress(i)); - } - - void GetContentMetaInfoList(Span *out_list, size_t i) const { - size_t preceding_content_meta_count = 0; - - /* Count the number of preceding content metas. */ - for (size_t j = 0; j < i; j++) { - preceding_content_meta_count += GetFirmwareVariationInfo(j)->content_meta_count; - } - - /* Output the list. */ - *out_list = Span(reinterpret_cast(GetContentMetaInfoAddress(preceding_content_meta_count)), GetFirmwareVariationInfo(i)->content_meta_count); - } - }; - - class SystemUpdateMetaExtendedDataReader : public SystemUpdateMetaExtendedDataReaderWriterBase { - public: - constexpr SystemUpdateMetaExtendedDataReader(const void *data, size_t size) : SystemUpdateMetaExtendedDataReaderWriterBase(data, size) { /* ... */ } - }; - } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_extended_data.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_extended_data.hpp new file mode 100644 index 000000000..92ecfdb48 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_extended_data.hpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ncm { + + enum class UpdateType : u8 { + ApplyAsDelta = 0, + Overwrite = 1, + Create = 2, + }; + + struct FragmentIndicator { + u16 content_info_index; + u16 fragment_index; + }; + + struct FragmentSet { + ContentId source_content_id; + ContentId destination_content_id; + u32 source_size_low; + u16 source_size_high; + u16 destination_size_high; + u32 destination_size_low; + u16 fragment_count; + ContentType target_content_type; + UpdateType update_type; + u8 reserved[4]; + + constexpr s64 GetSourceSize() const { + return (static_cast(this->source_size_high) << 32) + this->source_size_low; + } + + constexpr s64 GetDestinationSize() const { + return (static_cast(this->destination_size_high) << 32) + this->destination_size_low; + } + + constexpr void SetSourceSize(s64 size) { + this->source_size_low = size & 0xFFFFFFFFll; + this->source_size_high = static_cast(size >> 32); + } + + constexpr void SetDestinationSize(s64 size) { + this->destination_size_low = size & 0xFFFFFFFFll; + this->destination_size_high = static_cast(size >> 32); + } + }; + + struct SystemUpdateMetaExtendedDataHeader { + u32 unk; // Always seems to be set to 2 + u32 firmware_variation_count; + }; + + struct DeltaMetaExtendedDataHeader { + PatchId source_id; + PatchId destination_id; + u32 source_version; + u32 destination_version; + u16 fragment_set_count; + u8 reserved[6]; + }; + + struct PatchMetaExtendedDataHeader { + u32 history_count; + u32 delta_history_count; + u32 delta_count; + u32 fragment_set_count; + u32 history_content_total_count; + u32 delta_content_total_count; + u8 reserved[4]; + }; + + struct PatchHistoryHeader { + ContentMetaKey key; + Digest digest; + u16 content_count; + u8 reserver[2]; + }; + + struct PatchDeltaHistory { + PatchId source_id; + PatchId destination_id; + u32 source_version; + u32 destination_version; + u64 download_size; + u8 reserved[4]; + }; + + struct PatchDeltaHeader { + DeltaMetaExtendedDataHeader delta; + u16 content_count; + u8 reserved[4]; + }; + + template + class PatchMetaExtendedDataReaderWriterBase { + private: + MemberTypePointer data; + const size_t size; + public: + PatchMetaExtendedDataReaderWriterBase(MemberTypePointer d, size_t sz) : data(d), size(sz) { /* ... */ } + protected: + s32 CountFragmentSet(s32 delta_index) const { + auto delta_header = this->GetPatchDeltaHeader(0); + s32 count = 0; + for (s32 i = 0; i < delta_index; i++) { + count += delta_header[i].delta.fragment_set_count; + } + return count; + } + + s32 CountHistoryContent(s32 history_index) const { + auto history_header = this->GetPatchHistoryHeader(0); + s32 count = 0; + for (s32 i = 0; i < history_index; i++) { + count += history_header[i].content_count; + } + return count; + } + + s32 CountDeltaContent(s32 delta_index) const { + auto delta_header = this->GetPatchDeltaHeader(0); + s32 count = 0; + for (s32 i = 0; i < delta_index; i++) { + count += delta_header[i].content_count; + } + return count; + } + + s32 CountFragment(s32 index) const { + auto fragment_set = this->GetFragmentSet(0, 0); + s32 count = 0; + for (s32 i = 0; i < index; i++) { + count += fragment_set[i].fragment_count; + } + return count; + } + + DataTypePointer GetHeaderAddress() const { + return reinterpret_cast(this->data); + } + + DataTypePointer GetPatchHistoryHeaderAddress(s32 index) const { + auto header = this->GetHeader(); + AMS_ABORT_UNLESS(static_cast(index) < header->history_count); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * index; + } + + DataTypePointer GetPatchDeltaHistoryAddress(s32 index) const { + auto header = this->GetHeader(); + AMS_ABORT_UNLESS(static_cast(index) < header->delta_history_count); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * header->history_count + + sizeof(PatchDeltaHistory) * index; + } + + DataTypePointer GetPatchDeltaHeaderAddress(s32 index) const { + auto header = this->GetHeader(); + AMS_ABORT_UNLESS(static_cast(index) < header->delta_count); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * header->history_count + + sizeof(PatchDeltaHistory) * header->delta_history_count + + sizeof(PatchDeltaHeader) * index; + } + + DataTypePointer GetFragmentSetAddress(s32 delta_index, s32 fragment_set_index) const { + auto header = this->GetHeader(); + AMS_ABORT_UNLESS(static_cast(delta_index) < header->delta_count); + + auto delta_header = this->GetPatchDeltaHeader(delta_index); + AMS_ABORT_UNLESS(static_cast(fragment_set_index) < delta_header->delta.fragment_set_count); + + auto previous_fragment_set_count = this->CountFragmentSet(delta_index); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * header->history_count + + sizeof(PatchDeltaHistory) * header->delta_history_count + + sizeof(PatchDeltaHeader) * header->delta_count + + sizeof(FragmentSet) * (previous_fragment_set_count + fragment_set_index); + } + + DataTypePointer GetPatchHistoryContentInfoAddress(s32 history_index, s32 content_index) const { + auto header = this->GetHeader(); + auto history_header = this->GetPatchHistoryHeader(history_index); + AMS_ABORT_UNLESS(static_cast(content_index) < history_header->content_count); + + auto prev_history_count = this->CountHistoryContent(history_index); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * header->history_count + + sizeof(PatchDeltaHistory) * header->delta_history_count + + sizeof(PatchDeltaHeader) * header->delta_count + + sizeof(FragmentSet) * header->fragment_set_count + + sizeof(ContentInfo) * (prev_history_count + content_index); + } + + DataTypePointer GetPatchDeltaPackagedContentInfoAddress(s32 delta_index, s32 content_index) const { + auto header = this->GetHeader(); + auto delta_header = this->GetPatchDeltaHeader(delta_index); + AMS_ABORT_UNLESS(static_cast(content_index) < delta_header->content_count); + + auto content_count = this->CountDeltaContent(delta_index); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * header->history_count + + sizeof(PatchDeltaHistory) * header->delta_history_count + + sizeof(PatchDeltaHeader) * header->delta_count + + sizeof(FragmentSet) * header->fragment_set_count + + sizeof(ContentInfo) * header->history_content_total_count + + sizeof(PackagedContentInfo) * (content_count + content_index); + } + + DataTypePointer GetFragmentIndicatorAddress(s32 delta_index, s32 fragment_set_index, s32 index) const { + auto header = this->GetHeader(); + auto fragment_set = this->GetFragmentSet(delta_index, fragment_set_index); + AMS_ABORT_UNLESS(static_cast(index) < fragment_set->fragment_count); + + auto fragment_set_count = this->CountFragmentSet(delta_index); + auto fragment_count = this->CountFragment(fragment_set_count + fragment_set_index); + + return this->GetHeaderAddress() + + sizeof(PatchMetaExtendedDataHeader) + + sizeof(PatchHistoryHeader) * header->history_count + + sizeof(PatchDeltaHistory) * header->delta_history_count + + sizeof(PatchDeltaHeader) * header->delta_count + + sizeof(FragmentSet) * header->fragment_set_count + + sizeof(ContentInfo) * header->history_content_total_count + + sizeof(PackagedContentInfo) * header->delta_content_total_count + + sizeof(FragmentIndicator) * (fragment_count + index); + } + public: + const PatchMetaExtendedDataHeader *GetHeader() const { + return reinterpret_cast(this->GetHeaderAddress()); + } + + const PatchHistoryHeader *GetPatchHistoryHeader(s32 index) const { + return reinterpret_cast(this->GetPatchHistoryHeaderAddress(index)); + } + + const PatchDeltaHistory *GetPatchDeltaHistory(s32 index) const { + return reinterpret_cast(this->GetPatchDeltaHistoryAddress(index)); + } + + const ContentInfo *GetPatchHistoryContentInfo(s32 history_index, s32 content_index) const { + return reinterpret_cast(this->GetPatchHistoryContentInfoAddress(history_index, content_index)); + } + + const PatchDeltaHeader *GetPatchDeltaHeader(s32 index) const { + return reinterpret_cast(this->GetPatchDeltaHeaderAddress(index)); + } + + const PackagedContentInfo *GetPatchDeltaPackagedContentInfo(s32 delta_index, s32 content_index) const { + return reinterpret_cast(this->GetPatchDeltaPackagedContentInfoAddress(delta_index, content_index)); + } + + const FragmentSet *GetFragmentSet(s32 delta_index, s32 fragment_set_index) const { + return reinterpret_cast(this->GetFragmentSetIndex(delta_index, fragment_set_index)); + } + + const FragmentIndicator *GetFragmentIndicator(s32 delta_index, s32 fragment_set_index, s32 index) const { + return reinterpret_cast(this->GetFragmentIndicatorAddress(delta_index, fragment_set_index, index)); + } + + const FragmentIndicator *FindFragmentIndicator(s32 delta_index, s32 fragment_set_index, s32 fragment_index) const { + auto fragment_set = this->GetFragmentSet(delta_index, fragment_set_index); + auto fragment = this->GetFragmentIndicator(delta_index, fragment_set_index, 0); + for (s32 i = 0; i < fragment_set->fragment_count; i++) { + if (fragment[i].fragment_index == fragment_index) { + return std::addressof(fragment[i]); + } + } + return nullptr; + } + }; + + class PatchMetaExtendedDataReader : public PatchMetaExtendedDataReaderWriterBase { + public: + PatchMetaExtendedDataReader(const void *data, size_t size) : PatchMetaExtendedDataReaderWriterBase(data, size) { /* ... */ } + }; + + class SystemUpdateMetaExtendedDataReaderWriterBase { + private: + void *data; + const size_t size; + bool is_header_valid; + protected: + constexpr SystemUpdateMetaExtendedDataReaderWriterBase(const void *d, size_t sz) : data(const_cast(d)), size(sz), is_header_valid(true) { /* ... */ } + constexpr SystemUpdateMetaExtendedDataReaderWriterBase(void *d, size_t sz) : data(d), size(sz), is_header_valid(false) { /* ... */ } + + uintptr_t GetHeaderAddress() const { + return reinterpret_cast(this->data); + } + + uintptr_t GetFirmwarVariationIdStartAddress() const { + return this->GetHeaderAddress() + sizeof(SystemUpdateMetaExtendedDataHeader); + } + + uintptr_t GetFirmwareVariationIdAddress(size_t i) const { + return this->GetFirmwarVariationIdStartAddress() + i * sizeof(FirmwareVariationId); + } + + uintptr_t GetFirmwareVariationInfoStartAddress() const { + return this->GetFirmwareVariationIdAddress(this->GetFirmwareVariationCount()); + } + + uintptr_t GetFirmwareVariationInfoAddress(size_t i) const { + return this->GetFirmwarVariationIdStartAddress() + i * sizeof(FirmwareVariationInfo); + } + + uintptr_t GetContentMetaInfoStartAddress() const { + return this->GetFirmwareVariationInfoAddress(this->GetFirmwareVariationCount()); + } + + uintptr_t GetContentMetaInfoAddress(size_t i) const { + return this->GetContentMetaInfoStartAddress() + i * sizeof(ContentMetaInfo); + } + public: + const SystemUpdateMetaExtendedDataHeader *GetHeader() const { + AMS_ABORT_UNLESS(this->is_header_valid); + return reinterpret_cast(this->GetHeaderAddress()); + } + + size_t GetFirmwareVariationCount() const { + return this->GetHeader()->firmware_variation_count; + } + + const FirmwareVariationId *GetFirmwareVariationId(size_t i) const { + AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount()); + + return reinterpret_cast(this->GetFirmwareVariationIdAddress(i)); + } + + const FirmwareVariationInfo *GetFirmwareVariationInfo(size_t i) const { + AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount()); + + return reinterpret_cast(this->GetFirmwareVariationInfoAddress(i)); + } + + void GetContentMetaInfoList(Span *out_list, size_t i) const { + size_t preceding_content_meta_count = 0; + + /* Count the number of preceding content metas. */ + for (size_t j = 0; j < i; j++) { + preceding_content_meta_count += this->GetFirmwareVariationInfo(j)->content_meta_count; + } + + /* Output the list. */ + *out_list = Span(reinterpret_cast(this->GetContentMetaInfoAddress(preceding_content_meta_count)), this->GetFirmwareVariationInfo(i)->content_meta_count); + } + }; + + class SystemUpdateMetaExtendedDataReader : public SystemUpdateMetaExtendedDataReaderWriterBase { + public: + constexpr SystemUpdateMetaExtendedDataReader(const void *data, size_t size) : SystemUpdateMetaExtendedDataReaderWriterBase(data, size) { /* ... */ } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp index 2b796d5b0..0c8ed99ef 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_key.hpp @@ -34,15 +34,15 @@ namespace ams::ncm { ContentInstallType install_type; u8 padding[2]; - bool operator<(const ContentMetaKey& rhs) const { + bool operator<(const ContentMetaKey &rhs) const { return std::tie(this->id, this->version, this->type, this->install_type) < std::tie(rhs.id, rhs.version, rhs.type, rhs.install_type); } - constexpr bool operator==(const ContentMetaKey& rhs) const { + constexpr bool operator==(const ContentMetaKey &rhs) const { return std::tie(this->id, this->version, this->type, this->install_type) == std::tie(rhs.id, rhs.version, rhs.type, rhs.install_type); } - constexpr bool operator!=(const ContentMetaKey& rhs) const { + constexpr bool operator!=(const ContentMetaKey &rhs) const { return !(*this == rhs); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp index 41e31bd2c..d89fa1ea5 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp @@ -27,14 +27,14 @@ namespace ams::ncm { struct FirmwareVariationId { u32 value; - - bool operator==(const FirmwareVariationId& other) const { - return this->value == other.value; - } - - bool operator!=(const FirmwareVariationId& other) const { - return this->value != other.value; - } }; + constexpr inline bool operator==(const FirmwareVariationId &lhs, const FirmwareVariationId &rhs) { + return lhs.value == rhs.value; + } + + constexpr inline bool operator!=(const FirmwareVariationId &lhs, const FirmwareVariationId &rhs) { + return lhs.value != rhs.value; + } + } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp index 01548b998..ba70fe1fc 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress.hpp @@ -14,10 +14,18 @@ * along with this program. If not, see . */ #pragma once -#include namespace ams::ncm { + enum class InstallProgressState : u8 { + NotPrepared = 0, + DataPrepared = 1, + Prepared = 2, + Downloaded = 3, + Committed = 4, + Fatal = 5, + }; + struct InstallProgress { InstallProgressState state; u8 pad[3]; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp index 1b115d266..7f93a7dad 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp @@ -79,6 +79,8 @@ namespace ams::ncm { static_assert(sizeof(InstallContentMetaInfo) == 0x50); class InstallTaskBase { + NON_COPYABLE(InstallTaskBase); + NON_MOVEABLE(InstallTaskBase); private: crypto::Sha256Generator sha256_generator; StorageId install_storage; @@ -92,8 +94,6 @@ namespace ams::ncm { TimeSpan throughput_start_time; os::Mutex throughput_mutex; FirmwareVariationId firmware_variation_id; - public: - virtual ~InstallTaskBase() { /* ... */ }; private: ALWAYS_INLINE Result SetLastResultOnFailure(Result result) { if (R_FAILED(result)) { @@ -101,78 +101,103 @@ namespace ams::ncm { } return result; } - private: - Result PrepareImpl(); - Result CleanupOne(const InstallContentMeta &content_meta); - Result ExecuteImpl(); - Result CommitImpl(const StorageContentMetaKey *keys, s32 num_keys); - InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional is_temporary); - Result ReadContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const ContentMetaKey &key); - Result ListRightsIdsByInstallContentMeta(s32 *out_count, Span out_span, const InstallContentMeta &content_meta, s32 offset); - protected: - Result Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config); - Result CountInstallContentMetaData(s32 *out_count); - Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index); - Result WritePlaceHolderBuffer(InstallContentInfo *content_info, const void *data, size_t data_size); - Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary); - Result PrepareContentMeta(const InstallContentMetaInfo &meta_info, std::optional key, std::optional source_version); - Result GetInstallContentMetaDataFromPath(AutoBuffer *out, const Path &path, const InstallContentInfo &content_info, std::optional source_version); - Result PrepareContentMeta(ContentId content_id, s64 size, ContentMetaType meta_type, AutoBuffer *buffer); - void PrepareAgain(); - Result PrepareSystemUpdateDependency(); - Result PrepareContentMetaIfLatest(const ContentMetaKey &key); - Result GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out); - Result DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys); - Result CanContinue(); public: - 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 IncrementProgress(s64 size); - void UpdateThroughputMeasurement(s64 throughput); - bool IsNecessaryInstallTicket(const fs::RightsId &rights_id); - void SetTotalSize(s64 size); - Result PreparePlaceHolder(); - Result Cleanup(); - void CleanupProgress(); - Result ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset, ListContentMetaKeyFilter filter); - Result ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset); - Result ListApplicationContentMetaKey(s32 *out_keys_written, ApplicationContentMetaKey *out_keys, s32 out_keys_count, s32 offset); - Result Execute(); - void StartThroughputMeasurement(); - Result WritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info); - Result PrepareAndExecute(); - Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys); - Result Commit(const StorageContentMetaKey *keys, s32 num_keys); - Result Commit(); - Result IncludesExFatDriver(bool *out); - Result IsNewerThanInstalled(bool *out, const ContentMetaKey &key); - void ResetLastResult(); - s64 GetThroughput(); - Result CalculateContentsSize(s64 *out_size, const ContentMetaKey &key, StorageId storage_id); - Result FindMaxRequiredApplicationVersion(u32 *out); - Result FindMaxRequiredSystemVersion(u32 *out); - Result ListOccupiedSize(s32 *out_written, InstallTaskOccupiedSize *out_list, s32 out_list_size, s32 offset); - void SetFirmwareVariationId(FirmwareVariationId id); - Result ListRightsIds(s32 *out_count, Span out_span, const ContentMetaKey &key, s32 offset); - protected: - virtual Result OnPrepareComplete(); - virtual Result PrepareDependency(); + InstallTaskBase() : data(), progress(), cancel_requested() { /* ... */ } + virtual ~InstallTaskBase() { /* ... */ }; public: virtual void Cancel(); virtual void ResetCancel(); + + Result Prepare(); + Result GetPreparedPlaceHolderPath(Path *out_path, u64 id, ContentMetaType meta_type, ContentType type); + Result CalculateRequiredSize(s64 *out_size); + Result Cleanup(); + Result ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset, ListContentMetaKeyFilter filter); + Result ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset) { return this->ListContentMetaKey(out_keys_written, out_keys, out_keys_count, offset, ListContentMetaKeyFilter::All); } + Result ListApplicationContentMetaKey(s32 *out_keys_written, ApplicationContentMetaKey *out_keys, s32 out_keys_count, s32 offset); + Result Execute(); + Result PrepareAndExecute(); + Result Commit(const StorageContentMetaKey *keys, s32 num_keys); + Result Commit() { return this->Commit(nullptr, 0); } virtual InstallProgress GetProgress(); + void ResetLastResult(); + Result IncludesExFatDriver(bool *out); + InstallThroughput GetThroughput(); + Result CalculateContentsSize(s64 *out_size, const ContentMetaKey &key, StorageId storage_id); + Result ListOccupiedSize(s32 *out_written, InstallTaskOccupiedSize *out_list, s32 out_list_size, s32 offset); + + Result FindMaxRequiredApplicationVersion(u32 *out); + Result FindMaxRequiredSystemVersion(u32 *out); + protected: + Result Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config); + + Result PrepareContentMeta(const InstallContentMetaInfo &meta_info, std::optional key, std::optional source_version); + Result PrepareContentMeta(ContentId content_id, s64 size, ContentMetaType meta_type, AutoBuffer *buffer); + Result WritePlaceHolderBuffer(InstallContentInfo *content_info, const void *data, size_t data_size); + void PrepareAgain(); + + Result CountInstallContentMetaData(s32 *out_count); + Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index); + Result DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys); + + virtual Result PrepareDependency(); + Result PrepareSystemUpdateDependency(); + Result PrepareContentMetaIfLatest(const ContentMetaKey &key); + u32 GetConfig() const { return this->config; } + Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary); + + StorageId GetInstallStorage() const { return this->install_storage; } + + virtual Result OnPrepareComplete() { return ResultSuccess(); } + + Result GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out); + + Result CanContinue(); + private: + bool IsCancelRequested(); + Result PrepareImpl(); + Result ExecuteImpl(); + Result CommitImpl(const StorageContentMetaKey *keys, s32 num_keys); + Result CleanupOne(const InstallContentMeta &content_meta); + + Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys); + virtual Result PrepareInstallContentMetaData() = 0; - virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key); - virtual Result GetLatestVersion(std::optional *out_version, u64 id); - virtual Result CheckInstallable(); - virtual Result OnExecuteComplete(); + virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key) = 0; + virtual Result GetLatestVersion(std::optional *out_version, u64 id) { return ncm::ResultContentMetaNotFound(); } + + virtual Result OnExecuteComplete() { return ResultSuccess(); } + + Result WritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info); virtual Result OnWritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info) = 0; + + bool IsNecessaryInstallTicket(const fs::RightsId &rights_id); virtual Result InstallTicket(const fs::RightsId &rights_id, ContentMetaType meta_type) = 0; + + Result IsNewerThanInstalled(bool *out, const ContentMetaKey &key); + Result PreparePlaceHolder(); + + void SetProgressState(InstallProgressState state); + void IncrementProgress(s64 size); + void SetTotalSize(s64 size); + void SetLastResult(Result last_result); + void CleanupProgress(); + + void ResetThroughputMeasurement(); + void StartThroughputMeasurement(); + void UpdateThroughputMeasurement(s64 throughput); + + Result GetInstallContentMetaDataFromPath(AutoBuffer *out, const Path &path, const InstallContentInfo &content_info, std::optional source_version); + + InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional is_temporary); + + Result ReadContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const ContentMetaKey &key); + Result ListRightsIdsByInstallContentMeta(s32 *out_count, Span out_span, const InstallContentMeta &content_meta, s32 offset); + public: + virtual Result CheckInstallable() { return ResultSuccess(); } + + void SetFirmwareVariationId(FirmwareVariationId id) { this->firmware_variation_id = id; } + Result ListRightsIds(s32 *out_count, Span out_span, const ContentMetaKey &key, s32 offset); }; } 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 4fe12766a..524f93cb8 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_data.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_data.hpp @@ -64,7 +64,7 @@ namespace ams::ncm { Result last_result; SystemUpdateTaskApplyInfo system_update_task_apply_info; public: - MemoryInstallTaskData() { /* ... */ }; + MemoryInstallTaskData() : state(InstallProgressState::NotPrepared), last_result(ResultSuccess()) { /* ... */ }; ~MemoryInstallTaskData() { this->Cleanup(); } @@ -87,8 +87,8 @@ namespace ams::ncm { class FileInstallTaskData : public InstallTaskDataBase { private: struct Header { - s32 max_entries; - s32 count; + u32 max_entries; + u32 count; s64 last_data_offset; Result last_result; InstallProgressState progress_state; @@ -107,13 +107,9 @@ namespace ams::ncm { Header header; char path[64]; private: - static constexpr s64 GetEntryInfoOffset(s32 index) { - return index * sizeof(EntryInfo) + sizeof(Header); - } - static constexpr Header MakeInitialHeader(s32 max_entries) { return { - .max_entries = max_entries, + .max_entries = static_cast(max_entries), .count = 0, .last_data_offset = GetEntryInfoOffset(max_entries), .last_result = ResultSuccess(), @@ -121,6 +117,10 @@ namespace ams::ncm { .system_update_task_apply_info = SystemUpdateTaskApplyInfo::Unknown, }; } + + static constexpr s64 GetEntryInfoOffset(s32 index) { + return index * sizeof(EntryInfo) + sizeof(Header); + } public: static Result Create(const char *path, s32 max_entries); Result Initialize(const char *path); @@ -135,15 +135,15 @@ namespace ams::ncm { virtual Result Delete(const ContentMetaKey *keys, s32 num_keys) override; virtual Result Cleanup() override; private: - Result GetEntryInfo(EntryInfo *out_entry_info, s32 index); - - Result Read(void *out, size_t out_size, s64 offset); - Result Write(const void *data, size_t size, s64 offset); - Result WriteHeader(); - virtual Result GetSize(size_t *out_size, s32 index) override; virtual Result Get(s32 index, void *out, size_t out_size) override; virtual Result Update(s32 index, const void *data, size_t data_size) override; + + Result GetEntryInfo(EntryInfo *out_entry_info, s32 index); + + Result Write(const void *data, size_t size, s64 offset); + Result Read(void *out, size_t out_size, s64 offset); + Result WriteHeader(); }; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress_state.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_max_count.hpp similarity index 74% rename from libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress_state.hpp rename to libraries/libstratosphere/include/stratosphere/ncm/ncm_max_count.hpp index 682b656dd..5f82715df 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_progress_state.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_max_count.hpp @@ -18,13 +18,9 @@ namespace ams::ncm { - enum class InstallProgressState : u8 { - NotPrepared = 0, - DataPrepared = 1, - Prepared = 2, - Downloaded = 3, - Committed = 4, - Fatal = 5, - }; + constexpr inline s32 SystemMaxContentMetaCount = 0x800; + constexpr inline s32 GameCardMaxContentMetaCount = 0x800; + constexpr inline s32 UserMaxContentMetaCount = 0x2000; + constexpr inline s32 SdCardMaxContentMetaCount = 0x2000; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task.hpp index aa53b9a70..062f37a03 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task.hpp @@ -22,9 +22,6 @@ namespace ams::ncm { private: MemoryInstallTaskData data; public: - PackageInstallTask() { /* ... */ } - virtual ~PackageInstallTask() override { /* ... */ } - Result Initialize(const char *package_root, StorageId storage_id, void *buffer, size_t buffer_size, bool ignore_ticket); protected: bool IsContentMetaContentName(const char *name); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task_base.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task_base.hpp index 892921eb9..a2f987e87 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task_base.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_install_task_base.hpp @@ -35,13 +35,13 @@ namespace ams::ncm { return this->package_root.Get(); } private: + virtual Result OnWritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info) override; + virtual Result InstallTicket(const fs::RightsId &rights_id, ContentMetaType meta_type) override; + void CreateContentMetaPath(PackagePath *out_path, ContentId content_id); void CreateContentPath(PackagePath *out_path, ContentId content_id); void CreateTicketPath(PackagePath *out_path, fs::RightsId id); void CreateCertificatePath(PackagePath *out_path, fs::RightsId id); - private: - virtual Result OnWritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info) override; - virtual Result InstallTicket(const fs::RightsId &rights_id, ContentMetaType meta_type) override; }; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp index ca9c6204c..4648d20f6 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp @@ -35,9 +35,10 @@ namespace ams::ncm { protected: virtual Result PrepareInstallContentMetaData() override; private: - Result GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key); virtual Result PrepareDependency() override; virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) override; + + Result GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key); }; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp index 9b41a66d1..2da1eb6ca 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_placeholder_id.hpp @@ -21,19 +21,19 @@ namespace ams::ncm { struct alignas(8) PlaceHolderId { util::Uuid uuid; - bool operator==(const PlaceHolderId& other) const { + bool operator==(const PlaceHolderId &other) const { return this->uuid == other.uuid; } - bool operator!=(const PlaceHolderId& other) const { + bool operator!=(const PlaceHolderId &other) const { return this->uuid != other.uuid; } - bool operator==(const util::Uuid& other) const { + bool operator==(const util::Uuid &other) const { return this->uuid == other; } - bool operator!=(const util::Uuid& other) const { + bool operator!=(const util::Uuid &other) const { return this->uuid != other; } }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp index 532a79430..94e46aa74 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id.hpp @@ -26,6 +26,11 @@ namespace ams::ncm { BuiltInUser = 4, SdCard = 5, Any = 6, + + /* Aliases. */ + Card = GameCard, + BuildInSystem = BuiltInSystem, + BuildInUser = BuiltInUser, }; constexpr inline bool IsUniqueStorage(StorageId id) { diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_utils.hpp similarity index 92% rename from libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp rename to libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_utils.hpp index da4aa560b..371a71eed 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_id_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_storage_utils.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include namespace ams::ncm { @@ -63,11 +64,12 @@ namespace ams::ncm { break; AMS_UNREACHABLE_DEFAULT_CASE(); } - + return list; } - Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, size_t required_size); + Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, s64 required_size); + Result SelectPatchStorage(StorageId *out_storage_id, StorageId storage_id, PatchId patch_id); const char *GetStorageIdString(StorageId storage_id); const char *GetStorageIdStringForPlayReport(StorageId storage_id); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_submission_package_install_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_submission_package_install_task.hpp index 7a3b08b33..91f8b2001 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_submission_package_install_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_submission_package_install_task.hpp @@ -28,7 +28,7 @@ namespace ams::ncm { SubmissionPackageInstallTask(); virtual ~SubmissionPackageInstallTask() override; - Result Initialize(fs::FileHandle handle, StorageId storage_id, void *buffer, size_t buffer_size, bool ignore_ticket); + Result Initialize(fs::FileHandle handle, StorageId storage_id, void *buffer, size_t buffer_size, bool ignore_ticket = false); }; } diff --git a/libraries/libstratosphere/include/stratosphere/nim/nim_network_install_manager_api.hpp b/libraries/libstratosphere/include/stratosphere/nim/nim_network_install_manager_api.hpp index 6456444f5..c32f932c2 100644 --- a/libraries/libstratosphere/include/stratosphere/nim/nim_network_install_manager_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/nim/nim_network_install_manager_api.hpp @@ -24,7 +24,7 @@ namespace ams::nim { void FinalizeForNetworkInstallManager(); /* Service API. */ - Result DestroySystemUpdateTask(const SystemUpdateTaskId& id); + Result DestroySystemUpdateTask(const SystemUpdateTaskId &id); s32 ListSystemUpdateTask(SystemUpdateTaskId *out_list, size_t out_list_size); diff --git a/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp b/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp index 1219cb6e5..b5e5281d4 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp @@ -38,7 +38,7 @@ namespace ams::os { rhs.hnd = INVALID_HANDLE; } - ManagedHandle& operator=(ManagedHandle&& rhs) { + ManagedHandle &operator=(ManagedHandle&& rhs) { rhs.Swap(*this); return *this; } @@ -47,7 +47,7 @@ namespace ams::os { return this->hnd != INVALID_HANDLE; } - void Swap(ManagedHandle& rhs) { + void Swap(ManagedHandle &rhs) { std::swap(this->hnd, rhs.hnd); } diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp index ecc9e1629..952f44b27 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_scoped_holder.hpp @@ -47,12 +47,12 @@ namespace ams::sm { rhs.has_initialized = false; } - ScopedServiceHolder& operator=(ScopedServiceHolder&& rhs) { + ScopedServiceHolder &operator=(ScopedServiceHolder&& rhs) { rhs.Swap(*this); return *this; } - void Swap(ScopedServiceHolder& rhs) { + void Swap(ScopedServiceHolder &rhs) { std::swap(this->result, rhs.result); std::swap(this->has_initialized, rhs.has_initialized); } diff --git a/libraries/libstratosphere/source/lr/lr_location_redirector.cpp b/libraries/libstratosphere/source/lr/lr_location_redirector.cpp index 9c0445dc5..6414d1503 100644 --- a/libraries/libstratosphere/source/lr/lr_location_redirector.cpp +++ b/libraries/libstratosphere/source/lr/lr_location_redirector.cpp @@ -27,7 +27,7 @@ namespace ams::lr { Path path; u32 flags; public: - Redirection(ncm::ProgramId program_id, ncm::ProgramId owner_id, const Path& path, u32 flags) : + Redirection(ncm::ProgramId program_id, ncm::ProgramId owner_id, const Path &path, u32 flags) : program_id(program_id), owner_id(owner_id), path(path), flags(flags) { /* ... */ } ncm::ProgramId GetProgramId() const { diff --git a/libraries/libstratosphere/source/lr/lr_registered_data.hpp b/libraries/libstratosphere/source/lr/lr_registered_data.hpp index 109baa5f2..741f3d4fe 100644 --- a/libraries/libstratosphere/source/lr/lr_registered_data.hpp +++ b/libraries/libstratosphere/source/lr/lr_registered_data.hpp @@ -47,7 +47,7 @@ namespace ams::lr { inline void RegisterImpl(size_t i, const Key &key, const Value &value, const ncm::ProgramId owner_id) { /* Populate entry. */ - Entry& entry = this->entries[i]; + Entry &entry = this->entries[i]; entry.key = key; entry.value = value; entry.owner_id = owner_id; @@ -61,7 +61,7 @@ namespace ams::lr { bool Register(const Key &key, const Value &value, const ncm::ProgramId owner_id) { /* Try to find an existing value. */ for (size_t i = 0; i < this->GetCapacity(); i++) { - Entry& entry = this->entries[i]; + Entry &entry = this->entries[i]; if (entry.is_valid && entry.key == key) { this->RegisterImpl(i, key, value, owner_id); return true; @@ -70,7 +70,7 @@ namespace ams::lr { /* We didn't find an existing entry, so try to create a new one. */ for (size_t i = 0; i < this->GetCapacity(); i++) { - Entry& entry = this->entries[i]; + Entry &entry = this->entries[i]; if (!entry.is_valid) { this->RegisterImpl(i, key, value, owner_id); return true; @@ -83,7 +83,7 @@ namespace ams::lr { void Unregister(const Key &key) { /* Invalidate entries with a matching key. */ for (size_t i = 0; i < this->GetCapacity(); i++) { - Entry& entry = this->entries[i]; + Entry &entry = this->entries[i]; if (entry.is_valid && entry.key == key) { entry.is_valid = false; } @@ -93,7 +93,7 @@ namespace ams::lr { void UnregisterOwnerProgram(ncm::ProgramId owner_id) { /* Invalidate entries with a matching owner id. */ for (size_t i = 0; i < this->GetCapacity(); i++) { - Entry& entry = this->entries[i]; + Entry &entry = this->entries[i]; if (entry.owner_id == owner_id) { entry.is_valid = false; } @@ -103,7 +103,7 @@ namespace ams::lr { bool Find(Value *out, const Key &key) const { /* Locate a matching entry. */ for (size_t i = 0; i < this->GetCapacity(); i++) { - const Entry& entry = this->entries[i]; + const Entry &entry = this->entries[i]; if (entry.is_valid && entry.key == key) { *out = entry.value; return true; @@ -123,7 +123,7 @@ namespace ams::lr { void ClearExcluding(const ncm::ProgramId *ids, size_t num_ids) { /* Invalidate all entries unless excluded. */ for (size_t i = 0; i < this->GetCapacity(); i++) { - Entry& entry = this->entries[i]; + Entry &entry = this->entries[i]; if (!this->IsExcluded(entry.owner_id, ids, num_ids)) { entry.is_valid = false; diff --git a/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp index 403ea5c9a..f900775d7 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_info_utils.cpp @@ -33,15 +33,57 @@ namespace ams::ncm { return size; } - + + template + Result ForEachContentInfo(const ContentMetaKey &key, ncm::ContentMetaDatabase *db, Handler handler) { + constexpr s32 MaxPerIteration = 0x10; + ContentInfo info_list[MaxPerIteration]; + s32 offset = 0; + while (true) { + /* List the content infos. */ + s32 count; + R_TRY(db->ListContentInfo(std::addressof(count), info_list, MaxPerIteration, key, offset)); + + /* Handle all that we listed. */ + for (s32 i = 0; i < count; i++) { + bool done = false; + R_TRY(handler(std::addressof(done), info_list[i])); + if (done) { + break; + } + } + + /* Check if we're done. */ + if (count != MaxPerIteration) { + break; + } + + offset += count; + } + + return ResultSuccess(); + } + } - + 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; } + Result EstimateRequiredSize(s64 *out_size, const ContentMetaKey &key, ncm::ContentMetaDatabase *db) { + s64 size = 0; + R_TRY(ForEachContentInfo(key, db, [&size](bool *out_done, const ContentInfo &info) -> Result { + size += CalculateRequiredSize(info.GetSize(), MaxClusterSize); + *out_done = false; + return ResultSuccess(); + })); + + *out_size = size; + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp index 3b73df4cb..20bff9295 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -71,11 +71,6 @@ namespace ams::ncm { .space_id = fs::SaveDataSpaceId::SdSystem, }; - constexpr size_t MaxBuiltInSystemContentMetaCount = 0x800; - constexpr size_t MaxBuiltInUserContentMetaCount = 0x2000; - constexpr size_t MaxSdCardContentMetaCount = 0x2000; - constexpr size_t MaxGameCardContentMetaCount = 0x800; - using RootPath = kvdb::BoundedString<32>; inline void ReplaceMountName(char *out_path, const char *mount_name, const char *path) { @@ -342,7 +337,7 @@ namespace ams::ncm { R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem)); /* Next, the BuiltInSystem content meta entry. */ - R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, MaxBuiltInSystemContentMetaCount, std::addressof(g_system_content_meta_memory_resource))); + R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInSystem, BuiltInSystemSystemSaveDataInfo, SystemMaxContentMetaCount, std::addressof(g_system_content_meta_memory_resource))); if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) { R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem)); @@ -370,18 +365,18 @@ namespace ams::ncm { /* Now for BuiltInUser's content storage and content meta entries. */ R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[this->num_content_storage_entries++], StorageId::BuiltInUser, fs::ContentStorageId::User)); - R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, MaxBuiltInUserContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource))); + R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[this->num_content_meta_entries++], StorageId::BuiltInUser, BuiltInUserSystemSaveDataInfo, UserMaxContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource))); /* Beyond this point, N uses hardcoded indices. */ /* Next SdCard's content storage and content meta entries. */ R_TRY(this->InitializeContentStorageRoot(&this->content_storage_roots[2], StorageId::SdCard, fs::ContentStorageId::SdCard)); - R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, MaxSdCardContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource))); + R_TRY(this->InitializeContentMetaDatabaseRoot(&this->content_meta_database_roots[2], StorageId::SdCard, SdCardSystemSaveDataInfo, SdCardMaxContentMetaCount, std::addressof(g_sd_and_user_content_meta_memory_resource))); /* GameCard's content storage and content meta entries. */ /* N doesn't set a content storage id for game cards, so we'll just use 0 (System). */ R_TRY(this->InitializeGameCardContentStorageRoot(&this->content_storage_roots[3])); - R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], MaxGameCardContentMetaCount, std::addressof(g_gamecard_content_meta_memory_resource))); + R_TRY(this->InitializeGameCardContentMetaDatabaseRoot(&this->content_meta_database_roots[3], GameCardMaxContentMetaCount, std::addressof(g_gamecard_content_meta_memory_resource))); this->initialized = true; return ResultSuccess(); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp index df24a2b0a..2a0746e7d 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp @@ -59,6 +59,35 @@ namespace ams::ncm { dst->attributes = src.attributes; } + Result FindDeltaIndex(s32 *out_index, const PatchMetaExtendedDataReader &reader, u32 src_version, u32 dst_version) { + /* Iterate over all deltas. */ + auto header = reader.GetHeader(); + for (s32 i = 0; i < static_cast(header->delta_count); i++) { + /* Check if the current delta matches the versions. */ + auto delta = reader.GetPatchDeltaHeader(i); + if ((src_version == 0 || delta->delta.source_version == src_version) && delta->delta.destination_version == dst_version) { + *out_index = i; + return ResultSuccess(); + } + } + + /* We didn't find the delta. */ + return ncm::ResultDeltaNotFound(); + } + + s32 CountContentExceptForMeta(const PatchMetaExtendedDataReader &reader, s32 delta_index) { + /* Iterate over packaged content infos, checking for those which aren't metas. */ + s32 count = 0; + auto delta = reader.GetPatchDeltaHeader(delta_index); + for (s32 i = 0; i < static_cast(delta->content_count); i++) { + if (reader.GetPatchDeltaPackagedContentInfo(delta_index, i)->GetType() != ContentType::Meta) { + count++; + } + } + + return count; + } + } size_t PackagedContentMetaReader::CalculateConvertInstallContentMetaSize() const { @@ -72,7 +101,7 @@ namespace ams::ncm { /* Subtract the number of delta fragments for patches, include extended data. */ return this->CalculateSizeImpl(header->extended_header_size, header->content_count - this->CountDeltaFragments() + 1, header->content_meta_count, this->GetExtendedDataSize(), false); } - + /* No extended data or delta fragments by default. */ return this->CalculateSizeImpl(header->extended_header_size, header->content_count + 1, header->content_meta_count, 0, false); } @@ -148,6 +177,78 @@ namespace ams::ncm { } } + Result PackagedContentMetaReader::ConvertToFragmentOnlyInstallContentMeta(void *dst, size_t size, const InstallContentInfo &meta, u32 source_version) { + /* Ensure that we have enough space. */ + size_t required_size; + R_TRY(this->CalculateConvertFragmentOnlyInstallContentMetaSize(std::addressof(required_size), source_version)); + AMS_ABORT_UNLESS(size >= required_size); + + /* Find the delta index. */ + PatchMetaExtendedDataReader reader(this->GetExtendedData(), this->GetExtendedDataSize()); + s32 index; + R_TRY(FindDeltaIndex(std::addressof(index), reader, source_version, this->GetKey().version)); + auto delta = reader.GetPatchDeltaHeader(index); + + /* Prepare for conversion. */ + const auto *packaged_header = this->GetHeader(); + uintptr_t dst_addr = reinterpret_cast(dst); + + /* Convert the header. */ + InstallContentMetaHeader header; + ConvertPackageContentMetaHeaderToInstallContentMetaHeader(std::addressof(header), *packaged_header); + header.install_type = ContentInstallType::FragmentOnly; + + /* Set the content count. */ + auto fragment_count = CountContentExceptForMeta(reader, index); + header.content_count = static_cast(fragment_count) + 1; + + /* Copy the header. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(header), sizeof(header)); + dst_addr += sizeof(header); + + /* Copy the extended header. */ + std::memcpy(reinterpret_cast(dst_addr), reinterpret_cast(this->GetExtendedHeaderAddress()), packaged_header->extended_header_size); + dst_addr += packaged_header->extended_header_size; + + /* Copy the top level meta. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(meta), sizeof(meta)); + dst_addr += sizeof(meta); + + s32 count = 0; + for (s32 i = 0; i < static_cast(delta->content_count); i++) { + auto packaged_content_info = reader.GetPatchDeltaPackagedContentInfo(index, i); + if (packaged_content_info->GetType() != ContentType::Meta) { + /* Create the install content info. */ + InstallContentInfo install_content_info = InstallContentInfo::Make(*packaged_content_info, packaged_header->type); + + /* Copy the info. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(install_content_info), sizeof(InstallContentInfo)); + dst_addr += sizeof(InstallContentInfo); + + /* Increment the count. */ + count++; + } + } + + /* Assert that we copied the right number of infos. */ + AMS_ASSERT(count == fragment_count); + + return ResultSuccess(); + } + + Result PackagedContentMetaReader::CalculateConvertFragmentOnlyInstallContentMetaSize(size_t *out_size, u32 source_version) const { + /* Find the delta index. */ + PatchMetaExtendedDataReader reader(this->GetExtendedData(), this->GetExtendedDataSize()); + s32 index; + R_TRY(FindDeltaIndex(std::addressof(index), reader, source_version, this->GetKey().version)); + + /* Get the fragment count. */ + auto fragment_count = CountContentExceptForMeta(reader, index); + + /* Recalculate. */ + return CalculateSizeImpl(this->GetExtendedHeaderSize(), fragment_count + 1, 0, this->GetExtendedDataSize(), false); + } + void PackagedContentMetaReader::ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta) { /* Ensure we have enough space to convert. */ AMS_ABORT_UNLESS(size >= this->CalculateConvertContentMetaSize()); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp index 19f7c6ffb..9db289db8 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp @@ -18,7 +18,7 @@ namespace ams::ncm { - Result ContentMetaDatabaseImpl::GetContentIdImpl(ContentId *out, const ContentMetaKey& key, ContentType type, std::optional id_offset) const { + Result ContentMetaDatabaseImpl::GetContentIdImpl(ContentId *out, const ContentMetaKey &key, ContentType type, std::optional id_offset) const { R_TRY(this->EnsureEnabled()); /* Find the meta key. */ diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp index 180c1c481..3ccfaab46 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp @@ -25,7 +25,7 @@ namespace ams::ncm { ContentMetaDatabaseImpl(ContentMetaKeyValueStore *kvs) : ContentMetaDatabaseImplBase(kvs) { /* ... */ } private: /* Helpers. */ - Result GetContentIdImpl(ContentId *out, const ContentMetaKey& key, ContentType type, std::optional id_offset) const; + Result GetContentIdImpl(ContentId *out, const ContentMetaKey &key, ContentType type, std::optional id_offset) const; public: /* Actual commands. */ virtual Result Set(const ContentMetaKey &key, sf::InBuffer value) override; diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp index 11a9fd152..ab4bbb04e 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp @@ -88,15 +88,13 @@ namespace ams::ncm { /* TODO: ON_SCOPE_EXIT { }; */ /* No firmware variations to list. */ - if (reader.GetExtendedDataSize() == 0) { - return ResultSuccess(); - } + R_SUCCEED_IF(reader.GetExtendedDataSize() == 0); SystemUpdateMetaExtendedDataReader extended_data_reader(reader.GetExtendedData(), reader.GetExtendedDataSize()); std::optional firmware_variation_index = std::nullopt; /* Find the input firmware variation id. */ - for (s32 i = 0; i < extended_data_reader.GetFirmwareVariationCount(); i++) { + for (size_t i = 0; i < extended_data_reader.GetFirmwareVariationCount(); i++) { if (*extended_data_reader.GetFirmwareVariationId(i) == firmware_variation_id) { firmware_variation_index = i; break; @@ -104,26 +102,20 @@ namespace ams::ncm { } /* We couldn't find the input firmware variation id. */ - if (!firmware_variation_index) { - return ResultInvalidFirmwareVariation(); - } + R_UNLESS(firmware_variation_index, ncm::ResultInvalidFirmwareVariation()); /* Obtain the variation info. */ const FirmwareVariationInfo *variation_info = extended_data_reader.GetFirmwareVariationInfo(*firmware_variation_index); /* Success if refer to base, or unk is 1 (unk is usually 2). */ - if (variation_info->refer_to_base || extended_data_reader.GetHeader()->unk == 1) { - return ResultSuccess(); - } + R_SUCCEED_IF(variation_info->refer_to_base || extended_data_reader.GetHeader()->unk == 1); /* Output the content meta count. */ const u32 content_meta_count = variation_info->content_meta_count; *out_count = content_meta_count; - /* No content metas to list. */ - if (content_meta_count == 0) { - return ResultSuccess(); - } + /* We're done if there are no content metas to list. */ + R_SUCCEED_IF(content_meta_count); /* Allocate a buffer for the content meta infos. */ std::unique_ptr buffer(new (std::nothrow) ContentMetaInfo[content_meta_count]); diff --git a/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp b/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp index 6b57cbcfc..5032cf027 100644 --- a/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_install_task_base.cpp @@ -43,22 +43,12 @@ namespace ams::ncm { return false; } - } + bool IsExpectedKey(const ContentMetaKey &expected_key, const ContentMetaKey &actual_key) { + return expected_key.id == actual_key.id && + expected_key.version == actual_key.version && + expected_key.type == actual_key.type; + } - 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() { @@ -78,9 +68,11 @@ namespace ams::ncm { 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)); } @@ -89,88 +81,57 @@ namespace ams::ncm { return ResultSuccess(); } - 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()); + R_TRY(this->CountInstallContentMetaData(std::addressof(count))); /* Iterate over content meta. */ + std::optional placeholder_id; + std::optional storage_id; for (s32 i = 0; i < count; i++) { /* Obtain the content meta. */ InstallContentMeta content_meta; - R_TRY(this->data->Get(std::addressof(content_meta), i)); + R_TRY(this->GetInstallContentMetaData(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) { + const auto key = reader.GetKey(); + + if (key.id != id || key.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(); + /* Attempt to find a content info for the type. */ + for (size_t j = 0; j < reader.GetContentCount(); j++) { + const auto content_info = reader.GetContentInfo(j); + if (content_info->GetType() == type) { + placeholder_id = content_info->GetPlaceHolderId(); + storage_id = content_info->GetStorageId(); + break; + } } } - return ncm::ResultPlaceHolderNotFound(); + R_UNLESS(placeholder_id, ncm::ResultPlaceHolderNotFound()); + R_UNLESS(storage_id, ncm::ResultPlaceHolderNotFound()); + + /* Open the relevant content storage. */ + ContentStorage storage; + R_TRY(OpenContentStorage(std::addressof(storage), *storage_id)); + + /* Get the path. */ + storage.GetPlaceHolderPath(out_path, *placeholder_id); + + return ResultSuccess(); } - 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) { + Result InstallTaskBase::CalculateRequiredSize(s64 *out_size) { /* Count the number of content meta entries. */ s32 count; R_TRY(this->data->Count(std::addressof(count))); - size_t required_size = 0; + s64 required_size = 0; /* Iterate over each entry. */ for (s32 i = 0; i < count; i++) { /* Obtain the content meta. */ @@ -192,94 +153,26 @@ namespace ams::ncm { 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; - } + Result InstallTaskBase::PrepareImpl() { + /* Reset the throughput. */ + this->ResetThroughputMeasurement(); - 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; - R_TRY(this->data->Get(std::addressof(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); - - /* Check if we have the content already exists. */ - 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(); - } - } + /* Transition from NotPrepared to DataPrepared. */ + if (this->GetProgress().state == InstallProgressState::NotPrepared) { + R_TRY(this->PrepareInstallContentMetaData()); + R_TRY(this->PrepareDependency()); + R_TRY(this->CheckInstallable()); + this->SetProgressState(InstallProgressState::DataPrepared); } - this->SetTotalSize(total_size); - return ResultSuccess(); + /* Transition from DataPrepared to Prepared. */ + if (this->GetProgress().state == InstallProgressState::DataPrepared) { + R_TRY(this->PreparePlaceHolder()); + this->SetProgressState(InstallProgressState::Prepared); + } + + /* Signal prepare is completed. */ + return this->OnPrepareComplete(); } Result InstallTaskBase::Cleanup() { @@ -301,17 +194,19 @@ namespace ams::ncm { /* Cleanup the data and progress. */ R_TRY(this->data->Cleanup()); this->CleanupProgress(); + return ResultSuccess(); } Result InstallTaskBase::CleanupOne(const InstallContentMeta &content_meta) { /* Obtain a reader and get the storage id. */ const auto reader = content_meta.GetReader(); - R_SUCCEED_IF(reader.GetStorageId() == StorageId::None); + const auto storage_id = reader.GetStorageId(); + R_SUCCEED_IF(storage_id== StorageId::None); /* Open the relevant content storage. */ ContentStorage content_storage; - R_TRY(ncm::OpenContentStorage(&content_storage, reader.GetStorageId())); + R_TRY(ncm::OpenContentStorage(&content_storage, storage_id)); /* Iterate over content infos. */ for (size_t i = 0; i < reader.GetContentCount(); i++) { @@ -326,14 +221,6 @@ namespace ams::ncm { return ResultSuccess(); } - void InstallTaskBase::CleanupProgress() { - std::scoped_lock(this->progress_mutex); - this->progress.installed_size = 0; - this->progress.total_size = 0; - this->progress.state = InstallProgressState::NotPrepared; - this->progress.SetLastResult(ResultSuccess()); - } - Result InstallTaskBase::ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset, ListContentMetaKeyFilter filter) { /* Count the number of content meta entries. */ s32 count; @@ -346,7 +233,7 @@ namespace ams::ncm { } if (filter == ListContentMetaKeyFilter::All) { - const size_t num_keys = std::min(count, offset + out_keys_count); + const s32 num_keys = std::min(count, offset + out_keys_count); /* Iterate over content meta. */ for (s32 i = offset; i < num_keys; i++) { @@ -356,16 +243,14 @@ namespace ams::ncm { /* Write output StorageContentMetaKey. */ const auto reader = content_meta.GetReader(); - StorageContentMetaKey &storage_key = out_keys[i - offset]; - storage_key.key = reader.GetKey(); - storage_key.storage_id = reader.GetStorageId(); + out_keys[i - offset] = { reader.GetKey(), reader.GetStorageId() }; } /* Output the number of keys written. */ *out_keys_written = num_keys - offset; } else { s32 keys_written = 0; - s32 curr_offset = 0; + s32 cur_offset = 0; /* Iterate over content meta. */ for (s32 i = 0; i < count; i++) { @@ -378,23 +263,19 @@ namespace ams::ncm { const bool committed = reader.GetHeader()->committed; /* Apply filter. */ - if ((!committed && filter == ListContentMetaKeyFilter::Committed) || (committed && filter == ListContentMetaKeyFilter::NotCommitted)) { - continue; - } + if ((filter == ListContentMetaKeyFilter::Committed && committed) || (filter == ListContentMetaKeyFilter::NotCommitted && !committed)) { + /* Write output StorageContentMetaKey if at a suitable offset. */ + if (cur_offset >= offset) { + out_keys[keys_written++] = { reader.GetKey(), reader.GetStorageId() }; + } - /* Write output StorageContentMetaKey if at a suitable offset. */ - if (curr_offset >= offset) { - StorageContentMetaKey &storage_key = out_keys[keys_written++]; - storage_key.key = reader.GetKey(); - storage_key.storage_id = reader.GetStorageId(); - } + /* Increment the current offset. */ + cur_offset++; - /* Increment the current offset. */ - curr_offset++; - - /* We can't write any more output keys. */ - if (keys_written >= out_keys_count) { - break; + /* We can't write any more output keys. */ + if (keys_written >= out_keys_count) { + break; + } } } @@ -405,19 +286,21 @@ namespace ams::ncm { return ResultSuccess(); } - Result InstallTaskBase::ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset) { - return this->ListContentMetaKey(out_keys_written, out_keys, out_keys_count, offset, ListContentMetaKeyFilter::All); - } - Result InstallTaskBase::ListApplicationContentMetaKey(s32 *out_keys_written, ApplicationContentMetaKey *out_keys, s32 out_keys_count, s32 offset) { /* Count the number of content meta entries. */ s32 count; R_TRY(this->data->Count(std::addressof(count))); - s32 keys_written = 0; + /* Offset exceeds keys that can be written. */ + if (count <= offset) { + *out_keys_written = 0; + return ResultSuccess(); + } /* Iterate over content meta. */ - for (s32 i = offset; i < std::min(count, offset + out_keys_count); i++) { + const s32 max = std::min(count, offset + out_keys_count); + s32 keys_written = 0; + for (s32 i = offset; i < max; i++) { /* Obtain the content meta. */ InstallContentMeta content_meta; R_TRY(this->data->Get(std::addressof(content_meta), i)); @@ -426,14 +309,13 @@ namespace ams::ncm { const auto reader = content_meta.GetReader(); /* Ensure this key has an application id. */ - if (!reader.GetApplicationId()) { + const auto app_id = reader.GetApplicationId(); + if (!app_id) { continue; } /* Write output ApplicationContentMetaKey. */ - ApplicationContentMetaKey &out_key = out_keys[keys_written++]; - out_key.key = reader.GetKey(); - out_key.application_id = *reader.GetApplicationId(); + out_keys[keys_written++] = { reader.GetKey(), *app_id }; } *out_keys_written = keys_written; @@ -458,83 +340,35 @@ namespace ams::ncm { InstallContentMeta content_meta; R_TRY(this->data->Get(std::addressof(content_meta), i)); - /* Update the data when we are done. */ - ON_SCOPE_EXIT { this->data->Update(content_meta, i); }; + /* Update the data (and check result) when we are done. */ + const auto DoUpdate = [&]() ALWAYS_INLINE_LAMBDA { return this->data->Update(content_meta, i); }; + { + auto update_guard = SCOPE_GUARD { DoUpdate(); }; - /* Create a writer. */ - const auto writer = content_meta.GetWriter(); + /* Create a writer. */ + const auto writer = content_meta.GetWriter(); - /* Iterate over content infos. */ - for (size_t j = 0; j < writer.GetContentCount(); j++) { - auto *content_info = writer.GetWritableContentInfo(j); + /* Iterate over content infos. */ + for (size_t j = 0; j < writer.GetContentCount(); j++) { + auto *content_info = writer.GetWritableContentInfo(j); - /* Write prepared content infos. */ - if (content_info->install_state == InstallState::Prepared) { - R_TRY(this->WritePlaceHolder(writer.GetKey(), content_info)); - content_info->install_state = InstallState::Installed; + /* Write prepared content infos. */ + if (content_info->install_state == InstallState::Prepared) { + R_TRY(this->WritePlaceHolder(writer.GetKey(), content_info)); + content_info->install_state = InstallState::Installed; + } } + + /* Cancel so we can check the result of updating. */ + update_guard.Cancel(); } + R_TRY(DoUpdate()); } /* Execution has finished, signal this and update the state. */ R_TRY(this->OnExecuteComplete()); + this->SetProgressState(InstallProgressState::Downloaded); - return ResultSuccess(); - } - - void InstallTaskBase::StartThroughputMeasurement() { - std::scoped_lock lk(this->throughput_mutex); - this->throughput.installed = 0; - this->throughput.elapsed_time = TimeSpan(); - this->throughput_start_time = os::ConvertToTimeSpan(os::GetSystemTick()); - } - - Result InstallTaskBase::WritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info) { - if (content_info->is_sha256_calculated) { - /* Update the hash with the buffered data. */ - this->sha256_generator.InitializeWithContext(std::addressof(content_info->context)); - this->sha256_generator.Update(content_info->buffered_data, content_info->buffered_data_size); - } else { - /* Initialize the generator. */ - this->sha256_generator.Initialize(); - } - - { - ON_SCOPE_EXIT { - /* Update this content info's sha256 data. */ - this->sha256_generator.GetContext(std::addressof(content_info->context)); - content_info->buffered_data_size = this->sha256_generator.GetBufferedDataSize(); - this->sha256_generator.GetBufferedData(content_info->buffered_data, this->sha256_generator.GetBufferedDataSize()); - content_info->is_sha256_calculated = true; - }; - - /* Perform the placeholder write. */ - R_TRY(this->OnWritePlaceHolder(key, content_info)); - } - - /* Compare generated hash to expected hash if verification required. */ - if (content_info->verify_digest) { - u8 hash[crypto::Sha256Generator::HashSize]; - this->sha256_generator.GetHash(hash, crypto::Sha256Generator::HashSize); - R_UNLESS(std::memcmp(hash, content_info->digest.data, crypto::Sha256Generator::HashSize) == 0, ncm::ResultInvalidContentHash()); - } - - if (!(this->config & InstallConfig_IgnoreTicket)) { - ncm::RightsId rights_id; - { - /* Open the content storage and obtain the rights id. */ - ncm::ContentStorage storage; - R_TRY(OpenContentStorage(std::addressof(storage), content_info->storage_id)); - R_TRY(storage.GetRightsId(std::addressof(rights_id), content_info->placeholder_id)); - } - - /* Install a ticket if necessary. */ - if (this->IsNecessaryInstallTicket(rights_id.id)) { - R_TRY_CATCH(this->InstallTicket(rights_id.id, content_info->meta_type)) { - R_CATCH(ncm::ResultIgnorableInstallTicketFailure) { /* We can ignore the installation failure. */ } - } R_END_TRY_CATCH; - } - } return ResultSuccess(); } @@ -548,7 +382,7 @@ namespace ams::ncm { Result InstallTaskBase::VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys) { /* No keys to check. */ R_SUCCEED_IF(keys == nullptr); - + /* Count the number of content meta entries. */ s32 count; R_TRY(this->data->Count(std::addressof(count))); @@ -614,12 +448,12 @@ namespace ams::ncm { /* Helper for performing an update. */ const auto DoUpdate = [&]() ALWAYS_INLINE_LAMBDA { return this->data->Update(content_meta, i); }; - + /* Commit the current meta. */ { /* Ensure that if something goes wrong during commit, we still try to update. */ auto update_guard = SCOPE_GUARD { DoUpdate(); }; - + /* Open a writer. */ const auto writer = content_meta.GetWriter(); @@ -654,11 +488,11 @@ namespace ams::ncm { /* Mark storage id to be committed later. */ commit_list.Push(reader.GetStorageId()); - + /* We successfully commited this meta, so we want to check for errors when updating. */ update_guard.Cancel(); } - + /* Try to update, checking for failure. */ R_TRY(DoUpdate()); } @@ -685,10 +519,6 @@ namespace ams::ncm { return ResultSuccess(); } - Result InstallTaskBase::Commit() { - return this->Commit(nullptr, 0); - } - Result InstallTaskBase::IncludesExFatDriver(bool *out) { /* Count the number of content meta entries. */ s32 count; @@ -719,7 +549,7 @@ namespace ams::ncm { R_TRY(OpenContentStorage(&content_storage, content_info->storage_id)); /* Write data to the placeholder. */ - content_storage.WritePlaceHolder(content_info->placeholder_id, content_info->written, data, data_size); + R_TRY(content_storage.WritePlaceHolder(content_info->placeholder_id, content_info->written, data, data_size)); content_info->written += data_size; /* Update progress/throughput if content info isn't temporary. */ @@ -733,19 +563,54 @@ namespace ams::ncm { return ResultSuccess(); } - void InstallTaskBase::IncrementProgress(s64 size) { - std::scoped_lock lk(this->progress_mutex); - this->progress.installed_size += size; - } - - void InstallTaskBase::UpdateThroughputMeasurement(s64 throughput) { - std::scoped_lock lk(this->throughput_mutex); - - /* Update throughput only if start time has been set. */ - if (this->throughput_start_time.GetNanoSeconds() != 0) { - this->throughput.installed += throughput; - this->throughput.elapsed_time = os::ConvertToTimeSpan(os::GetSystemTick()) - this->throughput_start_time; + Result InstallTaskBase::WritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info) { + if (content_info->is_sha256_calculated) { + /* Update the hash with the buffered data. */ + this->sha256_generator.InitializeWithContext(std::addressof(content_info->context)); + this->sha256_generator.Update(content_info->buffered_data, content_info->buffered_data_size); + } else { + /* Initialize the generator. */ + this->sha256_generator.Initialize(); } + + { + ON_SCOPE_EXIT { + /* Update this content info's sha256 data. */ + this->sha256_generator.GetContext(std::addressof(content_info->context)); + content_info->buffered_data_size = this->sha256_generator.GetBufferedDataSize(); + this->sha256_generator.GetBufferedData(content_info->buffered_data, this->sha256_generator.GetBufferedDataSize()); + content_info->is_sha256_calculated = true; + }; + + /* Perform the placeholder write. */ + R_TRY(this->OnWritePlaceHolder(key, content_info)); + } + + /* Compare generated hash to expected hash if verification required. */ + if (content_info->verify_digest) { + u8 hash[crypto::Sha256Generator::HashSize]; + this->sha256_generator.GetHash(hash, crypto::Sha256Generator::HashSize); + R_UNLESS(std::memcmp(hash, content_info->digest.data, crypto::Sha256Generator::HashSize) == 0, ncm::ResultInvalidContentHash()); + } + + if (!(this->config & InstallConfig_IgnoreTicket)) { + ncm::RightsId rights_id; + { + /* Open the content storage and obtain the rights id. */ + ncm::ContentStorage storage; + R_TRY(OpenContentStorage(std::addressof(storage), content_info->storage_id)); + R_TRY(storage.GetRightsId(std::addressof(rights_id), content_info->placeholder_id)); + } + + /* Install a ticket if necessary. */ + if (this->IsNecessaryInstallTicket(rights_id.id)) { + R_TRY_CATCH(this->InstallTicket(rights_id.id, content_info->meta_type)) { + R_CATCH(ncm::ResultIgnorableInstallTicketFailure) { /* We can ignore the installation failure. */ } + } R_END_TRY_CATCH; + } + } + + return ResultSuccess(); } bool InstallTaskBase::IsNecessaryInstallTicket(const fs::RightsId &rights_id) { @@ -760,9 +625,94 @@ namespace ams::ncm { return false; } - void InstallTaskBase::SetTotalSize(s64 size) { - std::scoped_lock(this->progress_mutex); - this->progress.total_size = size; + 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; + R_TRY(this->data->Get(std::addressof(content_meta), i)); + + /* Update the data (and check result) when we are done. */ + const auto DoUpdate = [&]() ALWAYS_INLINE_LAMBDA { return this->data->Update(content_meta, i); }; + { + auto update_guard = SCOPE_GUARD { DoUpdate(); }; + + /* Automatically choose a suitable storage id. */ + auto reader = content_meta.GetReader(); + StorageId storage_id; + if (reader.GetStorageId() != StorageId::None) { + storage_id = reader.GetStorageId(); + } else { + StorageId install_storage = this->GetInstallStorage(); + R_TRY(ncm::SelectDownloadableStorage(std::addressof(storage_id), install_storage, reader.CalculateContentRequiredSize())); + } + + /* Open the relevant content storage. */ + ContentStorage content_storage; + R_TRY(ncm::OpenContentStorage(&content_storage, storage_id)); + + /* Update the storage id in the header. */ + auto writer = content_meta.GetWriter(); + 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); + + /* Check if we have the content already exists. */ + 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; + + /* Continue. */ + continue; + } + + 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(); + } + + /* Cancel so that we can check the result of updating. */ + update_guard.Cancel(); + } + R_TRY(DoUpdate()); + } + + this->SetTotalSize(total_size); + return ResultSuccess(); } Result InstallTaskBase::WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary) { @@ -778,27 +728,14 @@ namespace ams::ncm { /* Write install content info. */ R_TRY(this->WritePlaceHolder(meta_info.key, out_install_content_info)); - + /* Don't delete the placeholder. Set state to installed. */ placeholder_guard.Cancel(); out_install_content_info->install_state = InstallState::Installed; return ResultSuccess(); } - InstallContentInfo InstallTaskBase::MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional is_tmp) { - return { - .digest = info.digest, - .info = ContentInfo::Make(info.content_id, info.content_size, ContentType::Meta, 0), - .placeholder_id = placeholder_id, - .meta_type = info.key.type, - .install_state = InstallState::Prepared, - .verify_digest = info.verify_digest, - .storage_id = StorageId::BuiltInSystem, - .is_temporary = is_tmp ? *is_tmp : (this->install_storage != StorageId::BuiltInSystem), - }; - } - - Result InstallTaskBase::PrepareContentMeta(const InstallContentMetaInfo &meta_info, std::optional key, std::optional source_version) { + Result InstallTaskBase::PrepareContentMeta(const InstallContentMetaInfo &meta_info, std::optional expected_key, std::optional source_version) { /* Open the BuiltInSystem content storage. */ ContentStorage content_storage; R_TRY(OpenContentStorage(&content_storage, StorageId::BuiltInSystem)); @@ -806,20 +743,22 @@ namespace ams::ncm { /* Write content meta to a placeholder. */ InstallContentInfo content_info; R_TRY(this->WriteContentMetaToPlaceHolder(std::addressof(content_info), std::addressof(content_storage), meta_info, std::nullopt)); - + /* Get the path of the placeholder. */ Path path; content_storage.GetPlaceHolderPath(std::addressof(path), content_info.GetPlaceHolderId()); - + const bool is_temporary = content_info.is_temporary; auto temporary_guard = SCOPE_GUARD { content_storage.DeletePlaceHolder(content_info.GetPlaceHolderId()); }; /* Create a new temporary InstallContentInfo if relevant. */ if (is_temporary) { content_info = { - .digest = content_info.digest, - .info = content_info.info, + .digest = content_info.digest, + .info = content_info.info, .placeholder_id = content_info.GetPlaceHolderId(), + .meta_type = content_info.meta_type, + .verify_digest = content_info.verify_digest, }; } @@ -827,20 +766,21 @@ namespace ams::ncm { AutoBuffer meta; R_TRY(this->GetInstallContentMetaDataFromPath(std::addressof(meta), path, content_info, source_version)); - { - /* Create a writer. */ + /* Update the storage id if BuiltInSystem. */ + if (this->install_storage == StorageId::BuiltInSystem) { InstallContentMetaWriter writer(meta.Get(), meta.GetSize()); - ON_SCOPE_EXIT { this->data->Push(meta.Get(), meta.GetSize()); }; - - /* Update the storage id if BuiltInSystem. */ - if (this->install_storage == StorageId::BuiltInSystem) { - writer.SetStorageId(StorageId::BuiltInSystem); - } - - /* Ensure key matches, if provided. */ - R_UNLESS(!key || *key == writer.GetKey(), ncm::ResultUnexpectedContentMetaPrepared()); + writer.SetStorageId(StorageId::BuiltInSystem); } + /* Validate the expected key if we have an expectation. */ + if (expected_key) { + InstallContentMetaReader reader(meta.Get(), meta.GetSize()); + R_UNLESS(IsExpectedKey(*expected_key, reader.GetKey()), ncm::ResultUnexpectedContentMetaPrepared()); + } + + /* Push the data. */ + R_TRY(this->data->Push(meta.Get(), meta.GetSize())); + /* Don't delete the placeholder if not temporary. */ if (!is_temporary) { temporary_guard.Cancel(); @@ -848,32 +788,6 @@ namespace ams::ncm { return ResultSuccess(); } - Result InstallTaskBase::GetInstallContentMetaDataFromPath(AutoBuffer *out, const Path &path, const InstallContentInfo &content_info, std::optional source_version) { - AutoBuffer meta; - { - /* TODO: fs::ScopedAutoAbortDisabler aad; */ - R_TRY(ReadContentMetaPath(std::addressof(meta), path.str)); - } - - /* Create a reader. */ - PackagedContentMetaReader reader(meta.Get(), meta.GetSize()); - size_t meta_size; - - if (source_version) { - /* Convert to fragment only install content meta. */ - R_TRY(reader.CalculateConvertFragmentOnlyInstallContentMetaSize(std::addressof(meta_size), *source_version)); - R_TRY(out->Initialize(meta_size)); - reader.ConvertToFragmentOnlyInstallContentMeta(out->Get(), out->GetSize(), content_info, *source_version); - } else { - /* Convert to install content meta. */ - meta_size = reader.CalculateConvertInstallContentMetaSize(); - R_TRY(out->Initialize(meta_size)); - reader.ConvertToInstallContentMeta(out->Get(), out->GetSize(), content_info); - } - - return ResultSuccess(); - } - Result InstallTaskBase::PrepareContentMeta(ContentId content_id, s64 size, ContentMetaType meta_type, AutoBuffer *buffer) { /* Create a reader. */ PackagedContentMetaReader reader(buffer->Get(), buffer->GetSize()); @@ -884,7 +798,7 @@ namespace ams::ncm { /* Convert packaged content meta to install content meta. */ reader.ConvertToInstallContentMeta(tmp_buffer.Get(), tmp_buffer.GetSize(), InstallContentInfo::Make(ContentInfo::Make(content_id, size, ContentType::Meta), meta_type)); - + /* Push the content meta. */ this->data->Push(tmp_buffer.Get(), tmp_buffer.GetSize()); return ResultSuccess(); @@ -899,13 +813,13 @@ namespace ams::ncm { } Result InstallTaskBase::PrepareSystemUpdateDependency() { - /* Count the number of content meta entries. */ - s32 count; - R_TRY(this->data->Count(std::addressof(count))); - /* Cleanup on failure. */ auto guard = SCOPE_GUARD { this->Cleanup(); }; + /* Count the number of content meta entries. */ + s32 count; + R_TRY(this->CountInstallContentMetaData(std::addressof(count))); + /* Iterate over content meta. */ for (s32 i = 0; i < count; i++) { /* Obtain the content meta. */ @@ -924,24 +838,15 @@ namespace ams::ncm { std::unique_ptr content_meta_infos; s32 num_content_meta_infos; R_TRY(this->ReadContentMetaInfoList(std::addressof(num_content_meta_infos), std::addressof(content_meta_infos), reader.GetKey())); - + /* Iterate over content meta infos. */ for (s32 j = 0; j < num_content_meta_infos; j++) { ContentMetaInfo &content_meta_info = content_meta_infos[j]; const ContentMetaKey content_meta_info_key = content_meta_info.ToKey(); - /* If exfat driver is not included or is required. */ - if (!(content_meta_info.attributes & ContentMetaAttribute_IncludesExFatDriver) || this->config & InstallConfig_RequiresExFatDriver) { - /* Check if this content meta info is newer than what is already installed. */ - bool newer_than_installed; - R_TRY(this->IsNewerThanInstalled(std::addressof(newer_than_installed), content_meta_info_key)); - - if (newer_than_installed) { - /* Get and prepare install content meta info. */ - InstallContentMetaInfo install_content_meta_info; - R_TRY(this->GetInstallContentMetaInfo(std::addressof(install_content_meta_info), content_meta_info_key)); - R_TRY(this->PrepareContentMeta(install_content_meta_info, content_meta_info_key, std::nullopt)); - } + /* If exfat driver is not included or is required, prepare the content meta. */ + if (!(content_meta_info.attributes & ContentMetaAttribute_IncludesExFatDriver) || (this->config & InstallConfig_RequiresExFatDriver)) { + R_TRY(this->PrepareContentMetaIfLatest(content_meta_info_key)); } } } @@ -950,30 +855,67 @@ namespace ams::ncm { return ResultSuccess(); } - Result InstallTaskBase::ReadContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const ContentMetaKey &key) { - /* Get the install content meta info. */ - InstallContentMetaInfo install_content_meta_info; - R_TRY(this->GetInstallContentMetaInfo(std::addressof(install_content_meta_info), key)); + Result InstallTaskBase::GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out) { + /* Open the BuiltInSystem content meta database. */ + ContentMetaDatabase meta_db; + R_TRY(OpenContentMetaDatabase(std::addressof(meta_db), StorageId::BuiltInSystem)); - /* Open the BuiltInSystem content storage. */ - ContentStorage content_storage; - R_TRY(ncm::OpenContentStorage(&content_storage, StorageId::BuiltInSystem)); + /* Count the number of content meta entries. */ + s32 count; + R_TRY(this->data->Count(std::addressof(count))); - /* Write content meta to a placeholder. */ - InstallContentInfo content_info; - R_TRY(this->WriteContentMetaToPlaceHolder(std::addressof(content_info), std::addressof(content_storage), install_content_meta_info, true)); - - const PlaceHolderId placeholder_id = content_info.GetPlaceHolderId(); - - /* Get the path of the new placeholder. */ - Path path; - content_storage.GetPlaceHolderPath(std::addressof(path), placeholder_id); + /* Iterate over content meta. */ + for (s32 i = 0; i < count; i++) { + /* Obtain the content meta. */ + InstallContentMeta content_meta; + R_TRY(this->GetInstallContentMetaData(std::addressof(content_meta), i)); - /* Read the variation list. */ - R_TRY(ReadVariationContentMetaInfoList(out_count, out_meta_infos, path, this->firmware_variation_id)); + /* Create a reader. */ + const InstallContentMetaReader reader = content_meta.GetReader(); - /* Delete the placeholder. */ - content_storage.DeletePlaceHolder(placeholder_id); + /* Skip non system update content metas. */ + if (reader.GetHeader()->type != ContentMetaType::SystemUpdate) { + continue; + } + + /* List content meta infos. */ + std::unique_ptr content_meta_infos; + s32 num_content_meta_infos; + R_TRY(this->ReadContentMetaInfoList(std::addressof(num_content_meta_infos), std::addressof(content_meta_infos), reader.GetKey())); + + /* Iterate over content meta infos. */ + for (s32 j = 0; j < num_content_meta_infos; j++) { + const ContentMetaInfo &content_meta_info = content_meta_infos[j]; + bool found = true; + + /* Get the latest key. */ + ContentMetaKey installed_key; + R_TRY_CATCH(meta_db.GetLatest(std::addressof(installed_key), content_meta_info.id)) { + R_CATCH(ncm::ResultContentMetaNotFound) { + /* Key doesn't exist, this is okay. */ + found = false; + } + } R_END_TRY_CATCH; + + /* Exfat driver included, but not required. */ + if (content_meta_info.attributes & ContentMetaAttribute_IncludesExFatDriver && !(this->config & InstallConfig_RequiresExFatDriver)) { + continue; + } + + /* No reboot is required if we're installing a version below. */ + if (found && content_meta_info.version <= installed_key.version) { + continue; + } + + /* If not rebootless, a reboot is required. */ + if (!(content_meta_info.attributes & ContentMetaAttribute_Rebootless)) { + *out = SystemUpdateTaskApplyInfo::RequireReboot; + return ResultSuccess(); + } + } + } + + *out = SystemUpdateTaskApplyInfo::RequireNoReboot; return ResultSuccess(); } @@ -992,65 +934,6 @@ namespace ams::ncm { return ResultSuccess(); } - Result InstallTaskBase::GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out) { - /* Open the BuiltInSystem content meta database. */ - ContentMetaDatabase meta_db; - R_TRY(OpenContentMetaDatabase(std::addressof(meta_db), StorageId::BuiltInSystem)); - - /* Count the number of content meta entries. */ - s32 count; - R_TRY(this->data->Count(std::addressof(count))); - - /* 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)); - - /* Create a reader. */ - const InstallContentMetaReader reader = content_meta.GetReader(); - - /* Skip non system update content metas. */ - if (reader.GetHeader()->type != ContentMetaType::SystemUpdate) { - continue; - } - - /* List content meta infos. */ - std::unique_ptr content_meta_infos; - s32 num_content_meta_infos; - R_TRY(this->ReadContentMetaInfoList(std::addressof(num_content_meta_infos), std::addressof(content_meta_infos), reader.GetKey())); - - /* Iterate over content meta infos. */ - for (s32 j = 0; j < num_content_meta_infos; j++) { - const ContentMetaInfo &content_meta_info = content_meta_infos[j]; - bool not_found = false; - - /* Get the latest key. */ - ContentMetaKey installed_key; - R_TRY_CATCH(meta_db.GetLatest(std::addressof(installed_key), content_meta_info.id)) { - R_CATCH(ncm::ResultContentMetaNotFound) { - /* Key doesn't exist, this is okay. */ - not_found = true; - } - } R_END_TRY_CATCH; - - /* Exfat driver included, but not required. */ - if (content_meta_info.attributes & ContentMetaAttribute_IncludesExFatDriver && !(this->config & InstallConfig_RequiresExFatDriver)) { - continue; - } - - /* Not found or installed version is below the content meta info version, and this is not a rebootless content meta info. */ - if ((not_found || installed_key.version < content_meta_info.version) && !(content_meta_info.attributes & ContentMetaAttribute_Rebootless)) { - *out = SystemUpdateTaskApplyInfo::RequireReboot; - return ResultSuccess(); - } - } - } - - *out = SystemUpdateTaskApplyInfo::RequireNoReboot; - return ResultSuccess(); - } - Result InstallTaskBase::IsNewerThanInstalled(bool *out, const ContentMetaKey &key) { /* Obtain a list of suitable storage ids. */ auto storage_list = GetStorageList(this->install_storage); @@ -1059,19 +942,21 @@ namespace ams::ncm { for (s32 i = 0; i < storage_list.Count(); i++) { /* Open the content meta database. */ ContentMetaDatabase meta_db; - R_TRY(OpenContentMetaDatabase(std::addressof(meta_db), storage_list[i])); - + if (R_FAILED(OpenContentMetaDatabase(std::addressof(meta_db), storage_list[i]))) { + continue; + } + /* Get the latest key. */ - ContentMetaKey installed_key; - R_TRY_CATCH(meta_db.GetLatest(std::addressof(installed_key), key.id)) { - R_CATCH(ncm::ResultContentMetaNotFound) { - /* Key doesn't exist, this is okay. */ + ContentMetaKey latest_key; + R_TRY_CATCH(meta_db.GetLatest(std::addressof(latest_key), key.id)) { + R_CATCH(ncm::ResultContentMetaNotFound) { + /* Key doesn't exist, this is okay. */ continue; } } R_END_TRY_CATCH; /* Check if installed key is newer. */ - if (installed_key.version >= key.version) { + if (latest_key.version >= key.version) { *out = false; return ResultSuccess(); } @@ -1082,21 +967,24 @@ namespace ams::ncm { return ResultSuccess(); } + 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::DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys) { /* Count the number of content meta entries. */ s32 count; - R_TRY(this->data->Count(std::addressof(count))); - - /* Delete the data if count < 1. */ - if (count < 1) { - return this->data->Delete(keys, num_keys); - } + R_TRY(this->CountInstallContentMetaData(std::addressof(count))); /* 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)); + R_TRY(this->GetInstallContentMetaData(std::addressof(content_meta), i)); /* Cleanup if the input keys contain this key. */ if (Contains(keys, num_keys, content_meta.GetReader().GetKey())) { @@ -1104,9 +992,53 @@ namespace ams::ncm { } } + /* Delete the data if count < 1. */ + return this->data->Delete(keys, num_keys); + } + + Result InstallTaskBase::GetInstallContentMetaDataFromPath(AutoBuffer *out, const Path &path, const InstallContentInfo &content_info, std::optional source_version) { + AutoBuffer meta; + { + /* TODO: fs::ScopedAutoAbortDisabler aad; */ + R_TRY(ReadContentMetaPath(std::addressof(meta), path.str)); + } + + /* Create a reader. */ + PackagedContentMetaReader reader(meta.Get(), meta.GetSize()); + size_t meta_size; + + AutoBuffer install_meta_data; + if (source_version) { + /* Convert to fragment only install content meta. */ + R_TRY(reader.CalculateConvertFragmentOnlyInstallContentMetaSize(std::addressof(meta_size), *source_version)); + R_TRY(install_meta_data.Initialize(meta_size)); + reader.ConvertToFragmentOnlyInstallContentMeta(install_meta_data.Get(), install_meta_data.GetSize(), content_info, *source_version); + } else { + /* Convert to install content meta. */ + meta_size = reader.CalculateConvertInstallContentMetaSize(); + R_TRY(install_meta_data.Initialize(meta_size)); + reader.ConvertToInstallContentMeta(install_meta_data.Get(), install_meta_data.GetSize(), content_info); + } + + /* Set output. */ + *out = std::move(install_meta_data); + return ResultSuccess(); } + InstallContentInfo InstallTaskBase::MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional is_tmp) { + return { + .digest = info.digest, + .info = ContentInfo::Make(info.content_id, info.content_size, ContentType::Meta, 0), + .placeholder_id = placeholder_id, + .meta_type = info.key.type, + .install_state = InstallState::Prepared, + .verify_digest = info.verify_digest, + .storage_id = StorageId::BuiltInSystem, + .is_temporary = is_tmp ? *is_tmp : (this->install_storage != StorageId::BuiltInSystem), + }; + } + InstallProgress InstallTaskBase::GetProgress() { std::scoped_lock lk(this->progress_mutex); return this->progress; @@ -1116,9 +1048,52 @@ namespace ams::ncm { this->SetLastResult(ResultSuccess()); } - s64 InstallTaskBase::GetThroughput() { + void InstallTaskBase::SetTotalSize(s64 size) { + std::scoped_lock(this->progress_mutex); + this->progress.total_size = size; + } + + void InstallTaskBase::IncrementProgress(s64 size) { + std::scoped_lock lk(this->progress_mutex); + this->progress.installed_size += size; + } + + void InstallTaskBase::SetLastResult(Result last_result) { + std::scoped_lock lk(this->progress_mutex); + this->data->SetLastResult(last_result); + this->progress.SetLastResult(last_result); + } + + void InstallTaskBase::CleanupProgress() { + std::scoped_lock(this->progress_mutex); + this->progress = {}; + } + + InstallThroughput InstallTaskBase::GetThroughput() { std::scoped_lock lk(this->throughput_mutex); - return this->throughput.installed; + return this->throughput; + } + + void InstallTaskBase::ResetThroughputMeasurement() { + std::scoped_lock lk(this->throughput_mutex); + this->throughput = { .elapsed_time = TimeSpan() }; + this->throughput_start_time = TimeSpan(); + } + + void InstallTaskBase::StartThroughputMeasurement() { + std::scoped_lock lk(this->throughput_mutex); + this->throughput = { .elapsed_time = TimeSpan() }; + this->throughput_start_time = os::GetSystemTick().ToTimeSpan(); + } + + void InstallTaskBase::UpdateThroughputMeasurement(s64 throughput) { + std::scoped_lock lk(this->throughput_mutex); + + /* Update throughput only if start time has been set. */ + if (this->throughput_start_time.GetNanoSeconds() != 0) { + this->throughput.installed += throughput; + this->throughput.elapsed_time = os::GetSystemTick().ToTimeSpan() - this->throughput_start_time; + } } Result InstallTaskBase::CalculateContentsSize(s64 *out_size, const ContentMetaKey &key, StorageId storage_id) { @@ -1144,8 +1119,8 @@ namespace ams::ncm { continue; } - /* Storage id may either by Any, or it must not be None and match the input storage id. */ - if (storage_id != StorageId::Any && (storage_id == StorageId::None || storage_id != reader.GetStorageId())) { + /* If the storage is unique and doesn't match, continue. */ + if (IsUniqueStorage(storage_id) && reader.GetStorageId() != storage_id) { continue; } @@ -1156,6 +1131,11 @@ namespace ams::ncm { for (size_t j = 0; j < reader.GetContentCount(); j++) { const auto *content_info = reader.GetContentInfo(j); + /* If this content info isn't prepared, continue. */ + if (content_info->install_state == InstallState::NotPrepared) { + continue; + } + /* Check if this content info has a placeholder. */ bool has_placeholder; R_TRY(content_storage.HasPlaceHolder(std::addressof(has_placeholder), content_info->GetPlaceHolderId())); @@ -1174,6 +1154,33 @@ namespace ams::ncm { return ResultSuccess(); } + Result InstallTaskBase::ReadContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const ContentMetaKey &key) { + /* Get the install content meta info. */ + InstallContentMetaInfo install_content_meta_info; + R_TRY(this->GetInstallContentMetaInfo(std::addressof(install_content_meta_info), key)); + + /* Open the BuiltInSystem content storage. */ + ContentStorage content_storage; + R_TRY(ncm::OpenContentStorage(&content_storage, StorageId::BuiltInSystem)); + + /* Write content meta to a placeholder. */ + InstallContentInfo content_info; + R_TRY(this->WriteContentMetaToPlaceHolder(std::addressof(content_info), std::addressof(content_storage), install_content_meta_info, true)); + + const PlaceHolderId placeholder_id = content_info.GetPlaceHolderId(); + + /* Get the path of the new placeholder. */ + Path path; + content_storage.GetPlaceHolderPath(std::addressof(path), placeholder_id); + + /* Read the variation list. */ + R_TRY(ReadVariationContentMetaInfoList(out_count, out_meta_infos, path, this->firmware_variation_id)); + + /* Delete the placeholder. */ + content_storage.DeletePlaceHolder(placeholder_id); + return ResultSuccess(); + } + Result InstallTaskBase::FindMaxRequiredApplicationVersion(u32 *out) { /* Count the number of content meta entries. */ s32 count; @@ -1186,55 +1193,16 @@ namespace ams::ncm { /* Obtain the content meta. */ InstallContentMeta content_meta; R_TRY(this->data->Get(std::addressof(content_meta), i)); - + /* Create a reader. */ const InstallContentMetaReader reader = content_meta.GetReader(); /* Check if the meta type is for add on content. */ - if (reader.GetHeader()->type == ContentMetaType::AddOnContent) { + if (reader.GetKey().type == ContentMetaType::AddOnContent) { const auto *extended_header = reader.GetExtendedHeader(); - - /* Set the max version if higher. */ - if (extended_header->required_application_version >= max_version) { - max_version = extended_header->required_application_version; - } - } - } - - *out = max_version; - return ResultSuccess(); - } - - Result InstallTaskBase::FindMaxRequiredSystemVersion(u32 *out) { - /* Count the number of content meta entries. */ - s32 count; - R_TRY(this->data->Count(std::addressof(count))); - - u32 max_version = 0; - - /* 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)); - - /* Create a reader. */ - const InstallContentMetaReader reader = content_meta.GetReader(); - - if (reader.GetHeader()->type == ContentMetaType::Application) { - const auto *extended_header = reader.GetExtendedHeader(); /* Set the max version if higher. */ - if (extended_header->required_system_version >= max_version) { - max_version = extended_header->required_system_version; - } - } else if (reader.GetHeader()->type == ContentMetaType::Patch) { - const auto *extended_header = reader.GetExtendedHeader(); - - /* Set the max version if higher. */ - if (extended_header->required_system_version >= max_version) { - max_version = extended_header->required_system_version; - } + max_version = std::max(max_version, extended_header->required_application_version); } } @@ -1279,7 +1247,7 @@ namespace ams::ncm { /* Check if this content info has a placeholder. */ bool has_placeholder; R_TRY(content_storage.HasPlaceHolder(std::addressof(has_placeholder), content_info->GetPlaceHolderId())); - + if (has_placeholder) { total_size += content_info->GetSize(); } @@ -1293,27 +1261,71 @@ namespace ams::ncm { }; } + /* Write the out count. */ + *out_written = count; + + return ResultSuccess(); + } + + void InstallTaskBase::SetProgressState(InstallProgressState state) { + std::scoped_lock(this->progress_mutex); + this->data->SetState(state); + this->progress.state = state; + } + + Result InstallTaskBase::FindMaxRequiredSystemVersion(u32 *out) { + /* Count the number of content meta entries. */ + s32 count; + R_TRY(this->data->Count(std::addressof(count))); + + u32 max_version = 0; + + /* 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)); + + /* Create a reader. */ + const InstallContentMetaReader reader = content_meta.GetReader(); + + if (reader.GetHeader()->type == ContentMetaType::Application) { + const auto *extended_header = reader.GetExtendedHeader(); + + /* Set the max version if higher. */ + if (extended_header->required_system_version >= max_version) { + max_version = extended_header->required_system_version; + } + } else if (reader.GetHeader()->type == ContentMetaType::Patch) { + const auto *extended_header = reader.GetExtendedHeader(); + + /* Set the max version if higher. */ + if (extended_header->required_system_version >= max_version) { + max_version = extended_header->required_system_version; + } + } + } + + *out = max_version; return ResultSuccess(); } Result InstallTaskBase::CanContinue() { - auto progress = this->GetProgress(); - - if (progress.state == InstallProgressState::NotPrepared || progress.state == InstallProgressState::DataPrepared) { - R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled()); - } - - if (progress.state == InstallProgressState::Prepared) { - R_UNLESS(!this->IsCancelRequested(), ncm::ResultWritePlaceHolderCancelled()); + switch (this->GetProgress().state) { + case InstallProgressState::NotPrepared: + case InstallProgressState::DataPrepared: + R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled()); + break; + case InstallProgressState::Prepared: + R_UNLESS(!this->IsCancelRequested(), ncm::ResultWritePlaceHolderCancelled()); + break; + default: + break; } return ResultSuccess(); } - void InstallTaskBase::SetFirmwareVariationId(FirmwareVariationId id) { - this->firmware_variation_id = id; - } - Result InstallTaskBase::ListRightsIds(s32 *out_count, Span out_span, const ContentMetaKey &key, s32 offset) { /* Count the number of content meta entries. */ s32 count; @@ -1367,7 +1379,7 @@ namespace ams::ncm { /* Get the rights id. */ RightsId rights_id; R_TRY(content_storage.GetRightsId(std::addressof(rights_id), content_info->GetPlaceHolderId())); - + /* Skip empty rights ids. */ if (rights_id.id == fs::InvalidRightsId) { continue; diff --git a/libraries/libstratosphere/source/ncm/ncm_install_task_data.cpp b/libraries/libstratosphere/source/ncm/ncm_install_task_data.cpp index 7725d11b2..59c246403 100644 --- a/libraries/libstratosphere/source/ncm/ncm_install_task_data.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_install_task_data.cpp @@ -133,7 +133,7 @@ namespace ams::ncm { /* Put the data holder into the data list. */ this->data_list.push_back(*holder); - + /* Relinquish control over the memory allocated to the data holder. */ holder.release(); @@ -232,13 +232,6 @@ namespace ams::ncm { return this->Read(std::addressof(this->header), sizeof(Header), 0); } - Result FileInstallTaskData::Read(void *out, size_t out_size, s64 offset) { - fs::FileHandle file; - R_TRY(fs::OpenFile(std::addressof(file), this->path, fs::OpenMode_Read)); - ON_SCOPE_EXIT { fs::CloseFile(file); }; - return fs::ReadFile(file, offset, out, out_size); - } - Result FileInstallTaskData::GetProgress(InstallProgress *out_progress) { /* Initialize install progress. */ InstallProgress install_progress = { @@ -248,7 +241,7 @@ namespace ams::ncm { /* Only states after prepared are allowed. */ if (this->header.progress_state != InstallProgressState::NotPrepared && this->header.progress_state != InstallProgressState::DataPrepared) { - for (s32 i = 0; i < this->header.count; i++) { + for (size_t i = 0; i < this->header.count; i++) { /* Obtain the content meta for this entry. */ InstallContentMeta content_meta; R_TRY(InstallTaskDataBase::Get(std::addressof(content_meta), i)); @@ -267,11 +260,6 @@ namespace ams::ncm { return ResultSuccess(); } - Result FileInstallTaskData::GetEntryInfo(EntryInfo *out_entry_info, s32 index) { - AMS_ABORT_UNLESS(index < this->header.count); - return this->Read(out_entry_info, sizeof(EntryInfo), GetEntryInfoOffset(index)); - } - Result FileInstallTaskData::GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out_info) { *out_info = this->header.system_update_task_apply_info; return ResultSuccess(); @@ -282,10 +270,6 @@ namespace ams::ncm { return this->WriteHeader(); } - Result FileInstallTaskData::WriteHeader() { - return this->Write(std::addressof(this->header), sizeof(Header), 0); - } - Result FileInstallTaskData::SetLastResult(Result result) { this->header.last_result = result; return this->WriteHeader(); @@ -300,7 +284,7 @@ namespace ams::ncm { R_UNLESS(this->header.count < this->header.max_entries, ncm::ResultBufferInsufficient()); /* Create a new entry info. Data of the given size will be stored at the end of the file. */ - const EntryInfo entry_info = { this->header.last_data_offset, data_size }; + const EntryInfo entry_info = { this->header.last_data_offset, static_cast(data_size) }; /* Write the new entry info. */ R_TRY(this->Write(std::addressof(entry_info), sizeof(EntryInfo), GetEntryInfoOffset(this->header.count))); @@ -316,13 +300,6 @@ namespace ams::ncm { return this->WriteHeader(); } - Result FileInstallTaskData::Write(const void *data, size_t size, s64 offset) { - fs::FileHandle file; - R_TRY(fs::OpenFile(std::addressof(file), this->path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)); - ON_SCOPE_EXIT { fs::CloseFile(file); }; - return fs::WriteFile(file, offset, data, size, fs::WriteOption::Flush); - } - Result FileInstallTaskData::Count(s32 *out) { *out = this->header.count; return ResultSuccess(); @@ -394,4 +371,27 @@ namespace ams::ncm { return this->WriteHeader(); } + Result FileInstallTaskData::GetEntryInfo(EntryInfo *out_entry_info, s32 index) { + AMS_ABORT_UNLESS(static_cast(index) < this->header.count); + return this->Read(out_entry_info, sizeof(EntryInfo), GetEntryInfoOffset(index)); + } + + Result FileInstallTaskData::Write(const void *data, size_t size, s64 offset) { + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), this->path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + return fs::WriteFile(file, offset, data, size, fs::WriteOption::Flush); + } + + Result FileInstallTaskData::Read(void *out, size_t out_size, s64 offset) { + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), this->path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + return fs::ReadFile(file, offset, out, out_size); + } + + Result FileInstallTaskData::WriteHeader() { + return this->Write(std::addressof(this->header), sizeof(Header), 0); + } + } \ No newline at end of file diff --git a/libraries/libstratosphere/source/ncm/ncm_package_install_task_base.cpp b/libraries/libstratosphere/source/ncm/ncm_package_install_task_base.cpp index 7fe4d93bc..646841605 100644 --- a/libraries/libstratosphere/source/ncm/ncm_package_install_task_base.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_package_install_task_base.cpp @@ -32,6 +32,7 @@ namespace ams::ncm { } else { this->CreateContentPath(std::addressof(path), content_info->GetId()); } + /* Open the file. */ fs::FileHandle file; R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read)); @@ -55,18 +56,6 @@ namespace ams::ncm { return ResultSuccess(); } - void PackageInstallTaskBase::CreateContentMetaPath(PackagePath *out_path, ContentId content_id) { - char str[ContentIdStringLength + 1] = {}; - GetStringFromContentId(str, sizeof(str), content_id); - out_path->SetFormat("%s%s%s", this->package_root.Get(), str, ".cnmt.nca"); - } - - void PackageInstallTaskBase::CreateContentPath(PackagePath *out_path, ContentId content_id) { - char str[ContentIdStringLength + 1] = {}; - GetStringFromContentId(str, sizeof(str), content_id); - out_path->SetFormat("%s%s%s", this->package_root.Get(), str, ".nca"); - } - Result PackageInstallTaskBase::InstallTicket(const fs::RightsId &rights_id, ContentMetaType meta_type) { /* Read ticket from file. */ s64 ticket_size; @@ -113,6 +102,18 @@ namespace ams::ncm { return ResultSuccess(); } + void PackageInstallTaskBase::CreateContentPath(PackagePath *out_path, ContentId content_id) { + char str[ContentIdStringLength + 1] = {}; + GetStringFromContentId(str, sizeof(str), content_id); + out_path->SetFormat("%s%s%s", this->package_root.Get(), str, ".nca"); + } + + void PackageInstallTaskBase::CreateContentMetaPath(PackagePath *out_path, ContentId content_id) { + char str[ContentIdStringLength + 1] = {}; + GetStringFromContentId(str, sizeof(str), content_id); + out_path->SetFormat("%s%s%s", this->package_root.Get(), str, ".cnmt.nca"); + } + void PackageInstallTaskBase::CreateTicketPath(PackagePath *out_path, fs::RightsId id) { char str[RightsIdStringLength + 1] = {}; GetStringFromRightsId(str, sizeof(str), id); diff --git a/libraries/libstratosphere/source/ncm/ncm_package_system_update_task.cpp b/libraries/libstratosphere/source/ncm/ncm_package_system_update_task.cpp index eef0cdacf..881427225 100644 --- a/libraries/libstratosphere/source/ncm/ncm_package_system_update_task.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_package_system_update_task.cpp @@ -38,15 +38,10 @@ namespace ams::ncm { /* Activate the game card content meta database. */ R_TRY(ActivateContentMetaDatabase(StorageId::GameCard)); this->gamecard_content_meta_database_active = true; + auto meta_db_guard = SCOPE_GUARD { this->Inactivate(); }; /* Open the game card content meta database. */ OpenContentMetaDatabase(std::addressof(this->package_db), StorageId::GameCard); - auto meta_db_guard = SCOPE_GUARD { - if (this->gamecard_content_meta_database_active) { - InactivateContentMetaDatabase(StorageId::GameCard); - this->gamecard_content_meta_database_active = false; - } - }; ContentMetaDatabaseBuilder builder(std::addressof(this->package_db)); @@ -56,10 +51,8 @@ namespace ams::ncm { /* Create a new context file. */ fs::DeleteFile(context_path); - R_TRY(FileInstallTaskData::Create(context_path, 0x800)); - auto context_guard = SCOPE_GUARD { - fs::DeleteFile(context_path); - }; + R_TRY(FileInstallTaskData::Create(context_path, GameCardMaxContentMetaCount)); + auto context_guard = SCOPE_GUARD { fs::DeleteFile(context_path); }; /* Initialize data. */ R_TRY(this->data.Initialize(context_path)); @@ -113,30 +106,6 @@ namespace ams::ncm { return ResultSuccess(); } - Result PackageSystemUpdateTask::GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key) { - s32 ofs = 0; - while (true) { - /* List content infos. */ - s32 count; - ContentInfo info; - R_TRY(this->package_db.ListContentInfo(std::addressof(count), std::addressof(info), 1, key, ofs++)); - - /* No content infos left to list. */ - if (count == 0) { - break; - } - - /* Check if the info is for meta content. */ - if (info.GetType() == ContentType::Meta) { - *out = info; - return ResultSuccess(); - } - } - - /* Not found. */ - return ncm::ResultContentInfoNotFound(); - } - Result PackageSystemUpdateTask::PrepareInstallContentMetaData() { /* Obtain a SystemUpdate key. */ ContentMetaKey key; @@ -155,4 +124,28 @@ namespace ams::ncm { return this->PrepareSystemUpdateDependency(); } + Result PackageSystemUpdateTask::GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key) { + s32 ofs = 0; + while (true) { + /* List content infos. */ + s32 count; + ContentInfo info; + R_TRY(this->package_db.ListContentInfo(std::addressof(count), std::addressof(info), 1, key, ofs++)); + + /* No content infos left to list. */ + if (count == 0) { + break; + } + + /* Check if the info is for meta content. */ + if (info.GetType() == ContentType::Meta) { + *out = info; + return ResultSuccess(); + } + } + + /* Not found. */ + return ncm::ResultContentInfoNotFound(); + } + } diff --git a/libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_storage_utils.cpp similarity index 54% rename from libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp rename to libraries/libstratosphere/source/ncm/ncm_storage_utils.cpp index db37919bd..d5d337727 100644 --- a/libraries/libstratosphere/source/ncm/ncm_storage_id_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_storage_utils.cpp @@ -17,29 +17,7 @@ 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) { + Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, s64 required_size) { auto list = GetStorageList(storage_id); for (s32 i = 0; i < list.Count(); i++) { auto candidate = list[i]; @@ -73,14 +51,77 @@ namespace ams::ncm { return ncm::ResultNotEnoughInstallSpace(); } - const char *GetStorageIdString(StorageId storage_id) { - u8 id = static_cast(storage_id); - return id > 5 ? "(unknown)" : StorageIdStrings[id]; + Result SelectPatchStorage(StorageId *out_storage_id, StorageId storage_id, PatchId patch_id) { + auto list = GetStorageList(storage_id); + u32 version = 0; + *out_storage_id = storage_id; + + for (s32 i = 0; i < list.Count(); i++) { + auto candidate = list[i]; + + /* Open the content meta database. */ + ContentMetaDatabase content_meta_database; + if (R_FAILED(ncm::OpenContentMetaDatabase(std::addressof(content_meta_database), candidate))) { + continue; + } + + /* Get the latest key. */ + ContentMetaKey key; + R_TRY_CATCH(content_meta_database.GetLatest(std::addressof(key), patch_id.value)) { + R_CATCH(ncm::ResultContentMetaNotFound) { continue; } + } R_END_TRY_CATCH; + + if (key.version > version) { + version = key.version; + *out_storage_id = candidate; + } + } + + return ResultSuccess(); } - + + namespace { + + constexpr const char * const StorageIdStrings[] = { + "None", + "Host", + "GameCard", + "BuiltInSystem", + "BuiltInUser", + "SdCard" + }; + + constexpr const char * const StorageIdStringsForPlayReport[] = { + "None", + "Host", + "Card", + "BuildInSystem", + "BuildInUser", + "SdCard" + }; + + } + + const char *GetStorageIdString(StorageId storage_id) { + switch (storage_id) { + case StorageId::None: return "None"; + case StorageId::Host: return "Host"; + case StorageId::GameCard: return "GameCard"; + case StorageId::BuiltInSystem: return "BuiltInSystem"; + case StorageId::BuiltInUser: return "BuiltInUser"; + default: return "(unknown)"; + } + } + const char *GetStorageIdStringForPlayReport(StorageId storage_id) { - u8 id = static_cast(storage_id); - return id > 5 ? "(unknown)" : StorageIdStringsForPlayReport[id]; + switch (storage_id) { + case StorageId::None: return "None"; + case StorageId::Host: return "Host"; + case StorageId::Card: return "Card"; + case StorageId::BuildInSystem: return "BuildInSystem"; + case StorageId::BuildInUser: return "BuildInUser"; + default: return "(unknown)"; + } } } diff --git a/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp b/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp index 4a77452a7..156f924c8 100644 --- a/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp +++ b/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp @@ -36,7 +36,7 @@ namespace ams::nim { } /* Service API. */ - Result DestroySystemUpdateTask(const SystemUpdateTaskId& id) { + Result DestroySystemUpdateTask(const SystemUpdateTaskId &id) { static_assert(sizeof(SystemUpdateTaskId) == sizeof(::NimSystemUpdateTaskId)); return nimDestroySystemUpdateTask(reinterpret_cast(std::addressof(id))); } diff --git a/libraries/libvapours/include/vapours/results/ncm_results.hpp b/libraries/libvapours/include/vapours/results/ncm_results.hpp index 2bd841658..a1885b395 100644 --- a/libraries/libvapours/include/vapours/results/ncm_results.hpp +++ b/libraries/libvapours/include/vapours/results/ncm_results.hpp @@ -42,6 +42,7 @@ namespace ams::ncm { R_DEFINE_ERROR_RESULT(NotEnoughInstallSpace, 200); R_DEFINE_ERROR_RESULT(SystemUpdateNotFoundInPackage, 210); R_DEFINE_ERROR_RESULT(ContentInfoNotFound, 220); + R_DEFINE_ERROR_RESULT(DeltaNotFound, 237); R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240); R_DEFINE_ERROR_RESULT(IgnorableInstallTicketFailure, 280);