From 458599f17471d2c565b396b55a3ebd9ba84f368a Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 7 Mar 2020 16:07:36 -0800 Subject: [PATCH] ncm: implement packagedcontent -> content for building metadb --- .../ncm/ncm_content_info_data.hpp | 17 +++ .../stratosphere/ncm/ncm_content_meta.hpp | 35 ++++++ .../libstratosphere/source/fs/fs_content.cpp | 4 +- .../ncm/ncm_content_management_utils.cpp | 17 ++- .../source/ncm/ncm_content_meta.cpp | 101 ++++++++++++++++++ 5 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 libraries/libstratosphere/source/ncm/ncm_content_meta.cpp 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 8a5af8517..c29a7de62 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info_data.hpp @@ -25,4 +25,21 @@ namespace ams::ncm { u8 data[crypto::Sha256Generator::HashSize]; }; + struct PackagedContentInfo { + Digest digest; + ContentInfo info; + + constexpr const ContentId &GetId() const { + return this->info.GetId(); + } + + constexpr const ContentType GetType() const { + return this->info.GetType(); + } + + constexpr const u8 GetIdOffset() const { + return this->info.GetIdOffset(); + } + }; + } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp index f25e81ef6..cbb4ab5d6 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp @@ -61,6 +61,26 @@ namespace ams::ncm { static_assert(sizeof(ContentMetaHeader) == 0x8); + struct PackagedContentMetaHeader { + u64 id; + u32 version; + ContentMetaType type; + u8 reserved_0D; + u16 extended_header_size; + u16 content_count; + u16 content_meta_count; + u8 attributes; + u8 storage_id; + ContentInstallType install_type; + u8 reserved_17; + u32 required_download_system_version; + u8 reserved_1C[4]; + }; + static_assert(sizeof(PackagedContentMetaHeader) == 0x20); + static_assert(OFFSETOF(PackagedContentMetaHeader, reserved_0D) == 0x0D); + static_assert(OFFSETOF(PackagedContentMetaHeader, reserved_17) == 0x17); + static_assert(OFFSETOF(PackagedContentMetaHeader, reserved_1C) == 0x1C); + struct ApplicationMetaExtendedHeader { PatchId patch_id; u32 required_system_version; @@ -277,4 +297,19 @@ namespace ams::ncm { using ContentMetaAccessor::CalculateSize; }; + class PackagedContentMetaReader : public ContentMetaAccessor { + public: + constexpr PackagedContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ } + + size_t CalculateConvertContentMetaSize() const; + + void ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta); + + size_t CountDeltaFragments() const; + + static constexpr size_t CalculateSize(ContentMetaType type, size_t content_count, size_t content_meta_count, size_t extended_data_size) { + return ContentMetaAccessor::CalculateSize(type, content_count, content_meta_count, extended_data_size, true); + } + }; + } diff --git a/libraries/libstratosphere/source/fs/fs_content.cpp b/libraries/libstratosphere/source/fs/fs_content.cpp index d36aaef2f..d4abe6170 100644 --- a/libraries/libstratosphere/source/fs/fs_content.cpp +++ b/libraries/libstratosphere/source/fs/fs_content.cpp @@ -57,8 +57,8 @@ namespace ams::fs { } Result MountContent(const char *name, const char *path, ContentType content_type) { - /* This API only supports mounting Manual content. */ - AMS_ABORT_UNLESS(content_type == ContentType_Manual); + /* This API only supports mounting Meta content. */ + AMS_ABORT_UNLESS(content_type == ContentType_Meta); return MountContent(name, path, ncm::InvalidProgramId, content_type); } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp b/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp index ef89ee0b2..f130793a3 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_management_utils.cpp @@ -19,7 +19,22 @@ namespace ams::ncm { Result ContentMetaDatabaseBuilder::BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info) { - AMS_ABORT(); + /* Create a reader for the content meta. */ + ncm::PackagedContentMetaReader package_meta_reader(buf, size); + + /* Allocate space to hold the converted meta. */ + const size_t meta_size = package_meta_reader.CalculateConvertContentMetaSize(); + void *meta = std::malloc(meta_size); + ON_SCOPE_EXIT { std::free(meta); }; + + /* Convert the meta from packaged form to normal form. */ + package_meta_reader.ConvertToContentMeta(meta, meta_size, meta_info); + ncm::ContentMetaReader meta_reader(meta, meta_size); + + /* Insert the new metas into the database. */ + R_TRY(this->db->Set(package_meta_reader.GetKey(), meta_reader.GetData(), meta_reader.GetSize())); + + /* We're done. */ return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp new file mode 100644 index 000000000..6a57acd10 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + namespace { + + void ConvertPackageContentMetaHeaderToContentMetaHeader(ContentMetaHeader *dst, const PackagedContentMetaHeader &src) { + /* Clear destination. */ + *dst = {}; + + /* Set converted fields. */ + dst->extended_header_size = src.extended_header_size; + dst->content_meta_count = src.content_meta_count; + dst->content_count = src.content_meta_count; + dst->attributes = src.attributes; + } + + } + + size_t PackagedContentMetaReader::CountDeltaFragments() const { + size_t count = 0; + for (size_t i = 0; i < this->GetContentCount(); i++) { + if (this->GetContentInfo(i)->GetType() == ContentType::DeltaFragment) { + count++; + } + } + return count; + } + + size_t PackagedContentMetaReader::CalculateConvertContentMetaSize() const { + const auto *header = this->GetHeader(); + return this->CalculateSizeImpl(header->extended_header_size, header->content_count + 1, header->content_meta_count, 0, 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()); + + /* Prepare for conversion. */ + const auto *packaged_header = this->GetHeader(); + uintptr_t dst_addr = reinterpret_cast(dst); + + /* Convert the header. */ + ContentMetaHeader header; + ConvertPackageContentMetaHeaderToContentMetaHeader(std::addressof(header), *packaged_header); + header.content_count += 1; + + /* Don't include deltas. */ + if (packaged_header->type == ContentMetaType::Patch) { + header.content_count -= this->CountDeltaFragments(); + } + + /* 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); + + /* Copy content infos. */ + for (size_t i = 0; i < this->GetContentCount(); i++) { + /* Don't copy any delta fragments. */ + if (packaged_header->type == ContentMetaType::Patch) { + if (this->GetContentInfo(i)->GetType() == ContentType::DeltaFragment) { + continue; + } + } + + /* Copy the current info. */ + std::memcpy(reinterpret_cast(dst_addr), std::addressof(this->GetContentInfo(i)->info), sizeof(ContentInfo)); + dst_addr += sizeof(ContentInfo); + } + + /* Copy content meta infos. */ + for (size_t i = 0; i < this->GetContentMetaCount(); i++) { + std::memcpy(reinterpret_cast(dst_addr), this->GetContentMetaInfo(i), sizeof(ContentMetaInfo)); + dst_addr += sizeof(ContentMetaInfo); + } + } + +}