mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 12:34:47 +00:00
ncm: more install task progress
This commit is contained in:
parent
c1c15054d5
commit
d90004efb7
3 changed files with 271 additions and 60 deletions
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue