From 80b56b657598c44e88de781f05c7ef3c687b0597 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 17 Jan 2021 22:15:13 +0100 Subject: [PATCH 1/6] Android: Move InstallWAD to a new WiiUtils class I'm trying to move away from dumping every native method in NativeLibrary. --- .../dolphinemu/dolphinemu/NativeLibrary.java | 2 -- .../dolphinemu/ui/main/MainPresenter.java | 5 ++--- .../dolphinemu/dolphinemu/utils/WiiUtils.java | 6 +++++ Source/Android/jni/CMakeLists.txt | 1 + Source/Android/jni/MainAndroid.cpp | 9 -------- Source/Android/jni/WiiUtils.cpp | 22 +++++++++++++++++++ 6 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java create mode 100644 Source/Android/jni/WiiUtils.cpp diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 425551ff77..9a779e7f6b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -444,8 +444,6 @@ public final class NativeLibrary public static native void ReloadLoggerConfig(); - public static native boolean InstallWAD(String file); - public static native boolean ConvertDiscImage(String inPath, String outPath, int platform, int format, int blockSize, int compression, int compressionLevel, boolean scrub, CompressCallback callback); 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 1ded576794..9344a61e48 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 @@ -13,7 +13,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.dolphinemu.dolphinemu.BuildConfig; -import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; @@ -22,9 +21,9 @@ import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; import org.dolphinemu.dolphinemu.utils.ContentHandler; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; +import org.dolphinemu.dolphinemu.utils.WiiUtils; import java.util.Arrays; -import java.util.Set; public final class MainPresenter { @@ -162,7 +161,7 @@ public final class MainPresenter Thread installWADThread = new Thread(() -> { - if (NativeLibrary.InstallWAD(file)) + if (WiiUtils.installWAD(file)) { mainPresenterActivity.runOnUiThread( () -> Toast.makeText(mContext, R.string.wad_install_success, Toast.LENGTH_SHORT) 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 new file mode 100644 index 0000000000..6fc0b6d9ea --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java @@ -0,0 +1,6 @@ +package org.dolphinemu.dolphinemu.utils; + +public final class WiiUtils +{ + public static native boolean installWAD(String file); +} diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 7f31a144aa..405d621958 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(main SHARED IniFile.cpp MainAndroid.cpp NativeConfig.cpp + WiiUtils.cpp ) target_link_libraries(main diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 71c4fac565..ad91ce17c5 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -45,7 +45,6 @@ #include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/Profiler.h" #include "Core/State.h" -#include "Core/WiiUtils.h" #include "DiscIO/Blob.h" #include "DiscIO/Enums.h" @@ -596,14 +595,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadLogger Common::Log::LogManager::Init(); } -JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_InstallWAD(JNIEnv* env, - jclass, - jstring jFile) -{ - const std::string path = GetJString(env, jFile); - return static_cast(WiiUtils::InstallWAD(path)); -} - JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ConvertDiscImage( JNIEnv* env, jclass, jstring jInPath, jstring jOutPath, jint jPlatform, jint jFormat, jint jBlockSize, jint jCompression, jint jCompressionLevel, jboolean jScrub, jobject jCallback) diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp new file mode 100644 index 0000000000..c98dfe847c --- /dev/null +++ b/Source/Android/jni/WiiUtils.cpp @@ -0,0 +1,22 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include + +#include "jni/AndroidCommon/AndroidCommon.h" + +#include "Core/WiiUtils.h" + +extern "C" { + +JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_installWAD(JNIEnv* env, + jclass, + jstring jFile) +{ + const std::string path = GetJString(env, jFile); + return static_cast(WiiUtils::InstallWAD(path)); +} +} From 4a394ffc9c6cbaa7a11de2a658948b2594ca6ab4 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 17 Jan 2021 22:16:20 +0100 Subject: [PATCH 2/6] Android: Merge launchInstallWad into launchOpenFileActivity --- .../dolphinemu/dolphinemu/ui/main/MainActivity.java | 13 ++----------- .../dolphinemu/ui/main/MainPresenter.java | 5 +++-- .../org/dolphinemu/dolphinemu/ui/main/MainView.java | 4 +--- .../dolphinemu/ui/main/TvMainActivity.java | 13 ++----------- 4 files changed, 8 insertions(+), 27 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 7b4f886d87..90493e8cd4 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 @@ -175,21 +175,12 @@ public final class MainActivity extends AppCompatActivity implements MainView } @Override - public void launchOpenFileActivity() + public void launchOpenFileActivity(int requestCode) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); - startActivityForResult(intent, MainPresenter.REQUEST_GAME_FILE); - } - - @Override - public void launchInstallWAD() - { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - startActivityForResult(intent, MainPresenter.REQUEST_WAD_FILE); + startActivityForResult(intent, requestCode); } /** 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 9344a61e48..f51f269d58 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 @@ -91,11 +91,12 @@ public final class MainPresenter return true; case R.id.menu_open_file: - mView.launchOpenFileActivity(); + mView.launchOpenFileActivity(REQUEST_GAME_FILE); return true; case R.id.menu_install_wad: - new AfterDirectoryInitializationRunner().run(context, true, mView::launchInstallWAD); + new AfterDirectoryInitializationRunner().run(context, true, + () -> mView.launchOpenFileActivity(REQUEST_WAD_FILE)); return true; } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.java index fc4ed31513..b2e6b74495 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainView.java @@ -21,9 +21,7 @@ public interface MainView void launchFileListActivity(); - void launchOpenFileActivity(); - - void launchInstallWAD(); + void launchOpenFileActivity(int requestCode); /** * To be called when the game file cache is updated. 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 8e96083ce1..0e9150ee52 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 @@ -180,21 +180,12 @@ public final class TvMainActivity extends FragmentActivity implements MainView } @Override - public void launchOpenFileActivity() + public void launchOpenFileActivity(int requestCode) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); - startActivityForResult(intent, MainPresenter.REQUEST_GAME_FILE); - } - - @Override - public void launchInstallWAD() - { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - startActivityForResult(intent, MainPresenter.REQUEST_WAD_FILE); + startActivityForResult(intent, requestCode); } @Override From b08306d0b89a34b4dc04a324ce47b8d8833230a8 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 17 Jan 2021 22:18:31 +0100 Subject: [PATCH 3/6] Android: Refactor MainPresenter.installWad Also replacing a toast with a dialog so that you have proper time to read the message. --- .../dolphinemu/ui/main/MainPresenter.java | 50 +++++++++++-------- .../app/src/main/res/values/strings.xml | 1 + 2 files changed, 29 insertions(+), 22 deletions(-) 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 f51f269d58..489545e43a 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 @@ -7,7 +7,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; -import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -24,6 +23,7 @@ import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.WiiUtils; import java.util.Arrays; +import java.util.function.Supplier; public final class MainPresenter { @@ -150,32 +150,38 @@ public final class MainPresenter mDirToAdd = uri.toString(); } - public void installWAD(String file) + public void installWAD(String path) + { + runOnThreadAndShowResult(R.string.import_in_progress, () -> + { + boolean success = WiiUtils.installWAD(path); + int message = success ? R.string.wad_install_success : R.string.wad_install_failure; + return mContext.getResources().getString(message); + }); + } + + private void runOnThreadAndShowResult(int progressMessage, Supplier f) { final Activity mainPresenterActivity = (Activity) mContext; - AlertDialog dialog = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase).create(); - dialog.setTitle("Installing WAD"); - dialog.setMessage("Installing..."); - dialog.setCancelable(false); - dialog.show(); + AlertDialog progressDialog = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase) + .create(); + progressDialog.setTitle(progressMessage); + progressDialog.setCancelable(false); + progressDialog.show(); - Thread installWADThread = new Thread(() -> + new Thread(() -> { - if (WiiUtils.installWAD(file)) + String result = f.get(); + mainPresenterActivity.runOnUiThread(() -> { - mainPresenterActivity.runOnUiThread( - () -> Toast.makeText(mContext, R.string.wad_install_success, Toast.LENGTH_SHORT) - .show()); - } - else - { - mainPresenterActivity.runOnUiThread( - () -> Toast.makeText(mContext, R.string.wad_install_failure, Toast.LENGTH_SHORT) - .show()); - } - mainPresenterActivity.runOnUiThread(dialog::dismiss); - }, "InstallWAD"); - installWADThread.start(); + progressDialog.dismiss(); + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + builder.setMessage(result); + builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss()); + builder.show(); + }); + }, mContext.getResources().getString(progressMessage)).start(); } } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 0dc8701e90..043e6516d8 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -329,6 +329,7 @@ Refresh Library Open File Install WAD + Importing... Successfully installed this title to the NAND. Failed to install this title to the NAND. From cd4ccda51c7b16be602ad32ffdf672a3de7cffeb Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 17 Jan 2021 23:16:23 +0100 Subject: [PATCH 4/6] Android: Add "Import Wii Save" --- .../dolphinemu/ui/main/MainActivity.java | 5 +++ .../dolphinemu/ui/main/MainPresenter.java | 45 +++++++++++++++++-- .../dolphinemu/ui/main/TvMainActivity.java | 9 ++++ .../dolphinemu/utils/FileBrowserHelper.java | 3 ++ .../dolphinemu/dolphinemu/utils/WiiUtils.java | 8 ++++ .../app/src/main/res/menu/menu_game_grid.xml | 5 +++ .../app/src/main/res/values/strings.xml | 5 +++ Source/Android/jni/WiiUtils.cpp | 34 ++++++++++++++ Source/Core/Core/HW/WiiSave.h | 1 + 9 files changed, 111 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 90493e8cd4..2beb2112f2 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 @@ -220,6 +220,11 @@ public final class MainActivity extends AppCompatActivity implements MainView FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.WAD_EXTENSION, () -> mPresenter.installWAD(result.getData().toString())); break; + + case MainPresenter.REQUEST_WII_SAVE_FILE: + FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION, + () -> mPresenter.importWiiSave(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 489545e43a..c07fe5b388 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 @@ -31,6 +31,7 @@ public final class MainPresenter public static final int REQUEST_GAME_FILE = 2; 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; private final MainView mView; private final Context mContext; @@ -98,6 +99,11 @@ public final class MainPresenter new AfterDirectoryInitializationRunner().run(context, true, () -> mView.launchOpenFileActivity(REQUEST_WAD_FILE)); return true; + + case R.id.menu_import_wii_save: + new AfterDirectoryInitializationRunner().run(context, true, + () -> mView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE)); + return true; } return false; @@ -160,6 +166,33 @@ public final class MainPresenter }); } + public void importWiiSave(String path) + { + runOnThreadAndShowResult(R.string.import_in_progress, () -> + { + int result = WiiUtils.importWiiSave(path); + int message; + switch (result) + { + case WiiUtils.RESULT_SUCCESS: + message = R.string.wii_save_import_success; + break; + case WiiUtils.RESULT_CORRUPTED_SOURCE: + message = R.string.wii_save_import_corruped_source; + break; + case WiiUtils.RESULT_TITLE_MISSING: + message = R.string.wii_save_import_title_missing; + break; + case WiiUtils.RESULT_CANCELLED: + return null; + default: + message = R.string.wii_save_import_error; + break; + } + return mContext.getResources().getString(message); + }); + } + private void runOnThreadAndShowResult(int progressMessage, Supplier f) { final Activity mainPresenterActivity = (Activity) mContext; @@ -177,10 +210,14 @@ public final class MainPresenter { progressDialog.dismiss(); - AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); - builder.setMessage(result); - builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss()); - builder.show(); + if (result != null) + { + AlertDialog.Builder builder = + new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + builder.setMessage(result); + builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss()); + builder.show(); + } }); }, mContext.getResources().getString(progressMessage)).start(); } 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 0e9150ee52..d34c3d8518 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 @@ -244,6 +244,11 @@ public final class TvMainActivity extends FragmentActivity implements MainView FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.WAD_EXTENSION, () -> mPresenter.installWAD(result.getData().toString())); break; + + case MainPresenter.REQUEST_WII_SAVE_FILE: + FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION, + () -> mPresenter.importWiiSave(result.getData().toString())); + break; } } else @@ -344,6 +349,10 @@ public final class TvMainActivity extends FragmentActivity implements MainView R.drawable.ic_folder, R.string.grid_menu_install_wad)); + rowItems.add(new TvSettingsItem(R.id.menu_import_wii_save, + R.drawable.ic_folder, + R.string.grid_menu_import_wii_save)); + // 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/FileBrowserHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java index f68bc46857..94b10806df 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java @@ -37,6 +37,9 @@ public final class FileBrowserHelper GAME_LIKE_EXTENSIONS.add("dff"); } + public static final HashSet BIN_EXTENSION = new HashSet<>(Collections.singletonList( + "bin")); + public static final HashSet RAW_EXTENSION = new HashSet<>(Collections.singletonList( "raw")); 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 6fc0b6d9ea..404d7e9909 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 @@ -2,5 +2,13 @@ package org.dolphinemu.dolphinemu.utils; public final class WiiUtils { + public static final int RESULT_SUCCESS = 0; + public static final int RESULT_ERROR = 1; + public static final int RESULT_CANCELLED = 2; + public static final int RESULT_CORRUPTED_SOURCE = 3; + public static final int RESULT_TITLE_MISSING = 4; + public static native boolean installWAD(String file); + + public static native int importWiiSave(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 f4a489e824..6b0dde4aa2 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 @@ -25,4 +25,9 @@ android:title="@string/grid_menu_install_wad" 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 043e6516d8..1e1739df92 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -329,9 +329,14 @@ Refresh Library Open File Install WAD + Import Wii Save Importing... Successfully installed this title to the NAND. Failed to install this title to the NAND. + Successfully imported save file. + 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. Details diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index c98dfe847c..a2f607d68a 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -8,8 +8,32 @@ #include "jni/AndroidCommon/AndroidCommon.h" +#include "Core/HW/WiiSave.h" #include "Core/WiiUtils.h" +// The hardcoded values here must match WiiUtils.java +static jint ConvertCopyResult(WiiSave::CopyResult result) +{ + switch (result) + { + case WiiSave::CopyResult::Success: + return 0; + case WiiSave::CopyResult::Error: + return 1; + case WiiSave::CopyResult::Cancelled: + return 2; + case WiiSave::CopyResult::CorruptedSource: + return 3; + case WiiSave::CopyResult::TitleMissing: + return 4; + default: + ASSERT(false); + return 1; + } + + static_assert(static_cast(WiiSave::CopyResult::NumberOfEntries) == 5); +} + extern "C" { JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_installWAD(JNIEnv* env, @@ -19,4 +43,14 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_install const std::string path = GetJString(env, jFile); return static_cast(WiiUtils::InstallWAD(path)); } + +JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importWiiSave(JNIEnv* env, + jclass, + jstring jFile) +{ + const std::string path = GetJString(env, jFile); + const auto can_overwrite = [] { return true; }; // TODO + + return ConvertCopyResult(WiiSave::Import(path, can_overwrite)); +} } diff --git a/Source/Core/Core/HW/WiiSave.h b/Source/Core/Core/HW/WiiSave.h index f7d414586a..423f72b098 100644 --- a/Source/Core/Core/HW/WiiSave.h +++ b/Source/Core/Core/HW/WiiSave.h @@ -39,6 +39,7 @@ enum class CopyResult Cancelled, CorruptedSource, TitleMissing, + NumberOfEntries }; CopyResult Copy(Storage* source, Storage* destination); From 347551a01da643980fabfd3579ec34bb6cbb5419 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Jan 2021 12:22:26 +0100 Subject: [PATCH 5/6] Android: Implement save overwrite confirmation --- .../dolphinemu/ui/main/MainPresenter.java | 37 ++++++++++++++++++- .../dolphinemu/utils/BooleanSupplier.java | 6 +++ .../dolphinemu/dolphinemu/utils/WiiUtils.java | 2 +- .../app/src/main/res/values/strings.xml | 1 + Source/Android/jni/AndroidCommon/IDCache.cpp | 13 +++++++ Source/Android/jni/AndroidCommon/IDCache.h | 2 + Source/Android/jni/WiiUtils.cpp | 11 ++++-- 7 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/BooleanSupplier.java 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 c07fe5b388..1534b0c654 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 @@ -18,11 +18,14 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; +import org.dolphinemu.dolphinemu.utils.BooleanSupplier; import org.dolphinemu.dolphinemu.utils.ContentHandler; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.WiiUtils; import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.Supplier; public final class MainPresenter @@ -168,9 +171,41 @@ public final class MainPresenter public void importWiiSave(String path) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) + return; // TODO + + final Activity mainPresenterActivity = (Activity) mContext; + + CompletableFuture canOverwriteFuture = new CompletableFuture<>(); + runOnThreadAndShowResult(R.string.import_in_progress, () -> { - int result = WiiUtils.importWiiSave(path); + BooleanSupplier canOverwrite = () -> + { + mainPresenterActivity.runOnUiThread(() -> + { + AlertDialog.Builder builder = + new AlertDialog.Builder(mContext, R.style.DolphinDialogBase); + builder.setMessage(R.string.wii_save_exists); + builder.setCancelable(false); + builder.setPositiveButton(R.string.yes, (dialog, i) -> canOverwriteFuture.complete(true)); + builder.setNegativeButton(R.string.no, (dialog, i) -> canOverwriteFuture.complete(false)); + builder.show(); + }); + + try + { + return canOverwriteFuture.get(); + } + catch (ExecutionException | InterruptedException e) + { + // Shouldn't happen + throw new RuntimeException(e); + } + }; + + int result = WiiUtils.importWiiSave(path, canOverwrite); + int message; switch (result) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/BooleanSupplier.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/BooleanSupplier.java new file mode 100644 index 0000000000..d1c490fe2a --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/BooleanSupplier.java @@ -0,0 +1,6 @@ +package org.dolphinemu.dolphinemu.utils; + +public interface BooleanSupplier +{ + boolean get(); +} 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 404d7e9909..9a6440c9e5 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 @@ -10,5 +10,5 @@ public final class WiiUtils public static native boolean installWAD(String file); - public static native int importWiiSave(String file); + public static native int importWiiSave(String file, BooleanSupplier canOverwrite); } diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 1e1739df92..6a53b7ce3c 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -333,6 +333,7 @@ Importing... 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? Successfully imported save file. 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. diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 4c6c4b2ce7..b5ca604c6e 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -56,6 +56,9 @@ static jmethodID s_network_helper_get_network_ip_address; static jmethodID s_network_helper_get_network_prefix_length; static jmethodID s_network_helper_get_network_gateway; +static jclass s_boolean_supplier_class; +static jmethodID s_boolean_supplier_get; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -261,6 +264,11 @@ jmethodID GetNetworkHelperGetNetworkGateway() return s_network_helper_get_network_gateway; } +jmethodID GetBooleanSupplierGet() +{ + return s_boolean_supplier_get; +} + } // namespace IDCache #ifdef __cplusplus @@ -361,6 +369,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) s_network_helper_get_network_gateway = env->GetStaticMethodID(s_network_helper_class, "GetNetworkGateway", "()I"); + const jclass boolean_supplier_class = + env->FindClass("org/dolphinemu/dolphinemu/utils/BooleanSupplier"); + s_boolean_supplier_class = reinterpret_cast(env->NewGlobalRef(boolean_supplier_class)); + s_boolean_supplier_get = env->GetMethodID(s_boolean_supplier_class, "get", "()Z"); + return JNI_VERSION; } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index b633267f55..b0e7a3d814 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -56,4 +56,6 @@ jmethodID GetNetworkHelperGetNetworkIpAddress(); jmethodID GetNetworkHelperGetNetworkPrefixLength(); jmethodID GetNetworkHelperGetNetworkGateway(); +jmethodID GetBooleanSupplierGet(); + } // namespace IDCache diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index a2f607d68a..9b0601c1df 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -7,6 +7,7 @@ #include #include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" #include "Core/HW/WiiSave.h" #include "Core/WiiUtils.h" @@ -44,12 +45,14 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_install return static_cast(WiiUtils::InstallWAD(path)); } -JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importWiiSave(JNIEnv* env, - jclass, - jstring jFile) +JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importWiiSave( + JNIEnv* env, jclass, jstring jFile, jobject jCanOverwrite) { const std::string path = GetJString(env, jFile); - const auto can_overwrite = [] { return true; }; // TODO + const auto can_overwrite = [&] { + const jmethodID get = IDCache::GetBooleanSupplierGet(); + return static_cast(env->CallBooleanMethod(jCanOverwrite, get)); + }; return ConvertCopyResult(WiiSave::Import(path, can_overwrite)); } From 12aa1071cbb30269cd6526cfbbcd3c4143d584ac Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 18 Jan 2021 13:06:40 +0100 Subject: [PATCH 6/6] Android: Re-implement a subset of CompletableFuture Imagine if Android phones actually got updates --- .../dolphinemu/ui/main/MainPresenter.java | 5 +- .../dolphinemu/utils/CompletableFuture.java | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java 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 1534b0c654..ebd43513ab 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 @@ -19,12 +19,12 @@ import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; import org.dolphinemu.dolphinemu.utils.BooleanSupplier; +import org.dolphinemu.dolphinemu.utils.CompletableFuture; import org.dolphinemu.dolphinemu.utils.ContentHandler; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.WiiUtils; import java.util.Arrays; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; @@ -171,9 +171,6 @@ public final class MainPresenter public void importWiiSave(String path) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) - return; // TODO - final Activity mainPresenterActivity = (Activity) mContext; CompletableFuture canOverwriteFuture = new CompletableFuture<>(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java new file mode 100644 index 0000000000..bdd1b02faf --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CompletableFuture.java @@ -0,0 +1,95 @@ +package org.dolphinemu.dolphinemu.utils; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Simplified re-implementation of a subset of {@link java.util.concurrent.CompletableFuture}. + * Replace this class with that class once we have full Java 8 support (once we require API 24). + */ +public class CompletableFuture implements Future +{ + private final Lock lock = new ReentrantLock(); + private final Condition done = lock.newCondition(); + + private boolean isDone = false; + private T result = null; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCancelled() + { + return false; + } + + @Override + public boolean isDone() + { + return isDone; + } + + @Override + public T get() throws ExecutionException, InterruptedException + { + lock.lock(); + try + { + while (!isDone) + done.await(); + + return result; + } + finally + { + lock.unlock(); + } + } + + @Override + public T get(long timeout, TimeUnit unit) + throws ExecutionException, InterruptedException, TimeoutException + { + lock.lock(); + try + { + while (!isDone) + { + if (!done.await(timeout, unit)) + throw new TimeoutException(); + } + + return result; + } + finally + { + lock.unlock(); + } + } + + public boolean complete(T value) + { + lock.lock(); + try + { + boolean wasDone = isDone; + result = value; + isDone = true; + done.signalAll(); + return !wasDone; + } + finally + { + lock.unlock(); + } + } +}