Merge pull request #6270 from mahdihijazi/suppport_restore_state
[Android] Support restore emulator state
This commit is contained in:
commit
e705d43312
|
@ -294,8 +294,19 @@ public final class NativeLibrary
|
||||||
* Saves a game state to the slot number.
|
* Saves a game state to the slot number.
|
||||||
*
|
*
|
||||||
* @param slot The slot location to save state to.
|
* @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.
|
* Loads a game state from the slot number.
|
||||||
|
@ -304,6 +315,13 @@ public final class NativeLibrary
|
||||||
*/
|
*/
|
||||||
public static native void LoadState(int slot);
|
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
|
* Sets the current working user directory
|
||||||
* If not set, it auto-detects a location
|
* If not set, it auto-detects a location
|
||||||
|
@ -322,6 +340,11 @@ public final class NativeLibrary
|
||||||
*/
|
*/
|
||||||
public static native void Run(String path);
|
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
|
// Surface Handling
|
||||||
public static native void SurfaceChanged(Surface surf);
|
public static native void SurfaceChanged(Surface surf);
|
||||||
public static native void SurfaceDestroyed();
|
public static native void SurfaceDestroyed();
|
||||||
|
@ -335,6 +358,9 @@ public final class NativeLibrary
|
||||||
/** Stops emulation. */
|
/** Stops emulation. */
|
||||||
public static native void StopEmulation();
|
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
|
* Enables or disables CPU block profiling
|
||||||
* @param enable
|
* @param enable
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.activities;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.hardware.usb.UsbManager;
|
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.ControllerMappingHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
||||||
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -68,8 +66,15 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
|
|
||||||
private static boolean sIsGameCubeGame;
|
private static boolean sIsGameCubeGame;
|
||||||
|
|
||||||
|
private boolean activityRecreated;
|
||||||
private String mScreenPath;
|
private String mScreenPath;
|
||||||
private String mSelectedTitle;
|
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)
|
@Retention(SOURCE)
|
||||||
@IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE,
|
@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);
|
Intent launcher = new Intent(activity, EmulationActivity.class);
|
||||||
|
|
||||||
launcher.putExtra("SelectedGame", path);
|
launcher.putExtra(EXTRA_SELECTED_GAME, path);
|
||||||
launcher.putExtra("SelectedTitle", title);
|
launcher.putExtra(EXTRA_SELECTED_TITLE, title);
|
||||||
launcher.putExtra("ScreenPath", screenshotPath);
|
launcher.putExtra(EXTRA_SCREEN_PATH, screenshotPath);
|
||||||
launcher.putExtra("GridPosition", position);
|
launcher.putExtra(EXTRA_GRID_POSITION, position);
|
||||||
|
|
||||||
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
activity,
|
activity,
|
||||||
|
@ -158,13 +163,23 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Get params we were passed
|
if (savedInstanceState == null)
|
||||||
Intent gameToEmulate = getIntent();
|
{
|
||||||
String path = gameToEmulate.getStringExtra("SelectedGame");
|
// Get params we were passed
|
||||||
sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(path)) == Platform.GAMECUBE;
|
Intent gameToEmulate = getIntent();
|
||||||
mSelectedTitle = gameToEmulate.getStringExtra("SelectedTitle");
|
mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME);
|
||||||
mScreenPath = gameToEmulate.getStringExtra("ScreenPath");
|
mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
|
||||||
mPosition = gameToEmulate.getIntExtra("GridPosition", -1);
|
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");
|
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
|
||||||
mControllerMappingHelper = new ControllerMappingHelper();
|
mControllerMappingHelper = new ControllerMappingHelper();
|
||||||
|
|
||||||
|
@ -206,7 +221,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
.findFragmentById(R.id.frame_emulation_fragment);
|
.findFragmentById(R.id.frame_emulation_fragment);
|
||||||
if (mEmulationFragment == null)
|
if (mEmulationFragment == null)
|
||||||
{
|
{
|
||||||
mEmulationFragment = EmulationFragment.newInstance(path);
|
mEmulationFragment = EmulationFragment.newInstance(mPath);
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.add(R.id.frame_emulation_fragment, mEmulationFragment)
|
.add(R.id.frame_emulation_fragment, mEmulationFragment)
|
||||||
.commit();
|
.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
|
@Override
|
||||||
public void onBackPressed()
|
public void onBackPressed()
|
||||||
{
|
{
|
||||||
|
@ -412,7 +446,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
|
|
||||||
// Quick save / load
|
// Quick save / load
|
||||||
case MENU_ACTION_QUICK_SAVE:
|
case MENU_ACTION_QUICK_SAVE:
|
||||||
NativeLibrary.SaveState(9);
|
NativeLibrary.SaveState(9, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case MENU_ACTION_QUICK_LOAD:
|
case MENU_ACTION_QUICK_LOAD:
|
||||||
|
@ -436,27 +470,27 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
|
|
||||||
// Save state slots
|
// Save state slots
|
||||||
case MENU_ACTION_SAVE_SLOT1:
|
case MENU_ACTION_SAVE_SLOT1:
|
||||||
NativeLibrary.SaveState(0);
|
NativeLibrary.SaveState(0, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case MENU_ACTION_SAVE_SLOT2:
|
case MENU_ACTION_SAVE_SLOT2:
|
||||||
NativeLibrary.SaveState(1);
|
NativeLibrary.SaveState(1, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case MENU_ACTION_SAVE_SLOT3:
|
case MENU_ACTION_SAVE_SLOT3:
|
||||||
NativeLibrary.SaveState(2);
|
NativeLibrary.SaveState(2, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case MENU_ACTION_SAVE_SLOT4:
|
case MENU_ACTION_SAVE_SLOT4:
|
||||||
NativeLibrary.SaveState(3);
|
NativeLibrary.SaveState(3, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case MENU_ACTION_SAVE_SLOT5:
|
case MENU_ACTION_SAVE_SLOT5:
|
||||||
NativeLibrary.SaveState(4);
|
NativeLibrary.SaveState(4, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case MENU_ACTION_SAVE_SLOT6:
|
case MENU_ACTION_SAVE_SLOT6:
|
||||||
NativeLibrary.SaveState(5);
|
NativeLibrary.SaveState(5, false);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Load state slots
|
// Load state slots
|
||||||
|
@ -716,4 +750,9 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
{
|
{
|
||||||
return sIsGameCubeGame;
|
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.DirectoryStateReceiver;
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
import org.dolphinemu.dolphinemu.utils.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import rx.functions.Action1;
|
import rx.functions.Action1;
|
||||||
|
|
||||||
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback
|
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 DirectoryStateReceiver directoryStateReceiver;
|
||||||
|
|
||||||
|
private EmulationActivity activity;
|
||||||
|
|
||||||
public static EmulationFragment newInstance(String gamePath)
|
public static EmulationFragment newInstance(String gamePath)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -57,6 +61,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
|
|
||||||
if (context instanceof EmulationActivity)
|
if (context instanceof EmulationActivity)
|
||||||
{
|
{
|
||||||
|
activity = (EmulationActivity)context;
|
||||||
NativeLibrary.setEmulationActivity((EmulationActivity) context);
|
NativeLibrary.setEmulationActivity((EmulationActivity) context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -79,7 +84,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
|
|
||||||
String gamePath = getArguments().getString(KEY_GAMEPATH);
|
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();
|
super.onResume();
|
||||||
if (DirectoryInitializationService.areDolphinDirectoriesReady())
|
if (DirectoryInitializationService.areDolphinDirectoriesReady())
|
||||||
{
|
{
|
||||||
mEmulationState.run();
|
mEmulationState.run(activity.isActivityRecreated());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -155,7 +160,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
directoryStateReceiver =
|
directoryStateReceiver =
|
||||||
new DirectoryStateReceiver(directoryInitializationState -> {
|
new DirectoryStateReceiver(directoryInitializationState -> {
|
||||||
if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) {
|
if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) {
|
||||||
mEmulationState.run();
|
mEmulationState.run(activity.isActivityRecreated());
|
||||||
} else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) {
|
} else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) {
|
||||||
Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
||||||
.show();
|
.show();
|
||||||
|
@ -249,10 +254,13 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
private State state;
|
private State state;
|
||||||
private Surface mSurface;
|
private Surface mSurface;
|
||||||
private boolean mRunWhenSurfaceIsValid;
|
private boolean mRunWhenSurfaceIsValid;
|
||||||
|
private boolean loadPreviousTemporaryState;
|
||||||
|
private final String temporaryStatePath;
|
||||||
|
|
||||||
EmulationState(String gamePath)
|
EmulationState(String gamePath, String temporaryStatePath)
|
||||||
{
|
{
|
||||||
mGamePath = gamePath;
|
mGamePath = gamePath;
|
||||||
|
this.temporaryStatePath = temporaryStatePath;
|
||||||
// Starting state is stopped.
|
// Starting state is stopped.
|
||||||
state = State.STOPPED;
|
state = State.STOPPED;
|
||||||
}
|
}
|
||||||
|
@ -280,6 +288,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
{
|
{
|
||||||
if (state != State.STOPPED)
|
if (state != State.STOPPED)
|
||||||
{
|
{
|
||||||
|
Log.debug("[EmulationFragment] Stopping emulation.");
|
||||||
state = State.STOPPED;
|
state = State.STOPPED;
|
||||||
NativeLibrary.StopEmulation();
|
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 the surface is set, run now. Otherwise, wait for it to get set.
|
||||||
if (mSurface != null)
|
if (mSurface != null)
|
||||||
{
|
{
|
||||||
|
@ -362,12 +392,19 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
mRunWhenSurfaceIsValid = false;
|
mRunWhenSurfaceIsValid = false;
|
||||||
if (state == State.STOPPED)
|
if (state == State.STOPPED)
|
||||||
{
|
{
|
||||||
Log.debug("[EmulationFragment] Starting emulation thread.");
|
|
||||||
|
|
||||||
mEmulationThread = new Thread(() ->
|
mEmulationThread = new Thread(() ->
|
||||||
{
|
{
|
||||||
NativeLibrary.SurfaceChanged(mSurface);
|
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");
|
}, "NativeEmulation");
|
||||||
mEmulationThread.start();
|
mEmulationThread.start();
|
||||||
|
|
||||||
|
@ -385,4 +422,27 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
||||||
state = State.RUNNING;
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,10 @@
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "ButtonManager.h"
|
#include "ButtonManager.h"
|
||||||
|
|
||||||
|
@ -403,6 +405,8 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulati
|
||||||
jobject obj);
|
jobject obj);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv* env,
|
||||||
jobject obj);
|
jobject obj);
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
|
||||||
JNIEnv* env, jobject obj, jstring jDevice, jint Button, jint Action);
|
JNIEnv* env, jobject obj, jstring jDevice, jint Button, jint Action);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
|
||||||
|
@ -447,10 +451,18 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetFilename(
|
||||||
jstring jFile);
|
jstring jFile);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env,
|
||||||
jobject obj,
|
jobject obj,
|
||||||
jint slot);
|
jint slot,
|
||||||
|
jboolean wait);
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveStateAs(JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring path,
|
||||||
|
jboolean wait);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv* env,
|
||||||
jobject obj,
|
jobject obj,
|
||||||
jint slot);
|
jint slot);
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadStateAs(JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring path);
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_CreateUserDirectories(
|
Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_CreateUserDirectories(
|
||||||
JNIEnv* env, jobject obj);
|
JNIEnv* env, jobject obj);
|
||||||
|
@ -467,8 +479,11 @@ JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj);
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj);
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv* env, jobject obj);
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv* env, jobject obj);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* env, jobject obj,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2(
|
||||||
jstring jFile);
|
JNIEnv* env, jobject obj, jstring jFile);
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||||
|
JNIEnv* env, jobject obj, jstring jFile, jstring jSavestate, jboolean jDeleteSavestate);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
||||||
jobject obj,
|
jobject obj,
|
||||||
jobject surf);
|
jobject surf);
|
||||||
|
@ -496,11 +511,19 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulatio
|
||||||
Core::Stop();
|
Core::Stop();
|
||||||
s_update_main_frame_event.Set(); // Kick the waiting event
|
s_update_main_frame_event.Set(); // Kick the waiting event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunning(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return Core::IsRunning();
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
|
||||||
JNIEnv* env, jobject obj, jstring jDevice, jint Button, jint Action)
|
JNIEnv* env, jobject obj, jstring jDevice, jint Button, jint Action)
|
||||||
{
|
{
|
||||||
return ButtonManager::GamepadEvent(GetJString(env, jDevice), Button, Action);
|
return ButtonManager::GamepadEvent(GetJString(env, jDevice), Button, Action);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
|
||||||
JNIEnv* env, jobject obj, jstring jDevice, jint Axis, jfloat Value)
|
JNIEnv* env, jobject obj, jstring jDevice, jint Axis, jfloat Value)
|
||||||
{
|
{
|
||||||
|
@ -643,10 +666,20 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig(
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env,
|
||||||
jobject obj,
|
jobject obj,
|
||||||
jint slot)
|
jint slot,
|
||||||
|
jboolean wait)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(s_host_identity_lock);
|
std::lock_guard<std::mutex> guard(s_host_identity_lock);
|
||||||
State::Save(slot);
|
State::Save(slot, wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveStateAs(JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring path,
|
||||||
|
jboolean wait)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(s_host_identity_lock);
|
||||||
|
State::SaveAs(GetJString(env, path), wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv* env,
|
||||||
|
@ -657,6 +690,14 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JN
|
||||||
State::Load(slot);
|
State::Load(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadStateAs(JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring path)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(s_host_identity_lock);
|
||||||
|
State::LoadAs(GetJString(env, path));
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_SetSysDirectory(
|
Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_SetSysDirectory(
|
||||||
JNIEnv* env, jobject obj, jstring jPath)
|
JNIEnv* env, jobject obj, jstring jPath)
|
||||||
|
@ -762,10 +803,9 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimo
|
||||||
WiimoteReal::Refresh();
|
WiimoteReal::Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* env, jobject obj,
|
static void Run(const std::string& path, std::optional<std::string> savestate_path = {},
|
||||||
jstring jFile)
|
bool delete_savestate = false)
|
||||||
{
|
{
|
||||||
const std::string path = GetJString(env, jFile);
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", path.c_str());
|
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", path.c_str());
|
||||||
|
|
||||||
// Install our callbacks
|
// Install our callbacks
|
||||||
|
@ -781,7 +821,9 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv*
|
||||||
|
|
||||||
// No use running the loop when booting fails
|
// No use running the loop when booting fails
|
||||||
s_have_wm_user_stop = false;
|
s_have_wm_user_stop = false;
|
||||||
if (BootManager::BootCore(BootParameters::GenerateFromFile(path)))
|
std::unique_ptr<BootParameters> boot = BootParameters::GenerateFromFile(path, savestate_path);
|
||||||
|
boot->delete_savestate = delete_savestate;
|
||||||
|
if (BootManager::BootCore(std::move(boot)))
|
||||||
{
|
{
|
||||||
static constexpr int TIMEOUT = 10000;
|
static constexpr int TIMEOUT = 10000;
|
||||||
static constexpr int WAIT_STEP = 25;
|
static constexpr int WAIT_STEP = 25;
|
||||||
|
@ -812,6 +854,19 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2(
|
||||||
|
JNIEnv* env, jobject obj, jstring jFile)
|
||||||
|
{
|
||||||
|
Run(GetJString(env, jFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||||
|
JNIEnv* env, jobject obj, jstring jFile, jstring jSavestate, jboolean jDeleteSavestate)
|
||||||
|
{
|
||||||
|
Run(GetJString(env, jFile), GetJString(env, jSavestate), jDeleteSavestate);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,6 +76,7 @@ struct BootParameters
|
||||||
|
|
||||||
Parameters parameters;
|
Parameters parameters;
|
||||||
std::optional<std::string> savestate_path;
|
std::optional<std::string> savestate_path;
|
||||||
|
bool delete_savestate = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CBoot
|
class CBoot
|
||||||
|
|
|
@ -318,7 +318,7 @@ static void CPUSetInitialExecutionState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the CPU thread, which is a CPU + Video thread in Single Core mode.
|
// Create the CPU thread, which is a CPU + Video thread in Single Core mode.
|
||||||
static void CpuThread(const std::optional<std::string>& savestate_path)
|
static void CpuThread(const std::optional<std::string>& savestate_path, bool delete_savestate)
|
||||||
{
|
{
|
||||||
DeclareAsCPUThread();
|
DeclareAsCPUThread();
|
||||||
|
|
||||||
|
@ -342,7 +342,13 @@ static void CpuThread(const std::optional<std::string>& savestate_path)
|
||||||
EMM::InstallExceptionHandler(); // Let's run under memory watch
|
EMM::InstallExceptionHandler(); // Let's run under memory watch
|
||||||
|
|
||||||
if (savestate_path)
|
if (savestate_path)
|
||||||
QueueHostJob([&savestate_path] { ::State::LoadAs(*savestate_path); });
|
{
|
||||||
|
QueueHostJob([&savestate_path, delete_savestate] {
|
||||||
|
::State::LoadAs(*savestate_path);
|
||||||
|
if (delete_savestate)
|
||||||
|
File::Delete(*savestate_path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
s_is_started = true;
|
s_is_started = true;
|
||||||
CPUSetInitialExecutionState();
|
CPUSetInitialExecutionState();
|
||||||
|
@ -380,7 +386,8 @@ static void CpuThread(const std::optional<std::string>& savestate_path)
|
||||||
EMM::UninstallExceptionHandler();
|
EMM::UninstallExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FifoPlayerThread(const std::optional<std::string>& savestate_path)
|
static void FifoPlayerThread(const std::optional<std::string>& savestate_path,
|
||||||
|
bool delete_savestate)
|
||||||
{
|
{
|
||||||
DeclareAsCPUThread();
|
DeclareAsCPUThread();
|
||||||
const SConfig& _CoreParameter = SConfig::GetInstance();
|
const SConfig& _CoreParameter = SConfig::GetInstance();
|
||||||
|
@ -517,6 +524,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::optional<std::string> savestate_path = boot->savestate_path;
|
const std::optional<std::string> savestate_path = boot->savestate_path;
|
||||||
|
const bool delete_savestate = boot->delete_savestate;
|
||||||
|
|
||||||
// Load and Init Wiimotes - only if we are booting in Wii mode
|
// Load and Init Wiimotes - only if we are booting in Wii mode
|
||||||
if (core_parameter.bWii && !SConfig::GetInstance().m_bt_passthrough_enabled)
|
if (core_parameter.bWii && !SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||||
|
@ -556,7 +564,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
|
||||||
PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
|
PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
|
||||||
|
|
||||||
// Determine the CPU thread function
|
// Determine the CPU thread function
|
||||||
void (*cpuThreadFunc)(const std::optional<std::string>& savestate_path);
|
void (*cpuThreadFunc)(const std::optional<std::string>& savestate_path, bool delete_savestate);
|
||||||
if (std::holds_alternative<BootParameters::DFF>(boot->parameters))
|
if (std::holds_alternative<BootParameters::DFF>(boot->parameters))
|
||||||
cpuThreadFunc = FifoPlayerThread;
|
cpuThreadFunc = FifoPlayerThread;
|
||||||
else
|
else
|
||||||
|
@ -597,7 +605,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
|
||||||
Host_Message(WM_USER_CREATE);
|
Host_Message(WM_USER_CREATE);
|
||||||
|
|
||||||
// Spawn the CPU thread
|
// Spawn the CPU thread
|
||||||
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path);
|
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path, delete_savestate);
|
||||||
|
|
||||||
// become the GPU thread
|
// become the GPU thread
|
||||||
Fifo::RunGpuLoop();
|
Fifo::RunGpuLoop();
|
||||||
|
@ -615,7 +623,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
|
||||||
Common::SetCurrentThreadName("Emuthread - Idle");
|
Common::SetCurrentThreadName("Emuthread - Idle");
|
||||||
|
|
||||||
// Spawn the CPU+GPU thread
|
// Spawn the CPU+GPU thread
|
||||||
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path);
|
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path, delete_savestate);
|
||||||
|
|
||||||
while (CPU::GetState() != CPU::State::PowerDown)
|
while (CPU::GetState() != CPU::State::PowerDown)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue