mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	Merge pull request #10705 from german77/updates
android: Add update and DLC support
This commit is contained in:
		@@ -227,6 +227,8 @@ object NativeLibrary {
 | 
			
		||||
 | 
			
		||||
    external fun setAppDirectory(directory: String)
 | 
			
		||||
 | 
			
		||||
    external fun installFileToNand(filename: String): Int
 | 
			
		||||
 | 
			
		||||
    external fun initializeGpuDriver(
 | 
			
		||||
        hookLibDir: String?,
 | 
			
		||||
        customDriverDir: String?,
 | 
			
		||||
@@ -507,4 +509,15 @@ 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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -94,6 +94,11 @@ class HomeSettingsFragment : Fragment() {
 | 
			
		||||
                R.string.install_amiibo_keys_description,
 | 
			
		||||
                R.drawable.ic_nfc
 | 
			
		||||
            ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) },
 | 
			
		||||
            HomeSetting(
 | 
			
		||||
                R.string.install_game_content,
 | 
			
		||||
                R.string.install_game_content_description,
 | 
			
		||||
                R.drawable.ic_system_update_alt
 | 
			
		||||
            ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) },
 | 
			
		||||
            HomeSetting(
 | 
			
		||||
                R.string.select_games_folder,
 | 
			
		||||
                R.string.select_games_folder_description,
 | 
			
		||||
@@ -103,7 +108,12 @@ class HomeSettingsFragment : Fragment() {
 | 
			
		||||
                R.string.manage_save_data,
 | 
			
		||||
                R.string.import_export_saves_description,
 | 
			
		||||
                R.drawable.ic_save
 | 
			
		||||
            ) { ImportExportSavesFragment().show(parentFragmentManager, ImportExportSavesFragment.TAG) },
 | 
			
		||||
            ) {
 | 
			
		||||
                ImportExportSavesFragment().show(
 | 
			
		||||
                    parentFragmentManager,
 | 
			
		||||
                    ImportExportSavesFragment.TAG
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            HomeSetting(
 | 
			
		||||
                R.string.install_prod_keys,
 | 
			
		||||
                R.string.install_prod_keys_description,
 | 
			
		||||
 
 | 
			
		||||
@@ -467,4 +467,62 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    val installGameUpdate =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) {
 | 
			
		||||
            if (it == null)
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
 | 
			
		||||
            IndeterminateProgressDialogFragment.newInstance(
 | 
			
		||||
                this@MainActivity,
 | 
			
		||||
                R.string.install_game_content
 | 
			
		||||
            ) {
 | 
			
		||||
                val result = NativeLibrary.installFileToNand(it.toString())
 | 
			
		||||
                lifecycleScope.launch {
 | 
			
		||||
                    withContext(Dispatchers.Main) {
 | 
			
		||||
                        when (result) {
 | 
			
		||||
                            NativeLibrary.InstallFileToNandResult.Success -> {
 | 
			
		||||
                                Toast.makeText(
 | 
			
		||||
                                    applicationContext,
 | 
			
		||||
                                    R.string.install_game_content_success,
 | 
			
		||||
                                    Toast.LENGTH_SHORT
 | 
			
		||||
                                ).show()
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
 | 
			
		||||
                                Toast.makeText(
 | 
			
		||||
                                    applicationContext,
 | 
			
		||||
                                    R.string.install_game_content_success_overwrite,
 | 
			
		||||
                                    Toast.LENGTH_SHORT
 | 
			
		||||
                                ).show()
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
 | 
			
		||||
                                MessageDialogFragment.newInstance(
 | 
			
		||||
                                    R.string.install_game_content_failure,
 | 
			
		||||
                                    R.string.install_game_content_failure_base
 | 
			
		||||
                                ).show(supportFragmentManager, MessageDialogFragment.TAG)
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
 | 
			
		||||
                                MessageDialogFragment.newInstance(
 | 
			
		||||
                                    R.string.install_game_content_failure,
 | 
			
		||||
                                    R.string.install_game_content_failure_file_extension,
 | 
			
		||||
                                    R.string.install_game_content_help_link
 | 
			
		||||
                                ).show(supportFragmentManager, MessageDialogFragment.TAG)
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            else -> {
 | 
			
		||||
                                MessageDialogFragment.newInstance(
 | 
			
		||||
                                    R.string.install_game_content_failure,
 | 
			
		||||
                                    R.string.install_game_content_failure_description,
 | 
			
		||||
                                    R.string.install_game_content_help_link
 | 
			
		||||
                                ).show(supportFragmentManager, MessageDialogFragment.TAG)
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return@newInstance result
 | 
			
		||||
            }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,10 @@
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/cpu_manager.h"
 | 
			
		||||
#include "core/crypto/key_manager.h"
 | 
			
		||||
#include "core/file_sys/card_image.h"
 | 
			
		||||
#include "core/file_sys/registered_cache.h"
 | 
			
		||||
#include "core/file_sys/submission_package.h"
 | 
			
		||||
#include "core/file_sys/vfs.h"
 | 
			
		||||
#include "core/file_sys/vfs_real.h"
 | 
			
		||||
#include "core/frontend/applets/cabinet.h"
 | 
			
		||||
#include "core/frontend/applets/controller.h"
 | 
			
		||||
@@ -94,6 +97,74 @@ public:
 | 
			
		||||
        m_native_window = native_window;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int InstallFileToNand(std::string filename) {
 | 
			
		||||
        const auto 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;
 | 
			
		||||
            std::vector<u8> buffer(1_MiB);
 | 
			
		||||
 | 
			
		||||
            for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
 | 
			
		||||
                const auto 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,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
 | 
			
		||||
        m_system.GetFileSystemController().CreateFactories(*m_vfs);
 | 
			
		||||
 | 
			
		||||
        std::shared_ptr<FileSys::NSP> nsp;
 | 
			
		||||
        if (filename.ends_with("nsp")) {
 | 
			
		||||
            nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
 | 
			
		||||
            if (nsp->IsExtractedType()) {
 | 
			
		||||
                return InstallError;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (filename.ends_with("xci")) {
 | 
			
		||||
            const auto xci =
 | 
			
		||||
                std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
 | 
			
		||||
            nsp = xci->GetSecurePartitionNSP();
 | 
			
		||||
        } else {
 | 
			
		||||
            return ErrorFilenameExtension;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!nsp) {
 | 
			
		||||
            return InstallError;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nsp->GetStatus() != Loader::ResultStatus::Success) {
 | 
			
		||||
            return InstallError;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const auto 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 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) {
 | 
			
		||||
@@ -154,14 +225,14 @@ public:
 | 
			
		||||
        m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
 | 
			
		||||
                                                       m_vulkan_library);
 | 
			
		||||
 | 
			
		||||
        m_system.SetFilesystem(m_vfs);
 | 
			
		||||
 | 
			
		||||
        // Initialize system.
 | 
			
		||||
        auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
 | 
			
		||||
        m_software_keyboard = android_keyboard.get();
 | 
			
		||||
        m_system.SetShuttingDown(false);
 | 
			
		||||
        m_system.ApplySettings();
 | 
			
		||||
        m_system.HIDCore().ReloadInputDevices();
 | 
			
		||||
        m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
 | 
			
		||||
        m_system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
 | 
			
		||||
        m_system.SetAppletFrontendSet({
 | 
			
		||||
            nullptr,                     // Amiibo Settings
 | 
			
		||||
            nullptr,                     // Controller Selector
 | 
			
		||||
@@ -173,7 +244,8 @@ public:
 | 
			
		||||
            std::move(android_keyboard), // Software Keyboard
 | 
			
		||||
            nullptr,                     // Web Browser
 | 
			
		||||
        });
 | 
			
		||||
        m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem());
 | 
			
		||||
        m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
 | 
			
		||||
        m_system.GetFileSystemController().CreateFactories(*m_vfs);
 | 
			
		||||
 | 
			
		||||
        // Initialize account manager
 | 
			
		||||
        m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
 | 
			
		||||
@@ -398,7 +470,7 @@ private:
 | 
			
		||||
    InputCommon::InputSubsystem m_input_subsystem;
 | 
			
		||||
    Common::DetachedTasks m_detached_tasks;
 | 
			
		||||
    Core::PerfStatsResults m_perf_stats{};
 | 
			
		||||
    std::shared_ptr<FileSys::RealVfsFilesystem> m_vfs;
 | 
			
		||||
    std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
 | 
			
		||||
    Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
 | 
			
		||||
    bool m_is_running{};
 | 
			
		||||
    SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
 | 
			
		||||
@@ -466,6 +538,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
 | 
			
		||||
    Common::FS::SetAppDirectory(GetJString(env, j_directory));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env,
 | 
			
		||||
                                                            [[maybe_unused]] jclass clazz,
 | 
			
		||||
                                                            jstring j_file) {
 | 
			
		||||
    return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(
 | 
			
		||||
    JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
 | 
			
		||||
    jstring custom_driver_name, jstring file_redirect_dir) {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="48dp"
 | 
			
		||||
    android:height="48dp"
 | 
			
		||||
    android:viewportWidth="960"
 | 
			
		||||
    android:viewportHeight="960">
 | 
			
		||||
  <path
 | 
			
		||||
      android:fillColor="#FF000000"
 | 
			
		||||
      android:pathData="M140,800q-24,0 -42,-18t-18,-42v-520q0,-24 18,-42t42,-18h250v60L140,220v520h680v-520L570,220v-60h250q24,0 42,18t18,42v520q0,24 -18,42t-42,18L140,800ZM480,615L280,415l43,-43 127,127v-339h60v339l127,-127 43,43 -200,200Z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -102,6 +102,15 @@
 | 
			
		||||
    <string name="share_log">Share debug logs</string>
 | 
			
		||||
    <string name="share_log_description">Share yuzu\'s log file to debug issues</string>
 | 
			
		||||
    <string name="share_log_missing">No log file found</string>
 | 
			
		||||
    <string name="install_game_content">Install game content</string>
 | 
			
		||||
    <string name="install_game_content_description">Install game updates or DLC</string>
 | 
			
		||||
    <string name="install_game_content_failure">Error installing file to NAND</string>
 | 
			
		||||
    <string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string>
 | 
			
		||||
    <string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string>
 | 
			
		||||
    <string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string>
 | 
			
		||||
    <string name="install_game_content_success">Game content installed successfully</string>
 | 
			
		||||
    <string name="install_game_content_success_overwrite">Game content was overwritten successfully</string>
 | 
			
		||||
    <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
 | 
			
		||||
 | 
			
		||||
    <!-- About screen strings -->
 | 
			
		||||
    <string name="gaia_is_not_real">Gaia isn\'t real</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "core/file_sys/nca_metadata.h"
 | 
			
		||||
#include "core/file_sys/vfs.h"
 | 
			
		||||
 | 
			
		||||
namespace Core::Crypto {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user