ncm: more install task progress

This commit is contained in:
Adubbz 2020-03-16 22:47:03 +11:00 committed by Michael Scire
parent c1c15054d5
commit d90004efb7
3 changed files with 271 additions and 60 deletions

View file

@ -72,16 +72,14 @@ namespace ams::ncm {
u8 attributes;
u8 storage_id;
ContentInstallType install_type;
u8 reserved_17;
bool committed;
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);
/* TODO: Confirm this is correct. */
using InstallContentMetaHeader = PackagedContentMetaHeader;
struct ApplicationMetaExtendedHeader {
@ -294,6 +292,10 @@ namespace ams::ncm {
return false;
}
StorageId GetStorageId() const {
return static_cast<StorageId>(this->GetHeader()->storage_id);
}
std::optional<ApplicationId> GetApplicationId(const ContentMetaKey &key) const {
switch (key.type) {
case ContentMetaType::Application: return ApplicationId{ key.id };

View file

@ -19,13 +19,19 @@
namespace ams::ncm {
/* protected:
PrepareContentMeta (both), WritePlaceHolderBuffer, PrepareAgain, 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 {
All = 0,
Committed = 1,
NotCommitted = 2,
};
class InstallTaskBase {
private:
crypto::Sha256Generator sha256_generator;
@ -48,17 +54,30 @@ PrepareContentMeta (both), WritePlaceHolderBuffer, PrepareAgain, Get/Delete Inst
Result Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config);
Result CountInstallContentMetaData(s32 *out_count);
Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index);
void PrepareAgain();
public:
/* TODO: Fix access types. */
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 SetTotalSize(s64 size);
Result PreparePlaceHolder();
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 UpdateThroughputMeasurement(s64 throughput);
bool IsNecessaryInstallTicket(const fs::RightsId &rights_id);
void SetTotalSize(s64 size);
Result PreparePlaceHolder();
Result Cleanup();
Result CleanupOne(const InstallContentMeta &content_meta);
void CleanupProgress();
Result ListContentMetaKey(s32 *out_keys_written, StorageContentMetaKey *out_keys, s32 out_keys_count, s32 offset, ListContentMetaKeyFilter filter);
Result ListApplicationContentMetaKey(s32 *out_keys_written, ApplicationContentMetaKey *out_keys, s32 out_keys_count, s32 offset);
void ResetLastResult();
s64 GetThroughput();
protected:
virtual Result OnPrepareComplete();
virtual Result PrepareDependency();

View file

@ -194,61 +194,62 @@ namespace ams::ncm {
std::scoped_lock lk(placeholder_mutex);
InstallContentMeta content_meta;
if (R_SUCCEEDED(this->data->Get(&content_meta, i))) {
auto writer = content_meta.GetWriter();
StorageId storage_id = static_cast<StorageId>(writer.GetHeader()->storage_id);
R_TRY(this->data->Get(&content_meta, i));
/* Automatically choose a suitable storage id. */
if (storage_id == StorageId::None) {
R_TRY(ncm::SelectDownloadableStorage(std::addressof(storage_id), storage_id, writer.CalculateContentRequiredSize()));
}
auto writer = content_meta.GetWriter();
StorageId storage_id = static_cast<StorageId>(writer.GetHeader()->storage_id);
/* Update the data when we are done. */
ON_SCOPE_EXIT { this->data->Update(content_meta, i); };
/* Automatically choose a suitable storage id. */
if (storage_id == StorageId::None) {
R_TRY(ncm::SelectDownloadableStorage(std::addressof(storage_id), storage_id, writer.CalculateContentRequiredSize()));
}
/* Open the relevant content storage. */
ContentStorage content_storage;
R_TRY(ncm::OpenContentStorage(&content_storage, storage_id));
/* Update the data when we are done. */
ON_SCOPE_EXIT { this->data->Update(content_meta, i); };
/* 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);
/* Open the relevant content storage. */
ContentStorage content_storage;
R_TRY(ncm::OpenContentStorage(&content_storage, storage_id));
bool has_content;
R_TRY(content_storage.Has(&has_content, content_info->GetId()));
/* 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);
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();
}
/* Check if we have the content already exists. */
bool has_content;
R_TRY(content_storage.Has(&has_content, content_info->GetId()));
/* 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. */
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();
}
}
}
@ -257,8 +258,183 @@ namespace ams::ncm {
return ResultSuccess();
}
Result InstallTaskBase::Cleanup() {
/* 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++) {
/* Get the content meta. */
InstallContentMeta content_meta;
R_TRY(this->data->Get(&content_meta, i));
/* Cleanup the content meta. */
/* N doesn't check the result of this. */
this->CleanupOne(content_meta);
}
/* 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);
/* Open the relevant content storage. */
ContentStorage content_storage;
R_TRY(ncm::OpenContentStorage(&content_storage, reader.GetStorageId()));
/* Iterate over content infos. */
for (size_t i = 0; i < reader.GetContentCount(); i++) {
auto *content_info = reader.GetContentInfo(i);
/* Delete placeholders for Prepared or Installed content infos. */
if (content_info->install_state == InstallState::Prepared || content_info->install_state == InstallState::Installed) {
content_storage.DeletePlaceHolder(content_info->placeholder_id);
}
}
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;
R_TRY(this->data->Count(std::addressof(count)));
/* Offset exceeds keys that can be written. */
if (count <= offset) {
*out_keys_written = 0;
return ResultSuccess();
}
if (filter == ListContentMetaKeyFilter::All) {
const size_t num_keys = std::min(count, offset + out_keys_count);
/* Iterate over content meta. */
for (s32 i = offset; i < num_keys; i++) {
/* Obtain the content meta. */
InstallContentMeta content_meta;
R_TRY(this->data->Get(&content_meta, i));
/* 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();
}
/* Output the number of keys written. */
*out_keys_written = num_keys - offset;
} else {
s32 keys_written = 0;
s32 curr_offset = 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 and check if the content has been committed. */
const auto reader = content_meta.GetReader();
const bool committed = reader.GetHeader()->committed;
/* Apply filter. */
if ((!committed && filter == ListContentMetaKeyFilter::Committed) || (committed && filter == ListContentMetaKeyFilter::NotCommitted)) {
continue;
}
/* 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. */
curr_offset++;
/* We can't write any more output keys. */
if (keys_written >= out_keys_count) {
break;
}
}
/* Output the number of keys written. */
*out_keys_written = keys_written;
}
return ResultSuccess();
}
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;
/* Iterate over content meta. */
for (s32 i = offset; i < std::min(count, offset + out_keys_count); i++) {
/* Obtain the content meta. */
InstallContentMeta content_meta;
R_TRY(this->data->Get(&content_meta, i));
/* Create a reader. */
const auto reader = content_meta.GetReader();
/* Ensure this key has an application id. */
if (!reader.GetApplicationId()) {
continue;
}
/* Write output ApplicationContentMetaKey. */
ApplicationContentMetaKey &out_key = out_keys[keys_written++];
out_key.key = reader.GetKey();
out_key.application_id = *reader.GetApplicationId();
}
*out_keys_written = keys_written;
return ResultSuccess();
}
/* ... */
void InstallTaskBase::UpdateThroughputMeasurement(s64 throughput) {
std::scoped_lock lk(this->throughput_mutex);
if (this->throughput_start_time.GetNanoSeconds() != 0) {
this->throughput.installed += throughput;
/* TODO. */
}
}
bool InstallTaskBase::IsNecessaryInstallTicket(const fs::RightsId &rights_id) {
/* If the title has no rights, there's no ticket to install. */
fs::RightsId empty_rights_id = {};
if (std::memcmp(std::addressof(rights_id), std::addressof(empty_rights_id), sizeof(fs::RightsId)) == 0) {
return false;
}
/* TODO: Support detecting if a title requires rights. */
/* TODO: How should es be handled without undesired effects? */
return false;
}
void InstallTaskBase::SetTotalSize(s64 size) {
std::scoped_lock(this->progress_mutex);
this->progress.total_size = size;
@ -266,6 +442,10 @@ namespace ams::ncm {
/* ... */
void InstallTaskBase::PrepareAgain() {
this->SetProgressState(InstallProgressState::NotPrepared);
}
Result InstallTaskBase::PrepareDependency() {
return ResultSuccess();
}
@ -276,4 +456,14 @@ namespace ams::ncm {
std::scoped_lock lk(this->progress_mutex);
return this->progress;
}
void InstallTaskBase::ResetLastResult() {
this->SetLastResult(ResultSuccess());
}
s64 InstallTaskBase::GetThroughput() {
std::scoped_lock lk(this->throughput_mutex);
return this->throughput.installed;
}
}