frontend_common: Add content manager utility functions
Creates utility functions to remove/install DLC, updates, and base game content
This commit is contained in:
		@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.utils.DocumentsTree
 | 
				
			|||||||
import org.yuzu.yuzu_emu.utils.FileUtil
 | 
					import org.yuzu.yuzu_emu.utils.FileUtil
 | 
				
			||||||
import org.yuzu.yuzu_emu.utils.Log
 | 
					import org.yuzu.yuzu_emu.utils.Log
 | 
				
			||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
 | 
					import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
 | 
				
			||||||
 | 
					import org.yuzu.yuzu_emu.model.InstallResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Class which contains methods that interact
 | 
					 * Class which contains methods that interact
 | 
				
			||||||
@@ -235,9 +236,12 @@ object NativeLibrary {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Installs a nsp or xci file to nand
 | 
					     * Installs a nsp or xci file to nand
 | 
				
			||||||
     * @param filename String representation of file uri
 | 
					     * @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
 | 
					    external fun doesUpdateMatchProgram(programId: String, updatePath: String): Boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -609,15 +613,4 @@ object NativeLibrary {
 | 
				
			|||||||
        const val RELEASED = 0
 | 
					        const val RELEASED = 0
 | 
				
			||||||
        const val PRESSED = 1
 | 
					        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
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -42,3 +42,19 @@ double GetJDouble(JNIEnv* env, jobject jdouble) {
 | 
				
			|||||||
jobject ToJDouble(JNIEnv* env, double value) {
 | 
					jobject ToJDouble(JNIEnv* env, double value) {
 | 
				
			||||||
    return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <jni.h>
 | 
					#include <jni.h>
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetJString(JNIEnv* env, jstring jstr);
 | 
					std::string GetJString(JNIEnv* env, jstring jstr);
 | 
				
			||||||
jstring ToJString(JNIEnv* env, std::string_view str);
 | 
					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);
 | 
					double GetJDouble(JNIEnv* env, jobject jdouble);
 | 
				
			||||||
jobject ToJDouble(JNIEnv* env, double value);
 | 
					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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,6 +47,14 @@ static jclass s_double_class;
 | 
				
			|||||||
static jmethodID s_double_constructor;
 | 
					static jmethodID s_double_constructor;
 | 
				
			||||||
static jfieldID s_double_value_field;
 | 
					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;
 | 
					static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace IDCache {
 | 
					namespace IDCache {
 | 
				
			||||||
@@ -198,6 +206,30 @@ jfieldID GetDoubleValueField() {
 | 
				
			|||||||
    return s_double_value_field;
 | 
					    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
 | 
					} // namespace IDCache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
@@ -284,6 +316,18 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
 | 
				
			|||||||
    s_double_value_field = env->GetFieldID(double_class, "value", "D");
 | 
					    s_double_value_field = env->GetFieldID(double_class, "value", "D");
 | 
				
			||||||
    env->DeleteLocalRef(double_class);
 | 
					    env->DeleteLocalRef(double_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const jclass int_class = env->FindClass("java/lang/Integer");
 | 
				
			||||||
 | 
					    s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
 | 
				
			||||||
 | 
					    s_integer_constructor = env->GetMethodID(int_class, "<init>", "(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<jclass>(env->NewGlobalRef(boolean_class));
 | 
				
			||||||
 | 
					    s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
 | 
				
			||||||
 | 
					    s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
 | 
				
			||||||
 | 
					    env->DeleteLocalRef(boolean_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize Android Storage
 | 
					    // Initialize Android Storage
 | 
				
			||||||
    Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
 | 
					    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_pair_class);
 | 
				
			||||||
    env->DeleteGlobalRef(s_overlay_control_data_class);
 | 
					    env->DeleteGlobalRef(s_overlay_control_data_class);
 | 
				
			||||||
    env->DeleteGlobalRef(s_double_class);
 | 
					    env->DeleteGlobalRef(s_double_class);
 | 
				
			||||||
 | 
					    env->DeleteGlobalRef(s_integer_class);
 | 
				
			||||||
 | 
					    env->DeleteGlobalRef(s_boolean_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // UnInitialize applets
 | 
					    // UnInitialize applets
 | 
				
			||||||
    SoftwareKeyboard::CleanupJNI(env);
 | 
					    SoftwareKeyboard::CleanupJNI(env);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,4 +47,12 @@ jclass GetDoubleClass();
 | 
				
			|||||||
jmethodID GetDoubleConstructor();
 | 
					jmethodID GetDoubleConstructor();
 | 
				
			||||||
jfieldID GetDoubleValueField();
 | 
					jfieldID GetDoubleValueField();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jclass GetIntegerClass();
 | 
				
			||||||
 | 
					jmethodID GetIntegerConstructor();
 | 
				
			||||||
 | 
					jfieldID GetIntegerValueField();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jclass GetBooleanClass();
 | 
				
			||||||
 | 
					jmethodID GetBooleanConstructor();
 | 
				
			||||||
 | 
					jfieldID GetBooleanValueField();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace IDCache
 | 
					} // namespace IDCache
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@
 | 
				
			|||||||
#include <core/file_sys/patch_manager.h>
 | 
					#include <core/file_sys/patch_manager.h>
 | 
				
			||||||
#include <core/file_sys/savedata_factory.h>
 | 
					#include <core/file_sys/savedata_factory.h>
 | 
				
			||||||
#include <core/loader/nro.h>
 | 
					#include <core/loader/nro.h>
 | 
				
			||||||
 | 
					#include <frontend_common/content_manager.h>
 | 
				
			||||||
#include <jni.h>
 | 
					#include <jni.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/detached_tasks.h"
 | 
					#include "common/detached_tasks.h"
 | 
				
			||||||
@@ -100,67 +101,6 @@ void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
 | 
				
			|||||||
    m_native_window = 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<u8> 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<FileSys::NSP> nsp;
 | 
					 | 
				
			||||||
    if (file_extension == "nsp") {
 | 
					 | 
				
			||||||
        nsp = std::make_shared<FileSys::NSP>(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,
 | 
					void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
 | 
				
			||||||
                                           const std::string& custom_driver_dir,
 | 
					                                           const std::string& custom_driver_dir,
 | 
				
			||||||
                                           const std::string& custom_driver_name,
 | 
					                                           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,
 | 
					int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
 | 
				
			||||||
                                                            jstring j_file,
 | 
					                                                            jstring j_file, jobject jcallback) {
 | 
				
			||||||
                                                            jstring j_file_extension) {
 | 
					    auto jlambdaClass = env->GetObjectClass(jcallback);
 | 
				
			||||||
    return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file),
 | 
					    auto jlambdaInvokeMethod = env->GetMethodID(
 | 
				
			||||||
                                                             GetJString(env, j_file_extension));
 | 
					        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<int>(
 | 
				
			||||||
 | 
					        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,
 | 
					jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
#include "core/file_sys/registered_cache.h"
 | 
					#include "core/file_sys/registered_cache.h"
 | 
				
			||||||
#include "core/hle/service/acc/profile_manager.h"
 | 
					#include "core/hle/service/acc/profile_manager.h"
 | 
				
			||||||
#include "core/perf_stats.h"
 | 
					#include "core/perf_stats.h"
 | 
				
			||||||
 | 
					#include "frontend_common/content_manager.h"
 | 
				
			||||||
#include "jni/applets/software_keyboard.h"
 | 
					#include "jni/applets/software_keyboard.h"
 | 
				
			||||||
#include "jni/emu_window/emu_window.h"
 | 
					#include "jni/emu_window/emu_window.h"
 | 
				
			||||||
#include "video_core/rasterizer_interface.h"
 | 
					#include "video_core/rasterizer_interface.h"
 | 
				
			||||||
@@ -29,7 +30,6 @@ public:
 | 
				
			|||||||
    void SetNativeWindow(ANativeWindow* native_window);
 | 
					    void SetNativeWindow(ANativeWindow* native_window);
 | 
				
			||||||
    void SurfaceChanged();
 | 
					    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,
 | 
					    void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
 | 
				
			||||||
                             const std::string& custom_driver_name,
 | 
					                             const std::string& custom_driver_name,
 | 
				
			||||||
                             const std::string& file_redirect_dir);
 | 
					                             const std::string& file_redirect_dir);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@
 | 
				
			|||||||
add_library(frontend_common STATIC
 | 
					add_library(frontend_common STATIC
 | 
				
			||||||
    config.cpp
 | 
					    config.cpp
 | 
				
			||||||
    config.h
 | 
					    config.h
 | 
				
			||||||
 | 
					    content_manager.h
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
create_target_directory_groups(frontend_common)
 | 
					create_target_directory_groups(frontend_common)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										168
									
								
								src/frontend_common/content_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/frontend_common/content_manager.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
				
			|||||||
 | 
					// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <boost/algorithm/string.hpp>
 | 
				
			||||||
 | 
					#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<u64> 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<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
 | 
				
			||||||
 | 
					    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<u8> 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<FileSys::NSP> 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<FileSys::NSP>(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<bool(size_t, size_t)>& callback = std::function<bool(size_t, size_t)>()) {
 | 
				
			||||||
 | 
					    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<u8> 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<FileSys::NCA>(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
 | 
				
			||||||
@@ -47,6 +47,7 @@
 | 
				
			|||||||
#include "core/hle/service/am/applet_oe.h"
 | 
					#include "core/hle/service/am/applet_oe.h"
 | 
				
			||||||
#include "core/hle/service/am/applets/applets.h"
 | 
					#include "core/hle/service/am/applets/applets.h"
 | 
				
			||||||
#include "core/hle/service/set/system_settings_server.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/frontend/emulated_controller.h"
 | 
				
			||||||
#include "hid_core/hid_core.h"
 | 
					#include "hid_core/hid_core.h"
 | 
				
			||||||
#include "yuzu/multiplayer/state.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) {
 | 
					void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
 | 
				
			||||||
    const auto& fs_controller = system->GetFileSystemController();
 | 
					    const auto res =
 | 
				
			||||||
    const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
 | 
					        ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id);
 | 
				
			||||||
                     fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (res) {
 | 
					    if (res) {
 | 
				
			||||||
        QMessageBox::information(this, tr("Successfully Removed"),
 | 
					        QMessageBox::information(this, tr("Successfully Removed"),
 | 
				
			||||||
                                 tr("Successfully removed the installed base game."));
 | 
					                                 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) {
 | 
					void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
 | 
				
			||||||
    const auto update_id = program_id | 0x800;
 | 
					    const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
 | 
				
			||||||
    const auto& fs_controller = system->GetFileSystemController();
 | 
					 | 
				
			||||||
    const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
 | 
					 | 
				
			||||||
                     fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (res) {
 | 
					    if (res) {
 | 
				
			||||||
        QMessageBox::information(this, tr("Successfully Removed"),
 | 
					        QMessageBox::information(this, tr("Successfully Removed"),
 | 
				
			||||||
                                 tr("Successfully removed the installed update."));
 | 
					                                 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) {
 | 
					void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
 | 
				
			||||||
    u32 count{};
 | 
					    const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id);
 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (count == 0) {
 | 
					    if (count == 0) {
 | 
				
			||||||
        QMessageBox::warning(this, GetGameListErrorRemoving(type),
 | 
					        QMessageBox::warning(this, GetGameListErrorRemoving(type),
 | 
				
			||||||
                             tr("There are no DLC installed for this title."));
 | 
					                             tr("There are no DLC installed for this title."));
 | 
				
			||||||
@@ -3290,12 +3270,21 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
				
			|||||||
        install_progress->setLabelText(
 | 
					        install_progress->setLabelText(
 | 
				
			||||||
            tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName()));
 | 
					            tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        QFuture<InstallResult> future;
 | 
					        QFuture<ContentManager::InstallResult> future;
 | 
				
			||||||
        InstallResult result;
 | 
					        ContentManager::InstallResult result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
					        if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
				
			||||||
 | 
					            const auto progress_callback = [this](size_t size, size_t progress) {
 | 
				
			||||||
            future = QtConcurrent::run([this, &file] { return InstallNSP(file); });
 | 
					                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()) {
 | 
					            while (!future.isFinished()) {
 | 
				
			||||||
                QCoreApplication::processEvents();
 | 
					                QCoreApplication::processEvents();
 | 
				
			||||||
@@ -3311,16 +3300,16 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
				
			|||||||
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
 | 
					        std::this_thread::sleep_for(std::chrono::milliseconds(10));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch (result) {
 | 
					        switch (result) {
 | 
				
			||||||
        case InstallResult::Success:
 | 
					        case ContentManager::InstallResult::Success:
 | 
				
			||||||
            new_files.append(QFileInfo(file).fileName());
 | 
					            new_files.append(QFileInfo(file).fileName());
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case InstallResult::Overwrite:
 | 
					        case ContentManager::InstallResult::Overwrite:
 | 
				
			||||||
            overwritten_files.append(QFileInfo(file).fileName());
 | 
					            overwritten_files.append(QFileInfo(file).fileName());
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case InstallResult::Failure:
 | 
					        case ContentManager::InstallResult::Failure:
 | 
				
			||||||
            failed_files.append(QFileInfo(file).fileName());
 | 
					            failed_files.append(QFileInfo(file).fileName());
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case InstallResult::BaseInstallAttempted:
 | 
					        case ContentManager::InstallResult::BaseInstallAttempted:
 | 
				
			||||||
            failed_files.append(QFileInfo(file).fileName());
 | 
					            failed_files.append(QFileInfo(file).fileName());
 | 
				
			||||||
            detected_base_install = true;
 | 
					            detected_base_install = true;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
@@ -3354,96 +3343,7 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
				
			|||||||
    ui->action_Install_File_NAND->setEnabled(true);
 | 
					    ui->action_Install_File_NAND->setEnabled(true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
InstallResult GMainWindow::InstallNSP(const QString& filename) {
 | 
					ContentManager::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<u8> 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<FileSys::NSP> nsp;
 | 
					 | 
				
			||||||
    if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
 | 
					 | 
				
			||||||
        nsp = std::make_shared<FileSys::NSP>(
 | 
					 | 
				
			||||||
            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<u8> 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<FileSys::NCA>(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;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const QStringList tt_options{tr("System Application"),
 | 
					    const QStringList tt_options{tr("System Application"),
 | 
				
			||||||
                                 tr("System Archive"),
 | 
					                                 tr("System Archive"),
 | 
				
			||||||
                                 tr("System Application Update"),
 | 
					                                 tr("System Application Update"),
 | 
				
			||||||
@@ -3464,7 +3364,7 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) {
 | 
				
			|||||||
    if (!ok || index == -1) {
 | 
					    if (!ok || index == -1) {
 | 
				
			||||||
        QMessageBox::warning(this, tr("Failed to Install"),
 | 
					        QMessageBox::warning(this, tr("Failed to Install"),
 | 
				
			||||||
                             tr("The title type you selected for the NCA is invalid."));
 | 
					                             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.
 | 
					    // 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()
 | 
					    auto* registered_cache = is_application ? fs_controller.GetUserNANDContents()
 | 
				
			||||||
                                            : fs_controller.GetSystemNANDContents();
 | 
					                                            : fs_controller.GetSystemNANDContents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto res = registered_cache->InstallEntry(*nca, static_cast<FileSys::TitleType>(index),
 | 
					    const auto progress_callback = [this](size_t size, size_t progress) {
 | 
				
			||||||
                                                    true, qt_raw_copy);
 | 
					        emit UpdateInstallProgress();
 | 
				
			||||||
    if (res == FileSys::InstallResult::Success) {
 | 
					        if (install_progress->wasCanceled()) {
 | 
				
			||||||
        return InstallResult::Success;
 | 
					            return true;
 | 
				
			||||||
    } else if (res == FileSys::InstallResult::OverwriteExisting) {
 | 
					        }
 | 
				
			||||||
        return InstallResult::Overwrite;
 | 
					        return false;
 | 
				
			||||||
    } else {
 | 
					    };
 | 
				
			||||||
        return InstallResult::Failure;
 | 
					    return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache,
 | 
				
			||||||
    }
 | 
					                                      static_cast<FileSys::TitleType>(index), progress_callback);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GMainWindow::OnMenuRecentFile() {
 | 
					void GMainWindow::OnMenuRecentFile() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@
 | 
				
			|||||||
#include "common/announce_multiplayer_room.h"
 | 
					#include "common/announce_multiplayer_room.h"
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "configuration/qt_config.h"
 | 
					#include "configuration/qt_config.h"
 | 
				
			||||||
 | 
					#include "frontend_common/content_manager.h"
 | 
				
			||||||
#include "input_common/drivers/tas_input.h"
 | 
					#include "input_common/drivers/tas_input.h"
 | 
				
			||||||
#include "yuzu/compatibility_list.h"
 | 
					#include "yuzu/compatibility_list.h"
 | 
				
			||||||
#include "yuzu/hotkeys.h"
 | 
					#include "yuzu/hotkeys.h"
 | 
				
			||||||
@@ -124,13 +125,6 @@ enum class EmulatedDirectoryTarget {
 | 
				
			|||||||
    SDMC,
 | 
					    SDMC,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class InstallResult {
 | 
					 | 
				
			||||||
    Success,
 | 
					 | 
				
			||||||
    Overwrite,
 | 
					 | 
				
			||||||
    Failure,
 | 
					 | 
				
			||||||
    BaseInstallAttempted,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum class ReinitializeKeyBehavior {
 | 
					enum class ReinitializeKeyBehavior {
 | 
				
			||||||
    NoWarning,
 | 
					    NoWarning,
 | 
				
			||||||
    Warning,
 | 
					    Warning,
 | 
				
			||||||
@@ -427,8 +421,7 @@ private:
 | 
				
			|||||||
    void RemoveCacheStorage(u64 program_id);
 | 
					    void RemoveCacheStorage(u64 program_id);
 | 
				
			||||||
    bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
 | 
					    bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
 | 
				
			||||||
                               u64* selected_title_id, u8* selected_content_record_type);
 | 
					                               u64* selected_title_id, u8* selected_content_record_type);
 | 
				
			||||||
    InstallResult InstallNSP(const QString& filename);
 | 
					    ContentManager::InstallResult InstallNCA(const QString& filename);
 | 
				
			||||||
    InstallResult InstallNCA(const QString& filename);
 | 
					 | 
				
			||||||
    void MigrateConfigFiles();
 | 
					    void MigrateConfigFiles();
 | 
				
			||||||
    void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
 | 
					    void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
 | 
				
			||||||
                           std::string_view gpu_vendor = {});
 | 
					                           std::string_view gpu_vendor = {});
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user