mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-07-18 06:32:14 +02:00
ncm client: more progress
This commit is contained in:
parent
8fbb10a83c
commit
12670e1a01
@ -27,6 +27,7 @@
|
|||||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_manager_impl.hpp>
|
#include <stratosphere/ncm/ncm_content_manager_impl.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_firmware_variation.hpp>
|
||||||
#include <stratosphere/ncm/ncm_install_task.hpp>
|
#include <stratosphere/ncm/ncm_install_task.hpp>
|
||||||
#include <stratosphere/ncm/ncm_install_task_data.hpp>
|
#include <stratosphere/ncm/ncm_install_task_data.hpp>
|
||||||
#include <stratosphere/ncm/ncm_storage_id_utils.hpp>
|
#include <stratosphere/ncm/ncm_storage_id_utils.hpp>
|
||||||
|
@ -32,6 +32,23 @@ namespace ams::ncm {
|
|||||||
AlreadyExists,
|
AlreadyExists,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct InstallContentInfo {
|
struct InstallContentInfo {
|
||||||
Digest digest;
|
Digest digest;
|
||||||
crypto::Sha256Context context;
|
crypto::Sha256Context context;
|
||||||
@ -41,7 +58,7 @@ namespace ams::ncm {
|
|||||||
PlaceHolderId placeholder_id;
|
PlaceHolderId placeholder_id;
|
||||||
ContentMetaType meta_type;
|
ContentMetaType meta_type;
|
||||||
InstallState install_state;
|
InstallState install_state;
|
||||||
bool verify_hash;
|
bool verify_digest;
|
||||||
StorageId storage_id;
|
StorageId storage_id;
|
||||||
bool is_temporary;
|
bool is_temporary;
|
||||||
bool is_sha256_calculated;
|
bool is_sha256_calculated;
|
||||||
@ -83,25 +100,17 @@ namespace ams::ncm {
|
|||||||
constexpr s64 GetSizeWritten() const {
|
constexpr s64 GetSizeWritten() const {
|
||||||
return this->written;
|
return this->written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr InstallContentInfo Make(const PackagedContentInfo &info, ContentMetaType meta_type) {
|
||||||
|
return {
|
||||||
|
.digest = info.digest,
|
||||||
|
.info = info.info,
|
||||||
|
.meta_type = meta_type,
|
||||||
|
.verify_digest = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(InstallContentInfo) == 0xC8);
|
static_assert(sizeof(InstallContentInfo) == 0xC8);
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <stratosphere/ncm/ncm_content_meta_key.hpp>
|
#include <stratosphere/ncm/ncm_content_meta_key.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_info.hpp>
|
#include <stratosphere/ncm/ncm_content_info.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_info_data.hpp>
|
#include <stratosphere/ncm/ncm_content_info_data.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_firmware_variation.hpp>
|
||||||
#include <stratosphere/ncm/ncm_storage_id.hpp>
|
#include <stratosphere/ncm/ncm_storage_id.hpp>
|
||||||
|
|
||||||
namespace ams::ncm {
|
namespace ams::ncm {
|
||||||
@ -107,6 +108,15 @@ namespace ams::ncm {
|
|||||||
u32 padding;
|
u32 padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SystemUpdateMetaExtendedHeader {
|
||||||
|
u32 extended_data_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SystemUpdateMetaExtendedDataHeader {
|
||||||
|
u32 unk; // Always seems to be set to 2
|
||||||
|
u32 firmware_variation_count;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename ContentMetaHeaderType, typename ContentInfoType>
|
template<typename ContentMetaHeaderType, typename ContentInfoType>
|
||||||
class ContentMetaAccessor {
|
class ContentMetaAccessor {
|
||||||
public:
|
public:
|
||||||
@ -269,9 +279,10 @@ namespace ams::ncm {
|
|||||||
|
|
||||||
size_t GetExtendedDataSize() const {
|
size_t GetExtendedDataSize() const {
|
||||||
switch (this->GetHeader()->type) {
|
switch (this->GetHeader()->type) {
|
||||||
case ContentMetaType::Patch: return this->GetExtendedHeader<PatchMetaExtendedHeader>()->extended_data_size;
|
case ContentMetaType::Patch: return this->GetExtendedHeader<PatchMetaExtendedHeader>()->extended_data_size;
|
||||||
case ContentMetaType::Delta: return this->GetExtendedHeader<DeltaMetaExtendedHeader>()->extended_data_size;
|
case ContentMetaType::Delta: return this->GetExtendedHeader<DeltaMetaExtendedHeader>()->extended_data_size;
|
||||||
default: return 0;
|
case ContentMetaType::SystemUpdate: return this->GetExtendedHeader<SystemUpdateMetaExtendedHeader>()->extended_data_size;
|
||||||
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,9 +333,11 @@ namespace ams::ncm {
|
|||||||
public:
|
public:
|
||||||
constexpr PackagedContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
|
constexpr PackagedContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
|
||||||
|
|
||||||
|
size_t CalculateConvertInstallContentMetaSize() const;
|
||||||
size_t CalculateConvertContentMetaSize() const;
|
size_t CalculateConvertContentMetaSize() const;
|
||||||
|
|
||||||
void ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta);
|
void ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta);
|
||||||
|
void ConvertToInstallContentMeta(void *dst, size_t size, const InstallContentInfo &meta);
|
||||||
|
|
||||||
size_t CountDeltaFragments() const;
|
size_t CountDeltaFragments() const;
|
||||||
|
|
||||||
@ -352,4 +365,68 @@ namespace ams::ncm {
|
|||||||
using ContentMetaAccessor::SetStorageId;
|
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<void *>(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<uintptr_t>(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<const SystemUpdateMetaExtendedDataHeader *>(this->GetHeaderAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetFirmwareVariationCount() const {
|
||||||
|
return this->GetHeader()->firmware_variation_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
FirmwareVariationId *GetFirmwareVariationId(size_t i) const {
|
||||||
|
AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount());
|
||||||
|
|
||||||
|
return reinterpret_cast<FirmwareVariationId *>(this->GetFirmwareVariationIdAddress(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
FirmwareVariationInfo *GetFirmwareVariationInfo(size_t i) const {
|
||||||
|
AMS_ABORT_UNLESS(i < this->GetFirmwareVariationCount());
|
||||||
|
|
||||||
|
return reinterpret_cast<FirmwareVariationInfo *>(this->GetFirmwareVariationInfoAddress(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetContentMetaInfoList() const {
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
struct FirmwareVariationInfo {
|
||||||
|
bool refer_to_base;
|
||||||
|
u8 _0x1[3];
|
||||||
|
u32 content_meta_count;
|
||||||
|
u8 reserved[0x18];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FirmwareVariationId {
|
||||||
|
u32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -21,11 +21,6 @@ namespace ams::ncm {
|
|||||||
/* protected:
|
/* protected:
|
||||||
PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMetaData, PrepareDependency, PrepareSystemDependency, PrepareContentMetaIfLatest, GetConfig, WriteContentMetaToPlaceHolder, GetInstallStorage, GetSystemUpdateTaskApplyInfo, CanContinue
|
PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMetaData, PrepareDependency, PrepareSystemDependency, PrepareContentMetaIfLatest, GetConfig, WriteContentMetaToPlaceHolder, GetInstallStorage, GetSystemUpdateTaskApplyInfo, CanContinue
|
||||||
*/
|
*/
|
||||||
struct InstallThroughput {
|
|
||||||
s64 installed;
|
|
||||||
TimeSpan elapsed_time;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ListContentMetaKeyFilter : u8 {
|
enum class ListContentMetaKeyFilter : u8 {
|
||||||
All = 0,
|
All = 0,
|
||||||
Committed = 1,
|
Committed = 1,
|
||||||
@ -38,6 +33,40 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta
|
|||||||
InstallConfig_IgnoreTicket = (1 << 4),
|
InstallConfig_IgnoreTicket = (1 << 4),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct InstallThroughput {
|
||||||
|
s64 installed;
|
||||||
|
TimeSpan elapsed_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InstallContentMetaInfo {
|
||||||
|
ContentId content_id;
|
||||||
|
s64 content_size;
|
||||||
|
ContentMetaKey key;
|
||||||
|
bool verify_digest;
|
||||||
|
Digest digest;
|
||||||
|
|
||||||
|
static constexpr InstallContentMetaInfo MakeVerifiable(const ContentId &cid, s64 sz, const ContentMetaKey &ky, const Digest &d) {
|
||||||
|
return {
|
||||||
|
.content_id = cid,
|
||||||
|
.content_size = sz,
|
||||||
|
.key = ky,
|
||||||
|
.verify_digest = true,
|
||||||
|
.digest = d,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr InstallContentMetaInfo MakeUnverifiable(const ContentId &cid, s64 sz, const ContentMetaKey &ky) {
|
||||||
|
return {
|
||||||
|
.content_id = cid,
|
||||||
|
.content_size = sz,
|
||||||
|
.key = ky,
|
||||||
|
.verify_digest = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(InstallContentMetaInfo) == 0x50);
|
||||||
|
|
||||||
class InstallTaskBase {
|
class InstallTaskBase {
|
||||||
private:
|
private:
|
||||||
crypto::Sha256Generator sha256_generator;
|
crypto::Sha256Generator sha256_generator;
|
||||||
@ -51,9 +80,8 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta
|
|||||||
InstallThroughput throughput;
|
InstallThroughput throughput;
|
||||||
TimeSpan throughput_start_time;
|
TimeSpan throughput_start_time;
|
||||||
os::Mutex throughput_mutex;
|
os::Mutex throughput_mutex;
|
||||||
/* ... */
|
|
||||||
public:
|
public:
|
||||||
virtual ~InstallTaskBase() { /* TODO */ };
|
virtual ~InstallTaskBase() { /* ... */ };
|
||||||
private:
|
private:
|
||||||
ALWAYS_INLINE Result SetLastResultOnFailure(Result result) {
|
ALWAYS_INLINE Result SetLastResultOnFailure(Result result) {
|
||||||
if (R_FAILED(result)) {
|
if (R_FAILED(result)) {
|
||||||
@ -97,7 +125,13 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta
|
|||||||
Result PrepareAndExecute();
|
Result PrepareAndExecute();
|
||||||
Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys);
|
Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys);
|
||||||
Result Commit(const StorageContentMetaKey *keys, s32 num_keys);
|
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<bool> is_temporary);
|
||||||
|
InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional<bool> is_temporary);
|
||||||
|
|
||||||
|
Result IsNewerThanInstalled(bool *out, const ContentMetaKey &key);
|
||||||
|
Result DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys);
|
||||||
void ResetLastResult();
|
void ResetLastResult();
|
||||||
s64 GetThroughput();
|
s64 GetThroughput();
|
||||||
protected:
|
protected:
|
||||||
|
@ -30,6 +30,24 @@ namespace ams::ncm {
|
|||||||
dst->attributes = src.attributes;
|
dst->attributes = src.attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConvertPackageContentMetaHeaderToInstallContentMetaHeader(InstallContentMetaHeader *dst, const PackagedContentMetaHeader &src) {
|
||||||
|
/* Clear destination. */
|
||||||
|
*dst = {};
|
||||||
|
|
||||||
|
/* Set converted fields. */
|
||||||
|
dst->id = src.id;
|
||||||
|
dst->version = src.version;
|
||||||
|
dst->type = src.type;
|
||||||
|
dst->extended_header_size = src.extended_header_size;
|
||||||
|
dst->content_count = src.content_count;
|
||||||
|
dst->content_meta_count = src.content_meta_count;
|
||||||
|
dst->attributes = src.attributes;
|
||||||
|
dst->storage_id = src.storage_id;
|
||||||
|
dst->install_type = src.install_type;
|
||||||
|
dst->committed = src.committed;
|
||||||
|
dst->required_download_system_version = src.required_download_system_version;
|
||||||
|
}
|
||||||
|
|
||||||
void ConvertInstallContentMetaHeaderToContentMetaHeader(ContentMetaHeader *dst, const InstallContentMetaHeader &src) {
|
void ConvertInstallContentMetaHeaderToContentMetaHeader(ContentMetaHeader *dst, const InstallContentMetaHeader &src) {
|
||||||
/* Clear destination. */
|
/* Clear destination. */
|
||||||
*dst = {};
|
*dst = {};
|
||||||
@ -43,6 +61,22 @@ namespace ams::ncm {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t PackagedContentMetaReader::CalculateConvertInstallContentMetaSize() const {
|
||||||
|
/* Prepare the header. */
|
||||||
|
const auto *header = this->GetHeader();
|
||||||
|
|
||||||
|
if ((header->type == ContentMetaType::SystemUpdate && this->GetExtendedHeaderSize() > 0) || header->type == ContentMetaType::Delta) {
|
||||||
|
/* Newer SystemUpdates and Deltas contain extended data. */
|
||||||
|
return this->CalculateSizeImpl<InstallContentMetaHeader, InstallContentInfo>(header->extended_header_size, header->content_count + 1, header->content_meta_count, this->GetExtendedDataSize(), false);
|
||||||
|
} else if (header->type == ContentMetaType::Patch) {
|
||||||
|
/* Subtract the number of delta fragments for patches, include extended data. */
|
||||||
|
return this->CalculateSizeImpl<InstallContentMetaHeader, InstallContentInfo>(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<InstallContentMetaHeader, InstallContentInfo>(header->extended_header_size, header->content_count + 1, header->content_meta_count, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
size_t PackagedContentMetaReader::CountDeltaFragments() const {
|
size_t PackagedContentMetaReader::CountDeltaFragments() const {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (size_t i = 0; i < this->GetContentCount(); i++) {
|
for (size_t i = 0; i < this->GetContentCount(); i++) {
|
||||||
@ -58,6 +92,62 @@ namespace ams::ncm {
|
|||||||
return this->CalculateSizeImpl<ContentMetaHeader, ContentInfo>(header->extended_header_size, header->content_count + 1, header->content_meta_count, 0, false);
|
return this->CalculateSizeImpl<ContentMetaHeader, ContentInfo>(header->extended_header_size, header->content_count + 1, header->content_meta_count, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PackagedContentMetaReader::ConvertToInstallContentMeta(void *dst, size_t size, const InstallContentInfo &meta) {
|
||||||
|
/* Ensure we have enough space to convert. */
|
||||||
|
AMS_ABORT_UNLESS(size >= this->CalculateConvertInstallContentMetaSize());
|
||||||
|
|
||||||
|
/* Prepare for conversion. */
|
||||||
|
const auto *packaged_header = this->GetHeader();
|
||||||
|
uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
|
||||||
|
|
||||||
|
/* Convert the header. */
|
||||||
|
InstallContentMetaHeader header;
|
||||||
|
ConvertPackageContentMetaHeaderToInstallContentMetaHeader(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<void *>(dst_addr), std::addressof(header), sizeof(header));
|
||||||
|
dst_addr += sizeof(header);
|
||||||
|
|
||||||
|
/* Copy the extended header. */
|
||||||
|
std::memcpy(reinterpret_cast<void *>(dst_addr), reinterpret_cast<void *>(this->GetExtendedHeaderAddress()), packaged_header->extended_header_size);
|
||||||
|
dst_addr += packaged_header->extended_header_size;
|
||||||
|
|
||||||
|
/* Copy the top level meta. */
|
||||||
|
std::memcpy(reinterpret_cast<void *>(dst_addr), std::addressof(meta), sizeof(meta));
|
||||||
|
dst_addr += sizeof(meta);
|
||||||
|
|
||||||
|
/* Copy content infos. */
|
||||||
|
for (size_t i = 0; i < this->GetContentCount(); i++) {
|
||||||
|
const auto *packaged_content_info = this->GetContentInfo(i);
|
||||||
|
|
||||||
|
/* Don't copy any delta fragments. */
|
||||||
|
if (packaged_header->type == ContentMetaType::Patch) {
|
||||||
|
if (packaged_content_info->GetType() == ContentType::DeltaFragment) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the install content info. */
|
||||||
|
InstallContentInfo install_content_info = InstallContentInfo::Make(*packaged_content_info, packaged_header->type);
|
||||||
|
|
||||||
|
/* Copy the info. */
|
||||||
|
std::memcpy(reinterpret_cast<void *>(dst_addr), std::addressof(install_content_info), sizeof(InstallContentInfo));
|
||||||
|
dst_addr += sizeof(InstallContentInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy content meta infos. */
|
||||||
|
for (size_t i = 0; i < this->GetContentMetaCount(); i++) {
|
||||||
|
std::memcpy(reinterpret_cast<void *>(dst_addr), this->GetContentMetaInfo(i), sizeof(ContentMetaInfo));
|
||||||
|
dst_addr += sizeof(ContentMetaInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PackagedContentMetaReader::ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta) {
|
void PackagedContentMetaReader::ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta) {
|
||||||
/* Ensure we have enough space to convert. */
|
/* Ensure we have enough space to convert. */
|
||||||
AMS_ABORT_UNLESS(size >= this->CalculateConvertContentMetaSize());
|
AMS_ABORT_UNLESS(size >= this->CalculateConvertContentMetaSize());
|
||||||
|
@ -32,6 +32,17 @@ namespace ams::ncm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Contains(const ContentMetaKey *keys, s32 num_keys, const ContentMetaKey &key) {
|
||||||
|
for (s32 i = 0; i < num_keys; i++) {
|
||||||
|
/* Check if the key matches the input key. */
|
||||||
|
if (keys[i] == key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InstallTaskBase::OnPrepareComplete() {
|
Result InstallTaskBase::OnPrepareComplete() {
|
||||||
@ -498,7 +509,7 @@ namespace ams::ncm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Compare generated hash to expected hash if verification required. */
|
/* Compare generated hash to expected hash if verification required. */
|
||||||
if (content_info->verify_hash) {
|
if (content_info->verify_digest) {
|
||||||
u8 hash[crypto::Sha256Generator::HashSize];
|
u8 hash[crypto::Sha256Generator::HashSize];
|
||||||
this->sha256_generator.GetHash(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());
|
R_UNLESS(std::memcmp(hash, content_info->digest.data, crypto::Sha256Generator::HashSize) == 0, ncm::ResultInvalidContentHash());
|
||||||
@ -670,6 +681,83 @@ namespace ams::ncm {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::IncludesExFatDriver(bool *out) {
|
||||||
|
/* 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(&content_meta, i));
|
||||||
|
|
||||||
|
/* Check if the attributes are set for including the exfat driver. */
|
||||||
|
if (content_meta.GetReader().GetHeader()->attributes & ContentMetaAttribute_IncludesExFatDriver) {
|
||||||
|
*out = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = false;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::WritePlaceHolderBuffer(InstallContentInfo *content_info, const void *data, size_t data_size) {
|
||||||
|
R_UNLESS(!this->IsCancelRequested(), ncm::ResultWritePlaceHolderCancelled());
|
||||||
|
|
||||||
|
/* Open the content storage for the content info. */
|
||||||
|
ContentStorage content_storage;
|
||||||
|
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);
|
||||||
|
content_info->written += data_size;
|
||||||
|
|
||||||
|
/* Update progress/throughput if content info isn't temporary. */
|
||||||
|
if (!content_info->is_temporary) {
|
||||||
|
this->IncrementProgress(data_size);
|
||||||
|
this->UpdateThroughputMeasurement(data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the hash for the new data. */
|
||||||
|
this->sha256_generator.Update(data, data_size);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::WriteContentMetaToPlaceHolder(InstallContentInfo *install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional<bool> is_temporary) {
|
||||||
|
/* Generate a placeholder id. */
|
||||||
|
auto placeholder_id = storage->GeneratePlaceHolderId();
|
||||||
|
|
||||||
|
/* Create the placeholder. */
|
||||||
|
R_TRY(storage->CreatePlaceHolder(placeholder_id, meta_info.content_id, meta_info.content_size));
|
||||||
|
auto placeholder_guard = SCOPE_GUARD { storage->DeletePlaceHolder(placeholder_id); };
|
||||||
|
|
||||||
|
/* Output install content info. */
|
||||||
|
*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));
|
||||||
|
|
||||||
|
/* Don't delete the placeholder. Set state to installed. */
|
||||||
|
placeholder_guard.Cancel();
|
||||||
|
install_content_info->install_state = InstallState::Installed;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallContentInfo InstallTaskBase::MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, std::optional<bool> 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/* ... */
|
/* ... */
|
||||||
|
|
||||||
void InstallTaskBase::IncrementProgress(s64 size) {
|
void InstallTaskBase::IncrementProgress(s64 size) {
|
||||||
@ -716,6 +804,59 @@ namespace ams::ncm {
|
|||||||
|
|
||||||
/* ... */
|
/* ... */
|
||||||
|
|
||||||
|
Result InstallTaskBase::IsNewerThanInstalled(bool *out, const ContentMetaKey &key) {
|
||||||
|
/* Obtain a list of suitable storage ids. */
|
||||||
|
auto storage_list = GetStorageList(this->install_storage);
|
||||||
|
|
||||||
|
/* Iterate over storage ids. */
|
||||||
|
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]));
|
||||||
|
|
||||||
|
/* 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. */ }
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
|
/* Check if installed key is newer. */
|
||||||
|
if (installed_key.version >= key.version) {
|
||||||
|
*out = false;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input key is newer. */
|
||||||
|
*out = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
/* Cleanup if the input keys contain this key. */
|
||||||
|
if (Contains(keys, num_keys, content_meta.GetReader().GetKey())) {
|
||||||
|
R_TRY(this->CleanupOne(content_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
InstallProgress InstallTaskBase::GetProgress() {
|
InstallProgress InstallTaskBase::GetProgress() {
|
||||||
std::scoped_lock lk(this->progress_mutex);
|
std::scoped_lock lk(this->progress_mutex);
|
||||||
return this->progress;
|
return this->progress;
|
||||||
|
Loading…
Reference in New Issue
Block a user