mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
Merge pull request #6270 from mahdihijazi/suppport_restore_state
[Android] Support restore emulator state
This commit is contained in:
@ -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.
|
||||
@ -304,6 +315,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
|
||||
|
@ -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();
|
||||
@ -256,6 +271,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()
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user