From 3a8cffef57a8a9a6372985dab8a48fa2160f1ca8 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 16 Oct 2023 02:37:40 -0700 Subject: [PATCH] ncm: better detect + fix 17 brick after-the-fact This adds detection for missing-save or empty-save, and rebuilds in either case. --- .../ncm/ncm_content_manager_impl.hpp | 4 + .../source/ncm/ncm_content_manager_impl.cpp | 76 ++++++++++++++----- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index 2c4cb06f2..348acc486 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -239,8 +239,12 @@ namespace ams::ncm { Result InitializeIntegratedContentMetaDatabaseRoot(IntegratedContentMetaDatabaseRoot *out, const IntegratedContentStorageConfig *config, size_t root_idx, size_t root_count); Result BuildContentMetaDatabase(StorageId storage_id); + Result BuildContentMetaDatabaseImpl(StorageId storage_id); Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition); Result ImportContentMetaDatabaseImpl(ContentMetaDatabaseRoot *root, const char *import_mount_name); + private: + /* Helpers for unofficial functionality. */ + bool IsNeedRebuildSystemContentMetaDatabase(); public: /* Actual commands. */ Result CreateContentStorage(StorageId storage_id); diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp index 43a497557..05c8d90a6 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -784,27 +784,31 @@ namespace ams::ncm { } Result ContentManagerImpl::BuildContentMetaDatabase(StorageId storage_id) { - /* NOTE: we build on 17.0.0+, to work around a change in Nintendo save handling behavior. */ - if (hos::GetVersion() < hos::Version_5_0_0 || hos::GetVersion() >= hos::Version_17_0_0) { - /* Temporarily activate the database. */ - R_TRY(this->ActivateContentMetaDatabase(storage_id)); - ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(storage_id); }; - - /* Open the content meta database and storage. */ - ContentMetaDatabase meta_db; - ContentStorage storage; - R_TRY(ncm::OpenContentMetaDatabase(std::addressof(meta_db), storage_id)); - R_TRY(ncm::OpenContentStorage(std::addressof(storage), storage_id)); - - /* Create a builder, and build. */ - ContentMetaDatabaseBuilder builder(std::addressof(meta_db)); - R_RETURN(builder.BuildFromStorage(std::addressof(storage))); + if (hos::GetVersion() < hos::Version_5_0_0) { + /* On < 5.0.0, perform an actual build of the database. */ + R_RETURN(this->BuildContentMetaDatabaseImpl(storage_id)); } else { /* On 5.0.0+, building just performs an import. */ R_RETURN(this->ImportContentMetaDatabase(storage_id, false)); } } + Result ContentManagerImpl::BuildContentMetaDatabaseImpl(StorageId storage_id) { + /* Temporarily activate the database. */ + R_TRY(this->ActivateContentMetaDatabase(storage_id)); + ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(storage_id); }; + + /* Open the content meta database and storage. */ + ContentMetaDatabase meta_db; + ContentStorage storage; + R_TRY(ncm::OpenContentMetaDatabase(std::addressof(meta_db), storage_id)); + R_TRY(ncm::OpenContentStorage(std::addressof(storage), storage_id)); + + /* Create a builder, and build. */ + ContentMetaDatabaseBuilder builder(std::addressof(meta_db)); + R_RETURN(builder.BuildFromStorage(std::addressof(storage))); + } + Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition) { /* Only support importing BuiltInSystem. */ AMS_ABORT_UNLESS(storage_id == StorageId::BuiltInSystem); @@ -832,6 +836,31 @@ namespace ams::ncm { R_SUCCEED(); } + bool ContentManagerImpl::IsNeedRebuildSystemContentMetaDatabase() { + /* TODO: Should hos::GetVersion() >= hos::Version_17_0_0 be checked? */ + + /* If we do not actually have a content meta db, we should re-build. */ + if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) { + return true; + } + + /* We have a content meta db. Temporarily, activate it. */ + if (R_FAILED(this->ActivateContentMetaDatabase(StorageId::BuiltInSystem))) { + return true; + } + ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(StorageId::BuiltInSystem); }; + + /* Open the content meta database and storage. */ + ContentMetaDatabase meta_db; + R_ABORT_UNLESS(ncm::OpenContentMetaDatabase(std::addressof(meta_db), StorageId::BuiltInSystem)); + + /* List the meta db's contents. */ + const auto list_count = meta_db.ListContentMeta(nullptr, 0); + + /* We need to rebuild if the db has zero entries. */ + return list_count.total == 0; + } + Result ContentManagerImpl::Initialize(const ContentManagerConfig &config) { /* Initialize based on whether integrated content is enabled. */ if (config.IsIntegratedSystemContentEnabled()) { @@ -942,13 +971,26 @@ namespace ams::ncm { } R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem)); + /* NOTE: This logic is unofficial. */ + /* Beginning with 17.0.0+, save management behavior changed. The primary symptom of this is either verify fail */ + /* or an empty kvs, both of which we can fix by performing a rebuild. */ + if (this->IsNeedRebuildSystemContentMetaDatabase()) { + /* Clean up the system content meta database, to ensure creation can succeed. */ + this->CleanupContentMetaDatabase(StorageId::BuiltInSystem); + + /* Create the content metadatabase. */ + R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem)); + + /* Rebuild the content meta database. */ + R_TRY(this->BuildContentMetaDatabaseImpl(StorageId::BuiltInSystem)); + } + /* Setup the content meta database for system. */ if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) { R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem)); /* Try to build or import a database, depending on our configuration. */ - /* NOTE: To work around a change in save management behavior in 17.0.0+, we build the database if needed. */ - if (manager_config.ShouldBuildDatabase() || hos::GetVersion() >= hos::Version_17_0_0) { + if (manager_config.ShouldBuildDatabase()) { /* If we should build the database, do so. */ R_TRY(this->BuildContentMetaDatabase(StorageId::BuiltInSystem)); R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));