mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 12:34:47 +00:00
ncm client: more progress
This commit is contained in:
parent
8fbb10a83c
commit
12670e1a01
7 changed files with 413 additions and 29 deletions
|
@ -27,6 +27,7 @@
|
|||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_manager_impl.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_data.hpp>
|
||||
#include <stratosphere/ncm/ncm_storage_id_utils.hpp>
|
||||
|
|
|
@ -32,6 +32,23 @@ namespace ams::ncm {
|
|||
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 {
|
||||
Digest digest;
|
||||
crypto::Sha256Context context;
|
||||
|
@ -41,7 +58,7 @@ namespace ams::ncm {
|
|||
PlaceHolderId placeholder_id;
|
||||
ContentMetaType meta_type;
|
||||
InstallState install_state;
|
||||
bool verify_hash;
|
||||
bool verify_digest;
|
||||
StorageId storage_id;
|
||||
bool is_temporary;
|
||||
bool is_sha256_calculated;
|
||||
|
@ -83,25 +100,17 @@ namespace ams::ncm {
|
|||
constexpr s64 GetSizeWritten() const {
|
||||
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);
|
||||
|
||||
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_info.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_info_data.hpp>
|
||||
#include <stratosphere/ncm/ncm_firmware_variation.hpp>
|
||||
#include <stratosphere/ncm/ncm_storage_id.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
@ -107,6 +108,15 @@ namespace ams::ncm {
|
|||
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>
|
||||
class ContentMetaAccessor {
|
||||
public:
|
||||
|
@ -269,9 +279,10 @@ namespace ams::ncm {
|
|||
|
||||
size_t GetExtendedDataSize() const {
|
||||
switch (this->GetHeader()->type) {
|
||||
case ContentMetaType::Patch: return this->GetExtendedHeader<PatchMetaExtendedHeader>()->extended_data_size;
|
||||
case ContentMetaType::Delta: return this->GetExtendedHeader<DeltaMetaExtendedHeader>()->extended_data_size;
|
||||
default: return 0;
|
||||
case ContentMetaType::Patch: return this->GetExtendedHeader<PatchMetaExtendedHeader>()->extended_data_size;
|
||||
case ContentMetaType::Delta: return this->GetExtendedHeader<DeltaMetaExtendedHeader>()->extended_data_size;
|
||||
case ContentMetaType::SystemUpdate: return this->GetExtendedHeader<SystemUpdateMetaExtendedHeader>()->extended_data_size;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,9 +333,11 @@ namespace ams::ncm {
|
|||
public:
|
||||
constexpr PackagedContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
|
||||
|
||||
size_t CalculateConvertInstallContentMetaSize() const;
|
||||
size_t CalculateConvertContentMetaSize() const;
|
||||
|
||||
void ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta);
|
||||
void ConvertToInstallContentMeta(void *dst, size_t size, const InstallContentInfo &meta);
|
||||
|
||||
size_t CountDeltaFragments() const;
|
||||
|
||||
|
@ -352,4 +365,68 @@ 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<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:
|
||||
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 {
|
||||
All = 0,
|
||||
Committed = 1,
|
||||
|
@ -38,6 +33,40 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta
|
|||
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 {
|
||||
private:
|
||||
crypto::Sha256Generator sha256_generator;
|
||||
|
@ -51,9 +80,8 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta
|
|||
InstallThroughput throughput;
|
||||
TimeSpan throughput_start_time;
|
||||
os::Mutex throughput_mutex;
|
||||
/* ... */
|
||||
public:
|
||||
virtual ~InstallTaskBase() { /* TODO */ };
|
||||
virtual ~InstallTaskBase() { /* ... */ };
|
||||
private:
|
||||
ALWAYS_INLINE Result SetLastResultOnFailure(Result result) {
|
||||
if (R_FAILED(result)) {
|
||||
|
@ -97,7 +125,13 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, Get/Delete InstallContentMeta
|
|||
Result PrepareAndExecute();
|
||||
Result VerifyAllNotCommitted(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();
|
||||
s64 GetThroughput();
|
||||
protected:
|
||||
|
|
|
@ -30,6 +30,24 @@ namespace ams::ncm {
|
|||
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) {
|
||||
/* Clear destination. */
|
||||
*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 count = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
/* Ensure we have enough space to convert. */
|
||||
AMS_ABORT_UNLESS(size >= this->CalculateConvertContentMetaSize());
|
||||
|
|
|
@ -32,6 +32,17 @@ namespace ams::ncm {
|
|||
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() {
|
||||
|
@ -498,7 +509,7 @@ namespace ams::ncm {
|
|||
}
|
||||
|
||||
/* Compare generated hash to expected hash if verification required. */
|
||||
if (content_info->verify_hash) {
|
||||
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());
|
||||
|
@ -670,6 +681,83 @@ namespace ams::ncm {
|
|||
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) {
|
||||
|
@ -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() {
|
||||
std::scoped_lock lk(this->progress_mutex);
|
||||
return this->progress;
|
||||
|
|
Loading…
Add table
Reference in a new issue