mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-07-17 06:12:15 +02:00
ncm: begin implementing install task base
This commit is contained in:
parent
d31afcb99d
commit
ad9d61c0de
@ -21,10 +21,13 @@
|
|||||||
#include <stratosphere/ncm/ncm_auto_buffer.hpp>
|
#include <stratosphere/ncm/ncm_auto_buffer.hpp>
|
||||||
#include <stratosphere/ncm/ncm_make_path.hpp>
|
#include <stratosphere/ncm/ncm_make_path.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_id_utils.hpp>
|
#include <stratosphere/ncm/ncm_content_id_utils.hpp>
|
||||||
|
#include <stratosphere/ncm/ncm_content_info_utils.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_meta.hpp>
|
#include <stratosphere/ncm/ncm_content_meta.hpp>
|
||||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||||
#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_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_api.hpp>
|
#include <stratosphere/ncm/ncm_api.hpp>
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
constexpr inline s64 MaxClusterSize = 256_KB;
|
||||||
|
|
||||||
|
s64 CalculateRequiredSize(s64 file_size, s64 cluster_size = MaxClusterSize);
|
||||||
|
s64 CalculateRequiredSizeForExtension(s64 file_size, s64 cluster_size = MaxClusterSize);
|
||||||
|
|
||||||
|
}
|
@ -206,6 +206,10 @@ namespace ams::ncm {
|
|||||||
return this->size;
|
return this->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HeaderType *GetWritableHeader() const {
|
||||||
|
return reinterpret_cast<HeaderType *>(this->data);
|
||||||
|
}
|
||||||
|
|
||||||
const HeaderType *GetHeader() const {
|
const HeaderType *GetHeader() const {
|
||||||
AMS_ABORT_UNLESS(this->is_header_valid);
|
AMS_ABORT_UNLESS(this->is_header_valid);
|
||||||
return static_cast<const HeaderType *>(this->data);
|
return static_cast<const HeaderType *>(this->data);
|
||||||
@ -291,6 +295,19 @@ namespace ams::ncm {
|
|||||||
std::optional<ApplicationId> GetApplicationId() const {
|
std::optional<ApplicationId> GetApplicationId() const {
|
||||||
return this->GetApplicationId(this->GetKey());
|
return this->GetApplicationId(this->GetKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
s64 CalculateContentRequiredSize() const {
|
||||||
|
s64 required_size = 0;
|
||||||
|
for (size_t i = 0; i < this->GetContentCount(); i++) {
|
||||||
|
required_size += CalculateRequiredSize(this->GetContentInfo(i)->info.GetSize());
|
||||||
|
}
|
||||||
|
return required_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStorageId(StorageId storage_id) {
|
||||||
|
this->GetWritableHeader()->storage_id = static_cast<u8>(storage_id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContentMetaReader : public ContentMetaAccessor<ContentMetaHeader, ContentInfo> {
|
class ContentMetaReader : public ContentMetaAccessor<ContentMetaHeader, ContentInfo> {
|
||||||
@ -320,4 +337,14 @@ namespace ams::ncm {
|
|||||||
constexpr InstallContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
|
constexpr InstallContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InstallContentMetaWriter : public ContentMetaAccessor<InstallContentMetaHeader, InstallContentInfo> {
|
||||||
|
public:
|
||||||
|
InstallContentMetaWriter(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
|
||||||
|
|
||||||
|
using ContentMetaAccessor::CalculateSize;
|
||||||
|
using ContentMetaAccessor::CalculateContentRequiredSize;
|
||||||
|
using ContentMetaAccessor::GetWritableContentInfo;
|
||||||
|
using ContentMetaAccessor::SetStorageId;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere/ncm/ncm_install_task_data.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
/* protected:
|
||||||
|
PrepareContentMeta (both), WritePlaceHolderBuffer, PrepareAgain, Get/Delete InstallContentMetaData, PrepareDependency, PrepareSystemDependency, PrepareContentMetaIfLatest, GetConfig, WriteContentMetaToPlaceHolder, GetInstallStorage, GetSystemUpdateTaskApplyInfo, CanContinue
|
||||||
|
*/
|
||||||
|
struct InstallThroughput {
|
||||||
|
s64 installed;
|
||||||
|
TimeSpan elapsed_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InstallTaskBase {
|
||||||
|
private:
|
||||||
|
crypto::Sha256Generator sha256_generator;
|
||||||
|
StorageId install_storage;
|
||||||
|
InstallTaskDataBase *data;
|
||||||
|
InstallProgress progress;
|
||||||
|
os::Mutex progress_mutex;
|
||||||
|
u32 config;
|
||||||
|
os::Mutex cancel_mutex;
|
||||||
|
bool cancel_requested;
|
||||||
|
InstallThroughput throughput;
|
||||||
|
TimeSpan throughput_start_time;
|
||||||
|
os::Mutex throughput_mutex;
|
||||||
|
/* ... */
|
||||||
|
public:
|
||||||
|
virtual ~InstallTaskBase() { /* TODO */ };
|
||||||
|
private:
|
||||||
|
Result PrepareImpl();
|
||||||
|
protected:
|
||||||
|
Result Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config);
|
||||||
|
Result CountInstallContentMetaData(s32 *out_count);
|
||||||
|
Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index);
|
||||||
|
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();
|
||||||
|
protected:
|
||||||
|
virtual Result OnPrepareComplete();
|
||||||
|
virtual Result PrepareDependency();
|
||||||
|
public:
|
||||||
|
/* TODO: Fix access types. */
|
||||||
|
virtual void Cancel();
|
||||||
|
virtual void ResetCancel();
|
||||||
|
virtual InstallProgress GetProgress();
|
||||||
|
virtual Result PrepareInstallContentMetaData() = 0;
|
||||||
|
void *GetInstallContentMetaInfo;
|
||||||
|
virtual Result GetLatestVersion(std::optional<u32> *out_version, u64 id);
|
||||||
|
virtual Result CheckInstallable();
|
||||||
|
virtual Result OnExecuteComplete();
|
||||||
|
void *OnWritePlaceHolder;
|
||||||
|
void *InstallTicket;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,10 @@ namespace ams::ncm {
|
|||||||
InstallContentMetaReader GetReader() const {
|
InstallContentMetaReader GetReader() const {
|
||||||
return InstallContentMetaReader(this->data.get(), this->size);
|
return InstallContentMetaReader(this->data.get(), this->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstallContentMetaWriter GetWriter() const {
|
||||||
|
return InstallContentMetaWriter(this->data.get(), this->size);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class InstallTaskDataBase {
|
class InstallTaskDataBase {
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere/ncm/ncm_storage_id_utils.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
class StorageList {
|
||||||
|
public:
|
||||||
|
static constexpr s32 MaxCount = 10;
|
||||||
|
private:
|
||||||
|
StorageId ids[MaxCount];
|
||||||
|
s32 count;
|
||||||
|
public:
|
||||||
|
constexpr StorageList() : ids(), count() { /* ... */ }
|
||||||
|
|
||||||
|
void Push(StorageId storage_id) {
|
||||||
|
AMS_ABORT_UNLESS(this->count < MaxCount);
|
||||||
|
|
||||||
|
for (s32 i = 0; i < MaxCount; i++) {
|
||||||
|
if (this->ids[i] == storage_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->ids[this->count++] = storage_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 Count() const {
|
||||||
|
return this->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageId operator[](s32 i) const {
|
||||||
|
AMS_ABORT_UNLESS(i < this->count);
|
||||||
|
return this->ids[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr StorageList GetStorageList(StorageId storage_id) {
|
||||||
|
StorageList list;
|
||||||
|
switch (storage_id) {
|
||||||
|
case StorageId::BuiltInSystem:
|
||||||
|
case StorageId::BuiltInUser:
|
||||||
|
case StorageId::SdCard:
|
||||||
|
list.Push(storage_id);
|
||||||
|
break;
|
||||||
|
case StorageId::Any:
|
||||||
|
list.Push(StorageId::SdCard);
|
||||||
|
list.Push(StorageId::BuiltInUser);
|
||||||
|
break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, size_t required_size);
|
||||||
|
const char *GetStorageIdString(StorageId storage_id);
|
||||||
|
const char *GetStorageIdStringForPlayReport(StorageId storage_id);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr inline s64 EncryptionMetadataSize = 16_KB;
|
||||||
|
constexpr inline s64 ConcatenationFileSizeMax = 4_GB;
|
||||||
|
|
||||||
|
constexpr s64 CalculateAdditionalContentSize(s64 file_size, s64 cluster_size) {
|
||||||
|
/* Account for the encryption header. */
|
||||||
|
s64 size = EncryptionMetadataSize;
|
||||||
|
|
||||||
|
/* Account for the file size splitting costs. */
|
||||||
|
size += ((file_size / ConcatenationFileSizeMax) + 1) * cluster_size;
|
||||||
|
|
||||||
|
/* Account for various overhead costs. */
|
||||||
|
size += cluster_size * 3;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 CalculateRequiredSize(s64 file_size, s64 cluster_size) {
|
||||||
|
return file_size + CalculateAdditionalContentSize(file_size, cluster_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 CalculateRequiredSizeForExtension(s64 file_size, s64 cluster_size) {
|
||||||
|
return file_size + ((file_size / ConcatenationFileSizeMax) + 1) * cluster_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
279
libraries/libstratosphere/source/ncm/ncm_install_task.cpp
Normal file
279
libraries/libstratosphere/source/ncm/ncm_install_task.cpp
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
Result InstallTaskBase::OnPrepareComplete() {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::GetLatestVersion(std::optional<u32> *out_version, u64 id) {
|
||||||
|
return ncm::ResultContentMetaNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::CheckInstallable() {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::OnExecuteComplete() {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallTaskBase::Cancel() {
|
||||||
|
std::scoped_lock lk(this->cancel_mutex);
|
||||||
|
this->cancel_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallTaskBase::ResetCancel() {
|
||||||
|
std::scoped_lock lk(this->cancel_mutex);
|
||||||
|
this->cancel_requested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstallTaskBase::IsCancelRequested() {
|
||||||
|
std::scoped_lock lk(this->cancel_mutex);
|
||||||
|
return this->cancel_requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::Initialize(StorageId install_storage, InstallTaskDataBase *data, u32 config) {
|
||||||
|
R_UNLESS(IsInstallableStorage(install_storage), ncm::ResultUnknownStorage());
|
||||||
|
this->install_storage = install_storage;
|
||||||
|
this->data = data;
|
||||||
|
this->config = config;
|
||||||
|
return data->GetProgress(std::addressof(this->progress));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::Prepare() {
|
||||||
|
/* Call the implementation. */
|
||||||
|
Result result = this->PrepareImpl();
|
||||||
|
|
||||||
|
/* Update the last result. */
|
||||||
|
this->SetLastResult(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::PrepareImpl() {
|
||||||
|
/* Reset the throughput. */
|
||||||
|
this->ResetThroughputMeasurement();
|
||||||
|
|
||||||
|
/* Get the current progress. */
|
||||||
|
InstallProgress progress = this->GetProgress();
|
||||||
|
|
||||||
|
/* Transition from NotPrepared to DataPrepared. */
|
||||||
|
if (progress.state == InstallProgressState::NotPrepared) {
|
||||||
|
R_TRY(this->PrepareInstallContentMetaData());
|
||||||
|
R_TRY(this->PrepareDependency());
|
||||||
|
R_TRY(this->CheckInstallable());
|
||||||
|
this->SetProgressState(InstallProgressState::DataPrepared);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the current progress. */
|
||||||
|
progress = this->GetProgress();
|
||||||
|
|
||||||
|
/* Transition from DataPrepared to Prepared. */
|
||||||
|
if (progress.state == InstallProgressState::DataPrepared) {
|
||||||
|
R_TRY(this->PreparePlaceHolder());
|
||||||
|
this->SetProgressState(InstallProgressState::Prepared);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal prepare is completed. */
|
||||||
|
return this->OnPrepareComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallTaskBase::SetLastResult(Result last_result) {
|
||||||
|
std::scoped_lock lk(this->progress_mutex);
|
||||||
|
this->data->SetLastResult(last_result);
|
||||||
|
this->progress.SetLastResult(last_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::GetPreparedPlaceHolderPath(Path *out_path, u64 id, ContentMetaType meta_type, ContentType type) {
|
||||||
|
/* Count the number of content meta entries. */
|
||||||
|
s32 count;
|
||||||
|
R_TRY(this->data->Count(std::addressof(count)));
|
||||||
|
R_UNLESS(count > 0, ncm::ResultPlaceHolderNotFound());
|
||||||
|
|
||||||
|
/* Iterate over content meta. */
|
||||||
|
for (s32 i = 0; i < count; i++) {
|
||||||
|
/* Obtain the content meta. */
|
||||||
|
InstallContentMeta content_meta;
|
||||||
|
R_TRY(this->data->Get(std::addressof(content_meta), i));
|
||||||
|
const InstallContentMetaReader reader = content_meta.GetReader();
|
||||||
|
|
||||||
|
/* Ensure content meta matches the key and meta type. */
|
||||||
|
if (reader.GetKey().id != id || reader.GetKey().type != meta_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to obtain a content info for the content type. */
|
||||||
|
if (const auto content_info = reader.GetContentInfo(type); content_info != nullptr) {
|
||||||
|
/* Open the relevant content storage. */
|
||||||
|
ContentStorage content_storage;
|
||||||
|
R_TRY(ncm::OpenContentStorage(&content_storage, content_info->storage_id));
|
||||||
|
|
||||||
|
/* Get the placeholder path. */
|
||||||
|
/* N doesn't bother checking the result. */
|
||||||
|
content_storage.GetPlaceHolderPath(out_path, content_info->placeholder_id);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ncm::ResultPlaceHolderNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::CountInstallContentMetaData(s32 *out_count) {
|
||||||
|
return this->data->Count(out_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index) {
|
||||||
|
return this->data->Get(out_content_meta, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::CalculateRequiredSize(size_t *out_size) {
|
||||||
|
/* Count the number of content meta entries. */
|
||||||
|
s32 count;
|
||||||
|
R_TRY(this->data->Count(std::addressof(count)));
|
||||||
|
|
||||||
|
size_t required_size = 0;
|
||||||
|
/* Iterate over each entry. */
|
||||||
|
for (s32 i = 0; i < count; i++) {
|
||||||
|
/* Obtain the content meta. */
|
||||||
|
InstallContentMeta content_meta;
|
||||||
|
R_TRY(this->data->Get(std::addressof(content_meta), i));
|
||||||
|
const auto reader = content_meta.GetReader();
|
||||||
|
|
||||||
|
/* Sum the sizes from the content infos. */
|
||||||
|
for (size_t j = 0; j < reader.GetContentCount(); j++) {
|
||||||
|
const auto *content_info = reader.GetContentInfo(j);
|
||||||
|
|
||||||
|
if (content_info->install_state == InstallState::NotPrepared) {
|
||||||
|
required_size += ncm::CalculateRequiredSize(content_info->GetSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_size = required_size;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallTaskBase::ResetThroughputMeasurement() {
|
||||||
|
std::scoped_lock lk(this->throughput_mutex);
|
||||||
|
this->throughput.elapsed_time = TimeSpan();
|
||||||
|
this->throughput_start_time = TimeSpan();
|
||||||
|
this->throughput.installed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallTaskBase::SetProgressState(InstallProgressState state) {
|
||||||
|
std::scoped_lock(this->progress_mutex);
|
||||||
|
this->data->SetState(state);
|
||||||
|
this->progress.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallTaskBase::PreparePlaceHolder() {
|
||||||
|
static os::Mutex placeholder_mutex;
|
||||||
|
size_t total_size = 0;
|
||||||
|
|
||||||
|
/* Count the number of content meta entries. */
|
||||||
|
s32 count;
|
||||||
|
R_TRY(this->data->Count(std::addressof(count)));
|
||||||
|
|
||||||
|
for (s32 i = 0; i < count; i++) {
|
||||||
|
R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled());
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Automatically choose a suitable storage id. */
|
||||||
|
if (storage_id == StorageId::None) {
|
||||||
|
R_TRY(ncm::SelectDownloadableStorage(std::addressof(storage_id), storage_id, writer.CalculateContentRequiredSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the data when we are done. */
|
||||||
|
ON_SCOPE_EXIT { this->data->Update(content_meta, i); };
|
||||||
|
|
||||||
|
/* Open the relevant content storage. */
|
||||||
|
ContentStorage content_storage;
|
||||||
|
R_TRY(ncm::OpenContentStorage(&content_storage, storage_id));
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
bool has_content;
|
||||||
|
R_TRY(content_storage.Has(&has_content, content_info->GetId()));
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->SetTotalSize(total_size);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
void InstallTaskBase::SetTotalSize(s64 size) {
|
||||||
|
std::scoped_lock(this->progress_mutex);
|
||||||
|
this->progress.total_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
Result InstallTaskBase::PrepareDependency() {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
InstallProgress InstallTaskBase::GetProgress() {
|
||||||
|
std::scoped_lock lk(this->progress_mutex);
|
||||||
|
return this->progress;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::ncm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char *StorageIdStrings[] = {
|
||||||
|
"None",
|
||||||
|
"Host",
|
||||||
|
"GameCard",
|
||||||
|
"BuiltInSystem",
|
||||||
|
"BuiltInUser",
|
||||||
|
"SdCard"
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const char *StorageIdStringsForPlayReport[] = {
|
||||||
|
"None",
|
||||||
|
"Host",
|
||||||
|
"Card",
|
||||||
|
"BuildInSystem",
|
||||||
|
"BuildInUser",
|
||||||
|
"SdCard"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, size_t required_size) {
|
||||||
|
auto list = GetStorageList(storage_id);
|
||||||
|
for (s32 i = 0; i < list.Count(); i++) {
|
||||||
|
auto candidate = list[i];
|
||||||
|
|
||||||
|
/* Open the content meta database. NOTE: This is unused. */
|
||||||
|
ContentMetaDatabase content_meta_database;
|
||||||
|
if (R_FAILED(ncm::OpenContentMetaDatabase(std::addressof(content_meta_database), candidate))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the content storage. */
|
||||||
|
ContentStorage content_storage;
|
||||||
|
if (R_FAILED(ncm::OpenContentStorage(std::addressof(content_storage), candidate))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the free space on this storage. */
|
||||||
|
s64 free_space_size;
|
||||||
|
R_TRY(content_storage.GetFreeSpaceSize(std::addressof(free_space_size)));
|
||||||
|
|
||||||
|
/* There must be more free space than is required. */
|
||||||
|
if (free_space_size <= required_size) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output the storage id. */
|
||||||
|
*out_storage_id = storage_id;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ncm::ResultNotEnoughInstallSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GetStorageIdString(StorageId storage_id) {
|
||||||
|
u8 id = static_cast<u8>(storage_id);
|
||||||
|
return id > 5 ? "(unknown)" : StorageIdStrings[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GetStorageIdStringForPlayReport(StorageId storage_id) {
|
||||||
|
u8 id = static_cast<u8>(storage_id);
|
||||||
|
return id > 5 ? "(unknown)" : StorageIdStringsForPlayReport[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -38,6 +38,7 @@ namespace ams::ncm {
|
|||||||
R_DEFINE_ERROR_RESULT(InvalidPlaceHolderFile, 170);
|
R_DEFINE_ERROR_RESULT(InvalidPlaceHolderFile, 170);
|
||||||
R_DEFINE_ERROR_RESULT(BufferInsufficient, 180);
|
R_DEFINE_ERROR_RESULT(BufferInsufficient, 180);
|
||||||
R_DEFINE_ERROR_RESULT(WriteToReadOnlyContentStorage, 190);
|
R_DEFINE_ERROR_RESULT(WriteToReadOnlyContentStorage, 190);
|
||||||
|
R_DEFINE_ERROR_RESULT(NotEnoughInstallSpace, 200);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240);
|
R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310);
|
R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310);
|
||||||
@ -56,6 +57,10 @@ namespace ams::ncm {
|
|||||||
R_DEFINE_ERROR_RESULT(SdCardContentMetaDatabaseNotActive, 264);
|
R_DEFINE_ERROR_RESULT(SdCardContentMetaDatabaseNotActive, 264);
|
||||||
R_DEFINE_ERROR_RESULT(UnknownContentMetaDatabaseNotActive, 268);
|
R_DEFINE_ERROR_RESULT(UnknownContentMetaDatabaseNotActive, 268);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RANGE(InstallTaskCancelled, 290, 299);
|
||||||
|
R_DEFINE_ERROR_RESULT(CreatePlaceHolderCancelled, 291);
|
||||||
|
R_DEFINE_ERROR_RESULT(WritePlaceHolderCancelled, 292);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(InvalidArgument, 8181, 8191);
|
R_DEFINE_ERROR_RANGE(InvalidArgument, 8181, 8191);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidOffset, 8182);
|
R_DEFINE_ERROR_RESULT(InvalidOffset, 8182);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user