Merge pull request #6270 from mahdihijazi/suppport_restore_state

[Android] Support restore emulator state
This commit is contained in:
Markus Wick 2018-01-05 13:45:36 +01:00 committed by GitHub
commit e705d43312
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 234 additions and 45 deletions

View File

@ -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

View File

@ -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;
}
} }

View File

@ -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
}
}
} }

View File

@ -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

View File

@ -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

View File

@ -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)
{ {