From ccd3dd842f2bf7cf16c7b93e3b83a2afc8af4a69 Mon Sep 17 00:00:00 2001 From: t895 Date: Fri, 19 Jan 2024 00:56:43 -0500 Subject: [PATCH] frontend_common: Add content manager utility functions Creates utility functions to remove/install DLC, updates, and base game content --- .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 19 +- .../org/yuzu/yuzu_emu/model/InstallResult.kt | 15 ++ .../jni/android_common/android_common.cpp | 16 ++ .../main/jni/android_common/android_common.h | 7 + src/android/app/src/main/jni/id_cache.cpp | 46 +++++ src/android/app/src/main/jni/id_cache.h | 8 + src/android/app/src/main/jni/native.cpp | 80 ++------- src/android/app/src/main/jni/native.h | 2 +- src/frontend_common/CMakeLists.txt | 1 + src/frontend_common/content_manager.h | 168 ++++++++++++++++++ src/yuzu/main.cpp | 166 ++++------------- src/yuzu/main.h | 11 +- 12 files changed, 318 insertions(+), 221 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt create mode 100644 src/frontend_common/content_manager.h diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index b7556e3530..8cb98d6d7b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable +import org.yuzu.yuzu_emu.model.InstallResult /** * Class which contains methods that interact @@ -235,9 +236,12 @@ object NativeLibrary { /** * Installs a nsp or xci file to nand * @param filename String representation of file uri - * @param extension Lowercase string representation of file extension without "." + * @return int representation of [InstallResult] */ - external fun installFileToNand(filename: String, extension: String): Int + external fun installFileToNand( + filename: String, + callback: (max: Long, progress: Long) -> Boolean + ): Int external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean @@ -609,15 +613,4 @@ object NativeLibrary { const val RELEASED = 0 const val PRESSED = 1 } - - /** - * Result from installFileToNand - */ - object InstallFileToNandResult { - const val Success = 0 - const val SuccessFileOverwritten = 1 - const val Error = 2 - const val ErrorBaseGame = 3 - const val ErrorFilenameExtension = 4 - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt new file mode 100644 index 0000000000..0c3cd0521e --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/InstallResult.kt @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +enum class InstallResult(val int: Int) { + Success(0), + Overwrite(1), + Failure(2), + BaseInstallAttempted(3); + + companion object { + fun from(int: Int): InstallResult = entries.firstOrNull { it.int == int } ?: Success + } +} diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp index 1e884ffdd2..7018a52af3 100644 --- a/src/android/app/src/main/jni/android_common/android_common.cpp +++ b/src/android/app/src/main/jni/android_common/android_common.cpp @@ -42,3 +42,19 @@ double GetJDouble(JNIEnv* env, jobject jdouble) { jobject ToJDouble(JNIEnv* env, double value) { return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value); } + +s32 GetJInteger(JNIEnv* env, jobject jinteger) { + return env->GetIntField(jinteger, IDCache::GetIntegerValueField()); +} + +jobject ToJInteger(JNIEnv* env, s32 value) { + return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value); +} + +bool GetJBoolean(JNIEnv* env, jobject jboolean) { + return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField()); +} + +jobject ToJBoolean(JNIEnv* env, bool value) { + return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value); +} diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h index 8eb803e1b0..29a338c0ae 100644 --- a/src/android/app/src/main/jni/android_common/android_common.h +++ b/src/android/app/src/main/jni/android_common/android_common.h @@ -6,6 +6,7 @@ #include #include +#include "common/common_types.h" std::string GetJString(JNIEnv* env, jstring jstr); jstring ToJString(JNIEnv* env, std::string_view str); @@ -13,3 +14,9 @@ jstring ToJString(JNIEnv* env, std::u16string_view str); double GetJDouble(JNIEnv* env, jobject jdouble); jobject ToJDouble(JNIEnv* env, double value); + +s32 GetJInteger(JNIEnv* env, jobject jinteger); +jobject ToJInteger(JNIEnv* env, s32 value); + +bool GetJBoolean(JNIEnv* env, jobject jboolean); +jobject ToJBoolean(JNIEnv* env, bool value); diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index c79ad7d76b..19ced175fa 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -47,6 +47,14 @@ static jclass s_double_class; static jmethodID s_double_constructor; static jfieldID s_double_value_field; +static jclass s_integer_class; +static jmethodID s_integer_constructor; +static jfieldID s_integer_value_field; + +static jclass s_boolean_class; +static jmethodID s_boolean_constructor; +static jfieldID s_boolean_value_field; + static constexpr jint JNI_VERSION = JNI_VERSION_1_6; namespace IDCache { @@ -198,6 +206,30 @@ jfieldID GetDoubleValueField() { return s_double_value_field; } +jclass GetIntegerClass() { + return s_integer_class; +} + +jmethodID GetIntegerConstructor() { + return s_integer_constructor; +} + +jfieldID GetIntegerValueField() { + return s_integer_value_field; +} + +jclass GetBooleanClass() { + return s_boolean_class; +} + +jmethodID GetBooleanConstructor() { + return s_boolean_constructor; +} + +jfieldID GetBooleanValueField() { + return s_boolean_value_field; +} + } // namespace IDCache #ifdef __cplusplus @@ -284,6 +316,18 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { s_double_value_field = env->GetFieldID(double_class, "value", "D"); env->DeleteLocalRef(double_class); + const jclass int_class = env->FindClass("java/lang/Integer"); + s_integer_class = reinterpret_cast(env->NewGlobalRef(int_class)); + s_integer_constructor = env->GetMethodID(int_class, "", "(I)V"); + s_integer_value_field = env->GetFieldID(int_class, "value", "I"); + env->DeleteLocalRef(int_class); + + const jclass boolean_class = env->FindClass("java/lang/Boolean"); + s_boolean_class = reinterpret_cast(env->NewGlobalRef(boolean_class)); + s_boolean_constructor = env->GetMethodID(boolean_class, "", "(Z)V"); + s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); + env->DeleteLocalRef(boolean_class); + // Initialize Android Storage Common::FS::Android::RegisterCallbacks(env, s_native_library_class); @@ -310,6 +354,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { env->DeleteGlobalRef(s_pair_class); env->DeleteGlobalRef(s_overlay_control_data_class); env->DeleteGlobalRef(s_double_class); + env->DeleteGlobalRef(s_integer_class); + env->DeleteGlobalRef(s_boolean_class); // UnInitialize applets SoftwareKeyboard::CleanupJNI(env); diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h index 784d1412f6..0e5267b738 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h @@ -47,4 +47,12 @@ jclass GetDoubleClass(); jmethodID GetDoubleConstructor(); jfieldID GetDoubleValueField(); +jclass GetIntegerClass(); +jmethodID GetIntegerConstructor(); +jfieldID GetIntegerValueField(); + +jclass GetBooleanClass(); +jmethodID GetBooleanConstructor(); +jfieldID GetBooleanValueField(); + } // namespace IDCache diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index ed3b1353a0..b8fef5c6fd 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "common/detached_tasks.h" @@ -100,67 +101,6 @@ void EmulationSession::SetNativeWindow(ANativeWindow* native_window) { m_native_window = native_window; } -int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) { - jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, - std::size_t block_size) { - if (src == nullptr || dest == nullptr) { - return false; - } - if (!dest->Resize(src->GetSize())) { - return false; - } - - using namespace Common::Literals; - [[maybe_unused]] std::vector buffer(1_MiB); - - for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { - jconst read = src->Read(buffer.data(), buffer.size(), i); - dest->Write(buffer.data(), read, i); - } - return true; - }; - - enum InstallResult { - Success = 0, - SuccessFileOverwritten = 1, - InstallError = 2, - ErrorBaseGame = 3, - ErrorFilenameExtension = 4, - }; - - [[maybe_unused]] std::shared_ptr nsp; - if (file_extension == "nsp") { - nsp = std::make_shared(m_vfs->OpenFile(filename, FileSys::Mode::Read)); - if (nsp->IsExtractedType()) { - return InstallError; - } - } else { - return ErrorFilenameExtension; - } - - if (!nsp) { - return InstallError; - } - - if (nsp->GetStatus() != Loader::ResultStatus::Success) { - return InstallError; - } - - jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, - copy_func); - - switch (res) { - case FileSys::InstallResult::Success: - return Success; - case FileSys::InstallResult::OverwriteExisting: - return SuccessFileOverwritten; - case FileSys::InstallResult::ErrorBaseInstall: - return ErrorBaseGame; - default: - return InstallError; - } -} - void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, const std::string& custom_driver_name, @@ -512,10 +452,20 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject } int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance, - jstring j_file, - jstring j_file_extension) { - return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file), - GetJString(env, j_file_extension)); + jstring j_file, jobject jcallback) { + auto jlambdaClass = env->GetObjectClass(jcallback); + auto jlambdaInvokeMethod = env->GetMethodID( + jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { + auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, + ToJDouble(env, max), ToJDouble(env, progress)); + return GetJBoolean(env, jwasCancelled); + }; + + return static_cast( + ContentManager::InstallNSP(&EmulationSession::GetInstance().System(), + EmulationSession::GetInstance().System().GetFilesystem().get(), + GetJString(env, j_file), callback)); } jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 4a80495784..dadb138ad6 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -7,6 +7,7 @@ #include "core/file_sys/registered_cache.h" #include "core/hle/service/acc/profile_manager.h" #include "core/perf_stats.h" +#include "frontend_common/content_manager.h" #include "jni/applets/software_keyboard.h" #include "jni/emu_window/emu_window.h" #include "video_core/rasterizer_interface.h" @@ -29,7 +30,6 @@ public: void SetNativeWindow(ANativeWindow* native_window); void SurfaceChanged(); - int InstallFileToNand(std::string filename, std::string file_extension); void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, const std::string& custom_driver_name, const std::string& file_redirect_dir); diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt index 22e9337c4c..94d8cc4c3b 100644 --- a/src/frontend_common/CMakeLists.txt +++ b/src/frontend_common/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(frontend_common STATIC config.cpp config.h + content_manager.h ) create_target_directory_groups(frontend_common) diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h new file mode 100644 index 0000000000..8e55f4ca09 --- /dev/null +++ b/src/frontend_common/content_manager.h @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_types.h" +#include "common/literals.h" +#include "core/core.h" +#include "core/file_sys/common_funcs.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/submission_package.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h" + +namespace ContentManager { + +enum class InstallResult { + Success, + Overwrite, + Failure, + BaseInstallAttempted, +}; + +inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_controller, + const u64 title_id) { + return fs_controller.GetUserNANDContents()->RemoveExistingEntry(title_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(title_id); +} + +inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { + size_t count{}; + const auto& fs_controller = system->GetFileSystemController(); + const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( + FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + std::vector program_dlc_entries; + + for (const auto& entry : dlc_entries) { + if (FileSys::GetBaseTitleID(entry.title_id) == program_id) { + program_dlc_entries.push_back(entry.title_id); + } + } + + for (const auto& entry : program_dlc_entries) { + if (RemoveDLC(fs_controller, entry)) { + ++count; + } + } + return count; +} + +inline bool RemoveUpdate(const Service::FileSystem::FileSystemController& fs_controller, + const u64 program_id) { + const auto update_id = program_id | 0x800; + return fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); +} + +inline bool RemoveBaseContent(const Service::FileSystem::FileSystemController& fs_controller, + const u64 program_id) { + return fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); +} + +inline InstallResult InstallNSP( + Core::System* system, FileSys::VfsFilesystem* vfs, const std::string& filename, + const std::function& callback = std::function()) { + const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; + } + if (!dest->Resize(src->GetSize())) { + return false; + } + + using namespace Common::Literals; + std::vector buffer(1_MiB); + + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + if (callback(src->GetSize(), i)) { + dest->Resize(0); + return false; + } + const auto read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); + } + return true; + }; + + std::shared_ptr nsp; + FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read); + if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { + nsp = std::make_shared(file); + if (nsp->IsExtractedType()) { + return InstallResult::Failure; + } + } else { + return InstallResult::Failure; + } + + if (nsp->GetStatus() != Loader::ResultStatus::Success) { + return InstallResult::Failure; + } + const auto res = + system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); + switch (res) { + case FileSys::InstallResult::Success: + return InstallResult::Success; + case FileSys::InstallResult::OverwriteExisting: + return InstallResult::Overwrite; + case FileSys::InstallResult::ErrorBaseInstall: + return InstallResult::BaseInstallAttempted; + default: + return InstallResult::Failure; + } +} + +inline InstallResult InstallNCA( + FileSys::VfsFilesystem* vfs, const std::string& filename, + FileSys::RegisteredCache* registered_cache, const FileSys::TitleType title_type, + const std::function& callback = std::function()) { + const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, + std::size_t block_size) { + if (src == nullptr || dest == nullptr) { + return false; + } + if (!dest->Resize(src->GetSize())) { + return false; + } + + using namespace Common::Literals; + std::vector buffer(1_MiB); + + for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { + if (callback(src->GetSize(), i)) { + dest->Resize(0); + return false; + } + const auto read = src->Read(buffer.data(), buffer.size(), i); + dest->Write(buffer.data(), read, i); + } + return true; + }; + + const auto nca = std::make_shared(vfs->OpenFile(filename, FileSys::Mode::Read)); + const auto id = nca->GetStatus(); + + // Game updates necessary are missing base RomFS + if (id != Loader::ResultStatus::Success && + id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + return InstallResult::Failure; + } + + const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy); + if (res == FileSys::InstallResult::Success) { + return InstallResult::Success; + } else if (res == FileSys::InstallResult::OverwriteExisting) { + return InstallResult::Overwrite; + } else { + return InstallResult::Failure; + } +} + +} // namespace ContentManager diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3c562e3b28..05bd4174c7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -47,6 +47,7 @@ #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/set/system_settings_server.h" +#include "frontend_common/content_manager.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "yuzu/multiplayer/state.h" @@ -2476,10 +2477,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT } void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) { - const auto& fs_controller = system->GetFileSystemController(); - const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || - fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); - + const auto res = + ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id); if (res) { QMessageBox::information(this, tr("Successfully Removed"), tr("Successfully removed the installed base game.")); @@ -2491,11 +2490,7 @@ void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) { } void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { - const auto update_id = program_id | 0x800; - const auto& fs_controller = system->GetFileSystemController(); - const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || - fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); - + const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id); if (res) { QMessageBox::information(this, tr("Successfully Removed"), tr("Successfully removed the installed update.")); @@ -2506,22 +2501,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { } void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { - u32 count{}; - const auto& fs_controller = system->GetFileSystemController(); - const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( - FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); - - for (const auto& entry : dlc_entries) { - if (FileSys::GetBaseTitleID(entry.title_id) == program_id) { - const auto res = - fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) || - fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id); - if (res) { - ++count; - } - } - } - + const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id); if (count == 0) { QMessageBox::warning(this, GetGameListErrorRemoving(type), tr("There are no DLC installed for this title.")); @@ -3290,12 +3270,21 @@ void GMainWindow::OnMenuInstallToNAND() { install_progress->setLabelText( tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName())); - QFuture future; - InstallResult result; + QFuture future; + ContentManager::InstallResult result; if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { - - future = QtConcurrent::run([this, &file] { return InstallNSP(file); }); + const auto progress_callback = [this](size_t size, size_t progress) { + emit UpdateInstallProgress(); + if (install_progress->wasCanceled()) { + return true; + } + return false; + }; + future = QtConcurrent::run([this, &file, progress_callback] { + return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(), + progress_callback); + }); while (!future.isFinished()) { QCoreApplication::processEvents(); @@ -3311,16 +3300,16 @@ void GMainWindow::OnMenuInstallToNAND() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); switch (result) { - case InstallResult::Success: + case ContentManager::InstallResult::Success: new_files.append(QFileInfo(file).fileName()); break; - case InstallResult::Overwrite: + case ContentManager::InstallResult::Overwrite: overwritten_files.append(QFileInfo(file).fileName()); break; - case InstallResult::Failure: + case ContentManager::InstallResult::Failure: failed_files.append(QFileInfo(file).fileName()); break; - case InstallResult::BaseInstallAttempted: + case ContentManager::InstallResult::BaseInstallAttempted: failed_files.append(QFileInfo(file).fileName()); detected_base_install = true; break; @@ -3354,96 +3343,7 @@ void GMainWindow::OnMenuInstallToNAND() { ui->action_Install_File_NAND->setEnabled(true); } -InstallResult GMainWindow::InstallNSP(const QString& filename) { - const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, - const FileSys::VirtualFile& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr) { - return false; - } - if (!dest->Resize(src->GetSize())) { - return false; - } - - std::vector buffer(CopyBufferSize); - - for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { - if (install_progress->wasCanceled()) { - dest->Resize(0); - return false; - } - - emit UpdateInstallProgress(); - - const auto read = src->Read(buffer.data(), buffer.size(), i); - dest->Write(buffer.data(), read, i); - } - return true; - }; - - std::shared_ptr nsp; - if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { - nsp = std::make_shared( - vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - if (nsp->IsExtractedType()) { - return InstallResult::Failure; - } - } else { - return InstallResult::Failure; - } - - if (nsp->GetStatus() != Loader::ResultStatus::Success) { - return InstallResult::Failure; - } - const auto res = system->GetFileSystemController().GetUserNANDContents()->InstallEntry( - *nsp, true, qt_raw_copy); - switch (res) { - case FileSys::InstallResult::Success: - return InstallResult::Success; - case FileSys::InstallResult::OverwriteExisting: - return InstallResult::Overwrite; - case FileSys::InstallResult::ErrorBaseInstall: - return InstallResult::BaseInstallAttempted; - default: - return InstallResult::Failure; - } -} - -InstallResult GMainWindow::InstallNCA(const QString& filename) { - const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, - const FileSys::VirtualFile& dest, std::size_t block_size) { - if (src == nullptr || dest == nullptr) { - return false; - } - if (!dest->Resize(src->GetSize())) { - return false; - } - - std::vector buffer(CopyBufferSize); - - for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { - if (install_progress->wasCanceled()) { - dest->Resize(0); - return false; - } - - emit UpdateInstallProgress(); - - const auto read = src->Read(buffer.data(), buffer.size(), i); - dest->Write(buffer.data(), read, i); - } - return true; - }; - - const auto nca = - std::make_shared(vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - const auto id = nca->GetStatus(); - - // Game updates necessary are missing base RomFS - if (id != Loader::ResultStatus::Success && - id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { - return InstallResult::Failure; - } - +ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) { const QStringList tt_options{tr("System Application"), tr("System Archive"), tr("System Application Update"), @@ -3464,7 +3364,7 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) { if (!ok || index == -1) { QMessageBox::warning(this, tr("Failed to Install"), tr("The title type you selected for the NCA is invalid.")); - return InstallResult::Failure; + return ContentManager::InstallResult::Failure; } // If index is equal to or past Game, add the jump in TitleType. @@ -3478,15 +3378,15 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) { auto* registered_cache = is_application ? fs_controller.GetUserNANDContents() : fs_controller.GetSystemNANDContents(); - const auto res = registered_cache->InstallEntry(*nca, static_cast(index), - true, qt_raw_copy); - if (res == FileSys::InstallResult::Success) { - return InstallResult::Success; - } else if (res == FileSys::InstallResult::OverwriteExisting) { - return InstallResult::Overwrite; - } else { - return InstallResult::Failure; - } + const auto progress_callback = [this](size_t size, size_t progress) { + emit UpdateInstallProgress(); + if (install_progress->wasCanceled()) { + return true; + } + return false; + }; + return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache, + static_cast(index), progress_callback); } void GMainWindow::OnMenuRecentFile() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f3276da642..280fae5c35 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -16,6 +16,7 @@ #include "common/announce_multiplayer_room.h" #include "common/common_types.h" #include "configuration/qt_config.h" +#include "frontend_common/content_manager.h" #include "input_common/drivers/tas_input.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" @@ -124,13 +125,6 @@ enum class EmulatedDirectoryTarget { SDMC, }; -enum class InstallResult { - Success, - Overwrite, - Failure, - BaseInstallAttempted, -}; - enum class ReinitializeKeyBehavior { NoWarning, Warning, @@ -427,8 +421,7 @@ private: void RemoveCacheStorage(u64 program_id); bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, u64* selected_title_id, u8* selected_content_record_type); - InstallResult InstallNSP(const QString& filename); - InstallResult InstallNCA(const QString& filename); + ContentManager::InstallResult InstallNCA(const QString& filename); void MigrateConfigFiles(); void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {});