From 56f66fff4d0ed41281d11633529cf977628d8468 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 20 Mar 2020 17:06:23 -0700 Subject: [PATCH] ncm client: more progress --- .../ncm/ncm_content_info_data.hpp | 7 + .../stratosphere/ncm/ncm_content_meta.hpp | 13 +- .../ncm/ncm_content_meta_utils.hpp | 3 + .../ncm/ncm_firmware_variation.hpp | 8 + .../stratosphere/ncm/ncm_install_task.hpp | 15 +- .../source/ncm/ncm_content_meta_utils.cpp | 66 ++++++++ .../source/ncm/ncm_install_task.cpp | 148 +++++++++++++++++- .../include/vapours/results/ncm_results.hpp | 1 + 8 files changed, 251 insertions(+), 10 deletions(-) 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 07ace78cf..2922ed015 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp @@ -101,6 +101,13 @@ namespace ams::ncm { return this->written; } + static constexpr InstallContentInfo Make(const ContentInfo &info, ContentMetaType meta_type) { + return { + .info = info, + .meta_type = meta_type, + }; + } + static constexpr InstallContentInfo Make(const PackagedContentInfo &info, ContentMetaType meta_type) { return { .digest = info.digest, diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp index 9c8c848b8..2c7975228 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp @@ -281,7 +281,7 @@ namespace ams::ncm { switch (this->GetHeader()->type) { case ContentMetaType::Patch: return this->GetExtendedHeader()->extended_data_size; case ContentMetaType::Delta: return this->GetExtendedHeader()->extended_data_size; - case ContentMetaType::SystemUpdate: return this->GetExtendedHeader()->extended_data_size; + case ContentMetaType::SystemUpdate: return this->GetExtendedHeaderSize() == 0 ? 0 : this->GetExtendedHeader()->extended_data_size; default: return 0; } } @@ -357,7 +357,7 @@ namespace ams::ncm { class InstallContentMetaWriter : public ContentMetaAccessor { public: - InstallContentMetaWriter(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } + InstallContentMetaWriter(void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } using ContentMetaAccessor::CalculateSize; using ContentMetaAccessor::CalculateContentRequiredSize; @@ -412,13 +412,13 @@ namespace ams::ncm { return this->GetHeader()->firmware_variation_count; } - FirmwareVariationId *GetFirmwareVariationId(size_t i) const { + const FirmwareVariationId *GetFirmwareVariationId(size_t i) const { AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount()); return reinterpret_cast(this->GetFirmwareVariationIdAddress(i)); } - FirmwareVariationInfo *GetFirmwareVariationInfo(size_t i) const { + const FirmwareVariationInfo *GetFirmwareVariationInfo(size_t i) const { AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount()); return reinterpret_cast(this->GetFirmwareVariationInfoAddress(i)); @@ -437,4 +437,9 @@ namespace ams::ncm { } }; + 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_utils.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp index 6673e063d..72ffbfe61 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta_utils.hpp @@ -19,9 +19,12 @@ #include #include #include +#include namespace ams::ncm { Result ReadContentMetaPath(AutoBuffer *out, const char *path); + Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const Path &path, FirmwareVariationId firmware_variation_id); + } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp index 054824abb..41e31bd2c 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_firmware_variation.hpp @@ -27,6 +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; + } }; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp index a0bd12fb4..05e283d10 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task.hpp @@ -80,6 +80,7 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta InstallThroughput throughput; TimeSpan throughput_start_time; os::Mutex throughput_mutex; + FirmwareVariationId firmware_variation_id; public: virtual ~InstallTaskBase() { /* ... */ }; private: @@ -127,13 +128,23 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta Result Commit(const StorageContentMetaKey *keys, s32 num_keys); Result IncludesExFatDriver(bool *out); Result WritePlaceHolderBuffer(InstallContentInfo *content_info, const void *data, size_t data_size); - Result WriteContentMetaToPlaceHolder(InstallContentInfo *install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary); + Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary); InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional is_temporary); + Result PrepareContentMeta(ContentId content_id, s64 size, ContentMetaType meta_type, AutoBuffer *buffer); + + Result GetContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const ContentMetaKey &key); + Result IsNewerThanInstalled(bool *out, const ContentMetaKey &key); Result DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys); void ResetLastResult(); s64 GetThroughput(); + + Result FindMaxRequiredApplicationVersion(u32 *out); + Result FindMaxRequiredSystemVersion(u32 *out); + + Result CanContinue(); + void SetFirmwareVariationId(FirmwareVariationId id); protected: virtual Result OnPrepareComplete(); virtual Result PrepareDependency(); @@ -143,7 +154,7 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta virtual void ResetCancel(); virtual InstallProgress GetProgress(); virtual Result PrepareInstallContentMetaData() = 0; - void *GetInstallContentMetaInfo; + virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key); virtual Result GetLatestVersion(std::optional *out_version, u64 id); virtual Result CheckInstallable(); virtual Result OnExecuteComplete(); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp index df9a9e0e8..11a9fd152 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_utils.cpp @@ -77,4 +77,70 @@ namespace ams::ncm { return ncm::ResultContentMetaNotFound(); } + Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr *out_meta_infos, const Path &path, FirmwareVariationId firmware_variation_id) { + AutoBuffer meta; + { + /* TODO: fs::ScopedAutoAbortDisabler aad; */ + R_TRY(ReadContentMetaPath(std::addressof(meta), path.str)); + } + + PackagedContentMetaReader reader(meta.Get(), meta.GetSize()); + /* TODO: ON_SCOPE_EXIT { }; */ + + /* No firmware variations to list. */ + if (reader.GetExtendedDataSize() == 0) { + return ResultSuccess(); + } + + 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++) { + if (*extended_data_reader.GetFirmwareVariationId(i) == firmware_variation_id) { + firmware_variation_index = i; + break; + } + } + + /* We couldn't find the input firmware variation id. */ + if (!firmware_variation_index) { + return 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(); + } + + /* 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(); + } + + /* Allocate a buffer for the content meta infos. */ + std::unique_ptr buffer(new (std::nothrow) ContentMetaInfo[content_meta_count]); + AMS_ABORT_UNLESS(buffer != nullptr); + + /* Get the content meta infos. */ + Span meta_infos; + extended_data_reader.GetContentMetaInfoList(std::addressof(meta_infos), content_meta_count); + + /* Copy the meta infos to the buffer. */ + for (size_t i = 0; i < content_meta_count; i++) { + buffer[i] = meta_infos[i]; + } + + /* Output the content meta info buffer. */ + *out_meta_infos = std::move(buffer); + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/ncm/ncm_install_task.cpp b/libraries/libstratosphere/source/ncm/ncm_install_task.cpp index e1841d14e..a5593abdc 100644 --- a/libraries/libstratosphere/source/ncm/ncm_install_task.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_install_task.cpp @@ -725,7 +725,7 @@ namespace ams::ncm { return ResultSuccess(); } - Result InstallTaskBase::WriteContentMetaToPlaceHolder(InstallContentInfo *install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary) { + Result InstallTaskBase::WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional is_temporary) { /* Generate a placeholder id. */ auto placeholder_id = storage->GeneratePlaceHolderId(); @@ -734,14 +734,14 @@ namespace ams::ncm { auto placeholder_guard = SCOPE_GUARD { storage->DeletePlaceHolder(placeholder_id); }; /* Output install content info. */ - *install_content_info = this->MakeInstallContentInfoFrom(meta_info, placeholder_id, is_temporary); + *out_install_content_info = this->MakeInstallContentInfoFrom(meta_info, placeholder_id, is_temporary); /* Write install content info. */ - R_TRY(this->WritePlaceHolder(meta_info.key, 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(); - install_content_info->install_state = InstallState::Installed; + out_install_content_info->install_state = InstallState::Installed; return ResultSuccess(); } @@ -760,6 +760,24 @@ namespace ams::ncm { /* ... */ + Result InstallTaskBase::PrepareContentMeta(ContentId content_id, s64 size, ContentMetaType meta_type, AutoBuffer *buffer) { + /* Create a reader. */ + PackagedContentMetaReader reader(buffer->Get(), buffer->GetSize()); + + /* Initialize the temporary buffer. */ + AutoBuffer tmp_buffer; + R_TRY(tmp_buffer.Initialize(reader.CalculateConvertInstallContentMetaSize())); + + /* 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(); + } + + /* ... */ + void InstallTaskBase::IncrementProgress(s64 size) { std::scoped_lock lk(this->progress_mutex); this->progress.installed_size += size; @@ -802,6 +820,39 @@ namespace ams::ncm { return ResultSuccess(); } + // Result InstallTaskBase::PrepareSystemUpdateDependency() { + // /* TODO */ + + // return ResultSuccess(); + // } + + Result InstallTaskBase::GetContentMetaInfoList(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.placeholder_id; + + /* 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::IsNewerThanInstalled(bool *out, const ContentMetaKey &key) { @@ -871,4 +922,93 @@ namespace ams::ncm { return this->throughput.installed; } + /* ... */ + + Result InstallTaskBase::FindMaxRequiredApplicationVersion(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(&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) { + 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(&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()); + } + + return ResultSuccess(); + } + + void InstallTaskBase::SetFirmwareVariationId(FirmwareVariationId id) { + this->firmware_variation_id = id; + } } diff --git a/libraries/libvapours/include/vapours/results/ncm_results.hpp b/libraries/libvapours/include/vapours/results/ncm_results.hpp index 4bbb693c1..16c291d8c 100644 --- a/libraries/libvapours/include/vapours/results/ncm_results.hpp +++ b/libraries/libvapours/include/vapours/results/ncm_results.hpp @@ -45,6 +45,7 @@ namespace ams::ncm { R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310); R_DEFINE_ERROR_RESULT(ListPartiallyNotCommitted, 330); + R_DEFINE_ERROR_RESULT(InvalidFirmwareVariation, 380); R_DEFINE_ERROR_RANGE(ContentStorageNotActive, 250, 258); R_DEFINE_ERROR_RESULT(GameCardContentStorageNotActive, 251);