From ceacd0930b27c7402be78daf9f449997fe0ff2e0 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 18 Apr 2021 15:39:34 +0200 Subject: [PATCH] Android: Add "Import BootMii NAND Backup" --- .../dolphinemu/ui/main/MainActivity.java | 5 +++ .../dolphinemu/ui/main/MainPresenter.java | 39 +++++++++++++++++-- .../dolphinemu/ui/main/TvMainActivity.java | 9 +++++ .../dolphinemu/dolphinemu/utils/WiiUtils.java | 2 + .../app/src/main/res/menu/menu_game_grid.xml | 5 +++ .../app/src/main/res/values/strings.xml | 3 ++ Source/Android/jni/WiiUtils.cpp | 23 +++++++++++ 7 files changed, 82 insertions(+), 4 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java index 0418f453de..62d8ee8def 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java @@ -226,6 +226,11 @@ public final class MainActivity extends AppCompatActivity FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION, () -> mPresenter.importWiiSave(result.getData().toString())); break; + + case MainPresenter.REQUEST_NAND_BIN_FILE: + FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION, + () -> mPresenter.importNANDBin(result.getData().toString())); + break; } } else diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java index ed02a6f22e..7c1785f91c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java @@ -35,6 +35,7 @@ public final class MainPresenter public static final int REQUEST_SD_FILE = 3; public static final int REQUEST_WAD_FILE = 4; public static final int REQUEST_WII_SAVE_FILE = 5; + public static final int REQUEST_NAND_BIN_FILE = 6; private static boolean sShouldRescanLibrary = true; @@ -119,6 +120,11 @@ public final class MainPresenter new AfterDirectoryInitializationRunner().run(context, true, () -> mView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE)); return true; + + case R.id.menu_import_nand_backup: + new AfterDirectoryInitializationRunner().run(context, true, + () -> mView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE)); + return true; } return false; @@ -184,7 +190,7 @@ public final class MainPresenter public void installWAD(String path) { - runOnThreadAndShowResult(R.string.import_in_progress, () -> + runOnThreadAndShowResult(R.string.import_in_progress, 0, () -> { boolean success = WiiUtils.installWAD(path); int message = success ? R.string.wad_install_success : R.string.wad_install_failure; @@ -198,7 +204,7 @@ public final class MainPresenter CompletableFuture canOverwriteFuture = new CompletableFuture<>(); - runOnThreadAndShowResult(R.string.import_in_progress, () -> + runOnThreadAndShowResult(R.string.import_in_progress, 0, () -> { BooleanSupplier canOverwrite = () -> { @@ -248,13 +254,38 @@ public final class MainPresenter }); } - private void runOnThreadAndShowResult(int progressMessage, Supplier f) + public void importNANDBin(String path) + { + AlertDialog.Builder builder = + new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + + builder.setMessage(R.string.nand_import_warning); + builder.setNegativeButton(R.string.no, (dialog, i) -> dialog.dismiss()); + builder.setPositiveButton(R.string.yes, (dialog, i) -> + { + dialog.dismiss(); + + runOnThreadAndShowResult(R.string.import_in_progress, R.string.do_not_close_app, () -> + { + // ImportNANDBin doesn't provide any result value, unfortunately... + // It does however show a panic alert if something goes wrong. + WiiUtils.importNANDBin(path); + return null; + }); + }); + + builder.show(); + } + + private void runOnThreadAndShowResult(int progressTitle, int progressMessage, Supplier f) { final Activity mainPresenterActivity = (Activity) mContext; AlertDialog progressDialog = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase) .create(); - progressDialog.setTitle(progressMessage); + progressDialog.setTitle(progressTitle); + if (progressMessage != 0) + progressDialog.setMessage(mContext.getResources().getString(progressMessage)); progressDialog.setCancelable(false); progressDialog.show(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java index b03b894493..d35f440c0c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java @@ -264,6 +264,11 @@ public final class TvMainActivity extends FragmentActivity FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION, () -> mPresenter.importWiiSave(result.getData().toString())); break; + + case MainPresenter.REQUEST_NAND_BIN_FILE: + FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION, + () -> mPresenter.importNANDBin(result.getData().toString())); + break; } } else @@ -378,6 +383,10 @@ public final class TvMainActivity extends FragmentActivity R.drawable.ic_folder, R.string.grid_menu_import_wii_save)); + rowItems.add(new TvSettingsItem(R.id.menu_import_nand_backup, + R.drawable.ic_folder, + R.string.grid_menu_import_nand_backup)); + // Create a header for this row. HeaderItem header = new HeaderItem(R.string.preferences_settings, getString(R.string.preferences_settings)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java index 9a6440c9e5..74bb049df5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java @@ -11,4 +11,6 @@ public final class WiiUtils public static native boolean installWAD(String file); public static native int importWiiSave(String file, BooleanSupplier canOverwrite); + + public static native void importNANDBin(String file); } diff --git a/Source/Android/app/src/main/res/menu/menu_game_grid.xml b/Source/Android/app/src/main/res/menu/menu_game_grid.xml index 6b0dde4aa2..4b7d09ad97 100644 --- a/Source/Android/app/src/main/res/menu/menu_game_grid.xml +++ b/Source/Android/app/src/main/res/menu/menu_game_grid.xml @@ -30,4 +30,9 @@ android:title="@string/grid_menu_import_wii_save" app:showAsAction="never"/> + + diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 0b939e0faa..93c9c0c030 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -336,7 +336,9 @@ Open File Install WAD Import Wii Save + Import BootMii NAND Backup Importing... + Do not close the app! Successfully installed this title to the NAND. Failed to install this title to the NAND. Save data for this title already exists in the NAND. Consider backing up the current data before overwriting.\nOverwrite now? @@ -344,6 +346,7 @@ Failed to import save file. Your NAND may be corrupt, or something is preventing access to files within it. Failed to import save file. The given file appears to be corrupted or is not a valid Wii save. Failed to import save file. Please launch the game once, then try again. + Merging a new NAND over your currently selected NAND will overwrite any channels and savegames that already exist. This process is not reversible, so it is recommended that you keep backups of both NANDs. Are you sure you want to continue? Details diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index 9b0601c1df..d00b27de94 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -11,6 +11,7 @@ #include "Core/HW/WiiSave.h" #include "Core/WiiUtils.h" +#include "DiscIO/NANDImporter.h" // The hardcoded values here must match WiiUtils.java static jint ConvertCopyResult(WiiSave::CopyResult result) @@ -56,4 +57,26 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importWiiSa return ConvertCopyResult(WiiSave::Import(path, can_overwrite)); } + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importNANDBin(JNIEnv* env, + jclass, + jstring jFile) +{ + const std::string path = GetJString(env, jFile); + + return DiscIO::NANDImporter().ImportNANDBin( + path, + [] { + // This callback gets called every now and then in case we want to update the GUI. However, + // we have no way of knowing what the current progress is, so we can't do anything + // especially useful. DolphinQt chooses to show the elapsed time, for reference. + }, + [] { + // This callback gets called if the NAND file does not have decryption keys appended to it. + // We're supposed to ask the user for a separate file containing keys, but this is probably + // more work to implement on Android than it's worth, as this case almost never comes up. + PanicAlertFmtT("The decryption keys need to be appended to the NAND backup file."); + return ""; + }); +} }