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);