diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp index aba7f79c9..c2ee94de0 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp @@ -22,14 +22,16 @@ namespace ams::ncm { 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 auto it = this->kvs->lower_bound(key); + R_UNLESS(it != this->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, stored_key)); + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, found_key)); ContentMetaReader reader(meta, meta_size); @@ -79,12 +81,25 @@ namespace ams::ncm { Result ContentMetaDatabaseImpl::Get(sf::Out out_size, const ContentMetaKey &key, sf::OutBuffer out_value) { R_TRY(this->EnsureEnabled()); - return this->kvs->Get(out_size.GetPointer(), out_value.GetPointer(), out_value.GetSize(), key); + + /* Get the entry from our key-value store. */ + size_t size; + R_TRY_CATCH(this->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); + return ResultSuccess(); } Result ContentMetaDatabaseImpl::Remove(const ContentMetaKey &key) { R_TRY(this->EnsureEnabled()); - return this->kvs->Remove(key); + + R_TRY_CATCH(this->kvs->Remove(key)) { + R_CONVERT(kvdb::ResultKeyNotFound, ncm::ResultContentMetaNotFound()) + } R_END_TRY_CATCH; + + return ResultSuccess(); } Result ContentMetaDatabaseImpl::GetContentIdByType(sf::Out out_content_id, const ContentMetaKey &key, ContentType type) { @@ -121,7 +136,7 @@ namespace ams::ncm { /* Iterate over all entries. */ for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) { - ContentMetaKey key = entry->GetKey(); + 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))) { @@ -129,7 +144,7 @@ namespace ams::ncm { } /* If application id is present, check if it matches the filter. */ - if (application_id != InvalidProgramId) { + if (application_id != InvalidApplicationId) { /* Obtain the content meta for the key. */ const void *meta; size_t meta_size; @@ -169,10 +184,10 @@ namespace ams::ncm { /* Iterate over all entries. */ for (auto entry = this->kvs->begin(); entry != this->kvs->end(); entry++) { - ContentMetaKey key = entry->GetKey(); + const ContentMetaKey key = entry->GetKey(); /* Check if this entry matches the given filters. */ - if (!((type == ContentMetaType::Unknown || key.type == type))) { + if (!(type == ContentMetaType::Unknown || key.type == type)) { continue; } @@ -250,10 +265,14 @@ namespace ams::ncm { ContentMetaReader reader(meta, meta_size); /* Obtain the required system version. */ - if (key.type == ContentMetaType::Application) { - out_version.SetValue(reader.GetExtendedHeader()->required_system_version); - } else { - out_version.SetValue(reader.GetExtendedHeader()->required_system_version); + switch (key.type) { + case ContentMetaType::Application: + out_version.SetValue(reader.GetExtendedHeader()->required_system_version); + break; + case ContentMetaType::Patch: + out_version.SetValue(reader.GetExtendedHeader()->required_system_version); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); } return ResultSuccess(); @@ -333,14 +352,15 @@ namespace ams::ncm { /* 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++) { - if (content_id == reader.GetContentInfo(i)->GetId()) { - out.SetValue(true); - return ResultSuccess(); - } + R_SUCCEED_IF(content_id == reader.GetContentInfo(i)->GetId()); } + /* We didn't find a content info. */ out.SetValue(false); return ResultSuccess(); } @@ -395,13 +415,16 @@ namespace ams::ncm { /* 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(); + switch (key.type) { + case ContentMetaType::AddOnContent: + required_version = reader.GetExtendedHeader()->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_900); + required_version = reader.GetExtendedHeader()->required_application_version; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); } out_version.SetValue(required_version); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp index 2d77b5947..171180622 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl_base.hpp @@ -25,7 +25,7 @@ namespace ams::ncm { using ContentMetaKeyValueStore = ams::kvdb::MemoryKeyValueStore; protected: ContentMetaKeyValueStore *kvs; - char mount_name[16]; + char mount_name[fs::MountNameLengthMax + 1]; bool disabled; protected: ContentMetaDatabaseImplBase(ContentMetaKeyValueStore *kvs) : kvs(kvs), disabled(false) { /* ... */ } @@ -40,14 +40,6 @@ namespace ams::ncm { return ResultSuccess(); } - 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()) diff --git a/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp index 2f0c28225..3a1d2603d 100644 --- a/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.cpp @@ -18,26 +18,80 @@ namespace ams::ncm { - Result OnMemoryContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out out_key, u64 id) { + Result ContentMetaDatabaseImpl::GetLatestKeyImpl(ContentMetaKey *out_key, u64 id) const { R_TRY(this->EnsureEnabled()); - std::optional found_key; + std::optional found_key = std::nullopt; /* Find the last key with the desired program id. */ - for (auto entry = this->kvs->lower_bound(ContentMetaKey::Make(id, 0, ContentMetaType::Unknown)); entry != this->kvs->end(); entry++) { + for (auto entry = this->kvs->lower_bound(ContentMetaKey::MakeUnknownType(id, 0)); entry != this->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 = *found_key; return ResultSuccess(); } + Result OnMemoryContentMetaDatabaseImpl::List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &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 = this->kvs->begin(); entry != this->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); + return ResultSuccess(); + } + + Result OnMemoryContentMetaDatabaseImpl::GetLatestContentMetaKey(sf::Out out_key, u64 id) { + R_TRY(this->EnsureEnabled()); + return this->GetLatestKeyImpl(out_key.GetPointer(), id); + } + Result OnMemoryContentMetaDatabaseImpl::LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) { return ResultInvalidContentMetaDatabase(); } diff --git a/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp index d51ceed54..b298baa2c 100644 --- a/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_on_memory_content_meta_database_impl.hpp @@ -22,8 +22,10 @@ namespace ams::ncm { class OnMemoryContentMetaDatabaseImpl : public ContentMetaDatabaseImpl { public: OnMemoryContentMetaDatabaseImpl(ams::kvdb::MemoryKeyValueStore *kvs) : ContentMetaDatabaseImpl(kvs) { /* ... */ } + Result GetLatestKeyImpl(ContentMetaKey *out_key, u64 id) const; public: /* Actual commands. */ + virtual Result List(sf::Out out_entries_total, sf::Out out_entries_written, const sf::OutArray &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type) override; virtual Result GetLatestContentMetaKey(sf::Out out_key, u64 id) override; virtual Result LookupOrphanContent(const sf::OutArray &out_orphaned, const sf::InArray &content_ids) override; virtual Result Commit() override;