Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -1,131 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_content_manager_factory.hpp"
#include "ncm_remote_content_manager_impl.hpp"
namespace ams::ncm {
namespace {
constinit sf::SharedPointer<IContentManager> g_content_manager;
#if defined(ATMOSPHERE_OS_HORIZON)
constinit util::TypedStorage<sf::UnmanagedServiceObject<IContentManager, RemoteContentManagerImpl>> g_remote_manager_storage = {};
#endif
}
void Initialize() {
AMS_ASSERT(g_content_manager == nullptr);
#if defined(ATMOSPHERE_OS_HORIZON)
util::ConstructAt(g_remote_manager_storage);
g_content_manager = util::GetReference(g_remote_manager_storage).GetShared();
#else
g_content_manager = CreateDefaultContentManager(ContentManagerConfig{});
#endif
}
void Finalize() {
AMS_ASSERT(g_content_manager != nullptr);
g_content_manager.Reset();
#if defined(ATMOSPHERE_OS_HORIZON)
util::DestroyAt(g_remote_manager_storage);
#endif
}
void InitializeWithObject(sf::SharedPointer<IContentManager> manager_object) {
AMS_ASSERT(g_content_manager == nullptr);
g_content_manager = manager_object;
AMS_ASSERT(g_content_manager != nullptr);
}
/* Service API. */
Result CreateContentStorage(StorageId storage_id) {
R_RETURN(g_content_manager->CreateContentStorage(storage_id));
}
Result CreateContentMetaDatabase(StorageId storage_id) {
R_RETURN(g_content_manager->CreateContentMetaDatabase(storage_id));
}
Result VerifyContentStorage(StorageId storage_id) {
R_RETURN(g_content_manager->VerifyContentStorage(storage_id));
}
Result VerifyContentMetaDatabase(StorageId storage_id) {
R_RETURN(g_content_manager->VerifyContentMetaDatabase(storage_id));
}
Result OpenContentStorage(ContentStorage *out, StorageId storage_id) {
sf::SharedPointer<IContentStorage> content_storage;
R_TRY(g_content_manager->OpenContentStorage(std::addressof(content_storage), storage_id));
*out = ContentStorage(std::move(content_storage));
R_SUCCEED();
}
Result OpenContentMetaDatabase(ContentMetaDatabase *out, StorageId storage_id) {
sf::SharedPointer<IContentMetaDatabase> content_db;
R_TRY(g_content_manager->OpenContentMetaDatabase(std::addressof(content_db), storage_id));
*out = ContentMetaDatabase(std::move(content_db));
R_SUCCEED();
}
Result CleanupContentMetaDatabase(StorageId storage_id) {
R_RETURN(g_content_manager->CleanupContentMetaDatabase(storage_id));
}
Result ActivateContentStorage(StorageId storage_id) {
R_RETURN(g_content_manager->ActivateContentStorage(storage_id));
}
Result InactivateContentStorage(StorageId storage_id) {
R_RETURN(g_content_manager->InactivateContentStorage(storage_id));
}
Result ActivateContentMetaDatabase(StorageId storage_id) {
/* On < 2.0.0, this command doesn't exist, and databases are activated as needed on open. */
R_SUCCEED_IF(hos::GetVersion() < hos::Version_2_0_0);
R_RETURN(g_content_manager->ActivateContentMetaDatabase(storage_id));
}
Result InactivateContentMetaDatabase(StorageId storage_id) {
/* On < 2.0.0, this command doesn't exist. */
R_SUCCEED_IF(hos::GetVersion() < hos::Version_2_0_0);
R_RETURN(g_content_manager->InactivateContentMetaDatabase(storage_id));
}
Result InvalidateRightsIdCache() {
R_RETURN(g_content_manager->InvalidateRightsIdCache());
}
Result ActivateFsContentStorage(fs::ContentStorageId fs_content_storage_id) {
R_RETURN(g_content_manager->ActivateFsContentStorage(fs_content_storage_id));
}
/* Deprecated API. */
Result CloseContentStorageForcibly(StorageId storage_id) {
AMS_ABORT_UNLESS(hos::GetVersion() == hos::Version_1_0_0);
R_RETURN(g_content_manager->CloseContentStorageForcibly(storage_id));
}
Result CloseContentMetaDatabaseForcibly(StorageId storage_id) {
AMS_ABORT_UNLESS(hos::GetVersion() == hos::Version_1_0_0);
R_RETURN(g_content_manager->CloseContentMetaDatabaseForcibly(storage_id));
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (c) 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 {
void GetStringFromBytes(char *dst, const void *src, size_t count) {
for (size_t i = 0; i < count; i++) {
util::SNPrintf(dst + 2 * i, 3, "%02x", static_cast<const u8 *>(src)[i]);
}
}
bool GetBytesFromString(void *dst, size_t dst_size, const char *src, size_t src_size) {
/* Each byte is comprised of hex characters. */
if (!util::IsAligned(src_size, 2) || (dst_size * 2 < src_size)) {
return false;
}
/* Convert each character pair to a byte until we reach the end. */
for (size_t i = 0; i < src_size; i += 2) {
char tmp[3];
util::Strlcpy(tmp, src + i, sizeof(tmp));
char *err = nullptr;
reinterpret_cast<u8 *>(dst)[i / 2] = static_cast<u8>(std::strtoul(tmp, std::addressof(err), 16));
if (*err != '\x00') {
return false;
}
}
return true;
}
}
ContentIdString GetContentIdString(ContentId id) {
ContentIdString str;
GetStringFromContentId(str.data, sizeof(str), id);
return str;
}
void GetStringFromContentId(char *dst, size_t dst_size, ContentId id) {
AMS_ABORT_UNLESS(dst_size > ContentIdStringLength);
GetStringFromBytes(dst, std::addressof(id), sizeof(id));
}
void GetStringFromRightsId(char *dst, size_t dst_size, fs::RightsId id) {
AMS_ABORT_UNLESS(dst_size > RightsIdStringLength);
GetStringFromBytes(dst, std::addressof(id), sizeof(id));
}
void GetTicketFileStringFromRightsId(char *dst, size_t dst_size, fs::RightsId id) {
AMS_ABORT_UNLESS(dst_size > TicketFileStringLength);
ContentIdString str;
GetStringFromRightsId(str.data, sizeof(str), id);
util::SNPrintf(dst, dst_size, "%s.tik", str.data);
}
void GetCertificateFileStringFromRightsId(char *dst, size_t dst_size, fs::RightsId id) {
AMS_ABORT_UNLESS(dst_size > CertFileStringLength);
ContentIdString str;
GetStringFromRightsId(str.data, sizeof(str), id);
util::SNPrintf(dst, dst_size, "%s.cert", str.data);
}
util::optional<ContentId> GetContentIdFromString(const char *str, size_t len) {
if (len < ContentIdStringLength) {
return util::nullopt;
}
ContentId content_id;
return GetBytesFromString(std::addressof(content_id), sizeof(content_id), str, ContentIdStringLength) ? util::optional<ContentId>(content_id) : util::nullopt;
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (c) 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;
}
template<typename Handler>
Result ForEachContentInfo(const ContentMetaKey &key, ncm::ContentMetaDatabase *db, Handler handler) {
constexpr s32 MaxPerIteration = 0x10;
ContentInfo info_list[MaxPerIteration];
s32 offset = 0;
while (true) {
/* List the content infos. */
s32 count;
R_TRY(db->ListContentInfo(std::addressof(count), info_list, MaxPerIteration, key, offset));
/* Handle all that we listed. */
for (s32 i = 0; i < count; i++) {
bool done = false;
R_TRY(handler(std::addressof(done), info_list[i]));
if (done) {
break;
}
}
/* Check if we're done. */
if (count != MaxPerIteration) {
break;
}
offset += count;
}
R_SUCCEED();
}
}
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;
}
Result EstimateRequiredSize(s64 *out_size, const ContentMetaKey &key, ncm::ContentMetaDatabase *db) {
s64 size = 0;
R_TRY(ForEachContentInfo(key, db, [&size](bool *out_done, const ContentInfo &info) -> Result {
size += CalculateRequiredSize(info.GetSize(), MaxClusterSize);
*out_done = false;
R_SUCCEED();
}));
*out_size = size;
R_SUCCEED();
}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
namespace {
constexpr inline size_t MaxPackagePathLength = 0x100;
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *package_root_path, const char *entry_path) {
char package_path[MaxPackagePathLength];
const size_t path_len = util::SNPrintf(package_path, sizeof(package_path), "%s%s", package_root_path, entry_path);
AMS_ABORT_UNLESS(path_len < MaxPackagePathLength);
R_RETURN(fs::ConvertToFsCommonPath(dst, dst_size, package_path));
}
Result LoadContentMeta(ncm::AutoBuffer *out, const char *package_root_path, const fs::DirectoryEntry &entry) {
AMS_ABORT_UNLESS(impl::PathView(entry.name).HasSuffix(".cnmt.nca"));
char path[MaxPackagePathLength];
R_TRY(ConvertToFsCommonPath(path, sizeof(path), package_root_path, entry.name));
R_RETURN(ncm::TryReadContentMetaPath(out, path, ncm::ReadContentMetaPathWithoutExtendedDataOrDigest));
}
template<typename F>
Result ForEachFileInDirectory(const char *root_path, F f) {
/* Open the directory. */
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, fs::OpenDirectoryMode_File));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
while (true) {
/* Read the current entry. */
s64 count;
fs::DirectoryEntry entry;
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
if (count == 0) {
break;
}
/* Invoke our handler on the entry. */
bool done;
R_TRY(f(std::addressof(done), entry));
R_SUCCEED_IF(done);
}
R_SUCCEED();
}
}
Result ContentMetaDatabaseBuilder::BuildFromPackageContentMeta(void *buf, size_t size, const ContentInfo &meta_info) {
/* Create a reader for the content meta. */
ncm::PackagedContentMetaReader package_meta_reader(buf, size);
/* Allocate space to hold the converted meta. */
const size_t meta_size = package_meta_reader.CalculateConvertContentMetaSize();
std::unique_ptr<char[]> meta(new (std::nothrow) char[meta_size]);
/* Convert the meta from packaged form to normal form. */
package_meta_reader.ConvertToContentMeta(meta.get(), meta_size, meta_info);
ncm::ContentMetaReader meta_reader(meta.get(), meta_size);
/* Insert the new metas into the database. */
R_TRY(m_db->Set(package_meta_reader.GetKey(), meta_reader.GetData(), meta_reader.GetSize()));
/* We're done. */
R_SUCCEED();
}
Result ContentMetaDatabaseBuilder::BuildFromStorage(ContentStorage *storage) {
/* Get the total count of contents. */
s32 total_count;
R_TRY(storage->GetContentCount(std::addressof(total_count)));
/* Loop over all contents, looking for a package we can build from. */
const size_t MaxContentIds = 64;
ContentId content_ids[MaxContentIds];
for (s32 offset = 0; offset < total_count; /* ... */) {
/* List contents at the current offset. */
s32 count;
R_TRY(storage->ListContentId(std::addressof(count), content_ids, MaxContentIds, offset));
/* Loop the contents we listed, looking for a correct one. */
for (s32 i = 0; i < count; i++) {
/* Get the path for this content id. */
auto &content_id = content_ids[i];
ncm::Path path;
storage->GetPath(std::addressof(path), content_id);
/* Read the content meta path, and build. */
ncm::AutoBuffer package_meta;
if (R_SUCCEEDED(ncm::TryReadContentMetaPath(std::addressof(package_meta), path.str, ncm::ReadContentMetaPathWithoutExtendedDataOrDigest))) {
/* Get the size of the content. */
s64 size;
R_TRY(storage->GetSize(std::addressof(size), content_id));
/* Build. */
R_TRY(this->BuildFromPackageContentMeta(package_meta.Get(), package_meta.GetSize(), ContentInfo::Make(content_id, size, ContentInfo::DefaultContentAttributes, ContentType::Meta)));
}
}
/* Advance. */
offset += count;
}
/* Commit our changes. */
R_RETURN(m_db->Commit());
}
Result ContentMetaDatabaseBuilder::BuildFromPackage(const char *package_root_path) {
/* Build the database by writing every entry in the package. */
R_TRY(ForEachFileInDirectory(package_root_path, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
/* Never early terminate. */
*done = false;
/* We have nothing to list if we're not looking at a meta. */
R_SUCCEED_IF(!impl::PathView(entry.name).HasSuffix(".cnmt.nca"));
/* Read the content meta path, and build. */
ncm::AutoBuffer package_meta;
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root_path, entry));
/* Try to parse a content id from the name. */
auto content_id = GetContentIdFromString(entry.name, sizeof(entry.name));
R_UNLESS(content_id, ncm::ResultInvalidPackageFormat());
/* Build using the meta. */
R_RETURN(this->BuildFromPackageContentMeta(package_meta.Get(), package_meta.GetSize(), ContentInfo::Make(*content_id, entry.file_size, ContentInfo::DefaultContentAttributes, ContentType::Meta)));
}));
/* Commit our changes. */
R_RETURN(m_db->Commit());
}
Result ContentMetaDatabaseBuilder::Cleanup() {
/* This cleans up the content meta by removing all entries. */
while (true) {
/* List as many keys as we can. */
constexpr s32 MaxKeys = 64;
ContentMetaKey keys[MaxKeys];
auto list_count = m_db->ListContentMeta(keys, MaxKeys);
/* Remove the listed keys. */
for (auto i = 0; i < list_count.written; i++) {
R_TRY(m_db->Remove(keys[i]));
}
/* If there aren't more keys to read, we're done. */
if (list_count.written < MaxKeys) {
break;
}
}
/* Commit our deletions. */
R_RETURN(m_db->Commit());
}
Result ListApplicationPackage(s32 *out_count, ApplicationId *out_ids, size_t max_out_ids, const char *package_root_path) {
size_t count = 0;
R_TRY(ForEachFileInDirectory(package_root_path, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
/* Never early terminate. */
*done = false;
/* We have nothing to list if we're not looking at a meta. */
R_SUCCEED_IF(!impl::PathView(entry.name).HasSuffix(".cnmt.nca"));
/* Read the content meta path, and build. */
ncm::AutoBuffer package_meta;
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root_path, entry));
/* Create a reader for the meta. */
ncm::PackagedContentMetaReader package_meta_reader(package_meta.Get(), package_meta.GetSize());
/* Write the key to output if we're reading an application. */
const auto &key = package_meta_reader.GetKey();
if (key.type == ContentMetaType::Application) {
R_UNLESS(count < max_out_ids, ncm::ResultBufferInsufficient());
out_ids[count++] = { key.id };
}
R_SUCCEED();
}));
*out_count = static_cast<s32>(count);
R_SUCCEED();
}
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_content_manager_factory.hpp"
namespace ams::ncm {
sf::SharedPointer<IContentManager> CreateDefaultContentManager(const ContentManagerConfig &config) {
auto ref = sf::CreateSharedObjectEmplaced<IContentManager, ContentManagerImpl>();
R_ABORT_UNLESS(ref.GetImpl().Initialize(config));
return ref;
}
}

View File

@@ -1,23 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
sf::SharedPointer<IContentManager> CreateDefaultContentManager(const ContentManagerConfig &config);
}

View File

@@ -1,439 +0,0 @@
/*
* Copyright (c) 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 {
void ConvertPackageContentMetaHeaderToContentMetaHeader(ContentMetaHeader *dst, const PackagedContentMetaHeader &src) {
/* Set destination. */
*dst = {
.extended_header_size = src.extended_header_size,
.content_count = src.content_count,
.content_meta_count = src.content_meta_count,
.attributes = src.attributes,
.platform = src.platform,
};
}
void ConvertPackageContentMetaHeaderToInstallContentMetaHeader(InstallContentMetaHeader *dst, const PackagedContentMetaHeader &src) {
/* Set destination. */
static_assert(std::is_same<InstallContentMetaHeader, PackagedContentMetaHeader>::value);
std::memcpy(dst, std::addressof(src), sizeof(*dst));
}
void ConvertInstallContentMetaHeaderToContentMetaHeader(ContentMetaHeader *dst, const InstallContentMetaHeader &src) {
/* Set destination. */
*dst = {
.extended_header_size = src.extended_header_size,
.content_count = src.content_count,
.content_meta_count = src.content_meta_count,
.attributes = src.attributes,
.platform = src.platform,
};
}
Result FindDeltaIndex(s32 *out_index, const PatchMetaExtendedDataReader &reader, u32 src_version, u32 dst_version) {
/* Iterate over all deltas. */
auto header = reader.GetHeader();
for (s32 i = 0; i < static_cast<s32>(header->delta_count); i++) {
/* Check if the current delta matches the versions. */
auto delta = reader.GetPatchDeltaHeader(i);
if ((src_version == 0 || delta->delta.source_version == src_version) && delta->delta.destination_version == dst_version) {
*out_index = i;
R_SUCCEED();
}
}
/* We didn't find the delta. */
R_THROW(ncm::ResultDeltaNotFound());
}
s32 CountContentExceptForMeta(const PatchMetaExtendedDataReader &reader, s32 delta_index) {
/* Iterate over packaged content infos, checking for those which aren't metas. */
s32 count = 0;
auto delta = reader.GetPatchDeltaHeader(delta_index);
for (s32 i = 0; i < static_cast<s32>(delta->content_count); i++) {
if (reader.GetPatchDeltaPackagedContentInfo(delta_index, i)->GetType() != ContentType::Meta) {
count++;
}
}
return count;
}
}
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++) {
if (this->GetContentInfo(i)->GetType() == ContentType::DeltaFragment) {
count++;
}
}
return count;
}
size_t PackagedContentMetaReader::CalculateConvertContentMetaSize() const {
const auto *header = this->GetHeader();
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);
}
}
Result PackagedContentMetaReader::ConvertToFragmentOnlyInstallContentMeta(void *dst, size_t size, const InstallContentInfo &meta, u32 source_version) {
/* Ensure that we have enough space. */
size_t required_size;
R_TRY(this->CalculateConvertFragmentOnlyInstallContentMetaSize(std::addressof(required_size), source_version));
AMS_ABORT_UNLESS(size >= required_size);
/* Find the delta index. */
PatchMetaExtendedDataReader reader(this->GetExtendedData(), this->GetExtendedDataSize());
s32 index;
R_TRY(FindDeltaIndex(std::addressof(index), reader, source_version, this->GetKey().version));
auto delta = reader.GetPatchDeltaHeader(index);
/* 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.install_type = ContentInstallType::FragmentOnly;
/* Set the content count. */
auto fragment_count = CountContentExceptForMeta(reader, index);
header.content_count = static_cast<u16>(fragment_count) + 1;
/* 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);
s32 count = 0;
for (s32 i = 0; i < static_cast<s32>(delta->content_count); i++) {
auto packaged_content_info = reader.GetPatchDeltaPackagedContentInfo(index, i);
if (packaged_content_info->GetType() != ContentType::Meta) {
/* 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);
/* Increment the count. */
count++;
}
}
/* Assert that we copied the right number of infos. */
AMS_ASSERT(count == fragment_count);
R_SUCCEED();
}
Result PackagedContentMetaReader::CalculateConvertFragmentOnlyInstallContentMetaSize(size_t *out_size, u32 source_version) const {
/* Find the delta index. */
PatchMetaExtendedDataReader reader(this->GetExtendedData(), this->GetExtendedDataSize());
s32 index;
R_TRY(FindDeltaIndex(std::addressof(index), reader, source_version, this->GetKey().version));
/* Get the fragment count. */
const auto fragment_count = CountContentExceptForMeta(reader, index);
/* Recalculate. */
*out_size = this->CalculateConvertFragmentOnlyInstallContentMetaSize(fragment_count);
R_SUCCEED();
}
void PackagedContentMetaReader::ConvertToContentMeta(void *dst, size_t size, const ContentInfo &meta) {
/* Ensure we have enough space to convert. */
AMS_ABORT_UNLESS(size >= this->CalculateConvertContentMetaSize());
/* Prepare for conversion. */
const auto *packaged_header = this->GetHeader();
uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
/* Convert the header. */
ContentMetaHeader header;
ConvertPackageContentMetaHeaderToContentMetaHeader(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++) {
/* Don't copy any delta fragments. */
if (packaged_header->type == ContentMetaType::Patch) {
if (this->GetContentInfo(i)->GetType() == ContentType::DeltaFragment) {
continue;
}
}
/* Copy the current info. */
std::memcpy(reinterpret_cast<void *>(dst_addr), std::addressof(this->GetContentInfo(i)->info), sizeof(ContentInfo));
dst_addr += sizeof(ContentInfo);
}
/* 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);
}
}
size_t InstallContentMetaReader::CalculateConvertSize() const {
return CalculateSizeImpl<ContentMetaHeader, ContentInfo>(this->GetExtendedHeaderSize(), this->GetContentCount(), this->GetContentMetaCount(), this->GetExtendedDataSize(), false);
}
void InstallContentMetaReader::ConvertToContentMeta(void *dst, size_t size) const {
/* Ensure we have enough space to convert. */
AMS_ABORT_UNLESS(size >= this->CalculateConvertSize());
/* Prepare for conversion. */
const auto *install_header = this->GetHeader();
uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
/* Convert the header. */
ContentMetaHeader header;
ConvertInstallContentMetaHeaderToContentMetaHeader(std::addressof(header), *install_header);
/* 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()), install_header->extended_header_size);
dst_addr += install_header->extended_header_size;
/* Copy content infos. */
for (size_t i = 0; i < this->GetContentCount(); i++) {
/* Copy the current info. */
std::memcpy(reinterpret_cast<void *>(dst_addr), std::addressof(this->GetContentInfo(i)->info), sizeof(ContentInfo));
dst_addr += sizeof(ContentInfo);
}
/* 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);
}
}
Result MetaConverter::CountContentExceptForMeta(s32 *out, PatchMetaExtendedDataAccessor *accessor, const PatchDeltaHeader &header, s32 delta_index) {
/* Get the count. */
s32 count = 0;
for (auto i = 0; i < static_cast<int>(header.content_count); ++i) {
/* Get the delta content info. */
PackagedContentInfo content_info;
R_TRY(accessor->GetPatchDeltaContentInfo(std::addressof(content_info), delta_index, i));
if (content_info.GetType() != ContentType::Meta) {
++count;
}
}
*out = count;
R_SUCCEED();
}
Result MetaConverter::FindDeltaIndex(s32 *out, PatchMetaExtendedDataAccessor *accessor, u32 source_version, u32 destination_version) {
/* Get the header. */
PatchMetaExtendedDataHeader header;
header.delta_count = 0;
R_TRY(accessor->GetHeader(std::addressof(header)));
/* Iterate over all deltas. */
for (s32 i = 0; i < static_cast<s32>(header.delta_count); i++) {
/* Get the current patch delta header. */
PatchDeltaHeader delta_header;
R_TRY(accessor->GetPatchDeltaHeader(std::addressof(delta_header), i));
/* Check if the current delta matches the versions. */
if ((source_version == 0 || delta_header.delta.source_version == source_version) && delta_header.delta.destination_version == destination_version) {
*out = i;
R_SUCCEED();
}
}
/* We didn't find the delta. */
R_THROW(ncm::ResultDeltaNotFound());
}
Result MetaConverter::GetFragmentOnlyInstallContentMeta(AutoBuffer *out, const InstallContentInfo &meta, const PackagedContentMetaReader &reader, PatchMetaExtendedDataAccessor *accessor, u32 source_version) {
/* Find the appropriate delta index. */
s32 delta_index = 0;
R_TRY(FindDeltaIndex(std::addressof(delta_index), accessor, source_version, reader.GetHeader()->version));
/* Get the delta header. */
PatchDeltaHeader delta_header;
R_TRY(accessor->GetPatchDeltaHeader(std::addressof(delta_header), delta_index));
/* Count content except for meta. */
s32 fragment_count = 0;
R_TRY(CountContentExceptForMeta(std::addressof(fragment_count), accessor, delta_header, delta_index));
/* Determine the required size. */
const size_t meta_size = reader.CalculateConvertFragmentOnlyInstallContentMetaSize(fragment_count);
/* Initialize the out buffer. */
R_TRY(out->Initialize(meta_size));
/* Prepare for conversion. */
const auto *packaged_header = reader.GetHeader();
uintptr_t dst_addr = reinterpret_cast<uintptr_t>(out->Get());
/* Convert the header. */
InstallContentMetaHeader header;
ConvertPackageContentMetaHeaderToInstallContentMetaHeader(std::addressof(header), *packaged_header);
header.install_type = ContentInstallType::FragmentOnly;
/* Set the content count. */
header.content_count = static_cast<u16>(fragment_count) + 1;
/* 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), reader.GetExtendedHeader<void>(), 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);
s32 count = 0;
for (s32 i = 0; i < static_cast<s32>(delta_header.content_count); i++) {
/* Get the delta content info. */
PackagedContentInfo content_info;
R_TRY(accessor->GetPatchDeltaContentInfo(std::addressof(content_info), delta_index, i));
if (content_info.GetType() != ContentType::Meta) {
/* Create the install content info. */
InstallContentInfo install_content_info = InstallContentInfo::Make(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);
/* Increment the count. */
count++;
}
}
/* Assert that we copied the right number of infos. */
AMS_ASSERT(count == fragment_count);
R_SUCCEED();
}
}

View File

@@ -1,567 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_content_meta_database_impl.hpp"
namespace ams::ncm {
Result ContentMetaDatabaseImpl::GetContentInfoImpl(ContentInfo *out, const ContentMetaKey &key, ContentType type, util::optional<u8> id_offset) const {
R_TRY(this->EnsureEnabled());
/* Find the meta key. */
const auto it = m_kvs->lower_bound(key);
R_UNLESS(it != m_kvs->end(), ncm::ResultContentMetaNotFound());
R_UNLESS(it->GetKey().id == key.id, ncm::ResultContentMetaNotFound());
const auto found_key = it->GetKey();
/* Create a reader for this content meta. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, found_key));
ContentMetaReader reader(meta, meta_size);
/* Find the content info. */
const ContentInfo *content_info = nullptr;
if (id_offset) {
content_info = reader.GetContentInfo(type, *id_offset);
} else {
content_info = reader.GetContentInfo(type);
}
R_UNLESS(content_info != nullptr, ncm::ResultContentNotFound());
/* Save output. */
*out = *content_info;
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::Set(const ContentMetaKey &key, const sf::InBuffer &value) {
R_TRY(this->EnsureEnabled());
R_RETURN(m_kvs->Set(key, value.GetPointer(), value.GetSize()));
}
Result ContentMetaDatabaseImpl::Get(sf::Out<u64> out_size, const ContentMetaKey &key, const sf::OutBuffer &out_value) {
R_TRY(this->EnsureEnabled());
/* Get the entry from our key-value store. */
size_t size;
R_TRY_CATCH(m_kvs->Get(std::addressof(size), out_value.GetPointer(), out_value.GetSize(), key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
} R_END_TRY_CATCH;
out_size.SetValue(size);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::Remove(const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
R_TRY_CATCH(m_kvs->Remove(key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) {
R_RETURN(this->GetContentIdImpl(out_content_id.GetPointer(), key, type, util::nullopt));
}
Result ContentMetaDatabaseImpl::ListContentInfo(sf::Out<s32> out_count, const sf::OutArray<ContentInfo> &out_info, const ContentMetaKey &key, s32 offset) {
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
/* Obtain the content meta for the given key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Read content infos from the given offset up to the given count. */
size_t count;
for (count = 0; count < out_info.GetSize() && count + offset < reader.GetContentCount(); count++) {
out_info[count] = *reader.GetContentInfo(offset + count);
}
out_count.SetValue(count);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) {
R_TRY(this->EnsureEnabled());
size_t entries_total = 0;
size_t entries_written = 0;
/* Iterate over all entries. */
for (auto &entry : *m_kvs) {
const ContentMetaKey key = entry.GetKey();
/* Check if this entry matches the given filters. */
if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min <= key.id && key.id <= max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) {
continue;
}
/* If application id is present, check if it matches the filter. */
if (application_id != InvalidApplicationId) {
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Ensure application id matches, if present. */
if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id && application_id != *entry_application_id) {
continue;
}
}
/* Write the entry to the output buffer. */
if (entries_written < out_info.GetSize()) {
out_info[entries_written++] = key;
}
entries_total++;
}
out_entries_total.SetValue(entries_total);
out_entries_written.SetValue(entries_written);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) {
R_TRY(this->EnsureEnabled());
util::optional<ContentMetaKey> found_key = util::nullopt;
/* Find the last key with the desired program id. */
for (auto entry = m_kvs->lower_bound(ContentMetaKey::MakeUnknownType(id, 0)); entry != m_kvs->end(); entry++) {
/* No further entries will match the program id, discontinue. */
if (entry->GetKey().id != id) {
break;
}
/* We are only interested in keys with the Full content install type. */
if (entry->GetKey().install_type == ContentInstallType::Full) {
found_key = entry->GetKey();
}
}
/* Check if the key is absent. */
R_UNLESS(found_key, ncm::ResultContentMetaNotFound());
out_key.SetValue(*found_key);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::ListApplication(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ApplicationContentMetaKey> &out_keys, ContentMetaType type) {
R_TRY(this->EnsureEnabled());
size_t entries_total = 0;
size_t entries_written = 0;
/* Iterate over all entries. */
for (auto &entry : *m_kvs) {
const ContentMetaKey key = entry.GetKey();
/* Check if this entry matches the given filters. */
if (!(type == ContentMetaType::Unknown || key.type == type)) {
continue;
}
/* Check if the entry has an application id. */
ContentMetaReader reader(entry.GetValuePointer(), entry.GetValueSize());
if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id) {
/* Write the entry to the output buffer. */
if (entries_written < out_keys.GetSize()) {
out_keys[entries_written++] = { key, *entry_application_id };
}
entries_total++;
}
}
out_entries_total.SetValue(entries_total);
out_entries_written.SetValue(entries_written);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::Has(sf::Out<bool> out, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
*out = false;
/* Check if key is present. */
size_t size;
R_TRY_CATCH(m_kvs->GetValueSize(&size, key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ResultSuccess());
} R_END_TRY_CATCH;
*out = true;
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::HasAll(sf::Out<bool> out, const sf::InArray<ContentMetaKey> &keys) {
R_TRY(this->EnsureEnabled());
*out = false;
/* Check if keys are present. */
for (size_t i = 0; i < keys.GetSize(); i++) {
/* Check if we have the current key. */
bool has;
R_TRY(this->Has(std::addressof(has), keys[i]));
/* If we don't, then we can early return because we don't have all. */
R_SUCCEED_IF(!has);
}
*out = true;
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetSize(sf::Out<u64> out_size, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Determine the content meta size for the key. */
size_t size;
R_TRY(this->GetContentMetaSize(&size, key));
out_size.SetValue(size);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetRequiredSystemVersion(sf::Out<u32> out_version, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Only applications and patches have a required system version. */
R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch, ncm::ResultInvalidContentMetaKey());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Obtain the required system version. */
switch (key.type) {
case ContentMetaType::Application:
out_version.SetValue(reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_system_version);
break;
case ContentMetaType::Patch:
out_version.SetValue(reader.GetExtendedHeader<PatchMetaExtendedHeader>()->required_system_version);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetPatchContentMetaId(sf::Out<u64> out_patch_id, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Only applications can have patches. */
R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::AddOnContent, ncm::ResultInvalidContentMetaKey());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Obtain the patch id. */
switch (key.type) {
case ContentMetaType::Application:
out_patch_id.SetValue(reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->patch_id.value);
break;
case ContentMetaType::AddOnContent:
R_UNLESS(reader.GetExtendedHeaderSize() == sizeof(AddOnContentMetaExtendedHeader), ncm::ResultInvalidAddOnContentMetaExtendedHeader());
out_patch_id.SetValue(reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->data_patch_id.value);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::DisableForcibly() {
m_disabled = true;
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) {
R_TRY(this->EnsureEnabled());
R_UNLESS(out_orphaned.GetSize() >= content_ids.GetSize(), ncm::ResultBufferInsufficient());
/* Default to orphaned for all content ids. */
for (size_t i = 0; i < out_orphaned.GetSize(); i++) {
out_orphaned[i] = true;
}
auto IsOrphanedContent = [](const sf::InArray<ContentId> &list, const ncm::ContentId &id) ALWAYS_INLINE_LAMBDA -> util::optional<size_t> {
/* Check if any input content ids match our found content id. */
for (size_t i = 0; i < list.GetSize(); i++) {
if (list[i] == id) {
return util::make_optional(i);
}
}
return util::nullopt;
};
/* Iterate over all entries. */
for (auto &entry : *m_kvs) {
ContentMetaReader reader(entry.GetValuePointer(), entry.GetValueSize());
/* Check if any of this entry's content infos matches one of the content ids for lookup. */
/* If they do, then the content id isn't orphaned. */
for (size_t i = 0; i < reader.GetContentCount(); i++) {
if (auto found = IsOrphanedContent(content_ids, reader.GetContentInfo(i)->GetId()); found) {
out_orphaned[*found] = false;
}
}
}
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::Commit() {
R_TRY(this->EnsureEnabled());
/* Save and commit. */
R_TRY(m_kvs->Save());
R_RETURN(fs::CommitSaveData(m_mount_name));
}
Result ContentMetaDatabaseImpl::HasContent(sf::Out<bool> out, const ContentMetaKey &key, const ContentId &content_id) {
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Optimistically suppose that we will find the content. */
out.SetValue(true);
/* Check if any content infos contain a matching id. */
for (size_t i = 0; i < reader.GetContentCount(); i++) {
R_SUCCEED_IF(content_id == reader.GetContentInfo(i)->GetId());
}
/* We didn't find a content info. */
out.SetValue(false);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::ListContentMetaInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaInfo> &out_meta_info, const ContentMetaKey &key, s32 offset) {
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Read content meta infos from the given offset up to the given count. */
size_t count;
for (count = 0; count < out_meta_info.GetSize() && count + offset < reader.GetContentMetaCount(); count++) {
out_meta_info[count] = *reader.GetContentMetaInfo(count + offset);
}
/* Set the ouput value. */
out_entries_written.SetValue(count);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetAttributes(sf::Out<u8> out_attributes, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Set the ouput value. */
out_attributes.SetValue(reader.GetHeader()->attributes);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetRequiredApplicationVersion(sf::Out<u32> out_version, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Get the required version. */
u32 required_version;
switch (key.type) {
case ContentMetaType::AddOnContent:
required_version = reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->required_application_version;
break;
case ContentMetaType::Application:
/* As of 9.0.0, applications can be dependent on a specific base application version. */
AMS_ABORT_UNLESS(hos::GetVersion() >= hos::Version_9_0_0);
required_version = reader.GetExtendedHeader<ApplicationMetaExtendedHeader>()->required_application_version;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Set the ouput value. */
out_version.SetValue(required_version);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetContentIdByTypeAndIdOffset(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) {
R_RETURN(this->GetContentIdImpl(out_content_id.GetPointer(), key, type, util::make_optional(id_offset)));
}
Result ContentMetaDatabaseImpl::GetCount(sf::Out<u32> out_count) {
R_TRY(this->EnsureEnabled());
out_count.SetValue(m_kvs->GetCount());
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetOwnerApplicationId(sf::Out<ApplicationId> out_id, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Ensure this type of key has an owner. */
R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch || key.type == ContentMetaType::AddOnContent, ncm::ResultInvalidContentMetaKey());
/* Applications are their own owner. */
if (key.type == ContentMetaType::Application) {
out_id.SetValue({key.id});
R_SUCCEED();
}
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Get the owner application id. */
ApplicationId owner_application_id;
switch (key.type) {
case ContentMetaType::Patch:
owner_application_id = reader.GetExtendedHeader<PatchMetaExtendedHeader>()->application_id;
break;
case ContentMetaType::AddOnContent:
owner_application_id = reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->application_id;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Set the output value. */
out_id.SetValue(owner_application_id);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetContentAccessibilities(sf::Out<u8> out_accessibilities, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Ensure this type of key is for an add-on content. */
R_UNLESS(key.type == ContentMetaType::AddOnContent, ncm::ResultInvalidContentMetaKey());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Set the ouput value. */
out_accessibilities.SetValue(reader.GetExtendedHeader<AddOnContentMetaExtendedHeader>()->content_accessibilities);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::GetContentInfoByType(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type) {
R_RETURN(this->GetContentInfoImpl(out_content_info.GetPointer(), key, type, util::nullopt));
}
Result ContentMetaDatabaseImpl::GetContentInfoByTypeAndIdOffset(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type, u8 id_offset) {
R_RETURN(this->GetContentInfoImpl(out_content_info.GetPointer(), key, type, util::make_optional(id_offset)));
}
Result ContentMetaDatabaseImpl::GetPlatform(sf::Out<ncm::ContentMetaPlatform> out, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Set the ouput value. */
out.SetValue(reader.GetHeader()->platform);
R_SUCCEED();
}
Result ContentMetaDatabaseImpl::HasAttributes(sf::Out<u8> out, u8 attribute_mask) {
R_TRY(this->EnsureEnabled());
/* Create a variable to hold the combined attributes. */
u8 combined_attributes = 0;
/* Iterate over all entries. */
for (auto &entry : *m_kvs) {
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, entry.GetKey()));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Accumulate the set attributes from the current entry. */
combined_attributes |= (reader.GetHeader()->attributes) & attribute_mask;
/* If all the attributes we're looking for have been found, we're done. */
if ((combined_attributes & attribute_mask) == attribute_mask) {
break;
}
}
/* Set the output. */
*out = combined_attributes;
R_SUCCEED();
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (c) 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.hpp>
#include "ncm_content_meta_database_impl_base.hpp"
namespace ams::ncm {
class ContentMetaDatabaseImpl : public ContentMetaDatabaseImplBase {
public:
ContentMetaDatabaseImpl(ContentMetaKeyValueStore *kvs, const char *mount_name) : ContentMetaDatabaseImplBase(kvs, mount_name) { /* ... */ }
ContentMetaDatabaseImpl(ContentMetaKeyValueStore *kvs) : ContentMetaDatabaseImplBase(kvs) { /* ... */ }
private:
/* Helpers. */
Result GetContentInfoImpl(ContentInfo *out, const ContentMetaKey &key, ContentType type, util::optional<u8> id_offset) const;
Result GetContentIdImpl(ContentId *out, const ContentMetaKey &key, ContentType type, util::optional<u8> id_offset) const {
/* Get the content info. */
ContentInfo content_info;
R_TRY(this->GetContentInfoImpl(std::addressof(content_info), key, type, id_offset));
/* Set the output id. */
*out = content_info.GetId();
R_SUCCEED();
}
public:
/* Actual commands. */
virtual Result Set(const ContentMetaKey &key, const sf::InBuffer &value) override;
virtual Result Get(sf::Out<u64> out_size, const ContentMetaKey &key, const sf::OutBuffer &out_value) override;
virtual Result Remove(const ContentMetaKey &key) override;
virtual Result GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) override;
virtual Result ListContentInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentInfo> &out_info, const ContentMetaKey &key, s32 offset) override;
virtual Result List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override;
virtual Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) override;
virtual Result ListApplication(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ApplicationContentMetaKey> &out_keys, ContentMetaType meta_type) override;
virtual Result Has(sf::Out<bool> out, const ContentMetaKey &key) override;
virtual Result HasAll(sf::Out<bool> out, const sf::InArray<ContentMetaKey> &keys) override;
virtual Result GetSize(sf::Out<u64> out_size, const ContentMetaKey &key) override;
virtual Result GetRequiredSystemVersion(sf::Out<u32> out_version, const ContentMetaKey &key) override;
virtual Result GetPatchContentMetaId(sf::Out<u64> out_patch_id, const ContentMetaKey &key) override;
virtual Result DisableForcibly() override;
virtual Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) override;
virtual Result Commit() override;
virtual Result HasContent(sf::Out<bool> out, const ContentMetaKey &key, const ContentId &content_id) override;
virtual Result ListContentMetaInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaInfo> &out_meta_info, const ContentMetaKey &key, s32 offset) override;
virtual Result GetAttributes(sf::Out<u8> out_attributes, const ContentMetaKey &key) override;
virtual Result GetRequiredApplicationVersion(sf::Out<u32> out_version, const ContentMetaKey &key) override;
virtual Result GetContentIdByTypeAndIdOffset(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) override;
virtual Result GetCount(sf::Out<u32> out_count) override;
virtual Result GetOwnerApplicationId(sf::Out<ApplicationId> out_id, const ContentMetaKey &key) override;
virtual Result GetContentAccessibilities(sf::Out<u8> out_accessibilities, const ContentMetaKey &key) override;
virtual Result GetContentInfoByType(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type) override;
virtual Result GetContentInfoByTypeAndIdOffset(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type, u8 id_offset) override;
virtual Result GetPlatform(sf::Out<ncm::ContentMetaPlatform> out, const ContentMetaKey &key) override;
virtual Result HasAttributes(sf::Out<u8> out, u8 attr_mask) override;
};
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
class ContentMetaDatabaseImplBase {
NON_COPYABLE(ContentMetaDatabaseImplBase);
NON_MOVEABLE(ContentMetaDatabaseImplBase);
protected:
using ContentMetaKeyValueStore = ams::kvdb::MemoryKeyValueStore<ContentMetaKey>;
protected:
ContentMetaKeyValueStore *m_kvs;
char m_mount_name[fs::MountNameLengthMax + 1];
bool m_disabled;
protected:
ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs) : m_kvs(kvs), m_mount_name(), m_disabled(false) { /* ... */ }
ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs, const char *mount_name) : ContentMetaDatabaseImplBase(kvs) {
std::strcpy(m_mount_name, mount_name);
}
protected:
/* Helpers. */
Result EnsureEnabled() const {
R_UNLESS(!m_disabled, ncm::ResultInvalidContentMetaDatabase());
R_SUCCEED();
}
Result GetContentMetaSize(size_t *out, const ContentMetaKey &key) const {
R_TRY_CATCH(m_kvs->GetValueSize(out, key)) {
R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result GetContentMetaPointer(const void **out_value_ptr, size_t *out_size, const ContentMetaKey &key) const {
R_TRY(this->GetContentMetaSize(out_size, key));
R_RETURN(m_kvs->GetValuePointer(reinterpret_cast<const ContentMetaHeader **>(out_value_ptr), key));
}
public:
/* Actual commands. */
virtual Result Set(const ContentMetaKey &key, const sf::InBuffer &value) = 0;
virtual Result Get(sf::Out<u64> out_size, const ContentMetaKey &key, const sf::OutBuffer &out_value) = 0;
virtual Result Remove(const ContentMetaKey &key) = 0;
virtual Result GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) = 0;
virtual Result ListContentInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentInfo> &out_info, const ContentMetaKey &key, s32 offset) = 0;
virtual Result List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) = 0;
virtual Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) = 0;
virtual Result ListApplication(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ApplicationContentMetaKey> &out_keys, ContentMetaType meta_type) = 0;
virtual Result Has(sf::Out<bool> out, const ContentMetaKey &key) = 0;
virtual Result HasAll(sf::Out<bool> out, const sf::InArray<ContentMetaKey> &keys) = 0;
virtual Result GetSize(sf::Out<u64> out_size, const ContentMetaKey &key) = 0;
virtual Result GetRequiredSystemVersion(sf::Out<u32> out_version, const ContentMetaKey &key) = 0;
virtual Result GetPatchContentMetaId(sf::Out<u64> out_patch_id, const ContentMetaKey &key) = 0;
virtual Result DisableForcibly() = 0;
virtual Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) = 0;
virtual Result Commit() = 0;
virtual Result HasContent(sf::Out<bool> out, const ContentMetaKey &key, const ContentId &content_id) = 0;
virtual Result ListContentMetaInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaInfo> &out_meta_info, const ContentMetaKey &key, s32 offset) = 0;
virtual Result GetAttributes(sf::Out<u8> out_attributes, const ContentMetaKey &key) = 0;
virtual Result GetRequiredApplicationVersion(sf::Out<u32> out_version, const ContentMetaKey &key) = 0;
virtual Result GetContentIdByTypeAndIdOffset(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) = 0;
virtual Result GetCount(sf::Out<u32> out_count) = 0;
virtual Result GetOwnerApplicationId(sf::Out<ApplicationId> out_id, const ContentMetaKey &key) = 0;
virtual Result GetContentAccessibilities(sf::Out<u8> out_accessibilities, const ContentMetaKey &key) = 0;
virtual Result GetContentInfoByType(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type) = 0;
virtual Result GetContentInfoByTypeAndIdOffset(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type, u8 id_offset) = 0;
virtual Result GetPlatform(sf::Out<ncm::ContentMetaPlatform> out, const ContentMetaKey &key) = 0;
virtual Result HasAttributes(sf::Out<u8> out, u8 attr_mask) = 0;
};
static_assert(ncm::IsIContentMetaDatabase<ContentMetaDatabaseImplBase>);
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (c) 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 {
const char *GetContentMetaTypeString(ContentMetaType type) {
switch (type) {
case ContentMetaType::Application: return "Application";
case ContentMetaType::Patch: return "Patch";
case ContentMetaType::AddOnContent: return "AddOnContent";
case ContentMetaType::DataPatch: return "DataPatch";
default: return "(unknown)";
}
}
}

View File

@@ -1,291 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
namespace {
Result MountContentMetaByRemoteFileSystemProxy(const char *mount_name, const char *path, fs::ContentAttributes attr) {
R_RETURN(fs::MountContent(mount_name, path, attr, fs::ContentType_Meta));
}
constinit MountContentMetaFunction g_mount_content_meta_func = MountContentMetaByRemoteFileSystemProxy;
}
namespace impl {
Result MountContentMetaImpl(const char *mount_name, const char *path, fs::ContentAttributes attr) {
R_RETURN(g_mount_content_meta_func(mount_name, path, attr));
}
}
bool IsContentMetaFileName(const char *name) {
return impl::PathView(name).HasSuffix(".cnmt");
}
Result ReadContentMetaPathAlongWithExtendedDataAndDigest(AutoBuffer *out, const char *path, fs::ContentAttributes attr) {
/* Mount the content. */
auto mount_name = impl::CreateUniqueMountName();
R_TRY(impl::MountContentMetaImpl(mount_name.str, path, attr));
ON_SCOPE_EXIT { fs::Unmount(mount_name.str); };
/* Open the root directory. */
auto root_path = impl::GetRootDirectoryPath(mount_name);
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path.str, fs::OpenDirectoryMode_File));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
/* Loop directory reading until we find the entry we're looking for. */
while (true) {
/* Read one entry, and finish when we fail to read. */
fs::DirectoryEntry entry;
s64 num_read;
R_TRY(fs::ReadDirectory(std::addressof(num_read), std::addressof(entry), dir, 1));
if (num_read == 0) {
break;
}
/* If this is the content meta file, parse it. */
if (IsContentMetaFileName(entry.name)) {
/* Create the file path. */
impl::FilePathString file_path(root_path.str);
file_path.Append(entry.name);
/* Open the content meta file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), file_path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Get the meta size. */
s64 file_size;
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
const size_t meta_size = static_cast<size_t>(file_size);
/* Create a buffer for the meta. */
R_TRY(out->Initialize(meta_size));
/* Read the meta into the buffer. */
R_RETURN(fs::ReadFile(file, 0, out->Get(), meta_size));
}
}
R_THROW(ncm::ResultContentMetaNotFound());
}
Result ReadContentMetaPathAlongWithExtendedDataAndDigestSuppressingFsAbort(AutoBuffer *out, const char *path, fs::ContentAttributes attr) {
fs::ScopedAutoAbortDisabler aad;
R_RETURN(ReadContentMetaPathAlongWithExtendedDataAndDigest(out, path, attr));
}
Result ReadContentMetaPathWithoutExtendedDataOrDigest(AutoBuffer *out, const char *path, fs::ContentAttributes attr) {
/* Mount the content. */
auto mount_name = impl::CreateUniqueMountName();
R_TRY(impl::MountContentMetaImpl(mount_name.str, path, attr));
ON_SCOPE_EXIT { fs::Unmount(mount_name.str); };
/* Open the root directory. */
auto root_path = impl::GetRootDirectoryPath(mount_name);
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path.str, fs::OpenDirectoryMode_File));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
/* Loop directory reading until we find the entry we're looking for. */
while (true) {
/* Read one entry, and finish when we fail to read. */
fs::DirectoryEntry entry;
s64 num_read;
R_TRY(fs::ReadDirectory(std::addressof(num_read), std::addressof(entry), dir, 1));
if (num_read == 0) {
break;
}
/* If this is the content meta file, parse it. */
if (IsContentMetaFileName(entry.name)) {
/* Create the file path. */
impl::FilePathString file_path(root_path.str);
file_path.Append(entry.name);
/* Open the content meta file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), file_path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Get the meta size. */
s64 file_size;
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
const size_t meta_file_size = static_cast<size_t>(file_size);
/* Check that the meta size is large enough. */
R_UNLESS(meta_file_size >= sizeof(PackagedContentMetaHeader), ncm::ResultInvalidContentMetaFileSize());
/* Read the header. */
PackagedContentMetaHeader header;
size_t read_size = 0;
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, std::addressof(header), sizeof(header)));
/* Check the right size was read. */
R_UNLESS(read_size == sizeof(PackagedContentMetaHeader), ncm::ResultInvalidContentMetaFileSize());
/* Determine the meta size. */
const size_t meta_size = PackagedContentMetaReader(std::addressof(header), sizeof(header)).GetExtendedDataOffset();
/* Create a buffer for the meta. */
R_TRY(out->Initialize(meta_size));
/* Read the meta into the buffer. */
R_RETURN(fs::ReadFile(file, 0, out->Get(), meta_size));
}
}
R_THROW(ncm::ResultContentMetaNotFound());
}
Result ReadContentMetaPathWithoutExtendedDataOrDigestSuppressingFsAbort(AutoBuffer *out, const char *path, fs::ContentAttributes attr) {
fs::ScopedAutoAbortDisabler aad;
R_RETURN(ReadContentMetaPathAlongWithExtendedDataAndDigest(out, path, attr));
}
Result TryReadContentMetaPath(fs::ContentAttributes *out_attr, AutoBuffer *out, const char *path, ReadContentMetaPathFunction func) {
/* Try with attributes = none. */
fs::ContentAttributes attr = fs::ContentAttributes_None;
R_TRY_CATCH(func(out, path, attr)) {
R_CATCH(fs::ResultNcaHeaderSignature1VerificationFailed) {
/* On signature failure, try with attributes = all. */
attr = fs::ContentAttributes_All;
R_TRY(func(out, path, attr));
}
} R_END_TRY_CATCH;
/* Set output attributes. */
*out_attr = attr;
R_SUCCEED();
}
Result TryReadContentMetaPath(AutoBuffer *out, const char *path, ReadContentMetaPathFunction func) {
/* Try with attributes = none. */
fs::ContentAttributes attr = fs::ContentAttributes_None;
R_TRY_CATCH(func(out, path, attr)) {
R_CATCH(fs::ResultNcaHeaderSignature1VerificationFailed) {
/* On signature failure, try with attributes = all. */
attr = fs::ContentAttributes_All;
R_TRY(func(out, path, attr));
}
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const Path &path, fs::ContentAttributes attr, FirmwareVariationId firmware_variation_id) {
AutoBuffer meta;
R_TRY(ReadContentMetaPathAlongWithExtendedDataAndDigestSuppressingFsAbort(std::addressof(meta), path.str, attr));
/* Create a reader for the content meta. */
PackagedContentMetaReader reader(meta.Get(), meta.GetSize());
/* Define a helper to output the base meta infos. */
const auto ReadMetaInfoListFromBase = [&] () ALWAYS_INLINE_LAMBDA -> Result {
/* Output the base content meta info count. */
*out_count = reader.GetContentMetaCount();
/* Create a buffer to hold the infos. NOTE: N does not check for nullptr before accessing. */
std::unique_ptr<ContentMetaInfo[]> buffer(new (std::nothrow) ContentMetaInfo[reader.GetContentMetaCount()]);
AMS_ABORT_UNLESS(buffer != nullptr);
/* Copy all base meta infos to output */
for (size_t i = 0; i < reader.GetContentMetaCount(); i++) {
buffer[i] = *reader.GetContentMetaInfo(i);
}
/* Write out the buffer we've populated. */
*out_meta_infos = std::move(buffer);
R_SUCCEED();
};
/* If there are no firmware variations to list, read meta infos from base. */
R_UNLESS(reader.GetExtendedDataSize() != 0, ReadMetaInfoListFromBase());
SystemUpdateMetaExtendedDataReader extended_data_reader(reader.GetExtendedData(), reader.GetExtendedDataSize());
util::optional<s32> firmware_variation_index = util::nullopt;
/* NOTE: Atmosphere extension to support downgrading. */
/* If all firmware variations refer to base, don't require the current variation be present. */
bool force_refer_to_base = true;
/* Find the input firmware variation id. */
for (size_t i = 0; i < extended_data_reader.GetFirmwareVariationCount(); i++) {
if (*extended_data_reader.GetFirmwareVariationId(i) == firmware_variation_id) {
firmware_variation_index = i;
break;
} else {
/* Check if the current variation refers to base. */
const FirmwareVariationInfo *cur_variation_info = extended_data_reader.GetFirmwareVariationInfo(i);
const bool cur_refers_to_base = extended_data_reader.GetHeader()->version == 1 || cur_variation_info->refer_to_base;
/* We force referral to base on unsupported variation only if all supported variations refer to base. */
force_refer_to_base &= cur_refers_to_base;
}
}
/* We couldn't find the input firmware variation id. */
if (!firmware_variation_index) {
/* Unless we can force a referral to base, the firmware isn't supported. */
R_UNLESS(force_refer_to_base, ncm::ResultInvalidFirmwareVariation());
/* Force a referral to base. */
R_RETURN(ReadMetaInfoListFromBase());
}
/* Obtain the variation info. */
const FirmwareVariationInfo *variation_info = extended_data_reader.GetFirmwareVariationInfo(*firmware_variation_index);
/* Refer to base if variation info says we should, or if version is 1. */
const bool refer_to_base = extended_data_reader.GetHeader()->version == 1 || variation_info->refer_to_base;
R_UNLESS(!refer_to_base, ReadMetaInfoListFromBase());
/* Output the content meta count. */
const u32 content_meta_count = variation_info->content_meta_count;
*out_count = content_meta_count;
/* We're done if there are no content metas to list. */
R_SUCCEED_IF(content_meta_count == 0);
/* Allocate a buffer for the content meta infos. */
std::unique_ptr<ContentMetaInfo[]> buffer(new (std::nothrow) ContentMetaInfo[content_meta_count]);
AMS_ABORT_UNLESS(buffer != nullptr);
/* Get the content meta infos. */
Span<const ContentMetaInfo> meta_infos;
extended_data_reader.GetContentMetaInfoList(std::addressof(meta_infos), content_meta_count);
/* Copy the meta infos to the buffer. */
for (size_t i = 0; i < content_meta_count; i++) {
buffer[i] = meta_infos[i];
}
/* Output the content meta info buffer. */
*out_meta_infos = std::move(buffer);
R_SUCCEED();
}
void SetMountContentMetaFunction(MountContentMetaFunction func) {
g_mount_content_meta_func = func;
}
}

View File

@@ -1,936 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_content_storage_impl.hpp"
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
namespace {
constexpr inline const char * const BaseContentDirectory = "/registered";
void MakeBaseContentDirectoryPath(PathString *out, const char *root_path) {
out->AssignFormat("%s%s", root_path, BaseContentDirectory);
}
void MakeContentPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) {
PathString path;
MakeBaseContentDirectoryPath(std::addressof(path), root_path);
func(out, id, path);
}
Result EnsureContentDirectory(ContentId id, MakeContentPathFunction func, const char *root_path) {
PathString path;
MakeContentPath(std::addressof(path), id, func, root_path);
R_RETURN(fs::EnsureParentDirectory(path));
}
Result DeleteContentFile(ContentId id, MakeContentPathFunction func, const char *root_path) {
/* Create the content path. */
PathString path;
MakeContentPath(std::addressof(path), id, func, root_path);
/* Delete the content. */
R_TRY_CATCH(fs::DeleteFile(path)) {
R_CONVERT(fs::ResultPathNotFound, ncm::ResultContentNotFound())
} R_END_TRY_CATCH;
R_SUCCEED();
}
template<typename F>
Result TraverseDirectory(bool *out_should_continue, const char *root_path, int max_level, F f) {
/* If the level is zero, we're done. */
R_SUCCEED_IF(max_level <= 0);
/* On 1.0.0, NotRequireFileSize was not a valid open mode. */
const auto open_dir_mode = hos::GetVersion() >= hos::Version_2_0_0 ? (fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize) : (fs::OpenDirectoryMode_All);
/* Retry traversal upon request. */
bool retry_dir_read = true;
while (retry_dir_read) {
retry_dir_read = false;
/* Open the directory at the given path. All entry types are allowed. */
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, open_dir_mode));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
while (true) {
/* Read a single directory entry. */
fs::DirectoryEntry entry;
s64 entry_count;
R_TRY(fs::ReadDirectory(std::addressof(entry_count), std::addressof(entry), dir, 1));
/* Directory has no entries to process. */
if (entry_count == 0) {
break;
}
/* Path of the current entry. */
PathString current_path;
current_path.AssignFormat("%s/%s", root_path, entry.name);
/* Call the process function. */
bool should_continue = true;
bool should_retry_dir_read = false;
R_TRY(f(std::addressof(should_continue), std::addressof(should_retry_dir_read), current_path, entry));
/* If the provided function wishes to terminate immediately, we should respect it. */
if (!should_continue) {
*out_should_continue = false;
R_SUCCEED();
}
/* Mark for retry. */
if (should_retry_dir_read) {
retry_dir_read = true;
break;
}
/* If the entry is a directory, recurse. */
if (entry.type == fs::DirectoryEntryType_Directory) {
R_TRY(TraverseDirectory(std::addressof(should_continue), current_path, max_level - 1, f));
if (!should_continue) {
*out_should_continue = false;
R_SUCCEED();
}
}
}
}
R_SUCCEED();
}
template<typename F>
Result TraverseDirectory(const char *root_path, int max_level, F f) {
bool should_continue = false;
R_RETURN(TraverseDirectory(std::addressof(should_continue), root_path, max_level, f));
}
bool IsContentPath(const char *path) {
impl::PathView view(path);
/* Ensure nca suffix. */
if (!view.HasSuffix(".nca")) {
return false;
}
/* File name should be the size of a content id plus the nca file extension. */
auto file_name = view.GetFileName();
if (file_name.length() != ContentIdStringLength + 4) {
return false;
}
/* Ensure file name is comprised of hex characters. */
for (size_t i = 0; i < ContentIdStringLength; i++) {
if (!std::isxdigit(static_cast<unsigned char>(file_name[i]))) {
return false;
}
}
return true;
}
bool IsPlaceHolderPath(const char *path) {
return IsContentPath(path);
}
Result CleanDirectoryRecursively(const PathString &path) {
if (hos::GetVersion() >= hos::Version_3_0_0) {
R_TRY(fs::CleanDirectoryRecursively(path));
} else {
/* CleanDirectoryRecursively didn't exist on < 3.0.0, so we will polyfill it. */
/* We'll delete the directory, then recreate it. */
R_TRY(fs::DeleteDirectoryRecursively(path));
R_TRY(fs::CreateDirectory(path));
}
R_SUCCEED();
}
}
ContentStorageImpl::ContentIterator::~ContentIterator() {
for (size_t i = 0; i < m_depth; i++) {
fs::CloseDirectory(m_handles[i]);
}
}
Result ContentStorageImpl::ContentIterator::Initialize(const char *root_path, size_t max_depth) {
/* Initialize tracking variables. */
m_depth = 0;
m_max_depth = max_depth;
m_entry_count = 0;
/* Create the base content directory path. */
MakeBaseContentDirectoryPath(std::addressof(m_path), root_path);
/* Open the base directory. */
R_TRY(this->OpenCurrentDirectory());
R_SUCCEED();
}
Result ContentStorageImpl::ContentIterator::OpenCurrentDirectory() {
/* Determine valid directory mode (prior to 2.0.0, NotRequireFileSize was not valid). */
const auto open_mode = hos::GetVersion() >= hos::Version_2_0_0 ? (fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize) : (fs::OpenDirectoryMode_All);
/* Open the directory for our current path. */
R_TRY(fs::OpenDirectory(std::addressof(m_handles[m_depth]), m_path, open_mode));
/* Increase our depth. */
++m_depth;
R_SUCCEED();
}
Result ContentStorageImpl::ContentIterator::OpenDirectory(const char *dir) {
/* Set our current path. */
m_path.Assign(dir);
/* Open the directory. */
R_RETURN(this->OpenCurrentDirectory());
}
Result ContentStorageImpl::ContentIterator::GetNext(util::optional<fs::DirectoryEntry> *out) {
/* Iterate until we get the next entry. */
while (true) {
/* Ensure that we have entries loaded. */
R_TRY(this->LoadEntries());
/* If we failed to load any entries, there's nothing to get. */
if (m_entry_count <= 0) {
*out = util::nullopt;
R_SUCCEED();
}
/* Get the next entry. */
const auto &entry = m_entries[--m_entry_count];
/* Process the current entry. */
switch (entry.type) {
case fs::DirectoryEntryType_Directory:
/* If the entry if a directory, we want to recurse into it if we can. */
if (m_depth < m_max_depth) {
/* Construct the full path for the subdirectory. */
PathString entry_path;
entry_path.AssignFormat("%s/%s", m_path.Get(), entry.name);
/* Open the subdirectory. */
R_TRY(this->OpenDirectory(entry_path.Get()));
}
break;
case fs::DirectoryEntryType_File:
/* Otherwise, if the entry is a file, return it. */
*out = entry;
R_SUCCEED();
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
R_SUCCEED();
}
Result ContentStorageImpl::ContentIterator::LoadEntries() {
/* If we already have entries loaded, we don't need to do anything. */
R_SUCCEED_IF(m_entry_count != 0);
/* If we have no directories open, there's nothing for us to load. */
if (m_depth == 0) {
m_entry_count = 0;
R_SUCCEED();
}
/* Determine the maximum entries that we can load. */
const s64 max_entries = m_depth == m_max_depth ? MaxDirectoryEntries : 1;
/* Read entries from the current directory. */
s64 num_entries;
R_TRY(fs::ReadDirectory(std::addressof(num_entries), m_entries, m_handles[m_depth - 1], max_entries));
/* If we successfully read entries, load them. */
if (num_entries > 0) {
/* Reverse the order of the loaded entries, for our future convenience. */
for (fs::DirectoryEntry *start_entry = m_entries, *end_entry = m_entries + num_entries - 1; start_entry < end_entry; ++start_entry, --end_entry) {
std::swap(*start_entry, *end_entry);
}
/* Set our entry count. */
m_entry_count = num_entries;
R_SUCCEED();
}
/* We didn't read any entries, so we need to advance to the next directory. */
fs::CloseDirectory(m_handles[--m_depth]);
/* Find the index of the parent directory's substring. */
size_t i = m_path.GetLength() - 1;
while (m_path.Get()[i--] != '/') {
AMS_ABORT_UNLESS(i > 0);
}
/* Set the path to the parent directory. */
m_path = m_path.MakeSubString(0, i + 1);
/* Try to load again from the parent directory. */
R_RETURN(this->LoadEntries());
}
ContentStorageImpl::~ContentStorageImpl() {
this->InvalidateFileCache();
}
Result ContentStorageImpl::InitializeBase(const char *root_path) {
PathString path;
/* Create the content directory. */
MakeBaseContentDirectoryPath(std::addressof(path), root_path);
R_TRY(fs::EnsureDirectory(path));
/* Create the placeholder directory. */
PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path);
R_RETURN(fs::EnsureDirectory(path));
}
Result ContentStorageImpl::CleanupBase(const char *root_path) {
PathString path;
/* Create the content directory. */
MakeBaseContentDirectoryPath(std::addressof(path), root_path);
R_TRY(CleanDirectoryRecursively(path));
/* Create the placeholder directory. */
PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path);
R_RETURN(CleanDirectoryRecursively(path));
}
Result ContentStorageImpl::VerifyBase(const char *root_path) {
PathString path;
/* Check if root directory exists. */
bool has_dir;
R_TRY(fs::HasDirectory(std::addressof(has_dir), root_path));
R_UNLESS(has_dir, ncm::ResultContentStorageBaseNotFound());
/* Check if content directory exists. */
bool has_registered;
MakeBaseContentDirectoryPath(std::addressof(path), root_path);
R_TRY(fs::HasDirectory(std::addressof(has_registered), path));
/* Check if placeholder directory exists. */
bool has_placeholder;
PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), root_path);
R_TRY(fs::HasDirectory(std::addressof(has_placeholder), path));
/* Convert findings to results. */
R_UNLESS(has_registered || has_placeholder, ncm::ResultContentStorageBaseNotFound());
R_UNLESS(has_registered, ncm::ResultInvalidContentStorageBase());
R_UNLESS(has_placeholder, ncm::ResultInvalidContentStorageBase());
R_SUCCEED();
}
void ContentStorageImpl::InvalidateFileCache() {
if (m_cached_content_id != InvalidContentId) {
fs::CloseFile(m_cached_file_handle);
m_cached_content_id = InvalidContentId;
}
m_content_iterator = util::nullopt;
}
Result ContentStorageImpl::OpenContentIdFile(ContentId content_id) {
/* If the file is the currently cached one, we've nothing to do. */
R_SUCCEED_IF(m_cached_content_id == content_id);
/* Close any cached file. */
this->InvalidateFileCache();
/* Create the content path. */
PathString path;
MakeContentPath(std::addressof(path), content_id, m_make_content_path_func, m_root_path);
/* Open the content file and store to the cache. */
R_TRY_CATCH(fs::OpenFile(std::addressof(m_cached_file_handle), path, fs::OpenMode_Read)) {
R_CONVERT(ams::fs::ResultPathNotFound, ncm::ResultContentNotFound())
} R_END_TRY_CATCH;
m_cached_content_id = content_id;
R_SUCCEED();
}
Result ContentStorageImpl::Initialize(const char *path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache) {
R_TRY(this->EnsureEnabled());
/* Check paths exists for this content storage. */
R_TRY(VerifyBase(path));
/* Initialize members. */
m_root_path = PathString(path);
m_make_content_path_func = content_path_func;
m_placeholder_accessor.Initialize(std::addressof(m_root_path), placeholder_path_func, delay_flush);
m_rights_id_cache = rights_id_cache;
R_SUCCEED();
}
Result ContentStorageImpl::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
R_TRY(this->EnsureEnabled());
out.SetValue({util::GenerateUuid()});
R_SUCCEED();
}
Result ContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) {
R_TRY(this->EnsureEnabled());
R_TRY(EnsureContentDirectory(content_id, m_make_content_path_func, m_root_path));
R_RETURN(m_placeholder_accessor.CreatePlaceHolderFile(placeholder_id, size));
}
Result ContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
R_RETURN(m_placeholder_accessor.DeletePlaceHolderFile(placeholder_id));
}
Result ContentStorageImpl::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
/* Create the placeholder path. */
PathString placeholder_path;
m_placeholder_accessor.MakePath(std::addressof(placeholder_path), placeholder_id);
/* Check if placeholder file exists. */
bool has = false;
R_TRY(fs::HasFile(std::addressof(has), placeholder_path));
out.SetValue(has);
R_SUCCEED();
}
Result ContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) {
/* Ensure offset is valid. */
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
R_RETURN(m_placeholder_accessor.WritePlaceHolderFile(placeholder_id, offset, data.GetPointer(), data.GetSize()));
}
Result ContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
this->InvalidateFileCache();
R_TRY(this->EnsureEnabled());
/* Create the placeholder path. */
PathString placeholder_path;
m_placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
/* Create the content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Move the placeholder to the content path. */
R_TRY_CATCH(fs::RenameFile(placeholder_path, content_path)) {
R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists())
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result ContentStorageImpl::Delete(ContentId content_id) {
R_TRY(this->EnsureEnabled());
this->InvalidateFileCache();
R_RETURN(DeleteContentFile(content_id, m_make_content_path_func, m_root_path));
}
Result ContentStorageImpl::Has(sf::Out<bool> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Create the content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Check if the content file exists. */
bool has = false;
R_TRY(fs::HasFile(std::addressof(has), content_path));
out.SetValue(has);
R_SUCCEED();
}
Result ContentStorageImpl::GetPath(sf::Out<Path> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Create the content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Substitute our mount name for the common mount name. */
Path common_path;
R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), content_path));
out.SetValue(common_path);
R_SUCCEED();
}
Result ContentStorageImpl::GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
/* Obtain the placeholder path. */
PathString placeholder_path;
m_placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
/* Substitute our mount name for the common mount name. */
Path common_path;
R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), placeholder_path));
out.SetValue(common_path);
R_SUCCEED();
}
Result ContentStorageImpl::CleanupAllPlaceHolder() {
R_TRY(this->EnsureEnabled());
/* Clear the cache. */
m_placeholder_accessor.InvalidateAll();
/* Obtain the placeholder base directory path. */
PathString placeholder_dir;
PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(placeholder_dir), m_root_path);
/* Cleanup the placeholder base directory. */
CleanDirectoryRecursively(placeholder_dir);
R_SUCCEED();
}
Result ContentStorageImpl::ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
R_TRY(this->EnsureEnabled());
/* Obtain the placeholder base directory path. */
PathString placeholder_dir;
PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(placeholder_dir), m_root_path);
const size_t max_entries = out_buf.GetSize();
size_t entry_count = 0;
/* Traverse the placeholder base directory finding valid placeholder files. */
R_TRY(TraverseDirectory(placeholder_dir, m_placeholder_accessor.GetHierarchicalDirectoryDepth(), [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) -> Result {
AMS_UNUSED(current_path);
*should_continue = true;
*should_retry_dir_read = false;
/* We are only looking for files. */
if (entry.type == fs::DirectoryEntryType_File) {
R_UNLESS(entry_count <= max_entries, ncm::ResultBufferInsufficient());
/* Get the placeholder id from the filename. */
PlaceHolderId placeholder_id;
R_TRY(PlaceHolderAccessor::GetPlaceHolderIdFromFileName(std::addressof(placeholder_id), entry.name));
out_buf[entry_count++] = placeholder_id;
}
R_SUCCEED();
}));
out_count.SetValue(static_cast<s32>(entry_count));
R_SUCCEED();
}
Result ContentStorageImpl::GetContentCount(sf::Out<s32> out_count) {
R_TRY(this->EnsureEnabled());
/* Obtain the content base directory path. */
PathString path;
MakeBaseContentDirectoryPath(std::addressof(path), m_root_path);
const auto depth = GetHierarchicalContentDirectoryDepth(m_make_content_path_func);
size_t count = 0;
/* Traverse the content base directory finding all files. */
R_TRY(TraverseDirectory(path, depth, [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) -> Result {
AMS_UNUSED(current_path);
*should_continue = true;
*should_retry_dir_read = false;
/* Increment the count for each file found. */
if (entry.type == fs::DirectoryEntryType_File) {
count++;
}
R_SUCCEED();
}));
out_count.SetValue(static_cast<s32>(count));
R_SUCCEED();
}
Result ContentStorageImpl::ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out, s32 offset) {
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
if (!m_content_iterator.has_value() || !m_last_content_offset.has_value() || m_last_content_offset != offset) {
/* Create and initialize the content cache. */
m_content_iterator.emplace();
R_TRY(m_content_iterator->Initialize(m_root_path, GetHierarchicalContentDirectoryDepth(m_make_content_path_func)));
/* Advance to the desired offset. */
for (auto current_offset = 0; current_offset < offset; /* ... */) {
/* Get the next directory entry. */
util::optional<fs::DirectoryEntry> dir_entry;
R_TRY(m_content_iterator->GetNext(std::addressof(dir_entry)));
/* If we run out of entries before reaching the desired offset, we're done. */
if (!dir_entry) {
out_count.SetValue(0);
R_SUCCEED();
}
/* If the current entry is a valid content id, advance. */
if (GetContentIdFromString(dir_entry->name, std::strlen(dir_entry->name))) {
++current_offset;
}
}
}
/* Iterate, reading as many entries as we can. */
s32 count = 0;
while (count < static_cast<s32>(out.GetSize())) {
/* Get the next directory entry. */
util::optional<fs::DirectoryEntry> dir_entry;
R_TRY(m_content_iterator->GetNext(std::addressof(dir_entry)));
/* Don't continue if the directory entry is absent. */
if (!dir_entry) {
break;
}
/* Process the entry, if it's a valid content id. */
if (auto content_id = GetContentIdFromString(dir_entry->name, std::strlen(dir_entry->name)); content_id.has_value()) {
/* Output the content id. */
out[count++] = *content_id;
/* Update our last content offset. */
m_last_content_offset = offset + count;
}
}
/* Set the output count. */
*out_count = count;
R_SUCCEED();
}
Result ContentStorageImpl::GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Create the content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Open the content file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), content_path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Obtain the size of the content. */
s64 file_size;
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
out_size.SetValue(file_size);
R_SUCCEED();
}
Result ContentStorageImpl::DisableForcibly() {
m_disabled = true;
this->InvalidateFileCache();
m_placeholder_accessor.InvalidateAll();
R_SUCCEED();
}
Result ContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
R_TRY(this->EnsureEnabled());
/* Close any cached file. */
this->InvalidateFileCache();
/* Ensure the future content directory exists. */
R_TRY(EnsureContentDirectory(new_content_id, m_make_content_path_func, m_root_path));
/* Ensure the destination placeholder directory exists. */
R_TRY(m_placeholder_accessor.EnsurePlaceHolderDirectory(placeholder_id));
/* Obtain the placeholder path. */
PathString placeholder_path;
m_placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
/* Make the old content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), old_content_id, m_make_content_path_func, m_root_path);
/* Move the content to the placeholder path. */
R_TRY_CATCH(fs::RenameFile(content_path, placeholder_path)) {
R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultContentAlreadyExists())
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result ContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) {
R_TRY(this->EnsureEnabled());
R_RETURN(m_placeholder_accessor.SetPlaceHolderFileSize(placeholder_id, size));
}
Result ContentStorageImpl::ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) {
/* Ensure offset is valid. */
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
/* Create the content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Open the content file. */
R_TRY(this->OpenContentIdFile(content_id));
/* Read from the requested offset up to the requested size. */
R_RETURN(fs::ReadFile(m_cached_file_handle, offset, buf.GetPointer(), buf.GetSize()));
}
Result ContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
/* Obtain the regular rights id for the placeholder id. */
ncm::RightsId rights_id;
R_TRY(this->GetRightsIdFromPlaceHolderIdDeprecated2(std::addressof(rights_id), placeholder_id));
/* Output the fs rights id. */
out_rights_id.SetValue(rights_id.id);
R_SUCCEED();
}
Result ContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
R_RETURN(this->GetRightsIdFromPlaceHolderId(out_rights_id, placeholder_id, fs::ContentAttributes_None));
}
Result ContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Get the placeholder path. */
Path path;
R_TRY(this->GetPlaceHolderPath(std::addressof(path), placeholder_id));
/* Get the rights id for the placeholder id. */
R_RETURN(GetRightsId(out_rights_id.GetPointer(), path, attr));
}
Result ContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) {
/* Obtain the regular rights id for the content id. */
ncm::RightsId rights_id;
R_TRY(this->GetRightsIdFromContentIdDeprecated2(std::addressof(rights_id), content_id));
/* Output the fs rights id. */
out_rights_id.SetValue(rights_id.id);
R_SUCCEED();
}
Result ContentStorageImpl::GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_RETURN(this->GetRightsIdFromContentId(out_rights_id, content_id, fs::ContentAttributes_None));
}
Result ContentStorageImpl::GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Attempt to obtain the rights id from the cache. */
if (m_rights_id_cache->Find(out_rights_id.GetPointer(), content_id)) {
R_SUCCEED();
}
/* Get the path of the content. */
Path path;
R_TRY(this->GetPath(std::addressof(path), content_id));
/* Obtain the rights id for the content. */
ncm::RightsId rights_id;
R_TRY(GetRightsId(std::addressof(rights_id), path, attr));
/* Store the rights id to the cache. */
m_rights_id_cache->Store(content_id, rights_id);
out_rights_id.SetValue(rights_id);
R_SUCCEED();
}
Result ContentStorageImpl::WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) {
/* Ensure offset is valid. */
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
/* This command is for development hardware only. */
AMS_ABORT_UNLESS(spl::IsDevelopment());
/* Close any cached file. */
this->InvalidateFileCache();
/* Make the content path. */
PathString path;
MakeContentPath(std::addressof(path), content_id, m_make_content_path_func, m_root_path);
/* Open the content file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), path.Get(), fs::OpenMode_Write));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Write the provided data to the file. */
R_RETURN(fs::WriteFile(file, offset, data.GetPointer(), data.GetSize(), fs::WriteOption::Flush));
}
Result ContentStorageImpl::GetFreeSpaceSize(sf::Out<s64> out_size) {
R_RETURN(fs::GetFreeSpaceSize(out_size.GetPointer(), m_root_path));
}
Result ContentStorageImpl::GetTotalSpaceSize(sf::Out<s64> out_size) {
R_RETURN(fs::GetTotalSpaceSize(out_size.GetPointer(), m_root_path));
}
Result ContentStorageImpl::FlushPlaceHolder() {
m_placeholder_accessor.InvalidateAll();
R_SUCCEED();
}
Result ContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out<s64> out_size, PlaceHolderId placeholder_id) {
R_TRY(this->EnsureEnabled());
/* Attempt to get the placeholder file size. */
bool found = false;
s64 file_size = 0;
R_TRY(m_placeholder_accessor.TryGetPlaceHolderFileSize(std::addressof(found), std::addressof(file_size), placeholder_id));
/* Set the output if placeholder file is found. */
if (found) {
out_size.SetValue(file_size);
R_SUCCEED();
}
/* Get the path of the placeholder. */
PathString placeholder_path;
m_placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
/* Open the placeholder file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), placeholder_path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Get the size of the placeholder file. */
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
out_size.SetValue(file_size);
R_SUCCEED();
}
Result ContentStorageImpl::RepairInvalidFileAttribute() {
/* Callback for TraverseDirectory */
using PathChecker = bool (*)(const char *);
PathChecker path_checker = nullptr;
/* Set the archive bit appropriately for content/placeholders. */
auto fix_file_attributes = [&](bool *should_continue, bool *should_retry_dir_read, const char *current_path, const fs::DirectoryEntry &entry) {
*should_retry_dir_read = false;
*should_continue = true;
if (entry.type == fs::DirectoryEntryType_Directory) {
if (path_checker(current_path)) {
if (R_SUCCEEDED(fs::SetConcatenationFileAttribute(current_path))) {
*should_retry_dir_read = true;
}
}
}
R_SUCCEED();
};
/* Fix content. */
{
path_checker = IsContentPath;
PathString path;
MakeBaseContentDirectoryPath(std::addressof(path), m_root_path);
R_TRY(TraverseDirectory(path, GetHierarchicalContentDirectoryDepth(m_make_content_path_func), fix_file_attributes));
}
/* Fix placeholders. */
m_placeholder_accessor.InvalidateAll();
{
path_checker = IsPlaceHolderPath;
PathString path;
PlaceHolderAccessor::MakeBaseDirectoryPath(std::addressof(path), m_root_path);
R_TRY(TraverseDirectory(path, GetHierarchicalContentDirectoryDepth(m_make_content_path_func), fix_file_attributes));
}
R_SUCCEED();
}
Result ContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Attempt to find the rights id in the cache. */
if (m_rights_id_cache->Find(out_rights_id.GetPointer(), cache_content_id)) {
R_SUCCEED();
}
/* Get the placeholder path. */
PathString placeholder_path;
m_placeholder_accessor.GetPath(std::addressof(placeholder_path), placeholder_id);
/* Substitute mount name with the common mount name. */
Path common_path;
R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), placeholder_path));
/* Get the rights id. */
ncm::RightsId rights_id;
R_TRY(GetRightsId(std::addressof(rights_id), common_path, attr));
m_rights_id_cache->Store(cache_content_id, rights_id);
/* Set output. */
out_rights_id.SetValue(rights_id);
R_SUCCEED();
}
Result ContentStorageImpl::RegisterPath(const ContentId &content_id, const Path &path) {
AMS_UNUSED(content_id, path);
R_THROW(ncm::ResultInvalidOperation());
}
Result ContentStorageImpl::ClearRegisteredPath() {
R_THROW(ncm::ResultInvalidOperation());
}
Result ContentStorageImpl::GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Get the path of the content. */
Path path;
R_TRY(this->GetPath(std::addressof(path), content_id));
/* Obtain the program id for the content. */
ncm::ProgramId program_id;
R_TRY(fs::GetProgramId(std::addressof(program_id), path.str, attr));
out.SetValue(program_id);
R_SUCCEED();
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright (c) 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.hpp>
#include "ncm_content_storage_impl_base.hpp"
#include "ncm_placeholder_accessor.hpp"
namespace ams::ncm {
class ContentStorageImpl : public ContentStorageImplBase {
private:
class ContentIterator {
NON_COPYABLE(ContentIterator);
NON_MOVEABLE(ContentIterator);
static constexpr size_t MaxDirectoryHandles = 0x8;
static constexpr size_t MaxDirectoryEntries = 0x10;
public:
fs::DirectoryHandle m_handles[MaxDirectoryHandles]{};
size_t m_depth{};
size_t m_max_depth{};
PathString m_path{};
fs::DirectoryEntry m_entries[MaxDirectoryEntries]{};
s64 m_entry_count{};
public:
constexpr ContentIterator() { /* ... */ }
~ContentIterator();
Result Initialize(const char *root_path, size_t max_depth);
Result GetNext(util::optional<fs::DirectoryEntry> *out);
private:
Result OpenCurrentDirectory();
Result OpenDirectory(const char *dir);
Result LoadEntries();
};
static_assert(std::is_constructible<ContentIterator>::value);
protected:
PlaceHolderAccessor m_placeholder_accessor;
ContentId m_cached_content_id;
fs::FileHandle m_cached_file_handle;
RightsIdCache *m_rights_id_cache;
util::optional<ContentIterator> m_content_iterator;
util::optional<s32> m_last_content_offset;
public:
static Result InitializeBase(const char *root_path);
static Result CleanupBase(const char *root_path);
static Result VerifyBase(const char *root_path);
public:
ContentStorageImpl() : m_placeholder_accessor(), m_cached_content_id(InvalidContentId), m_cached_file_handle(), m_rights_id_cache(nullptr), m_content_iterator(util::nullopt), m_last_content_offset(util::nullopt) { /* ... */ }
~ContentStorageImpl();
Result Initialize(const char *root_path, MakeContentPathFunction content_path_func, MakePlaceHolderPathFunction placeholder_path_func, bool delay_flush, RightsIdCache *rights_id_cache);
private:
/* Helpers. */
Result OpenContentIdFile(ContentId content_id);
void InvalidateFileCache();
public:
/* Actual commands. */
virtual Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) override;
virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) override;
virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) override;
virtual Result HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) override;
virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) override;
virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) override;
virtual Result Delete(ContentId content_id) override;
virtual Result Has(sf::Out<bool> out, ContentId content_id) override;
virtual Result GetPath(sf::Out<Path> out, ContentId content_id) override;
virtual Result GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) override;
virtual Result CleanupAllPlaceHolder() override;
virtual Result ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) override;
virtual Result GetContentCount(sf::Out<s32> out_count) override;
virtual Result ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 start_offset) override;
virtual Result GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) override;
virtual Result DisableForcibly() override;
virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override;
virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) override;
virtual Result ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) override;
virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) override;
virtual Result GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) override;
virtual Result GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) override;
virtual Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) override;
virtual Result WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) override;
virtual Result GetFreeSpaceSize(sf::Out<s64> out_size) override;
virtual Result GetTotalSpaceSize(sf::Out<s64> out_size) override;
virtual Result FlushPlaceHolder() override;
virtual Result GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id) override;
virtual Result RepairInvalidFileAttribute() override;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) override;
virtual Result RegisterPath(const ContentId &content_id, const Path &path) override;
virtual Result ClearRegisteredPath() override;
virtual Result GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) override;
};
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
class ContentStorageImplBase {
NON_COPYABLE(ContentStorageImplBase);
NON_MOVEABLE(ContentStorageImplBase);
protected:
PathString m_root_path;
MakeContentPathFunction m_make_content_path_func;
bool m_disabled;
protected:
ContentStorageImplBase() : m_root_path(), m_make_content_path_func(), m_disabled(false) { /* ... */ }
protected:
/* Helpers. */
Result EnsureEnabled() const {
R_UNLESS(!m_disabled, ncm::ResultInvalidContentStorage());
R_SUCCEED();
}
static Result GetRightsId(ncm::RightsId *out_rights_id, const Path &path, fs::ContentAttributes attr) {
if (hos::GetVersion() >= hos::Version_3_0_0) {
R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), std::addressof(out_rights_id->key_generation), path.str, attr));
} else {
R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), path.str, attr));
out_rights_id->key_generation = 0;
}
R_SUCCEED();
}
public:
/* Actual commands. */
virtual Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) = 0;
virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) = 0;
virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) = 0;
virtual Result HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) = 0;
virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) = 0;
virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) = 0;
virtual Result Delete(ContentId content_id) = 0;
virtual Result Has(sf::Out<bool> out, ContentId content_id) = 0;
virtual Result GetPath(sf::Out<Path> out, ContentId content_id) = 0;
virtual Result GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) = 0;
virtual Result CleanupAllPlaceHolder() = 0;
virtual Result ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) = 0;
virtual Result GetContentCount(sf::Out<s32> out_count) = 0;
virtual Result ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 start_offset) = 0;
virtual Result GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) = 0;
virtual Result DisableForcibly() = 0;
virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) = 0;
virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) = 0;
virtual Result ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) = 0;
virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) = 0;
virtual Result GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) = 0;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) = 0;
virtual Result GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) = 0;
virtual Result GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) = 0;
virtual Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) = 0;
virtual Result WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) = 0;
virtual Result GetFreeSpaceSize(sf::Out<s64> out_size) = 0;
virtual Result GetTotalSpaceSize(sf::Out<s64> out_size) = 0;
virtual Result FlushPlaceHolder() = 0;
virtual Result GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id) = 0;
virtual Result RepairInvalidFileAttribute() = 0;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) = 0;
virtual Result RegisterPath(const ContentId &content_id, const Path &path) = 0;
virtual Result ClearRegisteredPath() = 0;
virtual Result GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) = 0;
/* 16.0.0 Alignment change hacks. */
Result CreatePlaceHolder_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id, s64 size) { R_RETURN(this->CreatePlaceHolder(placeholder_id, content_id, size)); }
Result Register_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id) { R_RETURN(this->Register(placeholder_id, content_id)); }
Result RevertToPlaceHolder_AtmosphereAlignmentFix(ncm::ContentId old_content_id, ncm::ContentId new_content_id, ncm::PlaceHolderId placeholder_id) { R_RETURN(this->RevertToPlaceHolder(placeholder_id, old_content_id, new_content_id)); }
Result GetRightsIdFromPlaceHolderIdWithCacheDeprecated(sf::Out<ncm::RightsId> out_rights_id, ContentId cache_content_id, PlaceHolderId placeholder_id) { R_RETURN(this->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id, fs::ContentAttributes_None)); }
};
static_assert(ncm::IsIContentStorage<ContentStorageImplBase>);
}

View File

@@ -1,430 +0,0 @@
/*
* Copyright (c) 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.hpp>
#include "ncm_file_mapper_file.hpp"
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
namespace impl {
template<typename T>
concept IsMappedMemorySpan = std::same_as<T, Span<u8>>;
constexpr inline u64 InitialIdCounterValue = 0x12345;
}
class SingleCacheMapperBase : public IMapper {
private:
bool m_is_mapped;
MappedMemory m_mapped_memory;
size_t m_accessible_size;
bool m_is_using;
bool m_is_dirty;
u64 m_id_counter;
u8 *m_buffer;
size_t m_buffer_size;
public:
SingleCacheMapperBase(Span<u8> span) : m_is_mapped(false), m_mapped_memory{}, m_accessible_size(0), m_is_using(false), m_is_dirty(false), m_id_counter(impl::InitialIdCounterValue), m_buffer(span.data()), m_buffer_size(span.size_bytes()) {
/* ... */
}
protected:
void Finalize() {
/* If we're unused and mapped, we should unmap. */
if (!m_is_using && m_is_mapped) {
this->Unmap();
}
}
private:
Result Unmap() {
/* Check pre-conditions. */
AMS_ASSERT(m_is_mapped);
/* If we're dirty, we'll need to flush the entry. */
if (m_is_dirty) {
/* Unmap our memory. */
MappedMemory mem = m_mapped_memory;
if (mem.offset + mem.buffer_size > m_accessible_size) {
mem.buffer_size = m_accessible_size - mem.offset;
}
R_TRY(this->UnmapImpl(std::addressof(mem)));
}
/* Set as dirty/not mapped. */
m_is_dirty = false;
m_is_mapped = false;
R_SUCCEED();
}
Result GetMappedMemoryImpl(MappedMemory *out, size_t offset, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(m_is_mapped);
/* Ensure the accessible size works. */
const bool can_update = this->IsAccessibleSizeUpdatable();
R_UNLESS((offset + size <= m_accessible_size || can_update), ncm::ResultMapperInvalidArgument());
/* Update our accessible size. */
m_accessible_size = std::max<size_t>(m_accessible_size, size + offset);
/* Set the output memory. */
*out = m_mapped_memory;
out->buffer_size = std::min<size_t>(out->buffer_size, m_accessible_size - out->offset);
R_SUCCEED();
}
public:
virtual Result GetMappedMemory(MappedMemory *out, size_t offset, size_t size) override final {
/* Ensure our memory is valid, if it's already mapped. */
if (m_is_mapped) {
/* If we can re-use the previous mapping, do so. */
if (m_mapped_memory.IsIncluded(offset, size)) {
/* If the memory is in use, we can't get a new mapping. */
R_UNLESS(!m_is_using, ncm::ResultMapperBusy());
/* Get the output memory. */
R_RETURN(this->GetMappedMemoryImpl(out, offset, size));
}
/* We don't have the correct data mapped, so we need to map. */
R_TRY(this->Unmap());
}
/* Map. */
R_TRY(this->MapImpl(std::addressof(m_mapped_memory), Span<u8>(m_buffer, m_buffer_size), offset, size));
/* Set our mapping id. */
m_mapped_memory.id = m_id_counter++;
/* Get the output memory. */
R_RETURN(this->GetMappedMemoryImpl(out, offset, size));
}
virtual Result MarkUsing(u64 id) override final {
/* Check that the mapping is correct. */
R_UNLESS(m_is_mapped, ncm::ResultMapperNotMapped());
R_UNLESS(m_mapped_memory.id == id, ncm::ResultMapperNotMapped());
/* Mark as using. */
m_is_using = true;
R_SUCCEED();
}
virtual Result UnmarkUsing(u64 id) override final {
/* Check that the mapping is correct. */
R_UNLESS(m_is_mapped, ncm::ResultMapperNotMapped());
R_UNLESS(m_mapped_memory.id == id, ncm::ResultMapperNotMapped());
/* Mark as not using. */
m_is_using = false;
R_SUCCEED();
}
virtual Result MarkDirty(u64 id) override final {
/* Check that the mapping is correct. */
R_UNLESS(m_is_mapped, ncm::ResultMapperNotMapped());
R_UNLESS(m_mapped_memory.id == id, ncm::ResultMapperNotMapped());
/* Mark as dirty. */
m_is_dirty = true;
R_SUCCEED();
}
};
template<size_t MaxEntries>
class MultiCacheReadonlyMapperBase : public IMapper {
private:
struct Entry {
MappedMemory memory;
u64 lru_counter;
u32 use_count;
bool is_mapped;
u8 *buffer;
size_t buffer_size;
};
private:
Entry m_entry_storages[MaxEntries];
Entry * const m_entries;
size_t m_entry_count;
u64 m_id_counter;
u64 m_lru_counter;
size_t m_accessible_size;
public:
template<impl::IsMappedMemorySpan... Args>
MultiCacheReadonlyMapperBase(Args... args) : m_entries(m_entry_storages), m_entry_count(sizeof...(Args)), m_id_counter(impl::InitialIdCounterValue), m_lru_counter(1), m_accessible_size(0) {
/* Check the argument count is valid. */
static_assert(sizeof...(Args) <= MaxEntries);
/* Initialize entries. */
auto InitializeEntry = [](Entry *entry, Span<u8> span) ALWAYS_INLINE_LAMBDA -> void {
*entry = {};
entry->buffer = span.data();
entry->buffer_size = span.size_bytes();
};
Entry *cur_entry = m_entries;
(InitializeEntry(cur_entry++, args), ...);
}
size_t GetSize() {
return m_accessible_size;
}
protected:
void SetSize(size_t size) {
m_accessible_size = size;
}
void Finalize() {
/* Mark all entries as unmapped. */
for (size_t i = 0; i < m_entry_count; ++i) {
/* We can't mark unmapped an entry which is in use. */
if (m_entries[i].use_count > 0) {
break;
}
if (m_entries[i].is_mapped) {
m_entries[i].is_mapped = false;
}
}
}
private:
Result GetMappedMemoryImpl(MappedMemory *out, const MappedMemory &src) {
/* Set the output memory. */
*out = src;
out->buffer_size = std::min<size_t>(out->buffer_size, m_accessible_size - out->offset);
R_SUCCEED();
}
public:
virtual Result GetMappedMemory(MappedMemory *out, size_t offset, size_t size) override final {
/* Try to find an entry which contains the desired region. */
for (size_t i = 0; i < m_entry_count; ++i) {
if (m_entries[i].is_mapped && m_entries[i].memory.IsIncluded(offset, size)) {
R_RETURN(this->GetMappedMemoryImpl(out, m_entries[i].memory));
}
}
/* Find the oldest entry. */
Entry *oldest = nullptr;
Entry *best_entry = nullptr;
for (size_t i = 0; i < m_entry_count; ++i) {
if (m_entries[i].is_mapped) {
if (m_entries[i].use_count == 0) {
if (oldest == nullptr || m_entries[i].lru_counter < oldest->lru_counter) {
oldest = std::addressof(m_entries[i]);
}
}
} else {
best_entry = std::addressof(m_entries[i]);
}
}
/* If we didn't find a free entry, use the oldest. */
best_entry = best_entry != nullptr ? best_entry : oldest;
R_UNLESS(best_entry != nullptr, ncm::ResultMapperBusy());
/* Ensure the best entry isn't mapped. */
if (best_entry->is_mapped) {
best_entry->is_mapped = false;
}
/* Map. */
R_TRY(this->MapImpl(std::addressof(best_entry->memory), Span<u8>(best_entry->buffer, best_entry->buffer_size), offset, size));
/* Set our mapping id. */
best_entry->memory.id = m_id_counter++;
/* Get the output memory. */
R_RETURN(this->GetMappedMemoryImpl(out, best_entry->memory));
}
virtual Result MarkUsing(u64 id) override final {
/* Try to unmark the entry. */
for (size_t i = 0; i < m_entry_count; ++i) {
if (m_entries[i].memory.id == id) {
++m_entries[i].use_count;
m_entries[i].lru_counter = m_lru_counter++;
R_SUCCEED();
}
}
/* We failed to unmark. */
R_THROW(ncm::ResultMapperNotMapped());
}
virtual Result UnmarkUsing(u64 id) override final {
/* Try to unmark the entry. */
for (size_t i = 0; i < m_entry_count; ++i) {
if (m_entries[i].memory.id == id) {
--m_entries[i].use_count;
R_SUCCEED();
}
}
/* We failed to unmark. */
R_THROW(ncm::ResultMapperNotMapped());
}
virtual Result MarkDirty(u64) override final{
R_THROW(ncm::ResultMapperNotSupported());
}
};
template<typename CacheMapperBase>
class ExtendedDataMapperBase : public CacheMapperBase {
private:
static constexpr size_t MappingAlignment = 1_KB;
private:
util::optional<impl::MountNameString> m_mount_name = util::nullopt;
ncm::FileMapperFile m_file_mapper{};
size_t m_extended_data_offset;
bool m_suppress_fs_auto_abort;
public:
template<typename... Args>
ExtendedDataMapperBase(Args &&... args) : CacheMapperBase(std::forward<Args>(args)...) { /* ... */ }
virtual ~ExtendedDataMapperBase() override {
/* Finalize. */
this->Finalize();
}
Result Initialize(const char *content_path, fs::ContentAttributes attr, bool suppress_fs_auto_abort) {
/* Set whether we should suppress fs aborts. */
m_suppress_fs_auto_abort = suppress_fs_auto_abort;
/* Suppress fs auto abort, if we need to. */
auto disable_aborts = this->GetFsAutoAbortDisabler();
/* Mount the content. */
auto mount_name = impl::CreateUniqueMountName();
R_TRY(impl::MountContentMetaImpl(mount_name.str, content_path, attr));
/* Set our mount name. */
m_mount_name.emplace(mount_name.str);
/* Open the root directory. */
auto root_path = impl::GetRootDirectoryPath(mount_name);
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path.str, fs::OpenDirectoryMode_File));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
/* Loop directory reading until we find the entry we're looking for. */
while (true) {
/* Read one entry, and finish when we fail to read. */
fs::DirectoryEntry entry;
s64 num_read;
R_TRY(fs::ReadDirectory(std::addressof(num_read), std::addressof(entry), dir, 1));
if (num_read == 0) {
break;
}
/* If this is the content meta file, parse it. */
if (IsContentMetaFileName(entry.name)) {
/* Create the file path. */
impl::FilePathString file_path(root_path.str);
file_path.Append(entry.name);
/* Setup our file mapped. */
R_TRY(m_file_mapper.Initialize(file_path, FileMapperFile::OpenMode::Read));
/* Read the extended header. */
PackagedContentMetaHeader pkg_header;
R_TRY(m_file_mapper.Read(0, std::addressof(pkg_header), sizeof(pkg_header)));
/* Set our extended data offset. */
m_extended_data_offset = PackagedContentMetaReader(std::addressof(pkg_header), sizeof(pkg_header)).GetExtendedDataOffset();
const size_t accessible_size = m_file_mapper.GetFileSize() >= m_extended_data_offset;
R_UNLESS(accessible_size, ncm::ResultInvalidContentMetaFileSize());
/* Set our accessible size. */
this->SetSize(accessible_size);
R_SUCCEED();
}
}
R_THROW(ncm::ResultContentMetaNotFound());
}
void Finalize() {
/* Suppress fs auto abort, if we need to. */
auto disable_aborts = this->GetFsAutoAbortDisabler();
/* Finalize our implementation. */
CacheMapperBase::Finalize();
/* Finalize our file mapper. */
m_file_mapper.Finalize();
/* Finalize our mount name. */
if (m_mount_name.has_value()) {
fs::Unmount(m_mount_name.value().Get());
m_mount_name = util::nullopt;
}
}
protected:
virtual Result MapImpl(MappedMemory *out, Span<u8> data, size_t offset, size_t size) override final {
/* Suppress fs auto abort, if we need to. */
auto disable_aborts = this->GetFsAutoAbortDisabler();
/* Get the requested map offset/size. */
u8 *map_data = data.data();
size_t map_size = data.size_bytes();
/* Align the mapping, and ensure it remains valid. */
const size_t aligned_offset = util::AlignDown(offset, MappingAlignment);
R_UNLESS((offset + size) - aligned_offset <= map_size, ncm::ResultMapperInvalidArgument());
/* Read the data. */
const size_t map_offset = m_extended_data_offset + aligned_offset;
if (map_offset + map_size >= m_file_mapper.GetFileSize()) {
map_size = m_file_mapper.GetFileSize() - map_offset;
}
R_TRY(m_file_mapper.Read(map_offset, map_data, map_size));
/* Create the output mapped memory. */
*out = MappedMemory {
.id = 0,
.offset = aligned_offset,
.buffer = map_data,
.buffer_size = map_size,
};
R_SUCCEED();
}
virtual Result UnmapImpl(MappedMemory *) override final {
R_THROW(ncm::ResultMapperNotSupported());
}
virtual bool IsAccessibleSizeUpdatable() override final {
return false;
}
private:
util::optional<fs::ScopedAutoAbortDisabler> GetFsAutoAbortDisabler() {
/* Create an abort disabler, if we should disable aborts. */
util::optional<fs::ScopedAutoAbortDisabler> disable_abort{util::nullopt};
if (m_suppress_fs_auto_abort) {
disable_abort.emplace();
}
return disable_abort;
}
};
template<size_t N>
using MultiCacheReadonlyMapper = ExtendedDataMapperBase<MultiCacheReadonlyMapperBase<N>>;
}

View File

@@ -1,123 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
class FileMapperFile {
public:
enum class OpenMode {
Read,
ReadWrite,
ReadWriteAppend,
};
private:
const char *m_path;
OpenMode m_mode;
util::optional<fs::FileHandle> m_file;
size_t m_file_size;
size_t m_max_size;
public:
FileMapperFile() : m_file(util::nullopt) { /* ... */ }
~FileMapperFile() {
this->Finalize();
}
Result Initialize(const char *path, OpenMode mode) {
/* Set our path/mode. */
m_path = path;
m_mode = mode;
/* Ensure we're open. */
R_TRY(this->EnsureOpen());
/* Get the file size. */
s64 size;
R_TRY(fs::GetFileSize(std::addressof(size), m_file.value()));
/* Set our file size/loaded size. */
m_file_size = static_cast<size_t>(size);
m_max_size = static_cast<size_t>(size);
R_SUCCEED();
}
void Finalize() {
/* If we have a file, close (and flush) it. */
if (m_file.has_value()) {
if (m_mode != OpenMode::Read) {
R_ABORT_UNLESS(fs::FlushFile(m_file.value()));
}
fs::CloseFile(m_file.value());
m_file = util::nullopt;
}
}
size_t GetFileSize() const { return m_file_size; }
size_t GetMaxSize() const { return m_max_size; }
Result Read(size_t offset, void *dst, size_t size) {
/* Determine the end offset. */
const size_t end_offset = offset + size;
/* Unless we're allowed to append, we need to have a big enough file. */
if (m_mode != OpenMode::ReadWriteAppend) {
R_UNLESS(end_offset <= m_file_size, ncm::ResultMapperInvalidArgument());
}
/* Clear the output. */
std::memset(dst, 0, size);
/* Check that our offset is valid. */
R_UNLESS(offset <= m_file_size, ncm::ResultMapperInvalidArgument());
/* Ensure we're open. */
R_TRY(this->EnsureOpen());
/* Read what we can. */
const size_t read_size = (offset + size >= m_file_size) ? (m_file_size - offset) : size;
AMS_ASSERT(read_size >= size);
R_TRY(fs::ReadFile(m_file.value(), offset, dst, read_size));
/* Update our max size. */
m_max_size = std::max<size_t>(m_max_size, offset + read_size);
R_SUCCEED();
}
private:
Result EnsureOpen() {
/* If we've opened, we're done. */
R_SUCCEED_IF(m_file.has_value());
/* Open based on our mode. */
fs::FileHandle file;
switch (m_mode) {
case OpenMode::Read: R_TRY(fs::OpenFile(std::addressof(file), m_path, fs::OpenMode_Read)); break;
case OpenMode::ReadWrite: R_TRY(fs::OpenFile(std::addressof(file), m_path, fs::OpenMode_ReadWrite)); break;
case OpenMode::ReadWriteAppend: R_TRY(fs::OpenFile(std::addressof(file), m_path, fs::OpenMode_All)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Set our file. */
m_file = file;
R_SUCCEED();
}
};
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_fs_utils.hpp"
namespace ams::ncm::impl {
namespace {
constinit std::atomic<u32> g_mount_name_count;
}
bool PathView::HasPrefix(util::string_view prefix) const {
return m_path.compare(0, prefix.length(), prefix) == 0;
}
bool PathView::HasSuffix(util::string_view suffix) const {
return m_path.compare(m_path.length() - suffix.length(), suffix.length(), suffix) == 0;
}
util::string_view PathView::GetFileName() const {
auto pos = m_path.find_last_of("/");
return pos != util::string_view::npos ? m_path.substr(pos + 1) : m_path;
}
MountName CreateUniqueMountName() {
MountName name = {};
util::SNPrintf(name.str, sizeof(name.str), "@ncm%08x", g_mount_name_count.fetch_add(1));
return name;
}
RootDirectoryPath GetRootDirectoryPath(const MountName &mount_name) {
RootDirectoryPath path = {};
util::SNPrintf(path.str, sizeof(path.str), "%s:/", mount_name.str);
return path;
}
Result CopyFile(const char *dst_path, const char *src_path) {
fs::FileHandle src_file, dst_file;
/* Open the source file and get its size. */
R_TRY(fs::OpenFile(std::addressof(src_file), src_path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(src_file); };
s64 file_size;
R_TRY(fs::GetFileSize(std::addressof(file_size), src_file));
/* Create the destination file. */
R_TRY(fs::CreateFile(dst_path, file_size));
/* Open the destination file. */
R_TRY(fs::OpenFile(std::addressof(dst_file), dst_path, fs::OpenMode_Write));
ON_SCOPE_EXIT { fs::CloseFile(dst_file); };
/* Allocate a buffer with which to copy. */
constexpr size_t BufferSize = 4_KB;
AutoBuffer buffer;
R_TRY(buffer.Initialize(BufferSize));
/* Repeatedly read until we've copied all the data. */
s64 offset = 0;
while (offset < file_size) {
const size_t read_size = std::min(static_cast<size_t>(file_size - offset), buffer.GetSize());
R_TRY(fs::ReadFile(src_file, offset, buffer.Get(), read_size));
R_TRY(fs::WriteFile(dst_file, offset, buffer.Get(), read_size, fs::WriteOption::None));
offset += read_size;
}
/* Flush the destination file. */
R_TRY(fs::FlushFile(dst_file));
R_SUCCEED();
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm::impl {
using FilePathString = kvdb::BoundedString<64>;
Result CopyFile(const char *dst_path, const char *src_path);
class PathView {
private:
util::string_view m_path;
public:
PathView(util::string_view p) : m_path(p) { /* ...*/ }
bool HasPrefix(util::string_view prefix) const;
bool HasSuffix(util::string_view suffix) const;
util::string_view GetFileName() const;
};
struct MountName {
char str[fs::MountNameLengthMax + 1];
};
using MountNameString = kvdb::BoundedString<sizeof(MountName{}.str)>;
struct RootDirectoryPath {
char str[fs::MountNameLengthMax + 3]; /* mount name + :/ */
};
MountName CreateUniqueMountName();
RootDirectoryPath GetRootDirectoryPath(const MountName &mount_name);
Result MountContentMetaImpl(const char *mount_name, const char *path, fs::ContentAttributes attr);
}

View File

@@ -1,250 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_host_content_storage_impl.hpp"
namespace ams::ncm {
Result HostContentStorageImpl::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
AMS_UNUSED(out);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) {
AMS_UNUSED(placeholder_id, content_id, size);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
AMS_UNUSED(placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) {
AMS_UNUSED(placeholder_id, offset, data);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
AMS_UNUSED(placeholder_id, content_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::Delete(ContentId content_id) {
AMS_UNUSED(content_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::Has(sf::Out<bool> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Attempt to locate the content. */
Path path;
R_TRY_CATCH(m_registered_content->GetPath(std::addressof(path), content_id)) {
/* The content is absent, this is fine. */
R_CATCH(ncm::ResultContentNotFound) {
out.SetValue(false);
R_SUCCEED();
}
} R_END_TRY_CATCH;
out.SetValue(true);
R_SUCCEED();
}
Result HostContentStorageImpl::GetPath(sf::Out<Path> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
R_RETURN(m_registered_content->GetPath(out.GetPointer(), content_id));
}
Result HostContentStorageImpl::GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::CleanupAllPlaceHolder() {
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
AMS_UNUSED(out_count, out_buf);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetContentCount(sf::Out<s32> out_count) {
AMS_UNUSED(out_count);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 offset) {
AMS_UNUSED(out_count, out_buf, offset);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) {
AMS_UNUSED(out_size, content_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result HostContentStorageImpl::DisableForcibly() {
m_disabled = true;
R_SUCCEED();
}
Result HostContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
AMS_UNUSED(placeholder_id, old_content_id, new_content_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) {
AMS_UNUSED(placeholder_id, size);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) {
AMS_UNUSED(buf, content_id, offset);
R_THROW(ncm::ResultInvalidOperation());
}
Result HostContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
AMS_UNUSED(out_rights_id, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
AMS_UNUSED(out_rights_id, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) {
AMS_UNUSED(out_rights_id, placeholder_id, attr);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) {
/* Obtain the regular rights id for the content id. */
ncm::RightsId rights_id;
R_TRY(this->GetRightsIdFromContentIdDeprecated2(std::addressof(rights_id), content_id));
/* Output the fs rights id. */
out_rights_id.SetValue(rights_id.id);
R_SUCCEED();
}
Result HostContentStorageImpl::GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_RETURN(this->GetRightsIdFromContentId(out_rights_id, content_id, fs::ContentAttributes_None));
}
Result HostContentStorageImpl::GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Get the content path. */
Path path;
R_TRY(m_registered_content->GetPath(std::addressof(path), content_id));
/* Acquire the rights id for the content. */
RightsId rights_id;
R_TRY_CATCH(GetRightsId(std::addressof(rights_id), path, attr)) {
/* The content is absent, output a blank rights id. */
R_CATCH(fs::ResultTargetNotFound) {
out_rights_id.SetValue({});
R_SUCCEED();
}
} R_END_TRY_CATCH;
/* Output the rights id. */
out_rights_id.SetValue(rights_id);
R_SUCCEED();
}
Result HostContentStorageImpl::WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) {
AMS_UNUSED(content_id, offset, data);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetFreeSpaceSize(sf::Out<s64> out_size) {
out_size.SetValue(0);
R_SUCCEED();
}
Result HostContentStorageImpl::GetTotalSpaceSize(sf::Out<s64> out_size) {
out_size.SetValue(0);
R_SUCCEED();
}
Result HostContentStorageImpl::FlushPlaceHolder() {
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::RepairInvalidFileAttribute() {
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCacheDeprecated(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) {
AMS_UNUSED(out_rights_id, placeholder_id, cache_content_id);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) {
AMS_UNUSED(out_rights_id, placeholder_id, cache_content_id, attr);
R_THROW(ncm::ResultNotSupported());
}
Result HostContentStorageImpl::RegisterPath(const ContentId &content_id, const Path &path) {
AMS_ABORT_UNLESS(spl::IsDevelopment());
R_RETURN(m_registered_content->RegisterPath(content_id, path));
}
Result HostContentStorageImpl::ClearRegisteredPath() {
AMS_ABORT_UNLESS(spl::IsDevelopment());
m_registered_content->ClearPaths();
R_SUCCEED();
}
Result HostContentStorageImpl::GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Get the content path. */
Path path;
R_TRY(m_registered_content->GetPath(std::addressof(path), content_id));
/* Check for correct extension. */
const auto path_len = std::strlen(path.str);
const char *extension = path.str + path_len - 1;
if (*extension == '/') {
--extension;
}
R_UNLESS(path_len >= 4 && std::memcmp(extension - 4, ".ncd", 4) == 0, ncm::ResultInvalidContentMetaDirectory());
/* Obtain the program id for the content. */
ncm::ProgramId program_id;
R_TRY(fs::GetProgramId(std::addressof(program_id), path.str, attr));
out.SetValue(program_id);
R_SUCCEED();
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
class HostContentStorageImpl {
protected:
RegisteredHostContent *m_registered_content;
bool m_disabled;
protected:
/* Helpers. */
Result EnsureEnabled() const {
R_UNLESS(!m_disabled, ncm::ResultInvalidContentStorage());
R_SUCCEED();
}
static Result GetRightsId(ncm::RightsId *out_rights_id, const Path &path, fs::ContentAttributes attr) {
if (hos::GetVersion() >= hos::Version_3_0_0) {
R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), std::addressof(out_rights_id->key_generation), path.str, attr));
} else {
R_TRY(fs::GetRightsId(std::addressof(out_rights_id->id), path.str, attr));
out_rights_id->key_generation = 0;
}
R_SUCCEED();
}
public:
HostContentStorageImpl(RegisteredHostContent *registered_content) : m_registered_content(registered_content), m_disabled(false) { /* ... */ }
public:
/* Actual commands. */
Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out);
Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size);
Result DeletePlaceHolder(PlaceHolderId placeholder_id);
Result HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id);
Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data);
Result Register(PlaceHolderId placeholder_id, ContentId content_id);
Result Delete(ContentId content_id);
Result Has(sf::Out<bool> out, ContentId content_id);
Result GetPath(sf::Out<Path> out, ContentId content_id);
Result GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id);
Result CleanupAllPlaceHolder();
Result ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf);
Result GetContentCount(sf::Out<s32> out_count);
Result ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 start_offset);
Result GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id);
Result DisableForcibly();
Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id);
Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size);
Result ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset);
Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id);
Result GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id);
Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr);
Result GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id);
Result GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id);
Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr);
Result WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data);
Result GetFreeSpaceSize(sf::Out<s64> out_size);
Result GetTotalSpaceSize(sf::Out<s64> out_size);
Result FlushPlaceHolder();
Result GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id);
Result RepairInvalidFileAttribute();
Result GetRightsIdFromPlaceHolderIdWithCacheDeprecated(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id);
Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr);
Result RegisterPath(const ContentId &content_id, const Path &path);
Result ClearRegisteredPath();
Result GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr);
/* 16.0.0 Alignment change hacks. */
Result CreatePlaceHolder_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id, s64 size) { R_RETURN(this->CreatePlaceHolder(placeholder_id, content_id, size)); }
Result Register_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id) { R_RETURN(this->Register(placeholder_id, content_id)); }
Result RevertToPlaceHolder_AtmosphereAlignmentFix(ncm::ContentId old_content_id, ncm::ContentId new_content_id, ncm::PlaceHolderId placeholder_id) { R_RETURN(this->RevertToPlaceHolder(placeholder_id, old_content_id, new_content_id)); }
Result GetRightsIdFromPlaceHolderIdWithCacheDeprecated(sf::Out<ncm::RightsId> out_rights_id, ContentId cache_content_id, PlaceHolderId placeholder_id) { R_RETURN(this->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id, fs::ContentAttributes_None)); }
};
static_assert(ncm::IsIContentStorage<HostContentStorageImpl>);
}

View File

@@ -1,397 +0,0 @@
/*
* Copyright (c) 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 {
using BoundedPath = kvdb::BoundedString<64>;
constexpr inline bool Includes(const ContentMetaKey *keys, s32 count, const ContentMetaKey &key) {
for (s32 i = 0; i < count; i++) {
if (keys[i] == key) {
return true;
}
}
return false;
}
}
Result InstallTaskDataBase::Get(InstallContentMeta *out, s32 index) {
/* Determine the data size. */
size_t data_size;
R_TRY(this->GetSize(std::addressof(data_size), index));
/* Create a buffer and read data into it. */
std::unique_ptr<char[]> buffer(new (std::nothrow) char[data_size]);
R_UNLESS(buffer != nullptr, ncm::ResultAllocationFailed());
R_TRY(this->Get(index, buffer.get(), data_size));
/* Output the buffer and size. */
out->data = std::move(buffer);
out->size = data_size;
R_SUCCEED();
}
Result InstallTaskDataBase::Update(const InstallContentMeta &content_meta, s32 index) {
R_RETURN(this->Update(index, content_meta.data.get(), content_meta.size));
}
Result InstallTaskDataBase::Has(bool *out, u64 id) {
s32 count;
R_TRY(this->Count(std::addressof(count)));
/* Iterate over each entry. */
for (s32 i = 0; i < count; i++) {
InstallContentMeta content_meta;
R_TRY(this->Get(std::addressof(content_meta), i));
/* If the id matches we are successful. */
if (content_meta.GetReader().GetKey().id == id) {
*out = true;
R_SUCCEED();
}
}
/* We didn't find the value. */
*out = false;
R_SUCCEED();
}
Result MemoryInstallTaskData::GetProgress(InstallProgress *out_progress) {
/* Initialize install progress. */
InstallProgress install_progress = {
.state = m_state,
};
install_progress.SetLastResult(m_last_result);
/* Only states after prepared are allowed. */
if (m_state != InstallProgressState::NotPrepared && m_state != InstallProgressState::DataPrepared) {
for (auto &data_holder : m_data_list) {
const InstallContentMetaReader reader = data_holder.GetReader();
/* Sum the sizes from this entry's content infos. */
for (size_t i = 0; i < reader.GetContentCount(); i++) {
const InstallContentInfo *content_info = reader.GetContentInfo(i);
install_progress.installed_size += content_info->GetSize();
install_progress.total_size += content_info->GetSizeWritten();
}
}
}
*out_progress = install_progress;
R_SUCCEED();
}
Result MemoryInstallTaskData::GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out_info) {
*out_info = m_system_update_task_apply_info;
R_SUCCEED();
}
Result MemoryInstallTaskData::SetState(InstallProgressState state) {
m_state = state;
R_SUCCEED();
}
Result MemoryInstallTaskData::SetLastResult(Result result) {
m_last_result = result;
R_SUCCEED();
}
Result MemoryInstallTaskData::SetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo info) {
m_system_update_task_apply_info = info;
R_SUCCEED();
}
Result MemoryInstallTaskData::Push(const void *data, size_t size) {
/* Allocate a new data holder. */
auto holder = std::unique_ptr<DataHolder>(new (std::nothrow) DataHolder());
R_UNLESS(holder != nullptr, ncm::ResultAllocationFailed());
/* Allocate memory for the content meta data. */
holder->data = std::unique_ptr<char[]>(new (std::nothrow) char[size]);
R_UNLESS(holder->data != nullptr, ncm::ResultAllocationFailed());
holder->size = size;
/* Copy data to the data holder. */
std::memcpy(holder->data.get(), data, size);
/* Put the data holder into the data list. */
m_data_list.push_back(*holder);
/* Relinquish control over the memory allocated to the data holder. */
holder.release();
R_SUCCEED();
}
Result MemoryInstallTaskData::Count(s32 *out) {
*out = m_data_list.size();
R_SUCCEED();
}
Result MemoryInstallTaskData::GetSize(size_t *out_size, s32 index) {
/* Find the correct entry in the list. */
s32 count = 0;
for (auto &data_holder : m_data_list) {
if (index == count++) {
*out_size = data_holder.size;
R_SUCCEED();
}
}
/* Out of bounds indexing is an unrecoverable error. */
AMS_ABORT();
}
Result MemoryInstallTaskData::Get(s32 index, void *out, size_t out_size) {
/* Find the correct entry in the list. */
s32 count = 0;
for (auto &data_holder : m_data_list) {
if (index == count++) {
R_UNLESS(out_size >= data_holder.size, ncm::ResultBufferInsufficient());
std::memcpy(out, data_holder.data.get(), data_holder.size);
R_SUCCEED();
}
}
/* Out of bounds indexing is an unrecoverable error. */
AMS_ABORT();
}
Result MemoryInstallTaskData::Update(s32 index, const void *data, size_t data_size) {
/* Find the correct entry in the list. */
s32 count = 0;
for (auto &data_holder : m_data_list) {
if (index == count++) {
R_UNLESS(data_size == data_holder.size, ncm::ResultBufferInsufficient());
std::memcpy(data_holder.data.get(), data, data_size);
R_SUCCEED();
}
}
/* Out of bounds indexing is an unrecoverable error. */
AMS_ABORT();
}
Result MemoryInstallTaskData::Delete(const ContentMetaKey *keys, s32 num_keys) {
/* Iterate over keys. */
for (s32 i = 0; i < num_keys; i++) {
const auto &key = keys[i];
/* Find and remove matching data from the list. */
for (auto &data_holder : m_data_list) {
if (key == data_holder.GetReader().GetKey()) {
m_data_list.erase(m_data_list.iterator_to(data_holder));
delete std::addressof(data_holder);
break;
}
}
}
R_SUCCEED();
}
Result MemoryInstallTaskData::Cleanup() {
while (!m_data_list.empty()) {
auto *data_holder = std::addressof(m_data_list.front());
m_data_list.pop_front();
delete data_holder;
}
R_SUCCEED();
}
Result FileInstallTaskData::Create(const char *path, s32 max_entries) {
/* Create the file. */
R_TRY(fs::CreateFile(path, 0));
/* Open the file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Create an initial header and write it to the file. */
const Header header = MakeInitialHeader(max_entries);
R_RETURN(fs::WriteFile(file, 0, std::addressof(header), sizeof(Header), fs::WriteOption::Flush));
}
Result FileInstallTaskData::Initialize(const char *path) {
std::strncpy(m_path, path, sizeof(m_path));
m_path[sizeof(m_path) - 1] = '\x00';
R_RETURN(this->Read(std::addressof(m_header), sizeof(Header), 0));
}
Result FileInstallTaskData::GetProgress(InstallProgress *out_progress) {
/* Initialize install progress. */
InstallProgress install_progress = {
.state = m_header.progress_state,
};
install_progress.SetLastResult(m_header.last_result);
/* Only states after prepared are allowed. */
if (m_header.progress_state != InstallProgressState::NotPrepared && m_header.progress_state != InstallProgressState::DataPrepared) {
for (size_t i = 0; i < m_header.count; i++) {
/* Obtain the content meta for this entry. */
InstallContentMeta content_meta;
R_TRY(InstallTaskDataBase::Get(std::addressof(content_meta), i));
const InstallContentMetaReader reader = content_meta.GetReader();
/* Sum the sizes from this entry's content infos. */
for (size_t j = 0; j < reader.GetContentCount(); j++) {
const InstallContentInfo *content_info = reader.GetContentInfo(j);
install_progress.installed_size += content_info->GetSize();
install_progress.total_size += content_info->GetSizeWritten();
}
}
}
*out_progress = install_progress;
R_SUCCEED();
}
Result FileInstallTaskData::GetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo *out_info) {
*out_info = m_header.system_update_task_apply_info;
R_SUCCEED();
}
Result FileInstallTaskData::SetState(InstallProgressState state) {
m_header.progress_state = state;
R_RETURN(this->WriteHeader());
}
Result FileInstallTaskData::SetLastResult(Result result) {
m_header.last_result = result;
R_RETURN(this->WriteHeader());
}
Result FileInstallTaskData::SetSystemUpdateTaskApplyInfo(SystemUpdateTaskApplyInfo info) {
m_header.system_update_task_apply_info = info;
R_RETURN(this->WriteHeader());
}
Result FileInstallTaskData::Push(const void *data, size_t data_size) {
R_UNLESS(m_header.count < m_header.max_entries, ncm::ResultBufferInsufficient());
/* Create a new entry info. Data of the given size will be stored at the end of the file. */
const EntryInfo entry_info = { m_header.last_data_offset, static_cast<s64>(data_size) };
/* Write the new entry info. */
R_TRY(this->Write(std::addressof(entry_info), sizeof(EntryInfo), GetEntryInfoOffset(m_header.count)));
/* Write the data to the offset in the entry info. */
R_TRY(this->Write(data, data_size, entry_info.offset));
/* Update the header for the new entry. */
m_header.last_data_offset += data_size;
m_header.count++;
/* Write the updated header. */
R_RETURN(this->WriteHeader());
}
Result FileInstallTaskData::Count(s32 *out) {
*out = m_header.count;
R_SUCCEED();
}
Result FileInstallTaskData::GetSize(size_t *out_size, s32 index) {
EntryInfo entry_info;
R_TRY(this->GetEntryInfo(std::addressof(entry_info), index));
*out_size = entry_info.size;
R_SUCCEED();
}
Result FileInstallTaskData::Get(s32 index, void *out, size_t out_size) {
/* Obtain the entry info. */
EntryInfo entry_info;
R_TRY(this->GetEntryInfo(std::addressof(entry_info), index));
/* Read the entry to the output buffer. */
R_UNLESS(entry_info.size <= static_cast<s64>(out_size), ncm::ResultBufferInsufficient());
R_RETURN(this->Read(out, out_size, entry_info.offset));
}
Result FileInstallTaskData::Update(s32 index, const void *data, size_t data_size) {
/* Obtain the entry info. */
EntryInfo entry_info;
R_TRY(this->GetEntryInfo(std::addressof(entry_info), index));
/* Data size must match existing data size. */
R_UNLESS(entry_info.size == static_cast<s64>(data_size), ncm::ResultBufferInsufficient());
R_RETURN(this->Write(data, data_size, entry_info.offset));
}
Result FileInstallTaskData::Delete(const ContentMetaKey *keys, s32 num_keys) {
/* Create the path for the temporary data. */
BoundedPath tmp_path(m_path);
tmp_path.Append(".tmp");
/* Create a new temporary install task data. */
FileInstallTaskData install_task_data;
R_TRY(FileInstallTaskData::Create(tmp_path, m_header.max_entries));
R_TRY(install_task_data.Initialize(tmp_path));
/* Get the number of entries. */
s32 count;
R_TRY(this->Count(std::addressof(count)));
/* Copy entries that are not excluded to the new install task data. */
for (s32 i = 0; i < count; i++) {
InstallContentMeta content_meta;
R_TRY(InstallTaskDataBase::Get(std::addressof(content_meta), i));
/* Check if entry is excluded. If not, push it to our new install task data. */
if (Includes(keys, num_keys, content_meta.GetReader().GetKey())) {
continue;
}
/* NOTE: Nintendo doesn't check that this operation succeeds. */
install_task_data.Push(content_meta.data.get(), content_meta.size);
}
/* Change from our current data to the new data. */
m_header = install_task_data.m_header;
R_TRY(fs::DeleteFile(m_path));
R_RETURN(fs::RenameFile(tmp_path, m_path));
}
Result FileInstallTaskData::Cleanup() {
m_header = MakeInitialHeader(m_header.max_entries);
R_RETURN(this->WriteHeader());
}
Result FileInstallTaskData::GetEntryInfo(EntryInfo *out_entry_info, s32 index) {
AMS_ABORT_UNLESS(static_cast<u32>(index) < m_header.count);
R_RETURN(this->Read(out_entry_info, sizeof(EntryInfo), GetEntryInfoOffset(index)));
}
Result FileInstallTaskData::Write(const void *data, size_t size, s64 offset) {
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), m_path, fs::OpenMode_Write | fs::OpenMode_AllowAppend));
ON_SCOPE_EXIT { fs::CloseFile(file); };
R_RETURN(fs::WriteFile(file, offset, data, size, fs::WriteOption::Flush));
}
Result FileInstallTaskData::Read(void *out, size_t out_size, s64 offset) {
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), m_path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
R_RETURN(fs::ReadFile(file, offset, out, out_size));
}
Result FileInstallTaskData::WriteHeader() {
R_RETURN(this->Write(std::addressof(m_header), sizeof(Header), 0));
}
}

View File

@@ -1,490 +0,0 @@
/*
* Copyright (c) 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 IntegratedContentMetaDatabaseImpl::Set(const ContentMetaKey &key, const sf::InBuffer &value) {
AMS_UNUSED(key, value);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentMetaDatabaseImpl::Get(sf::Out<u64> out_size, const ContentMetaKey &key, const sf::OutBuffer &out_value) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->Get(out_size, key, out_value));
}));
}
Result IntegratedContentMetaDatabaseImpl::Remove(const ContentMetaKey &key) {
AMS_UNUSED(key);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentMetaDatabaseImpl::GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetContentIdByType(out_content_id, key, type));
}));
}
Result IntegratedContentMetaDatabaseImpl::ListContentInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentInfo> &out_info, const ContentMetaKey &key, s32 offset) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), key));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentMetaNotFound());
/* If it does, list the content infos. */
R_RETURN(data.interface->ListContentInfo(out_entries_written, out_info, key, offset));
}));
}
Result IntegratedContentMetaDatabaseImpl::List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* List on all databases. */
s32 entries_total = 0;
s32 entries_written = 0;
R_TRY(m_list.ForAll([&](const auto &data) {
/* List on the current database. */
s32 cur_total;
s32 cur_written;
R_TRY(data.interface->List(std::addressof(cur_total), std::addressof(cur_written), sf::OutArray<ContentMetaKey>{out_info.GetPointer() + entries_written, out_info.GetSize() - entries_written}, meta_type, application_id, min, max, install_type));
/* Add to the totals. */
entries_total += cur_total;
entries_written += cur_written;
R_SUCCEED();
}));
/* Set output. */
*out_entries_total = entries_total;
*out_entries_written = entries_written;
R_SUCCEED();
}
Result IntegratedContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetLatestContentMetaKey(out_key, id));
}));
}
Result IntegratedContentMetaDatabaseImpl::ListApplication(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ApplicationContentMetaKey> &out_keys, ContentMetaType meta_type) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* List on all databases. */
s32 entries_total = 0;
s32 entries_written = 0;
R_TRY(m_list.ForAll([&](const auto &data) {
/* List on the current database. */
s32 cur_total;
s32 cur_written;
R_TRY(data.interface->ListApplication(std::addressof(cur_total), std::addressof(cur_written), sf::OutArray<ApplicationContentMetaKey>{out_keys.GetPointer() + entries_written, out_keys.GetSize() - entries_written}, meta_type));
/* Add to the totals. */
entries_total += cur_total;
entries_written += cur_written;
R_SUCCEED();
}));
/* Set output. */
*out_entries_total = entries_total;
*out_entries_written = entries_written;
R_SUCCEED();
}
Result IntegratedContentMetaDatabaseImpl::Has(sf::Out<bool> out, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* If we don't locate the content meta, set the output to false. */
*out = false;
ON_RESULT_INCLUDED(ncm::ResultContentMetaNotFound) { *out = false; };
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
R_TRY(data.interface->Has(out, key));
/* If it doesn't, continue on. */
R_UNLESS(*out, ncm::ResultContentMetaNotFound());
/* If it does, we're done looking. */
R_SUCCEED();
}));
}
Result IntegratedContentMetaDatabaseImpl::HasAll(sf::Out<bool> out, const sf::InArray<ContentMetaKey> &keys) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
*out = false;
/* Check if keys are present. */
for (size_t i = 0; i < keys.GetSize(); i++) {
/* Check if we have the current key. */
bool has;
R_TRY(this->Has(std::addressof(has), keys[i]));
/* If we don't, then we can early return because we don't have all. */
R_SUCCEED_IF(!has);
}
*out = true;
R_SUCCEED();
}
Result IntegratedContentMetaDatabaseImpl::GetSize(sf::Out<u64> out_size, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetSize(out_size, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetRequiredSystemVersion(sf::Out<u32> out_version, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetRequiredSystemVersion(out_version, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetPatchContentMetaId(sf::Out<u64> out_patch_id, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetPatchContentMetaId(out_patch_id, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::DisableForcibly() {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
m_disabled = true;
R_SUCCEED();
}
Result IntegratedContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) {
AMS_UNUSED(out_orphaned, content_ids);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentMetaDatabaseImpl::Commit() {
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentMetaDatabaseImpl::HasContent(sf::Out<bool> out, const ContentMetaKey &key, const ContentId &content_id) {
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
/* NOTE: Nintendo bug: Nintendo calls this->Has(), which is likely a copy paste error from ::HasAll... */
bool has;
R_TRY(data.interface->Has(std::addressof(has), key));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentMetaNotFound());
/* If it does, list the content infos. */
R_RETURN(data.interface->HasContent(out, key, content_id));
}));
}
Result IntegratedContentMetaDatabaseImpl::ListContentMetaInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaInfo> &out_meta_info, const ContentMetaKey &key, s32 offset) {
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), key));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentMetaNotFound());
/* If it does, list the content infos. */
R_RETURN(data.interface->ListContentMetaInfo(out_entries_written, out_meta_info, key, offset));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetAttributes(sf::Out<u8> out_attributes, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetAttributes(out_attributes, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetRequiredApplicationVersion(sf::Out<u32> out_version, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetRequiredApplicationVersion(out_version, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetContentIdByTypeAndIdOffset(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetContentIdByTypeAndIdOffset(out_content_id, key, type, id_offset));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetCount(sf::Out<u32> out_count) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* List on all databases. */
u32 total = 0;
R_TRY(m_list.ForAll([&](const auto &data) {
/* List on the current database. */
u32 cur;
R_TRY(data.interface->GetCount(std::addressof(cur)));
/* Add to the totals. */
total += cur;
R_SUCCEED();
}));
/* Set output. */
*out_count = total;
R_SUCCEED();
}
Result IntegratedContentMetaDatabaseImpl::GetOwnerApplicationId(sf::Out<ApplicationId> out_id, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetOwnerApplicationId(out_id, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetContentAccessibilities(sf::Out<u8> out_accessibilities, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetContentAccessibilities(out_accessibilities, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetContentInfoByType(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetContentInfoByType(out_content_info, key, type));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetContentInfoByTypeAndIdOffset(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type, u8 id_offset) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetContentInfoByTypeAndIdOffset(out_content_info, key, type, id_offset));
}));
}
Result IntegratedContentMetaDatabaseImpl::GetPlatform(sf::Out<ncm::ContentMetaPlatform> out, const ContentMetaKey &key) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentMetaNotFound());
/* Check each interface in turn. */
R_RETURN(m_list.TryEach([&](const auto &data) {
/* Try the current interface. */
R_RETURN(data.interface->GetPlatform(out, key));
}));
}
Result IntegratedContentMetaDatabaseImpl::HasAttributes(sf::Out<u8> out, u8 attr_mask) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Test whether we have the attributes on all databases. */
u8 combined_attributes = 0;
R_TRY(m_list.ForAll([&](const auto &data) {
/* Check the current database. */
u8 cur_attr = 0;
R_TRY(data.interface->HasAttributes(std::addressof(cur_attr), attr_mask));
/* Accumulate the attributes found in the current interface. */
combined_attributes |= cur_attr;
R_SUCCEED();
}));
/* Set the output. */
*out = combined_attributes;
R_SUCCEED();
}
}

View File

@@ -1,356 +0,0 @@
/*
* Copyright (c) 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 IntegratedContentStorageImpl::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
AMS_UNUSED(out);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) {
AMS_UNUSED(placeholder_id, content_id, size);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
AMS_UNUSED(placeholder_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(placeholder_id);
/* Integrated storages cannot have placeholders. */
*out = false;
R_SUCCEED();
}
Result IntegratedContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) {
AMS_UNUSED(placeholder_id, offset, data);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
AMS_UNUSED(placeholder_id, content_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::Delete(ContentId content_id) {
AMS_UNUSED(content_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::Has(sf::Out<bool> out, ContentId content_id) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* If we don't locate the content, set the output to false. */
*out = false;
ON_RESULT_INCLUDED(ncm::ResultContentNotFound) { *out = false; };
/* Check each interface in turn. */
R_TRY(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
R_TRY(data.interface->Has(out, content_id));
/* If it doesn't, continue on. */
R_UNLESS(*out, ncm::ResultContentNotFound());
/* If it does, we're done looking. */
R_SUCCEED();
}));
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetPath(sf::Out<Path> out, ContentId content_id) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentNotFound());
/* Check each interface in turn. */
R_TRY(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), content_id));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentNotFound());
/* If it does, get the path. */
R_RETURN(data.interface->GetPath(out, content_id));
}));
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::CleanupAllPlaceHolder() {
R_SUCCEED();
}
Result IntegratedContentStorageImpl::ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
AMS_UNUSED(out_buf);
*out_count = 0;
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetContentCount(sf::Out<s32> out_count) {
*out_count = 0;
R_SUCCEED();
}
Result IntegratedContentStorageImpl::ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 offset) {
AMS_UNUSED(out_buf, offset);
*out_count = 0;
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentNotFound());
/* Check each interface in turn. */
R_TRY(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), content_id));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentNotFound());
/* If it does, get the size. */
R_RETURN(data.interface->GetSizeFromContentId(out_size, content_id));
}));
R_SUCCEED();
}
Result IntegratedContentStorageImpl::DisableForcibly() {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
m_disabled = true;
R_SUCCEED();
}
Result IntegratedContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
AMS_UNUSED(placeholder_id, old_content_id, new_content_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) {
AMS_UNUSED(placeholder_id, size);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentNotFound());
/* Check each interface in turn. */
R_TRY(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), content_id));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentNotFound());
/* If it does, read the file. */
R_RETURN(data.interface->ReadContentIdFile(buf, content_id, offset));
}));
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
/* Obtain the regular rights id for the placeholder id. */
ncm::RightsId rights_id;
R_TRY(this->GetRightsIdFromPlaceHolderIdDeprecated2(std::addressof(rights_id), placeholder_id));
/* Output the fs rights id. */
out_rights_id.SetValue(rights_id.id);
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
R_RETURN(this->GetRightsIdFromPlaceHolderId(out_rights_id, placeholder_id, fs::ContentAttributes_None));
}
Result IntegratedContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) {
AMS_UNUSED(out_rights_id, placeholder_id, attr);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) {
/* Obtain the regular rights id for the content id. */
ncm::RightsId rights_id;
R_TRY(this->GetRightsIdFromContentIdDeprecated2(std::addressof(rights_id), content_id));
/* Output the fs rights id. */
out_rights_id.SetValue(rights_id.id);
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_RETURN(this->GetRightsIdFromContentId(out_rights_id, content_id, fs::ContentAttributes_None));
}
Result IntegratedContentStorageImpl::GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentNotFound());
/* Check each interface in turn. */
R_TRY(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), content_id));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentNotFound());
/* If it does, read the file. */
R_RETURN(data.interface->GetRightsIdFromContentId(out_rights_id, content_id, attr));
}));
R_SUCCEED();
}
Result IntegratedContentStorageImpl::WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) {
AMS_UNUSED(content_id, offset, data);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::GetFreeSpaceSize(sf::Out<s64> out_size) {
out_size.SetValue(0);
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetTotalSpaceSize(sf::Out<s64> out_size) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Determine the total size. */
s64 total_size = 0;
R_TRY(m_list.ForAll([&](const auto &data) {
/* Get the current size. */
s64 cur_size;
R_TRY(data.interface->GetTotalSpaceSize(std::addressof(cur_size)));
/* Add to the total. */
total_size += cur_size;
R_SUCCEED();
}));
*out_size = total_size;
R_SUCCEED();
}
Result IntegratedContentStorageImpl::FlushPlaceHolder() {
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::RepairInvalidFileAttribute() {
R_SUCCEED();
}
Result IntegratedContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) {
AMS_UNUSED(out_rights_id, placeholder_id, cache_content_id, attr);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::RegisterPath(const ContentId &content_id, const Path &path) {
AMS_UNUSED(content_id, path);
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::ClearRegisteredPath() {
R_THROW(ncm::ResultInvalidOperation());
}
Result IntegratedContentStorageImpl::GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Check that we're enabled. */
R_TRY(this->EnsureEnabled());
/* Check that our list has interfaces to check. */
R_UNLESS(m_list.GetCount() > 0, ncm::ResultContentNotFound());
/* Check each interface in turn. */
R_TRY(m_list.TryEach([&](const auto &data) {
/* Check if the current interface has it. */
bool has;
R_TRY(data.interface->Has(std::addressof(has), content_id));
/* If it doesn't, continue on. */
R_UNLESS(has, ncm::ResultContentNotFound());
/* If it does, read the file. */
R_RETURN(data.interface->GetProgramId(out, content_id, attr));
}));
R_SUCCEED();
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright (c) 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 {
void MakeContentName(PathString *out, ContentId id) {
out->AssignFormat("%s.nca", GetContentIdString(id).data);
}
void MakePlaceHolderName(PathString *out, PlaceHolderId id) {
auto &bytes = id.uuid.data;
char tmp[3];
/* Create a hex string from bytes. */
for (size_t i = 0; i < sizeof(bytes); i++) {
util::SNPrintf(tmp, util::size(tmp), "%02x", bytes[i]);
out->Append(tmp);
}
/* Append file extension. */
out->Append(".nca");
}
u16 Get16BitSha256HashPrefix(ContentId id) {
u8 hash[crypto::Sha256Generator::HashSize];
crypto::GenerateSha256(hash, sizeof(hash), std::addressof(id), sizeof(id));
return static_cast<u16>(hash[0]) | (static_cast<u16>(hash[1]) << 8);
}
u8 Get8BitSha256HashPrefix(ContentId id) {
u8 hash[crypto::Sha256Generator::HashSize];
crypto::GenerateSha256(hash, sizeof(hash), std::addressof(id), sizeof(id));
return hash[0];
}
u8 Get8BitSha256HashPrefix(PlaceHolderId id) {
u8 hash[crypto::Sha256Generator::HashSize];
crypto::GenerateSha256(hash, sizeof(hash), std::addressof(id), sizeof(id));
return hash[0];
}
}
void MakeFlatContentFilePath(PathString *out, ContentId content_id, const char *root_path) {
/* Create the content name from the content id. */
PathString content_name;
MakeContentName(std::addressof(content_name), content_id);
/* Format the output path. */
out->AssignFormat("%s/%s", root_path, content_name.Get());
}
void MakeSha256HierarchicalContentFilePath_ForFat4KCluster(PathString *out, ContentId content_id, const char *root_path) {
/* Hash the content id. */
const u16 hash = Get16BitSha256HashPrefix(content_id);
const u32 hash_upper = (hash >> 10) & 0x3f;
const u32 hash_lower = (hash >> 4) & 0x3f;
/* Create the content name from the content id. */
PathString content_name;
MakeContentName(std::addressof(content_name), content_id);
/* Format the output path. */
out->AssignFormat("%s/%08X/%08X/%s", root_path, hash_upper, hash_lower, content_name.Get());
}
void MakeSha256HierarchicalContentFilePath_ForFat32KCluster(PathString *out, ContentId content_id, const char *root_path) {
/* Hash the content id. */
const u32 hash = (Get16BitSha256HashPrefix(content_id) >> 6) & 0x3FF;
/* Create the content name from the content id. */
PathString content_name;
MakeContentName(std::addressof(content_name), content_id);
/* Format the output path. */
out->AssignFormat("%s/%08X/%s", root_path, hash, content_name.Get());
}
void MakeSha256HierarchicalContentFilePath_ForFat16KCluster(PathString *out, ContentId content_id, const char *root_path) {
/* Hash the content id. */
const u32 hash_byte = static_cast<u32>(Get8BitSha256HashPrefix(content_id));
/* Create the content name from the content id. */
PathString content_name;
MakeContentName(std::addressof(content_name), content_id);
/* Format the output path. */
out->AssignFormat("%s/%08X/%s", root_path, hash_byte, content_name.Get());
}
size_t GetHierarchicalContentDirectoryDepth(MakeContentPathFunction func) {
if (func == MakeFlatContentFilePath) {
return 1;
} else if (func == MakeSha256HierarchicalContentFilePath_ForFat4KCluster) {
return 3;
} else if (func == MakeSha256HierarchicalContentFilePath_ForFat16KCluster ||
func == MakeSha256HierarchicalContentFilePath_ForFat32KCluster) {
return 2;
} else {
AMS_ABORT();
}
}
void MakeFlatPlaceHolderFilePath(PathString *out, PlaceHolderId placeholder_id, const char *root_path) {
/* Create the placeholder name from the placeholder id. */
PathString placeholder_name;
MakePlaceHolderName(std::addressof(placeholder_name), placeholder_id);
/* Format the output path. */
out->AssignFormat("%s/%s", root_path, placeholder_name.Get());
}
void MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster(PathString *out, PlaceHolderId placeholder_id, const char *root_path) {
/* Hash the placeholder id. */
const u32 hash_byte = static_cast<u32>(Get8BitSha256HashPrefix(placeholder_id));
/* Create the placeholder name from the placeholder id. */
PathString placeholder_name;
MakePlaceHolderName(std::addressof(placeholder_name), placeholder_id);
/* Format the output path. */
out->AssignFormat("%s/%08X/%s", root_path, hash_byte, placeholder_name.Get());
}
size_t GetHierarchicalPlaceHolderDirectoryDepth(MakePlaceHolderPathFunction func) {
if (func == MakeFlatPlaceHolderFilePath) {
return 1;
} else if (func == MakeSha256HierarchicalPlaceHolderFilePath_ForFat16KCluster) {
return 2;
} else {
AMS_ABORT();
}
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (c) 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 {
void HeapState::Initialize(lmem::HeapHandle heap_handle) {
std::scoped_lock lk(m_mutex);
m_heap_handle = heap_handle;
}
void HeapState::Allocate(size_t size) {
std::scoped_lock lk(m_mutex);
m_total_alloc_size += size;
m_peak_total_alloc_size = std::max(m_total_alloc_size, m_peak_total_alloc_size);
m_peak_alloc_size = std::max(size, m_peak_alloc_size);
}
void HeapState::Free(size_t size) {
std::scoped_lock lk(m_mutex);
m_total_alloc_size -= size;
}
void HeapState::GetMemoryResourceState(MemoryResourceState *out) {
*out = {};
std::scoped_lock lk(m_mutex);
out->peak_total_alloc_size = m_peak_total_alloc_size;
out->peak_alloc_size = m_peak_alloc_size;
out->total_free_size = lmem::GetExpHeapTotalFreeSize(m_heap_handle);
out->allocatable_size = lmem::GetExpHeapAllocatableSize(m_heap_handle, alignof(s32));
}
HeapState &GetHeapState() {
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(HeapState, s_heap_state);
return s_heap_state;
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_on_memory_content_meta_database_impl.hpp"
namespace ams::ncm {
Result OnMemoryContentMetaDatabaseImpl::List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) {
/* NOTE: This function is *almost* identical to the ContentMetaDatabaseImpl equivalent. */
/* The only difference is that the min max comparison is exclusive for OnMemoryContentMetaDatabaseImpl, */
/* but inclusive for ContentMetaDatabaseImpl. This may or may not be a Nintendo bug? */
R_TRY(this->EnsureEnabled());
size_t entries_total = 0;
size_t entries_written = 0;
/* Iterate over all entries. */
for (auto entry = m_kvs->begin(); entry != m_kvs->end(); entry++) {
const ContentMetaKey key = entry->GetKey();
/* Check if this entry matches the given filters. */
if (!((meta_type == ContentMetaType::Unknown || key.type == meta_type) && (min < key.id && key.id < max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) {
continue;
}
/* If application id is present, check if it matches the filter. */
if (application_id != InvalidApplicationId) {
/* Obtain the content meta for the key. */
const void *meta;
size_t meta_size;
R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
/* Create a reader. */
ContentMetaReader reader(meta, meta_size);
/* Ensure application id matches, if present. */
if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id && application_id != *entry_application_id) {
continue;
}
}
/* Write the entry to the output buffer. */
if (entries_written < out_info.GetSize()) {
out_info[entries_written++] = key;
}
entries_total++;
}
out_entries_total.SetValue(entries_total);
out_entries_written.SetValue(entries_written);
R_SUCCEED();
}
Result OnMemoryContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) {
R_TRY(this->EnsureEnabled());
util::optional<ContentMetaKey> found_key = util::nullopt;
/* Find the last key with the desired program id. */
for (auto entry = m_kvs->lower_bound(ContentMetaKey::MakeUnknownType(id, 0)); entry != m_kvs->end(); entry++) {
/* No further entries will match the program id, discontinue. */
if (entry->GetKey().id != id) {
break;
}
/* On memory content database is interested in all keys. */
found_key = entry->GetKey();
}
/* Check if the key is absent. */
R_UNLESS(found_key, ncm::ResultContentMetaNotFound());
out_key.SetValue(*found_key);
R_SUCCEED();
}
Result OnMemoryContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) {
AMS_UNUSED(out_orphaned, content_ids);
R_THROW(ncm::ResultInvalidContentMetaDatabase());
}
Result OnMemoryContentMetaDatabaseImpl::Commit() {
R_TRY(this->EnsureEnabled());
R_SUCCEED();
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (c) 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.hpp>
#include "ncm_content_meta_database_impl.hpp"
namespace ams::ncm {
class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl {
public:
OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore<ContentMetaKey> *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ }
public:
/* Actual commands. */
virtual Result List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override;
virtual Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) override;
virtual Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) override;
virtual Result Commit() override;
};
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
bool PackageInstallTask::IsContentMetaContentName(const char *name) {
return impl::PathView(name).HasSuffix(".cnmt");
}
Result PackageInstallTask::Initialize(const char *package_root, StorageId storage_id, void *buffer, size_t buffer_size, bool ignore_ticket) {
R_RETURN(PackageInstallTaskBase::Initialize(package_root, buffer, buffer_size, storage_id, std::addressof(m_data), ignore_ticket ? InstallConfig_IgnoreTicket : InstallConfig_None));
}
Result PackageInstallTask::GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key) {
AMS_UNUSED(out_info, key);
R_THROW(ncm::ResultContentNotFound());
}
Result PackageInstallTask::PrepareInstallContentMetaData() {
/* Open the directory. */
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), this->GetPackageRootPath(), fs::OpenDirectoryMode_File));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
while (true) {
/* Read the current entry. */
s64 count;
fs::DirectoryEntry entry;
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
/* No more entries remain, we are done. */
if (count == 0) {
break;
}
/* Check if this entry is content meta. */
if (this->IsContentMetaContentName(entry.name)) {
/* Prepare content meta if id is valid. */
util::optional<ContentId> id = GetContentIdFromString(entry.name, strnlen(entry.name, fs::EntryNameLengthMax + 1));
R_UNLESS(id, ncm::ResultInvalidPackageFormat());
R_TRY(this->PrepareContentMeta(InstallContentMetaInfo::MakeUnverifiable(*id, entry.file_size), util::nullopt, util::nullopt));
}
}
R_SUCCEED();
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright (c) 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 PackageInstallTaskBase::Initialize(const char *package_root_path, void *buffer, size_t buffer_size, StorageId storage_id, InstallTaskDataBase *data, u32 config) {
R_TRY(InstallTaskBase::Initialize(storage_id, data, config));
m_package_root.Assign(package_root_path);
m_buffer = buffer;
m_buffer_size = buffer_size;
R_SUCCEED();
}
Result PackageInstallTaskBase::OnWritePlaceHolder(const ContentMetaKey &key, InstallContentInfo *content_info) {
AMS_UNUSED(key);
/* Get the file path. */
PackagePath path;
if (content_info->GetType() == ContentType::Meta) {
this->CreateContentMetaPath(std::addressof(path), content_info->GetId());
} else {
this->CreateContentPath(std::addressof(path), content_info->GetId());
}
/* Open the file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Continuously write the file to the placeholder until there is nothing left to write. */
while (true) {
/* Read as much of the remainder of the file as possible. */
size_t size_read;
R_TRY(fs::ReadFile(std::addressof(size_read), file, content_info->written, m_buffer, m_buffer_size));
/* There is nothing left to read. */
if (size_read == 0) {
break;
}
/* Write the placeholder. */
R_TRY(this->WritePlaceHolderBuffer(content_info, m_buffer, size_read));
}
R_SUCCEED();
}
Result PackageInstallTaskBase::InstallTicket(const fs::RightsId &rights_id, ContentMetaType meta_type) {
AMS_UNUSED(meta_type);
/* Read ticket from file. */
s64 ticket_size;
std::unique_ptr<char[]> ticket;
{
fs::FileHandle ticket_file;
{
PackagePath ticket_path;
this->CreateTicketPath(std::addressof(ticket_path), rights_id);
R_TRY(fs::OpenFile(std::addressof(ticket_file), ticket_path, fs::OpenMode_Read));
}
ON_SCOPE_EXIT { fs::CloseFile(ticket_file); };
R_TRY(fs::GetFileSize(std::addressof(ticket_size), ticket_file));
ticket.reset(new (std::nothrow) char[static_cast<size_t>(ticket_size)]);
R_UNLESS(ticket != nullptr, ncm::ResultAllocationFailed());
R_TRY(fs::ReadFile(ticket_file, 0, ticket.get(), static_cast<size_t>(ticket_size)));
}
/* Read certificate from file. */
s64 cert_size;
std::unique_ptr<char[]> cert;
{
fs::FileHandle cert_file;
{
PackagePath cert_path;
this->CreateCertificatePath(std::addressof(cert_path), rights_id);
R_TRY(fs::OpenFile(std::addressof(cert_file), cert_path, fs::OpenMode_Read));
}
ON_SCOPE_EXIT { fs::CloseFile(cert_file); };
R_TRY(fs::GetFileSize(std::addressof(cert_size), cert_file));
cert.reset(new (std::nothrow) char[static_cast<size_t>(cert_size)]);
R_UNLESS(cert != nullptr, ncm::ResultAllocationFailed());
R_TRY(fs::ReadFile(cert_file, 0, cert.get(), static_cast<size_t>(cert_size)));
}
/* TODO: es::ImportTicket() */
/* TODO: How should es be handled without undesired effects? */
R_SUCCEED();
}
void PackageInstallTaskBase::CreateContentPath(PackagePath *out_path, ContentId content_id) {
char str[ContentIdStringLength + 1] = {};
GetStringFromContentId(str, sizeof(str), content_id);
out_path->AssignFormat("%s%s%s", m_package_root.Get(), str, ".nca");
}
void PackageInstallTaskBase::CreateContentMetaPath(PackagePath *out_path, ContentId content_id) {
char str[ContentIdStringLength + 1] = {};
GetStringFromContentId(str, sizeof(str), content_id);
out_path->AssignFormat("%s%s%s", m_package_root.Get(), str, ".cnmt.nca");
}
void PackageInstallTaskBase::CreateTicketPath(PackagePath *out_path, fs::RightsId id) {
char str[RightsIdStringLength + 1] = {};
GetStringFromRightsId(str, sizeof(str), id);
out_path->AssignFormat("%s%s%s", m_package_root.Get(), str, ".tik");
}
void PackageInstallTaskBase::CreateCertificatePath(PackagePath *out_path, fs::RightsId id) {
char str[RightsIdStringLength + 1] = {};
GetStringFromRightsId(str, sizeof(str), id);
out_path->AssignFormat("%s%s%s", m_package_root.Get(), str, ".cert");
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (c) 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 PackageSystemDowngradeTask::PreCommit() {
constexpr size_t MaxContentMetas = 0x40;
auto &data = this->GetInstallData();
/* Count the number of content meta entries. */
s32 count;
R_TRY(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(data.Get(std::addressof(content_meta), i));
/* Create a reader. */
const auto reader = content_meta.GetReader();
const auto key = reader.GetKey();
/* Obtain a list of suitable storage ids. */
const auto storage_list = GetStorageList(this->GetInstallStorage());
/* Iterate over storage ids. */
for (s32 i = 0; i < storage_list.Count(); i++) {
/* Open the content meta database. */
ContentMetaDatabase meta_db;
if (R_FAILED(OpenContentMetaDatabase(std::addressof(meta_db), storage_list[i]))) {
continue;
}
/* List keys matching the type and id of the install content meta key. */
ncm::ContentMetaKey keys[MaxContentMetas];
const auto count = meta_db.ListContentMeta(keys, MaxContentMetas, key.type, { key.id });
/* Remove matching keys. */
for (auto i = 0; i < count.total; i++) {
meta_db.Remove(keys[i]);
}
}
}
R_SUCCEED();
}
Result PackageSystemDowngradeTask::Commit() {
R_TRY(this->PreCommit());
R_RETURN(InstallTaskBase::Commit());
}
Result PackageSystemDowngradeTask::PrepareContentMetaIfLatest(const ContentMetaKey &key) {
/* Get and prepare install content meta info. We aren't concerned if our key is older. */
InstallContentMetaInfo install_content_meta_info;
R_TRY(this->GetInstallContentMetaInfo(std::addressof(install_content_meta_info), key));
R_RETURN(this->PrepareContentMeta(install_content_meta_info, key, util::nullopt));
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright (c) 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 {
PackageSystemUpdateTask::~PackageSystemUpdateTask() {
if (m_context_path.GetLength() > 0) {
fs::DeleteFile(m_context_path);
}
this->Inactivate();
}
void PackageSystemUpdateTask::Inactivate() {
if (m_gamecard_content_meta_database_active) {
InactivateContentMetaDatabase(StorageId::GameCard);
m_gamecard_content_meta_database_active = false;
}
}
Result PackageSystemUpdateTask::Initialize(const char *package_root, const char *context_path, void *buffer, size_t buffer_size, bool requires_exfat_driver, FirmwareVariationId firmware_variation_id) {
/* Set the firmware variation id. */
this->SetFirmwareVariationId(firmware_variation_id);
/* Activate the game card content meta database. */
R_TRY(ActivateContentMetaDatabase(StorageId::GameCard));
m_gamecard_content_meta_database_active = true;
auto meta_db_guard = SCOPE_GUARD { this->Inactivate(); };
/* Open the game card content meta database. */
OpenContentMetaDatabase(std::addressof(m_package_db), StorageId::GameCard);
ContentMetaDatabaseBuilder builder(std::addressof(m_package_db));
/* Cleanup and build the content meta database. */
R_TRY(builder.Cleanup());
R_TRY(builder.BuildFromPackage(package_root));
/* Create a new context file. */
fs::DeleteFile(context_path);
R_TRY(FileInstallTaskData::Create(context_path, GameCardMaxContentMetaCount));
auto context_guard = SCOPE_GUARD { fs::DeleteFile(context_path); };
/* Initialize data. */
R_TRY(m_data.Initialize(context_path));
/* Initialize PackageInstallTaskBase. */
u32 config = !requires_exfat_driver ? InstallConfig_SystemUpdate : InstallConfig_SystemUpdate | InstallConfig_RequiresExFatDriver;
R_TRY(PackageInstallTaskBase::Initialize(package_root, buffer, buffer_size, StorageId::BuiltInSystem, std::addressof(m_data), config));
/* Cancel guards. */
context_guard.Cancel();
meta_db_guard.Cancel();
/* Set the context path. */
m_context_path.Assign(context_path);
R_SUCCEED();
}
util::optional<ContentMetaKey> PackageSystemUpdateTask::GetSystemUpdateMetaKey() {
StorageContentMetaKey storage_keys[0x10];
s32 ofs = 0;
s32 count = 0;
do {
/* List content meta keys. */
if (R_FAILED(this->ListContentMetaKey(std::addressof(count), storage_keys, util::size(storage_keys), ofs, ListContentMetaKeyFilter::All))) {
break;
}
/* Add listed keys to the offset. */
ofs += count;
/* Check if any of these keys are for a SystemUpdate. */
for (s32 i = 0; i < count; i++) {
const ContentMetaKey &key = storage_keys[i].key;
if (key.type == ContentMetaType::SystemUpdate) {
return key;
}
}
} while (count > 0);
return util::nullopt;
}
Result PackageSystemUpdateTask::GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) {
/* Get the content info for the key. */
ContentInfo info;
R_TRY(this->GetContentInfoOfContentMeta(std::addressof(info), key));
/* Create a new install content meta info. */
*out = InstallContentMetaInfo::MakeUnverifiable(info.GetId(), info.GetSize(), key);
R_SUCCEED();
}
Result PackageSystemUpdateTask::PrepareInstallContentMetaData() {
/* Obtain a SystemUpdate key. */
ContentMetaKey key;
auto list_count = m_package_db.ListContentMeta(std::addressof(key), 1, ContentMetaType::SystemUpdate);
R_UNLESS(list_count.written > 0, ncm::ResultSystemUpdateNotFoundInPackage());
/* Get the content info for the key. */
ContentInfo info;
R_TRY(this->GetContentInfoOfContentMeta(std::addressof(info), key));
/* Prepare the content meta. */
R_RETURN(this->PrepareContentMeta(InstallContentMetaInfo::MakeUnverifiable(info.GetId(), info.GetSize(), key), key, util::nullopt));
}
Result PackageSystemUpdateTask::PrepareDependency() {
R_RETURN(this->PrepareSystemUpdateDependency());
}
Result PackageSystemUpdateTask::GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key) {
s32 ofs = 0;
while (true) {
/* List content infos. */
s32 count;
ContentInfo info;
R_TRY(m_package_db.ListContentInfo(std::addressof(count), std::addressof(info), 1, key, ofs++));
/* No content infos left to list. */
if (count == 0) {
break;
}
/* Check if the info is for meta content. */
if (info.GetType() == ContentType::Meta) {
*out = info;
R_SUCCEED();
}
}
/* Not found. */
R_THROW(ncm::ResultContentInfoNotFound());
}
}

View File

@@ -1,249 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_placeholder_accessor.hpp"
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
namespace {
constexpr inline const char * const BasePlaceHolderDirectory = "/placehld";
constexpr inline const char * const PlaceHolderExtension = ".nca";
constexpr inline size_t PlaceHolderExtensionLength = 4;
constexpr inline size_t PlaceHolderFileNameLengthWithoutExtension = 2 * sizeof(PlaceHolderId);
constexpr inline size_t PlaceHolderFileNameLength = PlaceHolderFileNameLengthWithoutExtension + PlaceHolderExtensionLength;
void MakeBasePlaceHolderDirectoryPath(PathString *out, const char *root_path) {
out->AssignFormat("%s%s", root_path, BasePlaceHolderDirectory);
}
void MakePlaceHolderFilePath(PathString *out, PlaceHolderId id, MakePlaceHolderPathFunction func, const char *root_path) {
PathString path;
MakeBasePlaceHolderDirectoryPath(std::addressof(path), root_path);
func(out, id, path);
}
}
void PlaceHolderAccessor::MakePath(PathString *out_placeholder_path, PlaceHolderId placeholder_id) const {
MakePlaceHolderFilePath(out_placeholder_path, placeholder_id, m_make_placeholder_path_func, *m_root_path);
}
void PlaceHolderAccessor::MakeBaseDirectoryPath(PathString *out, const char *root_path) {
MakeBasePlaceHolderDirectoryPath(out, root_path);
}
Result PlaceHolderAccessor::EnsurePlaceHolderDirectory(PlaceHolderId placeholder_id) {
PathString path;
this->MakePath(std::addressof(path), placeholder_id);
R_RETURN(fs::EnsureParentDirectory(path));
}
Result PlaceHolderAccessor::GetPlaceHolderIdFromFileName(PlaceHolderId *out, const char *name) {
/* Ensure placeholder name is valid. */
R_UNLESS(strnlen(name, PlaceHolderFileNameLength) == PlaceHolderFileNameLength, ncm::ResultInvalidPlaceHolderFile());
R_UNLESS(strncmp(name + PlaceHolderFileNameLengthWithoutExtension, PlaceHolderExtension, PlaceHolderExtensionLength) == 0, ncm::ResultInvalidPlaceHolderFile());
/* Convert each character pair to a byte until we reach the end. */
PlaceHolderId placeholder_id = {};
for (size_t i = 0; i < sizeof(placeholder_id); i++) {
char tmp[3];
util::Strlcpy(tmp, name + i * 2, sizeof(tmp));
char *err = nullptr;
reinterpret_cast<u8 *>(std::addressof(placeholder_id))[i] = static_cast<u8>(std::strtoul(tmp, std::addressof(err), 16));
R_UNLESS(*err == '\x00', ncm::ResultInvalidPlaceHolderFile());
}
*out = placeholder_id;
R_SUCCEED();
}
Result PlaceHolderAccessor::Open(fs::FileHandle *out_handle, PlaceHolderId placeholder_id) {
/* Try to load from the cache. */
R_SUCCEED_IF(this->LoadFromCache(out_handle, placeholder_id));
/* Make the path of the placeholder. */
PathString placeholder_path;
this->MakePath(std::addressof(placeholder_path), placeholder_id);
/* Open the placeholder file. */
R_RETURN(fs::OpenFile(out_handle, placeholder_path, fs::OpenMode_Write));
}
bool PlaceHolderAccessor::LoadFromCache(fs::FileHandle *out_handle, PlaceHolderId placeholder_id) {
std::scoped_lock lk(m_cache_mutex);
/* Attempt to find an entry in the cache. */
CacheEntry *entry = this->FindInCache(placeholder_id);
if (!entry) {
return false;
}
/* No cached entry found. */
entry->id = InvalidPlaceHolderId;
*out_handle = entry->handle;
return true;
}
void PlaceHolderAccessor::StoreToCache(PlaceHolderId placeholder_id, fs::FileHandle handle) {
std::scoped_lock lk(m_cache_mutex);
/* Store placeholder id and file handle to a free entry. */
CacheEntry *entry = this->GetFreeEntry();
entry->id = placeholder_id;
entry->handle = handle;
entry->counter = m_cur_counter++;
}
void PlaceHolderAccessor::Invalidate(CacheEntry *entry) {
/* Flush and close the cached entry's file. */
if (entry != nullptr) {
fs::FlushFile(entry->handle);
fs::CloseFile(entry->handle);
entry->id = InvalidPlaceHolderId;
}
}
PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::FindInCache(PlaceHolderId placeholder_id) {
/* Ensure placeholder id is valid. */
if (placeholder_id == InvalidPlaceHolderId) {
return nullptr;
}
/* Attempt to find a cache entry with the same placeholder id. */
for (size_t i = 0; i < MaxCacheEntries; i++) {
if (placeholder_id == m_caches[i].id) {
return std::addressof(m_caches[i]);
}
}
return nullptr;
}
PlaceHolderAccessor::CacheEntry *PlaceHolderAccessor::GetFreeEntry() {
/* Try to find an already free entry. */
for (size_t i = 0; i < MaxCacheEntries; i++) {
if (m_caches[i].id == InvalidPlaceHolderId) {
return std::addressof(m_caches[i]);
}
}
/* Get the oldest entry. */
CacheEntry *entry = std::addressof(m_caches[0]);
for (size_t i = 1; i < MaxCacheEntries; i++) {
if (entry->counter < m_caches[i].counter) {
entry = std::addressof(m_caches[i]);
}
}
this->Invalidate(entry);
return entry;
}
void PlaceHolderAccessor::GetPath(PathString *placeholder_path, PlaceHolderId placeholder_id) {
{
std::scoped_lock lock(m_cache_mutex);
this->Invalidate(this->FindInCache(placeholder_id));
}
this->MakePath(placeholder_path, placeholder_id);
}
Result PlaceHolderAccessor::CreatePlaceHolderFile(PlaceHolderId placeholder_id, s64 size) {
/* Ensure the destination directory exists. */
R_TRY(this->EnsurePlaceHolderDirectory(placeholder_id));
/* Get the placeholder path. */
PathString placeholder_path;
this->GetPath(std::addressof(placeholder_path), placeholder_id);
/* Create the placeholder file. */
R_TRY_CATCH(fs::CreateFile(placeholder_path, size, fs::CreateOption_BigFile)) {
R_CONVERT(fs::ResultPathAlreadyExists, ncm::ResultPlaceHolderAlreadyExists())
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result PlaceHolderAccessor::DeletePlaceHolderFile(PlaceHolderId placeholder_id) {
/* Get the placeholder path. */
PathString placeholder_path;
this->GetPath(std::addressof(placeholder_path), placeholder_id);
/* Delete the placeholder file. */
R_TRY_CATCH(fs::DeleteFile(placeholder_path)) {
R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
} R_END_TRY_CATCH;
R_SUCCEED();
}
Result PlaceHolderAccessor::WritePlaceHolderFile(PlaceHolderId placeholder_id, s64 offset, const void *buffer, size_t size) {
/* Open the placeholder file. */
fs::FileHandle file;
R_TRY_CATCH(this->Open(std::addressof(file), placeholder_id)) {
R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
} R_END_TRY_CATCH;
/* Store opened files to the cache regardless of write failures. */
ON_SCOPE_EXIT { this->StoreToCache(placeholder_id, file); };
/* Write data to the placeholder file. */
R_RETURN(fs::WriteFile(file, offset, buffer, size, m_delay_flush ? fs::WriteOption::Flush : fs::WriteOption::None));
}
Result PlaceHolderAccessor::SetPlaceHolderFileSize(PlaceHolderId placeholder_id, s64 size) {
/* Open the placeholder file. */
fs::FileHandle file;
R_TRY_CATCH(this->Open(std::addressof(file), placeholder_id)) {
R_CONVERT(fs::ResultPathNotFound, ncm::ResultPlaceHolderNotFound())
} R_END_TRY_CATCH;
/* Close the file on exit. */
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Set the size of the placeholder file. */
R_RETURN(fs::SetFileSize(file, size));
}
Result PlaceHolderAccessor::TryGetPlaceHolderFileSize(bool *found_in_cache, s64 *out_size, PlaceHolderId placeholder_id) {
/* Attempt to find the placeholder in the cache. */
fs::FileHandle handle;
auto found = this->LoadFromCache(std::addressof(handle), placeholder_id);
if (found) {
/* Renew the entry in the cache. */
this->StoreToCache(placeholder_id, handle);
R_TRY(fs::GetFileSize(out_size, handle));
*found_in_cache = true;
} else {
*found_in_cache = false;
}
R_SUCCEED();
}
void PlaceHolderAccessor::InvalidateAll() {
/* Invalidate all cache entries. */
for (auto &entry : m_caches) {
if (entry.id != InvalidPlaceHolderId) {
this->Invalidate(std::addressof(entry));
}
}
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
class PlaceHolderAccessor {
private:
struct CacheEntry {
PlaceHolderId id;
fs::FileHandle handle;
u64 counter;
};
static constexpr size_t MaxCacheEntries = 0x2;
private:
std::array<CacheEntry, MaxCacheEntries> m_caches;
PathString *m_root_path;
u64 m_cur_counter;
os::SdkMutex m_cache_mutex;
MakePlaceHolderPathFunction m_make_placeholder_path_func;
bool m_delay_flush;
private:
Result Open(fs::FileHandle *out_handle, PlaceHolderId placeholder_id);
bool LoadFromCache(fs::FileHandle *out_handle, PlaceHolderId placeholder_id);
void StoreToCache(PlaceHolderId placeholder_id, fs::FileHandle handle);
void Invalidate(CacheEntry *entry);
CacheEntry *FindInCache(PlaceHolderId placeholder_id);
CacheEntry *GetFreeEntry();;
public:
PlaceHolderAccessor() : m_root_path(nullptr), m_cur_counter(0), m_cache_mutex(), m_make_placeholder_path_func(nullptr), m_delay_flush(false) {
for (size_t i = 0; i < MaxCacheEntries; i++) {
m_caches[i].id = InvalidPlaceHolderId;
}
}
~PlaceHolderAccessor() { this->InvalidateAll(); }
static void MakeBaseDirectoryPath(PathString *out, const char *root_path);
static Result GetPlaceHolderIdFromFileName(PlaceHolderId *out, const char *name);
public:
/* API. */
void Initialize(PathString *root, MakePlaceHolderPathFunction path_func, bool delay_flush) {
m_root_path = root;
m_make_placeholder_path_func = path_func;
m_delay_flush = delay_flush;
}
Result CreatePlaceHolderFile(PlaceHolderId placeholder_id, s64 size);
Result DeletePlaceHolderFile(PlaceHolderId placeholder_id);
Result WritePlaceHolderFile(PlaceHolderId placeholder_id, s64 offset, const void *buffer, size_t size);
Result SetPlaceHolderFileSize(PlaceHolderId placeholder_id, s64 size);
Result TryGetPlaceHolderFileSize(bool *out_found, s64 *out_size, PlaceHolderId placeholder_id);
void GetPath(PathString *out_placeholder_path, PlaceHolderId placeholder_id);
void MakePath(PathString *out_placeholder_path, PlaceHolderId placeholder_id) const;
void InvalidateAll();
Result EnsurePlaceHolderDirectory(PlaceHolderId placeholder_id);
size_t GetHierarchicalDirectoryDepth() const { return GetHierarchicalPlaceHolderDirectoryDepth(m_make_placeholder_path_func); }
};
}

View File

@@ -1,315 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_read_only_content_storage_impl.hpp"
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
namespace {
void MakeContentPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) {
return func(out, id, root_path);
}
void MakeGameCardContentMetaPath(PathString *out, ContentId id, MakeContentPathFunction func, const char *root_path) {
/* Determine the content path. */
PathString path;
func(std::addressof(path), id, root_path);
/* Substitute the .nca extension with .cmnt.nca. */
*out = path.MakeSubString(0, path.GetLength() - 4);
out->Append(".cnmt.nca");
}
Result OpenContentIdFileImpl(fs::FileHandle *out, ContentId id, MakeContentPathFunction func, const char *root_path) {
PathString path;
MakeContentPath(std::addressof(path), id, func, root_path);
/* Open the content file. */
/* If absent, make the path for game card content meta and open again. */
R_TRY_CATCH(fs::OpenFile(out, path, fs::OpenMode_Read)) {
R_CATCH(fs::ResultPathNotFound) {
MakeGameCardContentMetaPath(std::addressof(path), id, func, root_path);
R_TRY(fs::OpenFile(out, path, fs::OpenMode_Read));
}
} R_END_TRY_CATCH;
R_SUCCEED();
}
}
Result ReadOnlyContentStorageImpl::Initialize(const char *path, MakeContentPathFunction content_path_func) {
R_TRY(this->EnsureEnabled());
m_root_path.Assign(path);
m_make_content_path_func = content_path_func;
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
AMS_UNUSED(out);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) {
AMS_UNUSED(placeholder_id, content_id, size);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::DeletePlaceHolder(PlaceHolderId placeholder_id) {
AMS_UNUSED(placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) {
AMS_UNUSED(placeholder_id, offset, data);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::Register(PlaceHolderId placeholder_id, ContentId content_id) {
AMS_UNUSED(placeholder_id, content_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::Delete(ContentId content_id) {
AMS_UNUSED(content_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::Has(sf::Out<bool> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Make the content path. */
PathString content_path;
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Check if the file exists. */
bool has;
R_TRY(fs::HasFile(std::addressof(has), content_path));
/* If the file is absent, make the path for game card content meta and check presence again. */
if (!has) {
MakeGameCardContentMetaPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
R_TRY(fs::HasFile(std::addressof(has), content_path));
}
out.SetValue(has);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::GetPath(sf::Out<Path> out, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Make the path for game card content meta. */
PathString content_path;
MakeGameCardContentMetaPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
/* Check if the file exists. */
bool has_file;
R_TRY(fs::HasFile(std::addressof(has_file), content_path));
/* If the file is absent, make the path for regular content. */
if (!has_file) {
MakeContentPath(std::addressof(content_path), content_id, m_make_content_path_func, m_root_path);
}
/* Substitute mount name with the common mount name. */
Path common_path;
R_TRY(fs::ConvertToFsCommonPath(common_path.str, sizeof(common_path.str), content_path));
out.SetValue(common_path);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::CleanupAllPlaceHolder() {
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
AMS_UNUSED(out_count, out_buf);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetContentCount(sf::Out<s32> out_count) {
AMS_UNUSED(out_count);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 offset) {
AMS_UNUSED(out_count, out_buf, offset);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) {
R_TRY(this->EnsureEnabled());
/* Open the file for the content id. */
fs::FileHandle file;
R_TRY(OpenContentIdFileImpl(std::addressof(file), content_id, m_make_content_path_func, m_root_path));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Determine the file size. */
s64 file_size;
R_TRY(fs::GetFileSize(std::addressof(file_size), file));
out_size.SetValue(file_size);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::DisableForcibly() {
m_disabled = true;
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
AMS_UNUSED(placeholder_id, old_content_id, new_content_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) {
AMS_UNUSED(placeholder_id, size);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) {
/* Ensure offset is valid. */
R_UNLESS(offset >= 0, ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
/* Open the file for the content id. */
fs::FileHandle file;
R_TRY(OpenContentIdFileImpl(std::addressof(file), content_id, m_make_content_path_func, m_root_path));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Read from the given offset up to the given size. */
R_TRY(fs::ReadFile(file, offset, buf.GetPointer(), buf.GetSize()));
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
AMS_UNUSED(out_rights_id, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
AMS_UNUSED(out_rights_id, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) {
AMS_UNUSED(out_rights_id, placeholder_id, attr);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) {
/* Obtain the regular rights id for the content id. */
ncm::RightsId rights_id;
R_TRY(this->GetRightsIdFromContentIdDeprecated2(std::addressof(rights_id), content_id));
/* Output the fs rights id. */
out_rights_id.SetValue(rights_id.id);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_RETURN(this->GetRightsIdFromContentId(out_rights_id, content_id, fs::ContentAttributes_None));
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Get the content path. */
Path path;
R_TRY(this->GetPath(std::addressof(path), content_id));
/* Get the rights id. */
ncm::RightsId rights_id;
R_TRY(GetRightsId(std::addressof(rights_id), path, attr));
out_rights_id.SetValue(rights_id);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) {
AMS_UNUSED(content_id, offset, data);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetFreeSpaceSize(sf::Out<s64> out_size) {
out_size.SetValue(0);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::GetTotalSpaceSize(sf::Out<s64> out_size) {
out_size.SetValue(0);
R_SUCCEED();
}
Result ReadOnlyContentStorageImpl::FlushPlaceHolder() {
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id) {
AMS_UNUSED(out, placeholder_id);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::RepairInvalidFileAttribute() {
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) {
AMS_UNUSED(out_rights_id, placeholder_id, cache_content_id, attr);
R_THROW(ncm::ResultNotSupported());
}
Result ReadOnlyContentStorageImpl::RegisterPath(const ContentId &content_id, const Path &path) {
AMS_UNUSED(content_id, path);
R_THROW(ncm::ResultInvalidOperation());
}
Result ReadOnlyContentStorageImpl::ClearRegisteredPath() {
R_THROW(ncm::ResultInvalidOperation());
}
Result ReadOnlyContentStorageImpl::GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) {
R_TRY(this->EnsureEnabled());
/* Get the path of the content. */
Path path;
R_TRY(this->GetPath(std::addressof(path), content_id));
/* Obtain the program id for the content. */
ncm::ProgramId program_id;
R_TRY(fs::GetProgramId(std::addressof(program_id), path.str, attr));
out.SetValue(program_id);
R_SUCCEED();
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) 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.hpp>
#include "ncm_content_storage_impl_base.hpp"
namespace ams::ncm {
class ReadOnlyContentStorageImpl : public ContentStorageImplBase {
public:
Result Initialize(const char *root_path, MakeContentPathFunction content_path_func);
public:
/* Actual commands. */
virtual Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) override;
virtual Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) override;
virtual Result DeletePlaceHolder(PlaceHolderId placeholder_id) override;
virtual Result HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) override;
virtual Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) override;
virtual Result Register(PlaceHolderId placeholder_id, ContentId content_id) override;
virtual Result Delete(ContentId content_id) override;
virtual Result Has(sf::Out<bool> out, ContentId content_id) override;
virtual Result GetPath(sf::Out<Path> out, ContentId content_id) override;
virtual Result GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) override;
virtual Result CleanupAllPlaceHolder() override;
virtual Result ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) override;
virtual Result GetContentCount(sf::Out<s32> out_count) override;
virtual Result ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 start_offset) override;
virtual Result GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) override;
virtual Result DisableForcibly() override;
virtual Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) override;
virtual Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) override;
virtual Result ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) override;
virtual Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) override;
virtual Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) override;
virtual Result GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) override;
virtual Result GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) override;
virtual Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) override;
virtual Result WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) override;
virtual Result GetFreeSpaceSize(sf::Out<s64> out_size) override;
virtual Result GetTotalSpaceSize(sf::Out<s64> out_size) override;
virtual Result FlushPlaceHolder() override;
virtual Result GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id) override;
virtual Result RepairInvalidFileAttribute() override;
virtual Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) override;
virtual Result RegisterPath(const ContentId &content_id, const Path &path) override;
virtual Result ClearRegisteredPath() override;
virtual Result GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) override;
};
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (c) 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 {
class RegisteredHostContent::RegisteredPath : public util::IntrusiveListBaseNode<RegisteredPath> {
NON_COPYABLE(RegisteredPath);
NON_MOVEABLE(RegisteredPath);
private:
ContentId m_content_id;
Path m_path;
public:
RegisteredPath(const ncm::ContentId &content_id, const Path &p) : m_content_id(content_id), m_path(p) {
/* ... */
}
ncm::ContentId GetContentId() const {
return m_content_id;
}
void GetPath(Path *out) const {
*out = m_path;
}
void SetPath(const Path &path) {
m_path = path;
}
};
RegisteredHostContent::~RegisteredHostContent() {
/* ... */
}
Result RegisteredHostContent::RegisterPath(const ncm::ContentId &content_id, const ncm::Path &path) {
std::scoped_lock lk(m_mutex);
/* Replace the path of any existing entries. */
for (auto &registered_path : m_path_list) {
if (registered_path.GetContentId() == content_id) {
registered_path.SetPath(path);
R_SUCCEED();
}
}
/* Allocate a new registered path. TODO: Verify allocator. */
RegisteredPath *registered_path = new RegisteredPath(content_id, path);
R_UNLESS(registered_path != nullptr, ncm::ResultBufferInsufficient());
/* Insert the path into the list. */
m_path_list.push_back(*registered_path);
R_SUCCEED();
}
Result RegisteredHostContent::GetPath(Path *out, const ncm::ContentId &content_id) {
std::scoped_lock lk(m_mutex);
/* Obtain the path of the content. */
for (const auto &registered_path : m_path_list) {
if (registered_path.GetContentId() == content_id) {
registered_path.GetPath(out);
R_SUCCEED();
}
}
R_THROW(ncm::ResultContentNotFound());
}
void RegisteredHostContent::ClearPaths() {
/* Remove all paths. */
for (auto it = m_path_list.begin(); it != m_path_list.end(); /* ... */) {
auto *obj = std::addressof(*it);
it = m_path_list.erase(it);
delete obj;
}
}
}

View File

@@ -1,110 +0,0 @@
/*
* Copyright (c) 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.hpp>
#include "ncm_remote_content_storage_impl.hpp"
#include "ncm_remote_content_meta_database_impl.hpp"
namespace ams::ncm {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteContentManagerImpl {
private:
/* TODO: sf::ProxyObjectAllocator */
using ObjectFactory = sf::ObjectFactory<sf::StdAllocationPolicy<std::allocator>>;
public:
RemoteContentManagerImpl() { R_ABORT_UNLESS(::ncmInitialize()); }
~RemoteContentManagerImpl() { ::ncmExit(); }
public:
Result CreateContentStorage(StorageId storage_id) {
R_RETURN(::ncmCreateContentStorage(static_cast<NcmStorageId>(storage_id)));
}
Result CreateContentMetaDatabase(StorageId storage_id) {
R_RETURN(::ncmCreateContentMetaDatabase(static_cast<NcmStorageId>(storage_id)));
}
Result VerifyContentStorage(StorageId storage_id) {
R_RETURN(::ncmVerifyContentStorage(static_cast<NcmStorageId>(storage_id)));
}
Result VerifyContentMetaDatabase(StorageId storage_id) {
R_RETURN(::ncmVerifyContentMetaDatabase(static_cast<NcmStorageId>(storage_id)));
}
Result OpenContentStorage(sf::Out<sf::SharedPointer<IContentStorage>> out, StorageId storage_id) {
NcmContentStorage cs;
R_TRY(::ncmOpenContentStorage(std::addressof(cs), static_cast<NcmStorageId>(storage_id)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<IContentStorage, RemoteContentStorageImpl>(cs));
R_SUCCEED();
}
Result OpenContentMetaDatabase(sf::Out<sf::SharedPointer<IContentMetaDatabase>> out, StorageId storage_id) {
NcmContentMetaDatabase db;
R_TRY(::ncmOpenContentMetaDatabase(std::addressof(db), static_cast<NcmStorageId>(storage_id)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<IContentMetaDatabase, RemoteContentMetaDatabaseImpl>(db));
R_SUCCEED();
}
Result CloseContentStorageForcibly(StorageId storage_id) {
R_RETURN(::ncmCloseContentStorageForcibly(static_cast<NcmStorageId>(storage_id)));
}
Result CloseContentMetaDatabaseForcibly(StorageId storage_id) {
R_RETURN(::ncmCloseContentMetaDatabaseForcibly(static_cast<NcmStorageId>(storage_id)));
}
Result CleanupContentMetaDatabase(StorageId storage_id) {
R_RETURN(::ncmCleanupContentMetaDatabase(static_cast<NcmStorageId>(storage_id)));
}
Result ActivateContentStorage(StorageId storage_id) {
R_RETURN(::ncmActivateContentStorage(static_cast<NcmStorageId>(storage_id)));
}
Result InactivateContentStorage(StorageId storage_id) {
R_RETURN(::ncmInactivateContentStorage(static_cast<NcmStorageId>(storage_id)));
}
Result ActivateContentMetaDatabase(StorageId storage_id) {
R_RETURN(::ncmActivateContentMetaDatabase(static_cast<NcmStorageId>(storage_id)));
}
Result InactivateContentMetaDatabase(StorageId storage_id) {
R_RETURN(::ncmInactivateContentMetaDatabase(static_cast<NcmStorageId>(storage_id)));
}
Result InvalidateRightsIdCache() {
R_RETURN(::ncmInvalidateRightsIdCache());
}
Result GetMemoryReport(sf::Out<MemoryReport> out) {
/* TODO: libnx bindings */
AMS_UNUSED(out);
AMS_ABORT();
}
Result ActivateFsContentStorage(fs::ContentStorageId fs_content_storage_id) {
R_RETURN(::ncmActivateFsContentStorage(static_cast<::FsContentStorageId>(util::ToUnderlying(fs_content_storage_id))));
}
};
static_assert(ncm::IsIContentManager<RemoteContentManagerImpl>);
#endif
}

View File

@@ -1,204 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteContentMetaDatabaseImpl {
private:
::NcmContentMetaDatabase m_srv;
public:
RemoteContentMetaDatabaseImpl(::NcmContentMetaDatabase &db) : m_srv(db) { /* ... */ }
~RemoteContentMetaDatabaseImpl() { ::ncmContentMetaDatabaseClose(std::addressof(m_srv)); }
private:
ALWAYS_INLINE ::NcmContentMetaKey *Convert(ContentMetaKey *k) {
static_assert(sizeof(ContentMetaKey) == sizeof(::NcmContentMetaKey));
return reinterpret_cast<::NcmContentMetaKey *>(k);
}
ALWAYS_INLINE const ::NcmContentMetaKey *Convert(const ContentMetaKey *k) {
static_assert(sizeof(ContentMetaKey) == sizeof(::NcmContentMetaKey));
return reinterpret_cast<const ::NcmContentMetaKey *>(k);
}
ALWAYS_INLINE const ::NcmContentMetaKey *Convert(const ContentMetaKey &k) {
static_assert(sizeof(ContentMetaKey) == sizeof(::NcmContentMetaKey));
return reinterpret_cast<const ::NcmContentMetaKey *>(std::addressof(k));
}
ALWAYS_INLINE ::NcmApplicationContentMetaKey *Convert(ApplicationContentMetaKey *k) {
static_assert(sizeof(ApplicationContentMetaKey) == sizeof(::NcmApplicationContentMetaKey));
return reinterpret_cast<::NcmApplicationContentMetaKey *>(k);
}
ALWAYS_INLINE ::NcmContentInfo *Convert(ContentInfo *c) {
static_assert(sizeof(ContentInfo) == sizeof(::NcmContentInfo));
return reinterpret_cast<::NcmContentInfo *>(c);
}
ALWAYS_INLINE ::NcmContentId *Convert(ContentId *c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<::NcmContentId *>(c);
}
ALWAYS_INLINE ::NcmContentId *Convert(ContentId &c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<::NcmContentId *>(std::addressof(c));
}
ALWAYS_INLINE const ::NcmContentId *Convert(const ContentId *c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<const ::NcmContentId *>(c);
}
ALWAYS_INLINE const ::NcmContentId *Convert(const ContentId &c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<const ::NcmContentId *>(std::addressof(c));
}
public:
Result Set(const ContentMetaKey &key, const sf::InBuffer &value) {
R_RETURN(ncmContentMetaDatabaseSet(std::addressof(m_srv), Convert(key), value.GetPointer(), value.GetSize()));
}
Result Get(sf::Out<u64> out_size, const ContentMetaKey &key, const sf::OutBuffer &out_value) {
R_RETURN(ncmContentMetaDatabaseGet(std::addressof(m_srv), Convert(key), out_size.GetPointer(), out_value.GetPointer(), out_value.GetSize()));
}
Result Remove(const ContentMetaKey &key) {
R_RETURN(ncmContentMetaDatabaseRemove(std::addressof(m_srv), Convert(key)));
}
Result GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type) {
R_RETURN(ncmContentMetaDatabaseGetContentIdByType(std::addressof(m_srv), Convert(out_content_id.GetPointer()), Convert(key), static_cast<::NcmContentType>(type)));
}
Result ListContentInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentInfo> &out_info, const ContentMetaKey &key, s32 offset) {
R_RETURN(ncmContentMetaDatabaseListContentInfo(std::addressof(m_srv), out_entries_written.GetPointer(), Convert(out_info.GetPointer()), out_info.GetSize(), Convert(key), offset));
}
Result List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) {
R_RETURN(ncmContentMetaDatabaseList(std::addressof(m_srv), out_entries_total.GetPointer(), out_entries_written.GetPointer(), Convert(out_info.GetPointer()), out_info.GetSize(), static_cast<::NcmContentMetaType>(meta_type), application_id.value, min, max, static_cast<::NcmContentInstallType>(install_type)));
}
Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id) {
R_RETURN(ncmContentMetaDatabaseGetLatestContentMetaKey(std::addressof(m_srv), Convert(out_key.GetPointer()), static_cast<u64>(id)));
}
Result ListApplication(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ApplicationContentMetaKey> &out_keys, ContentMetaType meta_type) {
R_RETURN(ncmContentMetaDatabaseListApplication(std::addressof(m_srv), out_entries_total.GetPointer(), out_entries_written.GetPointer(), Convert(out_keys.GetPointer()), out_keys.GetSize(), static_cast<::NcmContentMetaType>(meta_type)));
}
Result Has(sf::Out<bool> out, const ContentMetaKey &key) {
R_RETURN(ncmContentMetaDatabaseHas(std::addressof(m_srv), out.GetPointer(), Convert(key)));
}
Result HasAll(sf::Out<bool> out, const sf::InArray<ContentMetaKey> &keys) {
R_RETURN(ncmContentMetaDatabaseHasAll(std::addressof(m_srv), out.GetPointer(), Convert(keys.GetPointer()), keys.GetSize()));
}
Result GetSize(sf::Out<u64> out_size, const ContentMetaKey &key) {
R_RETURN(ncmContentMetaDatabaseGetSize(std::addressof(m_srv), out_size.GetPointer(), Convert(key)));
}
Result GetRequiredSystemVersion(sf::Out<u32> out_version, const ContentMetaKey &key) {
R_RETURN(ncmContentMetaDatabaseGetRequiredSystemVersion(std::addressof(m_srv), out_version.GetPointer(), Convert(key)));
}
Result GetPatchContentMetaId(sf::Out<u64> out_patch_id, const ContentMetaKey &key) {
R_RETURN(ncmContentMetaDatabaseGetPatchContentMetaId(std::addressof(m_srv), out_patch_id.GetPointer(), Convert(key)));
}
Result DisableForcibly() {
R_RETURN(ncmContentMetaDatabaseDisableForcibly(std::addressof(m_srv)));
}
Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids) {
R_RETURN(ncmContentMetaDatabaseLookupOrphanContent(std::addressof(m_srv), out_orphaned.GetPointer(), Convert(content_ids.GetPointer()), std::min(out_orphaned.GetSize(), content_ids.GetSize())));
}
Result Commit() {
R_RETURN(ncmContentMetaDatabaseCommit(std::addressof(m_srv)));
}
Result HasContent(sf::Out<bool> out, const ContentMetaKey &key, const ContentId &content_id) {
R_RETURN(ncmContentMetaDatabaseHasContent(std::addressof(m_srv), out.GetPointer(), Convert(key), Convert(content_id)));
}
Result ListContentMetaInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaInfo> &out_meta_info, const ContentMetaKey &key, s32 offset) {
R_RETURN(ncmContentMetaDatabaseListContentMetaInfo(std::addressof(m_srv), out_entries_written.GetPointer(), out_meta_info.GetPointer(), out_meta_info.GetSize(), Convert(key), offset));
}
Result GetAttributes(sf::Out<u8> out_attributes, const ContentMetaKey &key) {
static_assert(sizeof(ContentMetaAttribute) == sizeof(u8));
R_RETURN(ncmContentMetaDatabaseGetAttributes(std::addressof(m_srv), Convert(key), out_attributes.GetPointer()));
}
Result GetRequiredApplicationVersion(sf::Out<u32> out_version, const ContentMetaKey &key) {
R_RETURN(ncmContentMetaDatabaseGetRequiredApplicationVersion(std::addressof(m_srv), out_version.GetPointer(), Convert(key)));
}
Result GetContentIdByTypeAndIdOffset(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) {
R_RETURN(ncmContentMetaDatabaseGetContentIdByTypeAndIdOffset(std::addressof(m_srv), Convert(out_content_id.GetPointer()), Convert(key), static_cast<::NcmContentType>(type), id_offset));
}
Result GetCount(sf::Out<u32> out_count) {
/* TODO: libnx bindings */
AMS_UNUSED(out_count);
AMS_ABORT();
}
Result GetOwnerApplicationId(sf::Out<ApplicationId> out_id, const ContentMetaKey &key) {
/* TODO: libnx bindings */
AMS_UNUSED(out_id, key);
AMS_ABORT();
}
Result GetContentAccessibilities(sf::Out<u8> out_accessibilities, const ContentMetaKey &key) {
/* TODO: libnx bindings */
AMS_UNUSED(out_accessibilities, key);
AMS_ABORT();
}
Result GetContentInfoByType(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type) {
/* TODO: libnx bindings */
AMS_UNUSED(out_content_info, key, type);
AMS_ABORT();
}
Result GetContentInfoByTypeAndIdOffset(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type, u8 id_offset) {
/* TODO: libnx bindings */
AMS_UNUSED(out_content_info, key, type, id_offset);
AMS_ABORT();
}
Result GetPlatform(sf::Out<ncm::ContentMetaPlatform> out, const ContentMetaKey &key) {
static_assert(sizeof(ncm::ContentMetaPlatform) == sizeof(u8));
R_RETURN(ncmContentMetaDatabaseGetPlatform(std::addressof(m_srv), reinterpret_cast<u8 *>(out.GetPointer()), Convert(key)));
}
Result HasAttributes(sf::Out<u8> out, u8 attr_mask) {
/* TODO: libnx bindings */
AMS_UNUSED(out, attr_mask);
AMS_ABORT();
}
};
static_assert(ncm::IsIContentMetaDatabase<RemoteContentMetaDatabaseImpl>);
#endif
}

View File

@@ -1,236 +0,0 @@
/*
* Copyright (c) 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.hpp>
namespace ams::ncm {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteContentStorageImpl {
private:
::NcmContentStorage m_srv;
public:
RemoteContentStorageImpl(::NcmContentStorage &cs) : m_srv(cs) { /* ... */ }
~RemoteContentStorageImpl() { ::ncmContentStorageClose(std::addressof(m_srv)); }
private:
ALWAYS_INLINE ::NcmPlaceHolderId *Convert(PlaceHolderId *p) {
static_assert(sizeof(PlaceHolderId) == sizeof(::NcmPlaceHolderId));
return reinterpret_cast<::NcmPlaceHolderId *>(p);
}
ALWAYS_INLINE ::NcmPlaceHolderId *Convert(PlaceHolderId &p) {
static_assert(sizeof(PlaceHolderId) == sizeof(::NcmPlaceHolderId));
return reinterpret_cast<::NcmPlaceHolderId *>(std::addressof(p));
}
ALWAYS_INLINE ::NcmContentId *Convert(ContentId *c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<::NcmContentId *>(c);
}
ALWAYS_INLINE ::NcmContentId *Convert(ContentId &c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<::NcmContentId *>(std::addressof(c));
}
ALWAYS_INLINE const ::NcmContentId *Convert(const ContentId *c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<const ::NcmContentId *>(c);
}
ALWAYS_INLINE const ::NcmContentId *Convert(const ContentId &c) {
static_assert(sizeof(ContentId) == sizeof(::NcmContentId));
return reinterpret_cast<const ::NcmContentId *>(std::addressof(c));
}
ALWAYS_INLINE ::FsContentAttributes Convert(fs::ContentAttributes attr) {
return static_cast<::FsContentAttributes>(static_cast<u8>(attr));
}
public:
Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out) {
R_RETURN(ncmContentStorageGeneratePlaceHolderId(std::addressof(m_srv), Convert(out.GetPointer())));
}
Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size) {
R_RETURN(ncmContentStorageCreatePlaceHolder(std::addressof(m_srv), Convert(content_id), Convert(placeholder_id), size));
}
Result DeletePlaceHolder(PlaceHolderId placeholder_id) {
R_RETURN(ncmContentStorageDeletePlaceHolder(std::addressof(m_srv), Convert(placeholder_id)));
}
Result HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id) {
R_RETURN(ncmContentStorageHasPlaceHolder(std::addressof(m_srv), out.GetPointer(), Convert(placeholder_id)));
}
Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data) {
R_RETURN(ncmContentStorageWritePlaceHolder(std::addressof(m_srv), Convert(placeholder_id), offset, data.GetPointer(), data.GetSize()));
}
Result Register(PlaceHolderId placeholder_id, ContentId content_id) {
R_RETURN(ncmContentStorageRegister(std::addressof(m_srv), Convert(content_id), Convert(placeholder_id)));
}
Result Delete(ContentId content_id) {
R_RETURN(ncmContentStorageDelete(std::addressof(m_srv), Convert(content_id)));
}
Result Has(sf::Out<bool> out, ContentId content_id) {
R_RETURN(ncmContentStorageHas(std::addressof(m_srv), out.GetPointer(), Convert(content_id)));
}
Result GetPath(sf::Out<Path> out, ContentId content_id) {
R_RETURN(ncmContentStorageGetPath(std::addressof(m_srv), out.GetPointer()->str, sizeof(out.GetPointer()->str), Convert(content_id)));
}
Result GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id) {
R_RETURN(ncmContentStorageGetPlaceHolderPath(std::addressof(m_srv), out.GetPointer()->str, sizeof(out.GetPointer()->str), Convert(placeholder_id)));
}
Result CleanupAllPlaceHolder() {
R_RETURN(ncmContentStorageCleanupAllPlaceHolder(std::addressof(m_srv)));
}
Result ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf) {
R_RETURN(ncmContentStorageListPlaceHolder(std::addressof(m_srv), Convert(out_buf.GetPointer()), out_buf.GetSize(), out_count.GetPointer()));
}
Result GetContentCount(sf::Out<s32> out_count) {
R_RETURN(ncmContentStorageGetContentCount(std::addressof(m_srv), out_count.GetPointer()));
}
Result ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 offset) {
R_RETURN(ncmContentStorageListContentId(std::addressof(m_srv), Convert(out_buf.GetPointer()), out_buf.GetSize(), out_count.GetPointer(), offset));
}
Result GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id) {
R_RETURN(ncmContentStorageGetSizeFromContentId(std::addressof(m_srv), out_size.GetPointer(), Convert(content_id)));
}
Result DisableForcibly() {
R_RETURN(ncmContentStorageDisableForcibly(std::addressof(m_srv)));
}
Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id) {
R_RETURN(ncmContentStorageRevertToPlaceHolder(std::addressof(m_srv), Convert(placeholder_id), Convert(old_content_id), Convert(new_content_id)));
}
Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size) {
R_RETURN(ncmContentStorageSetPlaceHolderSize(std::addressof(m_srv), Convert(placeholder_id), size));
}
Result ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset) {
R_RETURN(ncmContentStorageReadContentIdFile(std::addressof(m_srv), buf.GetPointer(), buf.GetSize(), Convert(content_id), offset));
}
Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
::NcmRightsId rights_id;
R_TRY(ncmContentStorageGetRightsIdFromPlaceHolderId(std::addressof(m_srv), std::addressof(rights_id), Convert(placeholder_id), Convert(fs::ContentAttributes_None)));
static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id));
std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer()));
R_SUCCEED();
}
Result GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id) {
R_RETURN(this->GetRightsIdFromPlaceHolderId(out_rights_id, placeholder_id, fs::ContentAttributes_None));
}
Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) {
::NcmRightsId rights_id;
R_TRY(ncmContentStorageGetRightsIdFromPlaceHolderId(std::addressof(m_srv), std::addressof(rights_id), Convert(placeholder_id), Convert(attr)));
static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id));
std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer()));
R_SUCCEED();
}
Result GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id) {
::NcmRightsId rights_id;
R_TRY(ncmContentStorageGetRightsIdFromContentId(std::addressof(m_srv), std::addressof(rights_id), Convert(content_id), Convert(fs::ContentAttributes_None)));
static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id));
std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer()));
R_SUCCEED();
}
Result GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id) {
R_RETURN(this->GetRightsIdFromContentId(out_rights_id, content_id, fs::ContentAttributes_None));
}
Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr) {
::NcmRightsId rights_id;
R_TRY(ncmContentStorageGetRightsIdFromContentId(std::addressof(m_srv), std::addressof(rights_id), Convert(content_id), Convert(attr)));
static_assert(sizeof(*out_rights_id.GetPointer()) <= sizeof(rights_id));
std::memcpy(out_rights_id.GetPointer(), std::addressof(rights_id), sizeof(*out_rights_id.GetPointer()));
R_SUCCEED();
}
Result WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data) {
R_RETURN(ncmContentStorageWriteContentForDebug(std::addressof(m_srv), Convert(content_id), offset, data.GetPointer(), data.GetSize()));
}
Result GetFreeSpaceSize(sf::Out<s64> out_size) {
R_RETURN(ncmContentStorageGetFreeSpaceSize(std::addressof(m_srv), out_size.GetPointer()));
}
Result GetTotalSpaceSize(sf::Out<s64> out_size) {
R_RETURN(ncmContentStorageGetTotalSpaceSize(std::addressof(m_srv), out_size.GetPointer()));
}
Result FlushPlaceHolder() {
R_RETURN(ncmContentStorageFlushPlaceHolder(std::addressof(m_srv)));
}
Result GetSizeFromPlaceHolderId(sf::Out<s64> out_size, PlaceHolderId placeholder_id) {
R_RETURN(ncmContentStorageGetSizeFromPlaceHolderId(std::addressof(m_srv), out_size.GetPointer(), Convert(placeholder_id)));
}
Result RepairInvalidFileAttribute() {
R_RETURN(ncmContentStorageRepairInvalidFileAttribute(std::addressof(m_srv)));
}
Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) {
static_assert(sizeof(::NcmRightsId) == sizeof(ncm::RightsId));
::NcmRightsId *out = reinterpret_cast<::NcmRightsId *>(out_rights_id.GetPointer());
R_RETURN(ncmContentStorageGetRightsIdFromPlaceHolderIdWithCache(std::addressof(m_srv), out, Convert(placeholder_id), Convert(cache_content_id), Convert(attr)));
}
Result RegisterPath(const ContentId &content_id, const Path &path) {
R_RETURN(ncmContentStorageRegisterPath(std::addressof(m_srv), Convert(content_id), path.str));
}
Result ClearRegisteredPath() {
R_RETURN(ncmContentStorageClearRegisteredPath(std::addressof(m_srv)));
}
Result GetProgramId(sf::Out<ncm::ProgramId> out, ContentId content_id, fs::ContentAttributes attr) {
static_assert(sizeof(ncm::ProgramId) == sizeof(u64));
R_RETURN(ncmContentStorageGetProgramId(std::addressof(m_srv), reinterpret_cast<u64 *>(out.GetPointer()), Convert(content_id), Convert(attr)));
}
/* 16.0.0 Alignment change hacks. */
Result CreatePlaceHolder_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id, s64 size) { R_RETURN(this->CreatePlaceHolder(placeholder_id, content_id, size)); }
Result Register_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id) { R_RETURN(this->Register(placeholder_id, content_id)); }
Result RevertToPlaceHolder_AtmosphereAlignmentFix(ncm::ContentId old_content_id, ncm::ContentId new_content_id, ncm::PlaceHolderId placeholder_id) { R_RETURN(this->RevertToPlaceHolder(placeholder_id, old_content_id, new_content_id)); }
Result GetRightsIdFromPlaceHolderIdWithCacheDeprecated(sf::Out<ncm::RightsId> out_rights_id, ContentId cache_content_id, PlaceHolderId placeholder_id) { R_RETURN(this->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id, fs::ContentAttributes_None)); }
};
static_assert(ncm::IsIContentStorage<RemoteContentStorageImpl>);
#endif
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright (c) 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 SelectDownloadableStorage(StorageId *out_storage_id, StorageId storage_id, s64 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;
R_SUCCEED();
}
R_THROW(ncm::ResultNotEnoughInstallSpace());
}
Result SelectPatchStorage(StorageId *out_storage_id, StorageId storage_id, PatchId patch_id) {
auto list = GetStorageList(storage_id);
u32 version = 0;
*out_storage_id = storage_id;
for (s32 i = 0; i < list.Count(); i++) {
auto candidate = list[i];
/* Open the content meta database. */
ContentMetaDatabase content_meta_database;
if (R_FAILED(ncm::OpenContentMetaDatabase(std::addressof(content_meta_database), candidate))) {
continue;
}
/* Get the latest key. */
ContentMetaKey key;
R_TRY_CATCH(content_meta_database.GetLatest(std::addressof(key), patch_id.value)) {
R_CATCH(ncm::ResultContentMetaNotFound) { continue; }
} R_END_TRY_CATCH;
if (key.version > version) {
version = key.version;
*out_storage_id = candidate;
}
}
R_SUCCEED();
}
const char *GetStorageIdString(StorageId storage_id) {
switch (storage_id) {
case StorageId::None: return "None";
case StorageId::Host: return "Host";
case StorageId::GameCard: return "GameCard";
case StorageId::BuiltInSystem: return "BuiltInSystem";
case StorageId::BuiltInUser: return "BuiltInUser";
default: return "(unknown)";
}
}
const char *GetStorageIdStringForPlayReport(StorageId storage_id) {
switch (storage_id) {
case StorageId::None: return "None";
case StorageId::Host: return "Host";
case StorageId::Card: return "Card";
case StorageId::BuildInSystem: return "BuildInSystem";
case StorageId::BuildInUser: return "BuildInUser";
default: return "(unknown)";
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (c) 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>
#include "ncm_fs_utils.hpp"
namespace ams::ncm {
class SubmissionPackageInstallTask::Impl {
private:
fs::FileHandleStorage m_storage;
util::optional<impl::MountName> m_mount_name;
public:
explicit Impl(fs::FileHandle file) : m_storage(file), m_mount_name(util::nullopt) { /* ... */ }
~Impl() {
if (m_mount_name) {
fs::fsa::Unregister(m_mount_name->str);
}
}
Result Initialize() {
AMS_ASSERT(!m_mount_name);
/* Allocate a partition file system. */
auto partition_file_system = std::make_unique<fssystem::PartitionFileSystem>();
R_UNLESS(partition_file_system != nullptr, ncm::ResultAllocationFailed());
/* Initialize the partition file system. */
R_TRY(partition_file_system->Initialize(std::addressof(m_storage)));
/* Create a mount name and register the file system. */
auto mount_name = impl::CreateUniqueMountName();
R_TRY(fs::fsa::Register(mount_name.str, std::move(partition_file_system)));
/* Initialize members. */
m_mount_name = mount_name;
R_SUCCEED();
}
const impl::MountName &GetMountName() const {
return *m_mount_name;
}
};
SubmissionPackageInstallTask::SubmissionPackageInstallTask() : m_impl(nullptr) { /* ... */ }
SubmissionPackageInstallTask::~SubmissionPackageInstallTask() { /* ... */ }
Result SubmissionPackageInstallTask::Initialize(fs::FileHandle file, StorageId storage_id, void *buffer, size_t buffer_size, bool ignore_ticket) {
AMS_ASSERT(!m_impl);
/* Allocate impl. */
m_impl.reset(new (std::nothrow) Impl(file));
R_UNLESS(m_impl != nullptr, ncm::ResultAllocationFailed());
/* Initialize impl. */
R_TRY(m_impl->Initialize());
/* Initialize parent. N doesn't check the result. */
PackageInstallTask::Initialize(impl::GetRootDirectoryPath(m_impl->GetMountName()).str, storage_id, buffer, buffer_size, ignore_ticket);
R_SUCCEED();
}
}