diff options
Diffstat (limited to 'src')
8 files changed, 117 insertions, 34 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 55abba093..53137b2e2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | |||
| @@ -261,7 +261,7 @@ object NativeLibrary { | |||
| 261 | /** | 261 | /** |
| 262 | * Begins emulation. | 262 | * Begins emulation. |
| 263 | */ | 263 | */ |
| 264 | external fun run(path: String?) | 264 | external fun run(path: String?, programIndex: Int = 0) |
| 265 | 265 | ||
| 266 | // Surface Handling | 266 | // Surface Handling |
| 267 | external fun surfaceChanged(surf: Surface?) | 267 | external fun surfaceChanged(surf: Surface?) |
| @@ -489,6 +489,12 @@ object NativeLibrary { | |||
| 489 | sEmulationActivity.get()!!.onEmulationStopped(status) | 489 | sEmulationActivity.get()!!.onEmulationStopped(status) |
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | @Keep | ||
| 493 | @JvmStatic | ||
| 494 | fun onProgramChanged(programIndex: Int) { | ||
| 495 | sEmulationActivity.get()!!.onProgramChanged(programIndex) | ||
| 496 | } | ||
| 497 | |||
| 492 | /** | 498 | /** |
| 493 | * Logs the Yuzu version, Android version and, CPU. | 499 | * Logs the Yuzu version, Android version and, CPU. |
| 494 | */ | 500 | */ |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 26cddecf4..564aaf305 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt | |||
| @@ -76,7 +76,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 76 | 76 | ||
| 77 | override fun onDestroy() { | 77 | override fun onDestroy() { |
| 78 | stopForegroundService(this) | 78 | stopForegroundService(this) |
| 79 | emulationViewModel.clear() | ||
| 80 | super.onDestroy() | 79 | super.onDestroy() |
| 81 | } | 80 | } |
| 82 | 81 | ||
| @@ -446,9 +445,14 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { | |||
| 446 | } | 445 | } |
| 447 | 446 | ||
| 448 | fun onEmulationStopped(status: Int) { | 447 | fun onEmulationStopped(status: Int) { |
| 449 | if (status == 0) { | 448 | if (status == 0 && emulationViewModel.programChanged.value == -1) { |
| 450 | finish() | 449 | finish() |
| 451 | } | 450 | } |
| 451 | emulationViewModel.setEmulationStopped(true) | ||
| 452 | } | ||
| 453 | |||
| 454 | fun onProgramChanged(programIndex: Int) { | ||
| 455 | emulationViewModel.setProgramChanged(programIndex) | ||
| 452 | } | 456 | } |
| 453 | 457 | ||
| 454 | private fun startMotionSensorListener() { | 458 | private fun startMotionSensorListener() { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index ef393c4be..1f591ced1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -424,10 +424,38 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 424 | } | 424 | } |
| 425 | } | 425 | } |
| 426 | } | 426 | } |
| 427 | launch { | ||
| 428 | repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| 429 | emulationViewModel.programChanged.collect { | ||
| 430 | if (it != 0) { | ||
| 431 | emulationViewModel.setEmulationStarted(false) | ||
| 432 | binding.drawerLayout.close() | ||
| 433 | binding.drawerLayout | ||
| 434 | .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) | ||
| 435 | ViewUtils.hideView(binding.surfaceInputOverlay) | ||
| 436 | ViewUtils.showView(binding.loadingIndicator) | ||
| 437 | } | ||
| 438 | } | ||
| 439 | } | ||
| 440 | } | ||
| 441 | launch { | ||
| 442 | repeatOnLifecycle(Lifecycle.State.CREATED) { | ||
| 443 | emulationViewModel.emulationStopped.collect { | ||
| 444 | if (it && emulationViewModel.programChanged.value != -1) { | ||
| 445 | if (perfStatsUpdater != null) { | ||
| 446 | perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) | ||
| 447 | } | ||
| 448 | emulationState.changeProgram(emulationViewModel.programChanged.value) | ||
| 449 | emulationViewModel.setProgramChanged(-1) | ||
| 450 | emulationViewModel.setEmulationStopped(false) | ||
| 451 | } | ||
| 452 | } | ||
| 453 | } | ||
| 454 | } | ||
| 427 | } | 455 | } |
| 428 | } | 456 | } |
| 429 | 457 | ||
| 430 | private fun startEmulation() { | 458 | private fun startEmulation(programIndex: Int = 0) { |
| 431 | if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) { | 459 | if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) { |
| 432 | if (!DirectoryInitialization.areDirectoriesReady) { | 460 | if (!DirectoryInitialization.areDirectoriesReady) { |
| 433 | DirectoryInitialization.start() | 461 | DirectoryInitialization.start() |
| @@ -435,7 +463,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 435 | 463 | ||
| 436 | updateScreenLayout() | 464 | updateScreenLayout() |
| 437 | 465 | ||
| 438 | emulationState.run(emulationActivity!!.isActivityRecreated) | 466 | emulationState.run(emulationActivity!!.isActivityRecreated, programIndex) |
| 439 | } | 467 | } |
| 440 | } | 468 | } |
| 441 | 469 | ||
| @@ -833,6 +861,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 833 | ) { | 861 | ) { |
| 834 | private var state: State | 862 | private var state: State |
| 835 | private var surface: Surface? = null | 863 | private var surface: Surface? = null |
| 864 | lateinit var emulationThread: Thread | ||
| 836 | 865 | ||
| 837 | init { | 866 | init { |
| 838 | // Starting state is stopped. | 867 | // Starting state is stopped. |
| @@ -878,7 +907,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 878 | } | 907 | } |
| 879 | 908 | ||
| 880 | @Synchronized | 909 | @Synchronized |
| 881 | fun run(isActivityRecreated: Boolean) { | 910 | fun run(isActivityRecreated: Boolean, programIndex: Int = 0) { |
| 882 | if (isActivityRecreated) { | 911 | if (isActivityRecreated) { |
| 883 | if (NativeLibrary.isRunning()) { | 912 | if (NativeLibrary.isRunning()) { |
| 884 | state = State.PAUSED | 913 | state = State.PAUSED |
| @@ -889,10 +918,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 889 | 918 | ||
| 890 | // If the surface is set, run now. Otherwise, wait for it to get set. | 919 | // If the surface is set, run now. Otherwise, wait for it to get set. |
| 891 | if (surface != null) { | 920 | if (surface != null) { |
| 892 | runWithValidSurface() | 921 | runWithValidSurface(programIndex) |
| 893 | } | 922 | } |
| 894 | } | 923 | } |
| 895 | 924 | ||
| 925 | @Synchronized | ||
| 926 | fun changeProgram(programIndex: Int) { | ||
| 927 | emulationThread.join() | ||
| 928 | emulationThread = Thread({ | ||
| 929 | Log.debug("[EmulationFragment] Starting emulation thread.") | ||
| 930 | NativeLibrary.run(gamePath, programIndex) | ||
| 931 | }, "NativeEmulation") | ||
| 932 | emulationThread.start() | ||
| 933 | } | ||
| 934 | |||
| 896 | // Surface callbacks | 935 | // Surface callbacks |
| 897 | @Synchronized | 936 | @Synchronized |
| 898 | fun newSurface(surface: Surface?) { | 937 | fun newSurface(surface: Surface?) { |
| @@ -932,7 +971,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 932 | } | 971 | } |
| 933 | } | 972 | } |
| 934 | 973 | ||
| 935 | private fun runWithValidSurface() { | 974 | private fun runWithValidSurface(programIndex: Int = 0) { |
| 936 | NativeLibrary.surfaceChanged(surface) | 975 | NativeLibrary.surfaceChanged(surface) |
| 937 | if (!emulationCanStart.invoke()) { | 976 | if (!emulationCanStart.invoke()) { |
| 938 | return | 977 | return |
| @@ -940,9 +979,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 940 | 979 | ||
| 941 | when (state) { | 980 | when (state) { |
| 942 | State.STOPPED -> { | 981 | State.STOPPED -> { |
| 943 | val emulationThread = Thread({ | 982 | emulationThread = Thread({ |
| 944 | Log.debug("[EmulationFragment] Starting emulation thread.") | 983 | Log.debug("[EmulationFragment] Starting emulation thread.") |
| 945 | NativeLibrary.run(gamePath) | 984 | NativeLibrary.run(gamePath, programIndex) |
| 946 | }, "NativeEmulation") | 985 | }, "NativeEmulation") |
| 947 | emulationThread.start() | 986 | emulationThread.start() |
| 948 | } | 987 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt index b66f47fe7..d024493cd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt | |||
| @@ -15,6 +15,12 @@ class EmulationViewModel : ViewModel() { | |||
| 15 | val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping | 15 | val isEmulationStopping: StateFlow<Boolean> get() = _isEmulationStopping |
| 16 | private val _isEmulationStopping = MutableStateFlow(false) | 16 | private val _isEmulationStopping = MutableStateFlow(false) |
| 17 | 17 | ||
| 18 | private val _emulationStopped = MutableStateFlow(false) | ||
| 19 | val emulationStopped = _emulationStopped.asStateFlow() | ||
| 20 | |||
| 21 | private val _programChanged = MutableStateFlow(-1) | ||
| 22 | val programChanged = _programChanged.asStateFlow() | ||
| 23 | |||
| 18 | val shaderProgress: StateFlow<Int> get() = _shaderProgress | 24 | val shaderProgress: StateFlow<Int> get() = _shaderProgress |
| 19 | private val _shaderProgress = MutableStateFlow(0) | 25 | private val _shaderProgress = MutableStateFlow(0) |
| 20 | 26 | ||
| @@ -35,6 +41,17 @@ class EmulationViewModel : ViewModel() { | |||
| 35 | _isEmulationStopping.value = value | 41 | _isEmulationStopping.value = value |
| 36 | } | 42 | } |
| 37 | 43 | ||
| 44 | fun setEmulationStopped(value: Boolean) { | ||
| 45 | if (value) { | ||
| 46 | _emulationStarted.value = false | ||
| 47 | } | ||
| 48 | _emulationStopped.value = value | ||
| 49 | } | ||
| 50 | |||
| 51 | fun setProgramChanged(programIndex: Int) { | ||
| 52 | _programChanged.value = programIndex | ||
| 53 | } | ||
| 54 | |||
| 38 | fun setShaderProgress(progress: Int) { | 55 | fun setShaderProgress(progress: Int) { |
| 39 | _shaderProgress.value = progress | 56 | _shaderProgress.value = progress |
| 40 | } | 57 | } |
| @@ -56,20 +73,4 @@ class EmulationViewModel : ViewModel() { | |||
| 56 | fun setDrawerOpen(value: Boolean) { | 73 | fun setDrawerOpen(value: Boolean) { |
| 57 | _drawerOpen.value = value | 74 | _drawerOpen.value = value |
| 58 | } | 75 | } |
| 59 | |||
| 60 | fun clear() { | ||
| 61 | setEmulationStarted(false) | ||
| 62 | setIsEmulationStopping(false) | ||
| 63 | setShaderProgress(0) | ||
| 64 | setTotalShaders(0) | ||
| 65 | setShaderMessage("") | ||
| 66 | } | ||
| 67 | |||
| 68 | companion object { | ||
| 69 | const val KEY_EMULATION_STARTED = "EmulationStarted" | ||
| 70 | const val KEY_IS_EMULATION_STOPPING = "IsEmulationStarting" | ||
| 71 | const val KEY_SHADER_PROGRESS = "ShaderProgress" | ||
| 72 | const val KEY_TOTAL_SHADERS = "TotalShaders" | ||
| 73 | const val KEY_SHADER_MESSAGE = "ShaderMessage" | ||
| 74 | } | ||
| 75 | } | 76 | } |
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index 96f2ad3d4..f30100bd8 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp | |||
| @@ -19,6 +19,7 @@ static jmethodID s_exit_emulation_activity; | |||
| 19 | static jmethodID s_disk_cache_load_progress; | 19 | static jmethodID s_disk_cache_load_progress; |
| 20 | static jmethodID s_on_emulation_started; | 20 | static jmethodID s_on_emulation_started; |
| 21 | static jmethodID s_on_emulation_stopped; | 21 | static jmethodID s_on_emulation_stopped; |
| 22 | static jmethodID s_on_program_changed; | ||
| 22 | 23 | ||
| 23 | static jclass s_game_class; | 24 | static jclass s_game_class; |
| 24 | static jmethodID s_game_constructor; | 25 | static jmethodID s_game_constructor; |
| @@ -123,6 +124,10 @@ jmethodID GetOnEmulationStopped() { | |||
| 123 | return s_on_emulation_stopped; | 124 | return s_on_emulation_stopped; |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 127 | jmethodID GetOnProgramChanged() { | ||
| 128 | return s_on_program_changed; | ||
| 129 | } | ||
| 130 | |||
| 126 | jclass GetGameClass() { | 131 | jclass GetGameClass() { |
| 127 | return s_game_class; | 132 | return s_game_class; |
| 128 | } | 133 | } |
| @@ -306,6 +311,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |||
| 306 | env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); | 311 | env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V"); |
| 307 | s_on_emulation_stopped = | 312 | s_on_emulation_stopped = |
| 308 | env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); | 313 | env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V"); |
| 314 | s_on_program_changed = | ||
| 315 | env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V"); | ||
| 309 | 316 | ||
| 310 | const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); | 317 | const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game"); |
| 311 | s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); | 318 | s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class)); |
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h index a002e705d..00e48afc0 100644 --- a/src/android/app/src/main/jni/id_cache.h +++ b/src/android/app/src/main/jni/id_cache.h | |||
| @@ -19,6 +19,7 @@ jmethodID GetExitEmulationActivity(); | |||
| 19 | jmethodID GetDiskCacheLoadProgress(); | 19 | jmethodID GetDiskCacheLoadProgress(); |
| 20 | jmethodID GetOnEmulationStarted(); | 20 | jmethodID GetOnEmulationStarted(); |
| 21 | jmethodID GetOnEmulationStopped(); | 21 | jmethodID GetOnEmulationStopped(); |
| 22 | jmethodID GetOnProgramChanged(); | ||
| 22 | 23 | ||
| 23 | jclass GetGameClass(); | 24 | jclass GetGameClass(); |
| 24 | jmethodID GetGameConstructor(); | 25 | jmethodID GetGameConstructor(); |
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 3fd9a500c..958a77ac7 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp | |||
| @@ -208,7 +208,8 @@ void EmulationSession::InitializeSystem(bool reload) { | |||
| 208 | m_system.GetFileSystemController().CreateFactories(*m_vfs); | 208 | m_system.GetFileSystemController().CreateFactories(*m_vfs); |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) { | 211 | Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath, |
| 212 | const std::size_t program_index) { | ||
| 212 | std::scoped_lock lock(m_mutex); | 213 | std::scoped_lock lock(m_mutex); |
| 213 | 214 | ||
| 214 | // Create the render window. | 215 | // Create the render window. |
| @@ -238,7 +239,8 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string | |||
| 238 | ConfigureFilesystemProvider(filepath); | 239 | ConfigureFilesystemProvider(filepath); |
| 239 | 240 | ||
| 240 | // Load the ROM. | 241 | // Load the ROM. |
| 241 | m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); | 242 | m_load_result = |
| 243 | m_system.Load(EmulationSession::GetInstance().Window(), filepath, 0, program_index); | ||
| 242 | if (m_load_result != Core::SystemResultStatus::Success) { | 244 | if (m_load_result != Core::SystemResultStatus::Success) { |
| 243 | return m_load_result; | 245 | return m_load_result; |
| 244 | } | 246 | } |
| @@ -248,6 +250,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string | |||
| 248 | m_system.GetCpuManager().OnGpuReady(); | 250 | m_system.GetCpuManager().OnGpuReady(); |
| 249 | m_system.RegisterExitCallback([&] { HaltEmulation(); }); | 251 | m_system.RegisterExitCallback([&] { HaltEmulation(); }); |
| 250 | 252 | ||
| 253 | // Register an ExecuteProgram callback such that Core can execute a sub-program | ||
| 254 | m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) { | ||
| 255 | m_next_program_index = program_index_; | ||
| 256 | EmulationSession::GetInstance().HaltEmulation(); | ||
| 257 | }); | ||
| 258 | |||
| 251 | OnEmulationStarted(); | 259 | OnEmulationStarted(); |
| 252 | return Core::SystemResultStatus::Success; | 260 | return Core::SystemResultStatus::Success; |
| 253 | } | 261 | } |
| @@ -255,6 +263,11 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string | |||
| 255 | void EmulationSession::ShutdownEmulation() { | 263 | void EmulationSession::ShutdownEmulation() { |
| 256 | std::scoped_lock lock(m_mutex); | 264 | std::scoped_lock lock(m_mutex); |
| 257 | 265 | ||
| 266 | if (m_next_program_index != -1) { | ||
| 267 | ChangeProgram(m_next_program_index); | ||
| 268 | m_next_program_index = -1; | ||
| 269 | } | ||
| 270 | |||
| 258 | m_is_running = false; | 271 | m_is_running = false; |
| 259 | 272 | ||
| 260 | // Unload user input. | 273 | // Unload user input. |
| @@ -402,6 +415,12 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { | |||
| 402 | static_cast<jint>(result)); | 415 | static_cast<jint>(result)); |
| 403 | } | 416 | } |
| 404 | 417 | ||
| 418 | void EmulationSession::ChangeProgram(std::size_t program_index) { | ||
| 419 | JNIEnv* env = IDCache::GetEnvForThread(); | ||
| 420 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(), | ||
| 421 | static_cast<jint>(program_index)); | ||
| 422 | } | ||
| 423 | |||
| 405 | u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { | 424 | u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { |
| 406 | auto program_id_string = GetJString(env, jprogramId); | 425 | auto program_id_string = GetJString(env, jprogramId); |
| 407 | try { | 426 | try { |
| @@ -411,7 +430,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { | |||
| 411 | } | 430 | } |
| 412 | } | 431 | } |
| 413 | 432 | ||
| 414 | static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | 433 | static Core::SystemResultStatus RunEmulation(const std::string& filepath, |
| 434 | const size_t program_index = 0) { | ||
| 415 | MicroProfileOnThreadCreate("EmuThread"); | 435 | MicroProfileOnThreadCreate("EmuThread"); |
| 416 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 436 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| 417 | 437 | ||
| @@ -424,7 +444,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { | |||
| 424 | 444 | ||
| 425 | SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); | 445 | SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); |
| 426 | 446 | ||
| 427 | jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath); | 447 | jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index); |
| 428 | if (result != Core::SystemResultStatus::Success) { | 448 | if (result != Core::SystemResultStatus::Success) { |
| 429 | return result; | 449 | return result; |
| 430 | } | 450 | } |
| @@ -689,11 +709,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj | |||
| 689 | Settings::LogSettings(); | 709 | Settings::LogSettings(); |
| 690 | } | 710 | } |
| 691 | 711 | ||
| 692 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz, | 712 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, |
| 693 | jstring j_path) { | 713 | jint j_program_index) { |
| 694 | const std::string path = GetJString(env, j_path); | 714 | const std::string path = GetJString(env, j_path); |
| 695 | 715 | ||
| 696 | const Core::SystemResultStatus result{RunEmulation(path)}; | 716 | const Core::SystemResultStatus result{RunEmulation(path, j_program_index)}; |
| 697 | if (result != Core::SystemResultStatus::Success) { | 717 | if (result != Core::SystemResultStatus::Success) { |
| 698 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), | 718 | env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), |
| 699 | IDCache::GetExitEmulationActivity(), static_cast<int>(result)); | 719 | IDCache::GetExitEmulationActivity(), static_cast<int>(result)); |
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index dadb138ad..bfe3fccca 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h | |||
| @@ -45,7 +45,8 @@ public: | |||
| 45 | const Core::PerfStatsResults& PerfStats(); | 45 | const Core::PerfStatsResults& PerfStats(); |
| 46 | void ConfigureFilesystemProvider(const std::string& filepath); | 46 | void ConfigureFilesystemProvider(const std::string& filepath); |
| 47 | void InitializeSystem(bool reload); | 47 | void InitializeSystem(bool reload); |
| 48 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath); | 48 | Core::SystemResultStatus InitializeEmulation(const std::string& filepath, |
| 49 | const std::size_t program_index = 0); | ||
| 49 | 50 | ||
| 50 | bool IsHandheldOnly(); | 51 | bool IsHandheldOnly(); |
| 51 | void SetDeviceType([[maybe_unused]] int index, int type); | 52 | void SetDeviceType([[maybe_unused]] int index, int type); |
| @@ -60,6 +61,7 @@ public: | |||
| 60 | private: | 61 | private: |
| 61 | static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); | 62 | static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); |
| 62 | static void OnEmulationStopped(Core::SystemResultStatus result); | 63 | static void OnEmulationStopped(Core::SystemResultStatus result); |
| 64 | static void ChangeProgram(std::size_t program_index); | ||
| 63 | 65 | ||
| 64 | private: | 66 | private: |
| 65 | // Window management | 67 | // Window management |
| @@ -84,4 +86,7 @@ private: | |||
| 84 | // Synchronization | 86 | // Synchronization |
| 85 | std::condition_variable_any m_cv; | 87 | std::condition_variable_any m_cv; |
| 86 | mutable std::mutex m_mutex; | 88 | mutable std::mutex m_mutex; |
| 89 | |||
| 90 | // Program index for next boot | ||
| 91 | std::atomic<s32> m_next_program_index = -1; | ||
| 87 | }; | 92 | }; |