mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 12:34:47 +00:00
ncm: begin implementing install task base
This commit is contained in:
parent
d31afcb99d
commit
ad9d61c0de
10 changed files with 630 additions and 0 deletions
|
@ -21,10 +21,13 @@
|
|||
#include <stratosphere/ncm/ncm_auto_buffer.hpp>
|
||||
#include <stratosphere/ncm/ncm_make_path.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_database.hpp>
|
||||
#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_install_task.hpp>
|
||||
#include <stratosphere/ncm/ncm_install_task_data.hpp>
|
||||
#include <stratosphere/ncm/ncm_storage_id_utils.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;
|
||||
}
|
||||
|
||||
HeaderType *GetWritableHeader() const {
|
||||
return reinterpret_cast<HeaderType *>(this->data);
|
||||
}
|
||||
|
||||
const HeaderType *GetHeader() const {
|
||||
AMS_ABORT_UNLESS(this->is_header_valid);
|
||||
return static_cast<const HeaderType *>(this->data);
|
||||
|
@ -291,6 +295,19 @@ namespace ams::ncm {
|
|||
std::optional<ApplicationId> GetApplicationId() const {
|
||||
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> {
|
||||
|
@ -320,4 +337,14 @@ namespace ams::ncm {
|
|||
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 {
|
||||
return InstallContentMetaReader(this->data.get(), this->size);
|
||||
}
|
||||
|
||||
InstallContentMetaWriter GetWriter() const {
|
||||
return InstallContentMetaWriter(this->data.get(), this->size);
|
||||
}
|
||||
};
|
||||
|
||||
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(BufferInsufficient, 180);
|
||||
R_DEFINE_ERROR_RESULT(WriteToReadOnlyContentStorage, 190);
|
||||
R_DEFINE_ERROR_RESULT(NotEnoughInstallSpace, 200);
|
||||
R_DEFINE_ERROR_RESULT(InvalidContentMetaKey, 240);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(ContentStorageBaseNotFound, 310);
|
||||
|
@ -56,6 +57,10 @@ namespace ams::ncm {
|
|||
R_DEFINE_ERROR_RESULT(SdCardContentMetaDatabaseNotActive, 264);
|
||||
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_RESULT(InvalidOffset, 8182);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue