diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp
index b59a88f4c..343073124 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp
@@ -17,5 +17,6 @@
#pragma once
#include "ncm/ncm_types.hpp"
+#include "ncm/ncm_content_meta.hpp"
#include "ncm/ncm_i_content_meta_database.hpp"
#include "ncm/ncm_i_content_storage.hpp"
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp
new file mode 100644
index 000000000..836ee56e6
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_meta.hpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2018-2020 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 .
+ */
+
+#pragma once
+#include
+
+namespace ams::ncm {
+
+ struct ContentMetaHeader {
+ u16 extended_header_size;
+ u16 content_count;
+ u16 content_meta_count;
+ ContentMetaAttribute attributes;
+ StorageId storage_id;
+ };
+
+ static_assert(sizeof(ContentMetaHeader) == 0x8, "ContentMetaHeader definition!");
+
+ struct ApplicationMetaExtendedHeader {
+ ProgramId patch_id;
+ u32 required_system_version;
+ u32 required_application_version;
+ };
+
+ struct PatchMetaExtendedHeader {
+ ProgramId application_id;
+ u32 required_system_version;
+ u32 extended_data_size;
+ u8 reserved[0x8];
+ };
+
+ struct AddOnContentMetaExtendedHeader {
+ ProgramId application_id;
+ u32 required_application_version;
+ u32 padding;
+ };
+
+ struct DeltaMetaExtendedHeader {
+ ProgramId application_id;
+ u32 extended_data_size;
+ u32 padding;
+ };
+
+ template
+ class ContentMetaAccessor {
+ public:
+ using HeaderType = ContentMetaHeaderType;
+ using InfoType = ContentInfoType;
+ private:
+ void *data;
+ const size_t size;
+ bool is_header_valid;
+ private:
+ static size_t GetExtendedHeaderSize(ContentMetaType type) {
+ switch (type) {
+ case ContentMetaType::Application: return sizeof(ApplicationMetaExtendedHeader);
+ case ContentMetaType::Patch: return sizeof(PatchMetaExtendedHeader);
+ case ContentMetaType::AddOnContent: return sizeof(AddOnContentMetaExtendedHeader);
+ case ContentMetaType::Delta: return sizeof(DeltaMetaExtendedHeader);
+ default: return 0;
+ }
+ }
+ protected:
+ constexpr ContentMetaAccessor(const void *d, size_t sz) : data(const_cast(d)), size(sz), is_header_valid(true) { /* ... */ }
+ constexpr ContentMetaAccessor(void *d, size_t sz) : data(d), size(sz), is_header_valid(false) { /* ... */ }
+
+ template
+ static constexpr size_t CalculateSizeImpl(size_t ext_header_size, size_t content_count, size_t content_meta_count, size_t extended_data_size, bool has_digest) {
+ return sizeof(NewHeaderType) + ext_header_size + content_count * sizeof(NewInfoType) + content_meta_count * sizeof(ContentMetaInfo) + extended_data_size + (has_digest ? sizeof(Digest) : 0);
+ }
+
+ static constexpr size_t CalculateSize(ContentMetaType type, size_t content_count, size_t content_meta_count, size_t extended_data_size, bool has_digest = false) {
+ return CalculateSizeImpl(GetExtendedHeaderSize(type), content_count, content_meta_count, extended_data_size, has_digest);
+ }
+
+ uintptr_t GetExtendedHeaderAddress() const {
+ return reinterpret_cast(this->data) + sizeof(HeaderType);
+ }
+
+ uintptr_t GetContentInfoStartAddress() const {
+ return this->GetExtendedHeaderAddress() + this->GetExtendedHeaderSize();
+ }
+
+ uintptr_t GetContentInfoAddress(size_t i) const {
+ return this->GetContentInfoStartAddress() + i * sizeof(InfoType);
+ }
+
+ uintptr_t GetContentMetaInfoStartAddress() const {
+ return this->GetContentInfoAddress(this->GetContentCount());
+ }
+
+ uintptr_t GetContentMetaInfoAddress(size_t i) const {
+ return this->GetContentMetaInfoStartAddress() + i * sizeof(ContentMetaInfo);
+ }
+
+ uintptr_t GetExtendedDataAddress() const {
+ return this->GetContentMetaInfoAddress(this->GetContentMetaCount());
+ }
+
+ uintptr_t GetDigestAddress() const {
+ return this->GetExtendedDataAddress() + this->GetExtendedDataSize();
+ }
+
+ InfoType *GetWritableContentInfo(size_t i) const {
+ AMS_ABORT_UNLESS(i < this->GetContentCount());
+
+ return reinterpret_cast(this->GetContentInfoAddress(i));
+ }
+
+ InfoType *GetWritableContentInfo(ContentType type) const {
+ InfoType *found = nullptr;
+ for (size_t i = 0; i < this->GetContentCount(); i++) {
+ /* We want to find the info with the lowest id offset and the correct type. */
+ InfoType *info = this->GetWritableContentInfo(i);
+ if (info->GetType() == type && (found == nullptr || info->GetIdOffset() < found->GetIdOffset())) {
+ found = info;
+ }
+ }
+ return found;
+ }
+
+ InfoType *GetWritableContentInfo(ContentType type, u8 id_ofs) const {
+ for (size_t i = 0; i < this->GetContentCount(); i++) {
+ /* We want to find the info with the correct id offset and the correct type. */
+ if (InfoType *info = this->GetWritableContentInfo(i); info->GetType() == type && info->GetIdOffset() == id_ofs) {
+ return info;
+ }
+ }
+ return nullptr;
+ }
+
+ public:
+ const void *GetData() const {
+ return this->data;
+ }
+
+ size_t GetSize() const {
+ return this->size;
+ }
+
+ const HeaderType *GetHeader() const {
+ AMS_ABORT_UNLESS(this->is_header_valid);
+ return static_cast(this->data);
+ }
+
+ ContentMetaKey GetKey() const {
+ auto header = this->GetHeader();
+ return ContentMetaKey::Make(header->id, header->version, header->type, header->install_type);
+ }
+
+ size_t GetExtendedHeaderSize() const {
+ return this->GetHeader()->extended_header_size;
+ }
+
+ template
+ const ExtendedHeaderType *GetExtendedHeader() const {
+ return reinterpret_cast(this->GetExtendedHeaderAddress());
+ }
+
+ size_t GetContentCount() const {
+ return this->GetHeader()->content_count;
+ }
+
+ const InfoType *GetContentInfo(size_t i) const {
+ AMS_ABORT_UNLESS(i < this->GetContentCount());
+
+ return this->GetWritableContentInfo(i);
+ }
+
+ const InfoType *GetContentInfo(ContentType type) const {
+ return this->GetWritableContentInfo(type);
+ }
+
+ const InfoType *GetContentInfo(ContentType type, u8 id_ofs) const {
+ return this->GetWritableContentInfo(type, id_ofs);
+ }
+
+ size_t GetContentMetaCount() const {
+ return this->GetHeader()->content_meta_count;
+ }
+
+ const ContentMetaInfo *GetContentMetaInfo(size_t i) const {
+ AMS_ABORT_UNLESS(i < this->GetContentMetaCount());
+
+ return reinterpret_cast(this->GetContentMetaInfoAddress(i));
+ }
+
+ size_t GetExtendedDataSize() const {
+ switch (this->GetHeader()->type) {
+ case ContentMetaType::Patch: return this->GetExtendedHeader()->extended_data_size;
+ case ContentMetaType::Delta: return this->GetExtendedHeader()->extended_data_size;
+ default: return 0;
+ }
+ }
+
+ const void *GetExtendedData() const {
+ return reinterpret_cast(this->GetExtendedDataAddress());
+ }
+
+ const Digest *GetDigest() const {
+ return reinterpret_cast(this->GetDigestAddress());
+ }
+
+ bool HasContent(const ContentId &id) const {
+ for (size_t i = 0; i < this->GetContentCount(); i++) {
+ if (id == this->GetContentInfo(i)->GetId()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ std::optional GetApplicationId(const ContentMetaKey &key) const {
+ switch (key.type) {
+ case ContentMetaType::Application: return key.id;
+ case ContentMetaType::Patch: return this->GetExtendedHeader()->application_id;
+ case ContentMetaType::AddOnContent: return this->GetExtendedHeader()->application_id;
+ case ContentMetaType::Delta: return this->GetExtendedHeader()->application_id;
+ default: return std::nullopt;
+ }
+ }
+
+ std::optional GetApplicationId() const {
+ return this->GetApplicationId(this->GetKey());
+ }
+ };
+
+ class ContentMetaReader : public ContentMetaAccessor {
+ public:
+ constexpr ContentMetaReader(const void *data, size_t size) : ContentMetaAccessor(data, size) { /* ... */ }
+
+ using ContentMetaAccessor::CalculateSize;
+ };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp
index eb74addec..f09c7a244 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp
@@ -69,10 +69,6 @@ namespace ams::ncm {
virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) = 0;
virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) = 0;
virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) = 0;
-
- /* APIs. */
- virtual Result GetLatestProgram(ContentId *out_content_id, ProgramId program_id) = 0;
- virtual Result GetLatestData(ContentId *out_content_id, ProgramId program_id) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Set),
diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
index d370c40dd..feb560361 100644
--- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
+++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_types.hpp
@@ -108,12 +108,42 @@ namespace ams::ncm {
struct ContentInfo {
ContentId content_id;
- u8 size[6];
+ u32 size_low;
+ u16 size_high;
ContentType content_type;
u8 id_offset;
+
+ constexpr const ContentId &GetId() const {
+ return this->content_id;
+ }
+
+ constexpr u64 GetSize() const {
+ return (static_cast(this->size_high) << 32) | static_cast(this->size_low);
+ }
+
+ constexpr ContentType GetType() const {
+ return this->content_type;
+ }
+
+ constexpr u8 GetIdOffset() const {
+ return this->id_offset;
+ }
+
+ static constexpr ContentInfo Make(ContentId id, u64 size, ContentType type, u8 id_ofs) {
+ const u32 size_low = size & 0xFFFFFFFFu;
+ const u16 size_high = static_cast(size >> 32);
+ return {
+ .content_id = id,
+ .size_low = size_low,
+ .size_high = size_high,
+ .content_type = type,
+ .id_offset = id_ofs,
+ };
+ }
};
- static_assert(sizeof(ContentInfo) == 0x18, "ContentInfo definition!");
+ static_assert(sizeof(std::is_pod::value));
+ static_assert(sizeof(ContentInfo) == 0x18);
using MakeContentPathFunc = void (*)(char *out, ContentId content_id, const char *root);
using MakePlaceHolderPathFunc = void (*)(char *out, PlaceHolderId placeholder_id, const char *root);
@@ -564,19 +594,19 @@ namespace ams::ncm {
} else if (this->id != other.id) {
return false;
}
-
+
if (this->version < other.version) {
return true;
} else if (this->version != other.version) {
return false;
}
-
+
if (this->type < other.type) {
return true;
} else if (this->type != other.type) {
return false;
}
-
+
return this->install_type < other.install_type;
}
@@ -602,8 +632,29 @@ namespace ams::ncm {
static_assert(sizeof(ContentMetaKey) == 0x10, "ContentMetaKey definition!");
- /* Used by system updates. They share the exact same struct as ContentMetaKey */
- using ContentMetaInfo = ContentMetaKey;
+ /* Used by system updates. */
+ struct ContentMetaInfo {
+ ProgramId id;
+ u32 version;
+ ContentMetaType type;
+ u8 attributes;
+ u8 padding[2];
+
+ static constexpr ContentMetaInfo Make(ProgramId program_id, u32 version, ContentMetaType type, u8 attributes) {
+ return {
+ .id = program_id,
+ .version = version,
+ .type = type,
+ .attributes = attributes,
+ };
+ }
+
+ constexpr ContentMetaKey ToKey() {
+ return ContentMetaKey::Make(this->id, this->version, this->type);
+ }
+ };
+
+ static_assert(sizeof(ContentMetaInfo) == 0x10);
struct ApplicationContentMetaKey {
ContentMetaKey key;
@@ -612,4 +663,8 @@ namespace ams::ncm {
static_assert(sizeof(ApplicationContentMetaKey) == 0x18, "ApplicationContentMetaKey definition!");
+ struct Digest {
+ u8 data[crypto::Sha256Generator::HashSize];
+ };
+
}
diff --git a/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp b/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp
index d3cbbe5cf..0612cafbe 100644
--- a/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp
+++ b/stratosphere/ncm/source/ncm_content_meta_database_impl.cpp
@@ -19,131 +19,52 @@
namespace ams::ncm {
- namespace {
-
- struct ContentMetaHeader {
- u16 extended_header_size;
- u16 content_count;
- u16 content_meta_count;
- ContentMetaAttribute attributes;
- StorageId storage_id;
- };
-
- static_assert(sizeof(ContentMetaHeader) == 0x8, "ContentMetaHeader definition!");
-
- struct ApplicationMetaExtendedHeader {
- ProgramId patch_id;
- u32 required_system_version;
- u32 required_application_version;
- };
-
- struct PatchMetaExtendedHeader {
- ProgramId application_id;
- u32 required_system_version;
- u32 extended_data_size;
- u8 reserved[0x8];
- };
-
- struct AddOnContentMetaExtendedHeader {
- ProgramId application_id;
- u32 required_application_version;
- u32 padding;
- };
-
- struct SystemUpdateMetaExtendedHeader {
- u32 extended_data_size;
- };
-
- template
- inline const ExtendedHeaderType *GetValueExtendedHeader(const ContentMetaHeader *header) {
- return reinterpret_cast(header + 1);
- }
-
- inline const ContentInfo *GetValueContentInfos(const ContentMetaHeader *header) {
- return reinterpret_cast(reinterpret_cast(GetValueExtendedHeader(header)) + header->extended_header_size);
- }
-
- inline const ContentMetaInfo *GetValueContentMetaInfos(const ContentMetaHeader *header) {
- return reinterpret_cast(GetValueContentInfos(header) + header->content_count);
- }
-
- Result GetContentMetaSize(size_t *out, const ContentMetaKey &key, const kvdb::MemoryKeyValueStore *kvs) {
- R_TRY_CATCH(kvs->GetValueSize(out, key)) {
- R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
- } R_END_TRY_CATCH;
-
- return ResultSuccess();
- }
-
- Result GetContentMetaValuePointer(const ContentMetaHeader **out_value_ptr, size_t *out_size, const ContentMetaKey &key, const kvdb::MemoryKeyValueStore *kvs) {
- R_TRY(GetContentMetaSize(out_size, key, kvs));
- return kvs->GetValuePointer(out_value_ptr, key);
- }
-
- }
-
Result ContentMetaDatabaseImpl::GetContentIdByTypeImpl(ContentId *out, const ContentMetaKey& key, ContentType type, std::optional id_offset) {
R_TRY(this->EnsureEnabled());
+ /* Find the meta key. */
ContentMetaKeyValueStore::Entry *entry = nullptr;
R_TRY(this->FindContentMetaKeyValue(std::addressof(entry), key));
-
const auto stored_key = entry->GetKey();
- const ContentMetaHeader *header = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&header, &value_size, stored_key, this->kvs));
+ /* Create a reader for this content meta. */
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, stored_key));
- R_UNLESS(header->content_count != 0, ncm::ResultContentNotFound());
-
- const ContentInfo *content_infos = GetValueContentInfos(header);
- const ContentInfo *found_content_info = nullptr;
+ ContentMetaReader reader(meta, meta_size);
+ /* Find the content info. */
+ const ContentInfo *content_info = nullptr;
if (id_offset) {
- for (size_t i = 0; i < header->content_count; i++) {
- const ContentInfo *content_info = &content_infos[i];
-
- if (content_info->content_type == type && content_info->id_offset == *id_offset) {
- found_content_info = content_info;
- break;
- }
- }
+ content_info = reader.GetContentInfo(type, *id_offset);
} else {
- const ContentInfo *lowest_id_offset_info = nullptr;
-
- for (size_t i = 0; i < header->content_count; i++) {
- const ContentInfo *content_info = &content_infos[i];
-
- if (content_info->content_type == type && (!lowest_id_offset_info || lowest_id_offset_info->id_offset > content_info->id_offset)) {
- lowest_id_offset_info = content_info;
- }
- }
-
- found_content_info = lowest_id_offset_info;
+ content_info = reader.GetContentInfo(type);
}
+ R_UNLESS(content_info != nullptr, ncm::ResultContentNotFound());
- R_UNLESS(found_content_info, ncm::ResultContentNotFound());
- *out = found_content_info->content_id;
+ /* Save output. */
+ *out = content_info->content_id;
return ResultSuccess();
}
Result ContentMetaDatabaseImpl::GetLatestContentMetaKeyImpl(ContentMetaKey *out_key, ProgramId id) {
R_TRY(this->EnsureEnabled());
-
- ContentMetaKey key = {0};
- key.id = id;
- for (auto entry = this->kvs->lower_bound(key); entry != this->kvs->end(); entry++) {
- if (entry->GetKey().id != key.id) {
+
+ std::optional found_key = std::nullopt;
+ for (auto entry = this->kvs->lower_bound(ContentMetaKey::Make(id, 0, ContentMetaType::Unknown)); entry != this->kvs->end(); entry++) {
+ if (entry->GetKey().id != id) {
break;
}
if (entry->GetKey().install_type == ContentInstallType::Full) {
- *out_key = entry->GetKey();
- return ResultSuccess();
+ found_key = entry->GetKey();
}
}
+ R_UNLESS(found_key, ncm::ResultContentMetaNotFound());
- return ncm::ResultContentMetaNotFound();
+ *out_key = *found_key;
+ return ResultSuccess();
}
Result ContentMetaDatabaseImpl::Set(const ContentMetaKey &key, sf::InBuffer value) {
@@ -169,62 +90,45 @@ namespace ams::ncm {
R_UNLESS(offset <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
- const ContentMetaHeader *header = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&header, &value_size, key, this->kvs));
- const auto content_infos = GetValueContentInfos(header);
-
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
+
+ ContentMetaReader reader(meta, meta_size);
+
size_t count;
- for (count = 0; offset + count < header->content_count && count < out_info.GetSize(); count++) {
- out_info[count] = content_infos[offset + count];
+ for (count = 0; count < out_info.GetSize() && count + offset < reader.GetContentCount(); count++) {
+ out_info[count] = *reader.GetContentInfo(offset + count);
}
out_count.SetValue(count);
return ResultSuccess();
}
- Result ContentMetaDatabaseImpl::List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType type, ProgramId application_program_id, ProgramId program_id_min, ProgramId program_id_max, ContentInstallType install_type) {
+ Result ContentMetaDatabaseImpl::List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType type, ProgramId application_id, ProgramId program_id_min, ProgramId program_id_max, ContentInstallType install_type) {
R_TRY(this->EnsureEnabled());
-
+
size_t entries_total = 0;
size_t entries_written = 0;
- /* If there are no entries then we've already successfully listed them all. */
- if (this->kvs->GetCount() == 0) {
- out_entries_total.SetValue(entries_total);
- out_entries_written.SetValue(entries_written);
- return ResultSuccess();
- }
-
for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
ContentMetaKey key = entry->GetKey();
/* Check if this entry matches the given filters. */
- if (!((type == ContentMetaType::Unknown || key.type == type) && (program_id_min <= key.id && key.id <= program_id_max) && (key.install_type == ContentInstallType::Unknown || key.install_type == install_type))) {
+ if (!((type == ContentMetaType::Unknown || key.type == type) && (program_id_min <= key.id && key.id <= program_id_max) && (install_type == ContentInstallType::Unknown || key.install_type == install_type))) {
continue;
}
- if (static_cast(application_program_id) != 0) {
- size_t meta_size = 0;
- const ContentMetaHeader *meta = nullptr;
- R_TRY(GetContentMetaValuePointer(&meta, &meta_size, key, this->kvs));
+ /* If application id is present, check if it matches the filter. */
+ if (application_id != InvalidProgramId) {
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
- /* Each of these types are owned by an application. We need to check if their owner application matches the filter. */
- if (key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch || key.type == ContentMetaType::AddOnContent || key.type == ContentMetaType::Delta) {
- ProgramId entry_application_id = key.id;
-
- switch (key.type) {
- case ContentMetaType::Application:
- break;
- default:
- /* The first u64 of all non-application extended headers is the application program id. */
- entry_application_id = *GetValueExtendedHeader(meta);
- }
+ ContentMetaReader reader(meta, meta_size);
- /* Application id doesn't match filter, skip this entry. */
- if (entry_application_id != application_program_id) {
- continue;
- }
+ if (const auto entry_application_id = reader.GetApplicationId(key); entry_application_id && application_id != *entry_application_id) {
+ continue;
}
}
@@ -232,7 +136,6 @@ namespace ams::ncm {
if (entries_written < out_info.GetSize()) {
out_info[entries_written++] = key;
}
-
entries_total++;
}
@@ -241,54 +144,33 @@ namespace ams::ncm {
return ResultSuccess();
}
- Result ContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out out_key, ProgramId program_id) {
+ Result ContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out out_key, ProgramId program_id) {
R_TRY(this->EnsureEnabled());
return this->GetLatestContentMetaKeyImpl(out_key.GetPointer(), program_id);
}
Result ContentMetaDatabaseImpl::ListApplication(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_keys, ContentMetaType type) {
R_TRY(this->EnsureEnabled());
-
+
size_t entries_total = 0;
size_t entries_written = 0;
- /* If there are no entries then we've already successfully listed them all. */
- if (this->kvs->GetCount() == 0) {
- out_entries_total.SetValue(entries_total);
- out_entries_written.SetValue(entries_written);
- return ResultSuccess();
- }
-
for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
ContentMetaKey key = entry->GetKey();
/* Check if this entry matches the given filters. */
- if (!((static_cast(type) == 0 || key.type == type))) {
+ if (!((type == ContentMetaType::Unknown || key.type == type))) {
continue;
}
- const ContentMetaHeader *value = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&value, &value_size, key, this->kvs));
-
- if (key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch || key.type == ContentMetaType::AddOnContent || key.type == ContentMetaType::Delta) {
- ProgramId application_id = key.id;
-
- switch (key.type) {
- case ContentMetaType::Application:
- break;
- default:
- /* The first u64 of all non-application extended headers is the application program id. */
- application_id = *GetValueExtendedHeader(value);
- }
+ /* 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()) {
- ApplicationContentMetaKey *out_app_key = &out_keys[entries_written++];
- out_app_key->application_program_id = application_id;
- out_app_key->key = key;
+ out_keys[entries_written++] = { key, *entry_application_id };
}
-
entries_total++;
}
}
@@ -301,23 +183,21 @@ namespace ams::ncm {
Result ContentMetaDatabaseImpl::Has(sf::Out out, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
- if (this->kvs->GetCount() == 0) {
- out.SetValue(false);
- return ResultSuccess();
- }
+ *out = false;
- bool has = false;
- const auto it = this->kvs->lower_bound(key);
- if (it != this->kvs->end()) {
- has = it->GetKey() == key;
- }
+ size_t size;
+ R_TRY_CATCH(this->kvs->GetValueSize(&size, key)) {
+ R_CONVERT(kvdb::ResultKeyNotFound, ResultSuccess());
+ } R_END_TRY_CATCH;
- out.SetValue(has);
+ *out = true;
return ResultSuccess();
}
Result ContentMetaDatabaseImpl::HasAll(sf::Out out, const sf::InArray &keys) {
R_TRY(this->EnsureEnabled());
+ *out = false;
+
for (size_t i = 0; i < keys.GetSize(); i++) {
bool has;
R_TRY(this->Has(std::addressof(has), keys[i]));
@@ -330,13 +210,11 @@ namespace ams::ncm {
Result ContentMetaDatabaseImpl::GetSize(sf::Out out_size, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
- R_UNLESS(this->kvs->GetCount() != 0, ncm::ResultContentMetaNotFound());
- ContentMetaKeyValueStore::Entry *entry = nullptr;
- R_TRY(this->FindContentMetaKeyValue(std::addressof(entry), key));
-
- out_size.SetValue(entry->GetValueSize());
+ size_t size;
+ R_TRY(this->GetContentMetaSize(&size, key));
+ out_size.SetValue(size);
return ResultSuccess();
}
@@ -344,14 +222,16 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled());
R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch, ncm::ResultInvalidContentMetaKey());
- const ContentMetaHeader *value = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&value, &value_size, key, this->kvs));
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
+
+ ContentMetaReader reader(meta, meta_size);
if (key.type == ContentMetaType::Application) {
- out_version.SetValue(GetValueExtendedHeader(value)->required_system_version);
+ out_version.SetValue(reader.GetExtendedHeader()->required_system_version);
} else {
- out_version.SetValue(GetValueExtendedHeader(value)->required_system_version);
+ out_version.SetValue(reader.GetExtendedHeader()->required_system_version);
}
return ResultSuccess();
@@ -361,11 +241,13 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled());
R_UNLESS(key.type == ContentMetaType::Application, ncm::ResultInvalidContentMetaKey());
- const ContentMetaHeader *value = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&value, &value_size, key, this->kvs));
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
- out_patch_id.SetValue(GetValueExtendedHeader(value)->patch_id);
+ ContentMetaReader reader(meta, meta_size);
+
+ out_patch_id.SetValue(reader.GetExtendedHeader()->patch_id);
return ResultSuccess();
}
@@ -382,25 +264,25 @@ namespace ams::ncm {
for (size_t i = 0; i < out_orphaned.GetSize(); i++) {
out_orphaned[i] = true;
}
-
+
R_UNLESS(this->kvs->GetCount() != 0, ResultSuccess());
-
- for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
- const auto header = reinterpret_cast(entry->GetValuePointer());
- const ContentInfo *content_infos = GetValueContentInfos(header);
- auto IsOrphanedContent = [](const sf::InArray &list, const ncm::ContentId &id) ALWAYS_INLINE_LAMBDA {
- for (size_t i = 0; i < list.GetSize(); i++) {
- if (list[i] == id) {
- return std::make_optional(i);
- }
+
+ auto IsOrphanedContent = [](const sf::InArray &list, const ncm::ContentId &id) ALWAYS_INLINE_LAMBDA {
+ for (size_t i = 0; i < list.GetSize(); i++) {
+ if (list[i] == id) {
+ return std::make_optional(i);
}
- return std::optional(std::nullopt);
- };
+ }
+ return std::optional(std::nullopt);
+ };
+
+ for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) {
+ 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 < header->content_count; i++) {
- if (auto found = IsOrphanedContent(content_ids, content_infos[i].content_id); found) {
+ for (size_t i = 0; i < reader.GetContentCount(); i++) {
+ if (auto found = IsOrphanedContent(content_ids, reader.GetContentInfo(i)->GetId()); found) {
out_orphaned[*found] = false;
}
}
@@ -416,17 +298,15 @@ namespace ams::ncm {
}
Result ContentMetaDatabaseImpl::HasContent(sf::Out out, const ContentMetaKey &key, ContentId content_id) {
- const ContentMetaHeader *header = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&header, &value_size, key, this->kvs));
- const ContentInfo *content_infos = GetValueContentInfos(header);
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
- if (header->content_count > 0) {
- for (size_t i = 0; i < header->content_count; i++) {
- if (content_id == content_infos[i].content_id) {
- out.SetValue(true);
- return ResultSuccess();
- }
+ ContentMetaReader reader(meta, meta_size);
+ for (size_t i = 0; i < reader.GetContentCount(); i++) {
+ if (content_id == reader.GetContentInfo(i)->GetId()) {
+ out.SetValue(true);
+ return ResultSuccess();
}
}
@@ -434,74 +314,64 @@ namespace ams::ncm {
return ResultSuccess();
}
- Result ContentMetaDatabaseImpl::ListContentMetaInfo(sf::Out out_entries_written, const sf::OutArray &out_meta_info, const ContentMetaKey &key, u32 start_index) {
- R_UNLESS(start_index <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
+ Result ContentMetaDatabaseImpl::ListContentMetaInfo(sf::Out out_entries_written, const sf::OutArray &out_meta_info, const ContentMetaKey &key, u32 offset) {
+ R_UNLESS(offset <= std::numeric_limits::max(), ncm::ResultInvalidOffset());
R_TRY(this->EnsureEnabled());
-
- const ContentMetaHeader *header = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&header, &value_size, key, this->kvs));
- const auto content_meta_infos = GetValueContentMetaInfos(header);
- size_t entries_written = 0;
- for (size_t i = start_index; i < out_meta_info.GetSize(); i++) {
- /* We have no more entries we can read out. */
- if (header->content_meta_count <= start_index + i) {
- break;
- }
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
- out_meta_info[i] = content_meta_infos[i];
- entries_written = i + 1;
+ ContentMetaReader reader(meta, meta_size);
+
+ size_t count;
+ for (count = 0; count < out_meta_info.GetSize() && count + offset <= reader.GetContentMetaCount(); count++) {
+ out_meta_info[count] = *reader.GetContentMetaInfo(count + offset);
}
- out_entries_written.SetValue(entries_written);
+ out_entries_written.SetValue(count);
return ResultSuccess();
}
Result ContentMetaDatabaseImpl::GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
- const ContentMetaHeader *header = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&header, &value_size, key, this->kvs));
- out_attributes.SetValue(header->attributes);
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
+
+ ContentMetaReader reader(meta, meta_size);
+
+ out_attributes.SetValue(reader.GetHeader()->attributes);
return ResultSuccess();
}
Result ContentMetaDatabaseImpl::GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) {
R_TRY(this->EnsureEnabled());
- const ContentMetaHeader *value = nullptr;
- size_t value_size = 0;
- R_TRY(GetContentMetaValuePointer(&value, &value_size, key, this->kvs));
+ const void *meta;
+ size_t meta_size;
+ R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key));
- /* As of 9.0.0, applications can be dependent on a specific base application version. */
- if (hos::GetVersion() >= hos::Version_900 && key.type == ContentMetaType::Application) {
- out_version.SetValue(GetValueExtendedHeader(value)->required_application_version);
- return ResultSuccess();
+ ContentMetaReader reader(meta, meta_size);
+
+ /* Get the required version. */
+ u32 required_version;
+ if (key.type == ContentMetaType::Application && hos::GetVersion() >= hos::Version_900) {
+ /* As of 9.0.0, applications can be dependent on a specific base application version. */
+ required_version = reader.GetExtendedHeader()->required_application_version;
+ } else if (key.type == ContentMetaType::AddOnContent) {
+ required_version = reader.GetExtendedHeader()->required_application_version;
+ } else {
+ return ncm::ResultInvalidContentMetaKey();
}
- R_UNLESS(key.type == ContentMetaType::AddOnContent, ncm::ResultInvalidContentMetaKey());
- out_version.SetValue(GetValueExtendedHeader(value)->required_application_version);
+ out_version.SetValue(required_version);
return ResultSuccess();
}
Result ContentMetaDatabaseImpl::GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) {
- return this->GetContentIdByTypeImpl(out_content_id.GetPointer(), key, type, std::optional(id_offset));
- }
-
- Result ContentMetaDatabaseImpl::GetLatestProgram(ContentId *out_content_id, ProgramId program_id) {
- ContentMetaKey key;
-
- R_TRY(this->GetLatestContentMetaKey(&key, program_id));
- return this->GetContentIdByType(out_content_id, key, ContentType::Program);
- }
-
- Result ContentMetaDatabaseImpl::GetLatestData(ContentId *out_content_id, ProgramId program_id) {
- ContentMetaKey key;
-
- R_TRY(this->GetLatestContentMetaKey(&key, program_id));
- return this->GetContentIdByType(out_content_id, key, ContentType::Data);
+ return this->GetContentIdByTypeImpl(out_content_id.GetPointer(), key, type, std::make_optional(id_offset));
}
}
diff --git a/stratosphere/ncm/source/ncm_content_meta_database_impl.hpp b/stratosphere/ncm/source/ncm_content_meta_database_impl.hpp
index d57a7e7f2..e3eb9b884 100644
--- a/stratosphere/ncm/source/ncm_content_meta_database_impl.hpp
+++ b/stratosphere/ncm/source/ncm_content_meta_database_impl.hpp
@@ -50,10 +50,6 @@ namespace ams::ncm {
virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) override;
virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) override;
virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) override;
-
- /* APIs. */
- virtual Result GetLatestProgram(ContentId *out_content_id, ProgramId program_id) override;
- virtual Result GetLatestData(ContentId *out_content_id, ProgramId program_id) override;
};
}
diff --git a/stratosphere/ncm/source/ncm_content_meta_database_impl_base.hpp b/stratosphere/ncm/source/ncm_content_meta_database_impl_base.hpp
index 2c0007760..951e23f84 100644
--- a/stratosphere/ncm/source/ncm_content_meta_database_impl_base.hpp
+++ b/stratosphere/ncm/source/ncm_content_meta_database_impl_base.hpp
@@ -35,18 +35,31 @@ namespace ams::ncm {
std::strcpy(this->mount_name, mount_name);
}
protected:
- Result EnsureEnabled() {
+ Result EnsureEnabled() const {
R_UNLESS(!this->disabled, ncm::ResultInvalidContentMetaDatabase());
return ResultSuccess();
}
- Result FindContentMetaKeyValue(ContentMetaKeyValueStore::Entry **out_entry, const ContentMetaKey &key) {
+ Result FindContentMetaKeyValue(ContentMetaKeyValueStore::Entry **out_entry, const ContentMetaKey &key) const {
const auto it = this->kvs->lower_bound(key);
R_UNLESS(it != this->kvs->end(), ncm::ResultContentMetaNotFound());
R_UNLESS(it->GetKey().id == key.id, ncm::ResultContentMetaNotFound());
*out_entry = it;
return ResultSuccess();
}
+
+ Result GetContentMetaSize(size_t *out, const ContentMetaKey &key) const {
+ R_TRY_CATCH(this->kvs->GetValueSize(out, key)) {
+ R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound())
+ } R_END_TRY_CATCH;
+
+ return ResultSuccess();
+ }
+
+ Result GetContentMetaPointer(const void **out_value_ptr, size_t *out_size, const ContentMetaKey &key) const {
+ R_TRY(this->GetContentMetaSize(out_size, key));
+ return this->kvs->GetValuePointer(reinterpret_cast(out_value_ptr), key);
+ }
};
}
diff --git a/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.cpp b/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.cpp
index 21d55030e..0bbe14885 100644
--- a/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.cpp
+++ b/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.cpp
@@ -22,7 +22,7 @@ namespace ams::ncm {
R_TRY(this->EnsureEnabled());
const ContentMetaKey key = ContentMetaKey::Make(program_id, 0, ContentMetaType::Unknown);
-
+
std::optional found_key;
for (auto entry = this->kvs->lower_bound(key); entry != this->kvs->end(); entry++) {
if (entry->GetKey().id != program_id) {
@@ -31,8 +31,8 @@ namespace ams::ncm {
found_key = entry->GetKey();
}
-
R_UNLESS(found_key, ncm::ResultContentMetaNotFound());
+
*out_key = *found_key;
return ResultSuccess();
}
diff --git a/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.hpp b/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.hpp
index 572be6b9f..c7c1dc1df 100644
--- a/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.hpp
+++ b/stratosphere/ncm/source/ncm_on_memory_content_meta_database_impl.hpp
@@ -20,11 +20,10 @@
#include "ncm_content_meta_database_impl.hpp"
namespace ams::ncm {
-
+
class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl {
public:
- OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore *kvs) : ContentMetaDatabaseImpl(kvs) {
- }
+ OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ }
public:
virtual Result GetLatestContentMetaKey(sf::Out out_key, ProgramId id) override;
virtual Result LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) override;