diff options
| author | Markus Wick <degasus@users.noreply.github.com> | 2018-01-05 13:45:36 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-01-05 13:45:36 +0100 |
| commit | e705d433126e00e3dc39e7a25a182fcd3b555a2b (patch) | |
| tree | d362f74f96adb576fe0bfbff3e8b11cf4236b100 /Source/Android/app/src/main/java | |
| parent | e31f8f8ecb901a81cdf6075ca305032a6ba3366b (diff) | |
| parent | 136e835fb489f2b0412c760739dd5c668055fb56 (diff) | |
Merge pull request #6270 from mahdihijazi/suppport_restore_state
[Android] Support restore emulator state
Diffstat (limited to 'Source/Android/app/src/main/java')
3 files changed, 155 insertions, 30 deletions
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 8d193e75ec..e82de7fa01 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 @@ -294,8 +294,19 @@ public final class NativeLibrary * Saves a game state to the slot number. * * @param slot The slot location to save state to. + * @param wait If false, returns as early as possible. + * If true, returns once the savestate has been written to disk. */ - public static native void SaveState(int slot); + public static native void SaveState(int slot, boolean wait); + + /** + * Saves a game state to the specified path. + * + * @param path The path to save state to. + * @param wait If false, returns as early as possible. + * If true, returns once the savestate has been written to disk. + */ + public static native void SaveStateAs(String path, boolean wait); /** * Loads a game state from the slot number. @@ -305,6 +316,13 @@ public final class NativeLibrary public static native void LoadState(int slot); /** + * Loads a game state from the specified path. + * + * @param path The path to load state from. + */ + public static native void LoadStateAs(String path); + + /** * Sets the current working user directory * If not set, it auto-detects a location */ @@ -322,6 +340,11 @@ public final class NativeLibrary */ public static native void Run(String path); + /** + * Begins emulation from the specified savestate. + */ + public static native void Run(String path, String savestatePath, boolean deleteSavestate); + // Surface Handling public static native void SurfaceChanged(Surface surf); public static native void SurfaceDestroyed(); @@ -335,6 +358,9 @@ public final class NativeLibrary /** Stops emulation. */ public static native void StopEmulation(); + /** Returns true if emulation is running (or is paused). */ + public static native boolean IsRunning(); + /** * Enables or disables CPU block profiling * @param enable diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 56873cab76..80972f0c7f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.activities; import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.hardware.usb.UsbManager; @@ -42,7 +41,6 @@ import org.dolphinemu.dolphinemu.utils.Animations; import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.Java_GCAdapter; import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter; -import org.dolphinemu.dolphinemu.utils.Log; import java.lang.annotation.Retention; import java.util.List; @@ -68,8 +66,15 @@ public final class EmulationActivity extends AppCompatActivity private static boolean sIsGameCubeGame; + private boolean activityRecreated; private String mScreenPath; private String mSelectedTitle; + private String mPath; + + public static final String EXTRA_SELECTED_GAME = "SelectedGame"; + public static final String EXTRA_SELECTED_TITLE = "SelectedTitle"; + public static final String EXTRA_SCREEN_PATH = "ScreenPath"; + public static final String EXTRA_GRID_POSITION = "GridPosition"; @Retention(SOURCE) @IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE, @@ -138,10 +143,10 @@ public final class EmulationActivity extends AppCompatActivity { Intent launcher = new Intent(activity, EmulationActivity.class); - launcher.putExtra("SelectedGame", path); - launcher.putExtra("SelectedTitle", title); - launcher.putExtra("ScreenPath", screenshotPath); - launcher.putExtra("GridPosition", position); + launcher.putExtra(EXTRA_SELECTED_GAME, path); + launcher.putExtra(EXTRA_SELECTED_TITLE, title); + launcher.putExtra(EXTRA_SCREEN_PATH, screenshotPath); + launcher.putExtra(EXTRA_GRID_POSITION, position); ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( activity, @@ -158,13 +163,23 @@ public final class EmulationActivity extends AppCompatActivity { super.onCreate(savedInstanceState); - // Get params we were passed - Intent gameToEmulate = getIntent(); - String path = gameToEmulate.getStringExtra("SelectedGame"); - sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(path)) == Platform.GAMECUBE; - mSelectedTitle = gameToEmulate.getStringExtra("SelectedTitle"); - mScreenPath = gameToEmulate.getStringExtra("ScreenPath"); - mPosition = gameToEmulate.getIntExtra("GridPosition", -1); + if (savedInstanceState == null) + { + // Get params we were passed + Intent gameToEmulate = getIntent(); + mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME); + mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE); + mScreenPath = gameToEmulate.getStringExtra(EXTRA_SCREEN_PATH); + mPosition = gameToEmulate.getIntExtra(EXTRA_GRID_POSITION, -1); + activityRecreated = false; + } + else + { + activityRecreated = true; + restoreState(savedInstanceState); + } + + sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(mPath)) == Platform.GAMECUBE; mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen"); mControllerMappingHelper = new ControllerMappingHelper(); @@ -206,7 +221,7 @@ public final class EmulationActivity extends AppCompatActivity .findFragmentById(R.id.frame_emulation_fragment); if (mEmulationFragment == null) { - mEmulationFragment = EmulationFragment.newInstance(path); + mEmulationFragment = EmulationFragment.newInstance(mPath); getSupportFragmentManager().beginTransaction() .add(R.id.frame_emulation_fragment, mEmulationFragment) .commit(); @@ -257,6 +272,25 @@ public final class EmulationActivity extends AppCompatActivity } @Override + protected void onSaveInstanceState(Bundle outState) + { + mEmulationFragment.saveTemporaryState(); + outState.putString(EXTRA_SELECTED_GAME, mPath); + outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle); + outState.putString(EXTRA_SCREEN_PATH, mScreenPath); + outState.putInt(EXTRA_GRID_POSITION, mPosition); + super.onSaveInstanceState(outState); + } + + protected void restoreState(Bundle savedInstanceState) + { + mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME); + mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE); + mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH); + mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION); + } + + @Override public void onBackPressed() { if (!mDeviceHasTouchScreen) @@ -412,7 +446,7 @@ public final class EmulationActivity extends AppCompatActivity // Quick save / load case MENU_ACTION_QUICK_SAVE: - NativeLibrary.SaveState(9); + NativeLibrary.SaveState(9, false); return; case MENU_ACTION_QUICK_LOAD: @@ -436,27 +470,27 @@ public final class EmulationActivity extends AppCompatActivity // Save state slots case MENU_ACTION_SAVE_SLOT1: - NativeLibrary.SaveState(0); + NativeLibrary.SaveState(0, false); return; case MENU_ACTION_SAVE_SLOT2: - NativeLibrary.SaveState(1); + NativeLibrary.SaveState(1, false); return; case MENU_ACTION_SAVE_SLOT3: - NativeLibrary.SaveState(2); + NativeLibrary.SaveState(2, false); return; case MENU_ACTION_SAVE_SLOT4: - NativeLibrary.SaveState(3); + NativeLibrary.SaveState(3, false); return; case MENU_ACTION_SAVE_SLOT5: - NativeLibrary.SaveState(4); + NativeLibrary.SaveState(4, false); return; case MENU_ACTION_SAVE_SLOT6: - NativeLibrary.SaveState(5); + NativeLibrary.SaveState(5, false); return; // Load state slots @@ -716,4 +750,9 @@ public final class EmulationActivity extends AppCompatActivity { return sIsGameCubeGame; } + + public boolean isActivityRecreated() + { + return activityRecreated; + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index 27d32db0d5..1d6a67e6c4 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -25,6 +25,8 @@ import org.dolphinemu.dolphinemu.services.DirectoryInitializationService.Directo import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver; import org.dolphinemu.dolphinemu.utils.Log; +import java.io.File; + import rx.functions.Action1; public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback @@ -39,6 +41,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private DirectoryStateReceiver directoryStateReceiver; + private EmulationActivity activity; + public static EmulationFragment newInstance(String gamePath) { @@ -57,6 +61,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C if (context instanceof EmulationActivity) { + activity = (EmulationActivity)context; NativeLibrary.setEmulationActivity((EmulationActivity) context); } else @@ -79,7 +84,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); String gamePath = getArguments().getString(KEY_GAMEPATH); - mEmulationState = new EmulationState(gamePath); + mEmulationState = new EmulationState(gamePath, getTemporaryStateFilePath()); } /** @@ -120,7 +125,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C super.onResume(); if (DirectoryInitializationService.areDolphinDirectoriesReady()) { - mEmulationState.run(); + mEmulationState.run(activity.isActivityRecreated()); } else { @@ -155,7 +160,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C directoryStateReceiver = new DirectoryStateReceiver(directoryInitializationState -> { if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) { - mEmulationState.run(); + mEmulationState.run(activity.isActivityRecreated()); } else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) { Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT) .show(); @@ -249,10 +254,13 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private State state; private Surface mSurface; private boolean mRunWhenSurfaceIsValid; + private boolean loadPreviousTemporaryState; + private final String temporaryStatePath; - EmulationState(String gamePath) + EmulationState(String gamePath, String temporaryStatePath) { mGamePath = gamePath; + this.temporaryStatePath = temporaryStatePath; // Starting state is stopped. state = State.STOPPED; } @@ -280,6 +288,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { if (state != State.STOPPED) { + Log.debug("[EmulationFragment] Stopping emulation."); state = State.STOPPED; NativeLibrary.StopEmulation(); } @@ -307,8 +316,29 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C } } - public synchronized void run() + public synchronized void run(boolean isActivityRecreated) { + if (isActivityRecreated) + { + if (NativeLibrary.IsRunning()) + { + loadPreviousTemporaryState = false; + state = State.PAUSED; + deleteFile(temporaryStatePath); + } + else + { + loadPreviousTemporaryState = true; + } + } + else + { + Log.debug("[EmulationFragment] activity resumed or fresh start"); + loadPreviousTemporaryState = false; + // activity resumed without being killed or this is the first run + deleteFile(temporaryStatePath); + } + // If the surface is set, run now. Otherwise, wait for it to get set. if (mSurface != null) { @@ -362,12 +392,19 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C mRunWhenSurfaceIsValid = false; if (state == State.STOPPED) { - Log.debug("[EmulationFragment] Starting emulation thread."); - mEmulationThread = new Thread(() -> { NativeLibrary.SurfaceChanged(mSurface); - NativeLibrary.Run(mGamePath); + if (loadPreviousTemporaryState) + { + Log.debug("[EmulationFragment] Starting emulation thread from previous state."); + NativeLibrary.Run(mGamePath, temporaryStatePath, true); + } + else + { + Log.debug("[EmulationFragment] Starting emulation thread."); + NativeLibrary.Run(mGamePath); + } }, "NativeEmulation"); mEmulationThread.start(); @@ -385,4 +422,27 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C state = State.RUNNING; } } + + public void saveTemporaryState() + { + NativeLibrary.SaveStateAs(getTemporaryStateFilePath(), true); + } + + private String getTemporaryStateFilePath() + { + return getContext().getFilesDir() + File.separator + "temp.sav"; + } + + private static void deleteFile(String path) + { + try + { + File file = new File(path); + file.delete(); + } + catch (Exception ex) + { + // fail safely + } + } } |
