2020-04-19 23:04:05 +02:00
// SPDX-License-Identifier: MPL-2.0
2020-03-27 20:36:02 +01:00
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
2020-03-26 15:33:19 +01:00
# include <csignal>
2020-11-18 20:47:09 +01:00
# include <pthread.h>
2021-03-03 21:35:24 +01:00
# include <android/asset_manager_jni.h>
2021-03-03 21:42:51 +01:00
# include <sys/system_properties.h>
2019-09-24 22:54:27 +02:00
# include "skyline/common.h"
2021-09-27 18:24:07 +02:00
# include "skyline/common/language.h"
2020-12-05 18:41:52 +01:00
# include "skyline/common/signal.h"
2022-07-22 10:53:28 +02:00
# include "skyline/common/android_settings.h"
2021-03-20 17:52:08 +01:00
# include "skyline/common/trace.h"
2020-12-05 18:41:52 +01:00
# include "skyline/loader/loader.h"
2021-03-03 21:35:24 +01:00
# include "skyline/vfs/android_asset_filesystem.h"
2019-09-24 22:54:27 +02:00
# include "skyline/os.h"
2019-12-05 16:35:34 +01:00
# include "skyline/jvm.h"
2020-10-28 17:00:39 +01:00
# include "skyline/gpu.h"
2021-10-13 19:08:48 +02:00
# include "skyline/audio.h"
2020-04-26 01:34:35 +02:00
# include "skyline/input.h"
2020-11-03 10:44:09 +01:00
# include "skyline/kernel/types/KProcess.h"
2019-09-24 22:54:27 +02:00
2021-06-27 03:26:16 +02:00
jint Fps ; //!< An approximation of the amount of frames being submitted every second
jfloat AverageFrametimeMs ; //!< The average time it takes for a frame to be rendered and presented in milliseconds
jfloat AverageFrametimeDeviationMs ; //!< The average deviation of the average frametimes in milliseconds
2020-11-03 10:44:09 +01:00
std : : weak_ptr < skyline : : kernel : : OS > OsWeak ;
std : : weak_ptr < skyline : : gpu : : GPU > GpuWeak ;
2021-10-13 19:08:48 +02:00
std : : weak_ptr < skyline : : audio : : Audio > AudioWeak ;
2020-11-03 10:44:09 +01:00
std : : weak_ptr < skyline : : input : : Input > InputWeak ;
2023-02-27 19:00:03 +01:00
std : : weak_ptr < skyline : : Settings > SettingsWeak ;
2019-12-02 18:40:53 +01:00
2021-03-03 21:42:51 +01:00
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
static std : : string GetTimeZoneName ( ) {
2021-06-27 03:26:16 +02:00
const char * nameEnv = getenv ( " TZ " ) ;
2021-03-03 21:42:51 +01:00
if ( nameEnv )
return std : : string ( nameEnv ) ;
char propBuf [ PROP_VALUE_MAX ] ;
if ( __system_property_get ( " persist.sys.timezone " , propBuf ) ) {
std : : string nameProp ( propBuf ) ;
// Flip -/+, see https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=53;drc=master;bpv=1;bpt=1
if ( nameProp . size ( ) > 2 ) {
if ( nameProp [ 2 ] = = ' - ' )
nameProp [ 2 ] = ' + ' ;
else if ( nameProp [ 2 ] = = ' + ' )
nameProp [ 2 ] = ' - ' ;
}
return nameProp ;
}
// Fallback to GMT
return " GMT " ;
}
2021-10-27 23:27:44 +02:00
extern " C " JNIEXPORT void Java_emu_skyline_SkylineApplication_initializeLog (
JNIEnv * env ,
jobject ,
2022-07-19 13:08:15 +02:00
jstring publicAppFilesPathJstring ,
2021-10-27 23:27:44 +02:00
jint logLevel
) {
2022-07-19 13:08:15 +02:00
skyline : : JniString publicAppFilesPath ( env , publicAppFilesPathJstring ) ;
2021-10-27 23:27:44 +02:00
skyline : : Logger : : configLevel = static_cast < skyline : : Logger : : LogLevel > ( logLevel ) ;
2022-07-19 13:08:15 +02:00
skyline : : Logger : : LoaderContext . Initialize ( publicAppFilesPath + " logs/loader.sklog " ) ;
2021-10-27 23:27:44 +02:00
}
2021-08-28 12:30:34 +02:00
extern " C " JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication (
2021-08-29 15:27:31 +02:00
JNIEnv * env ,
jobject instance ,
2021-08-28 12:30:34 +02:00
jstring romUriJstring ,
jint romType ,
jint romFd ,
2022-07-19 13:08:15 +02:00
jobject settingsInstance ,
2022-04-06 07:48:26 +02:00
jstring publicAppFilesPathJstring ,
jstring privateAppFilesPathJstring ,
2021-12-08 23:08:55 +01:00
jstring nativeLibraryPathJstring ,
2021-08-28 12:30:34 +02:00
jobject assetManager
) {
2021-03-04 14:30:14 +01:00
skyline : : signal : : ScopedStackBlocker stackBlocker ; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
2021-10-24 21:45:29 +02:00
Fps = 0 ;
AverageFrametimeMs = AverageFrametimeDeviationMs = 0.0f ;
2019-12-05 16:35:34 +01:00
2020-10-28 17:00:39 +01:00
pthread_setname_np ( pthread_self ( ) , " EmuMain " ) ;
2019-12-02 18:40:53 +01:00
2020-09-26 07:17:57 +02:00
auto jvmManager { std : : make_shared < skyline : : JvmManager > ( env , instance ) } ;
2022-07-19 13:08:15 +02:00
2023-02-27 19:00:03 +01:00
std : : shared_ptr < skyline : : Settings > settings { std : : make_shared < skyline : : AndroidSettings > ( env , settingsInstance ) } ;
2020-08-08 21:38:51 +02:00
2022-04-06 07:48:26 +02:00
skyline : : JniString publicAppFilesPath ( env , publicAppFilesPathJstring ) ;
skyline : : Logger : : EmulationContext . Initialize ( publicAppFilesPath + " logs/emulation.sklog " ) ;
2019-12-02 18:40:53 +01:00
2020-09-26 07:17:57 +02:00
auto start { std : : chrono : : steady_clock : : now ( ) } ;
2019-12-02 18:40:53 +01:00
2021-03-11 19:41:12 +01:00
// Initialize tracing
perfetto : : TracingInitArgs args ;
args . backends | = perfetto : : kSystemBackend ;
2023-02-04 21:46:53 +01:00
args . shmem_size_hint_kb = 0x200000 ;
2021-03-11 19:41:12 +01:00
perfetto : : Tracing : : Initialize ( args ) ;
perfetto : : TrackEvent : : Register ( ) ;
2019-09-24 22:54:27 +02:00
try {
2021-12-08 23:08:55 +01:00
skyline : : JniString nativeLibraryPath ( env , nativeLibraryPathJstring ) ;
2022-04-06 07:48:26 +02:00
skyline : : JniString privateAppFilesPath { env , privateAppFilesPathJstring } ;
2021-12-08 23:08:55 +01:00
2021-08-29 15:27:31 +02:00
auto os { std : : make_shared < skyline : : kernel : : OS > (
2021-10-13 19:08:48 +02:00
jvmManager ,
settings ,
2022-04-06 07:48:26 +02:00
publicAppFilesPath ,
privateAppFilesPath ,
2021-12-08 23:08:55 +01:00
nativeLibraryPath ,
2021-10-13 19:08:48 +02:00
GetTimeZoneName ( ) ,
std : : make_shared < skyline : : vfs : : AndroidAssetFileSystem > ( AAssetManager_fromJava ( env , assetManager ) )
) } ;
2020-11-03 10:44:09 +01:00
OsWeak = os ;
GpuWeak = os - > state . gpu ;
2021-10-13 19:08:48 +02:00
AudioWeak = os - > state . audio ;
2020-11-03 10:44:09 +01:00
InputWeak = os - > state . input ;
2023-02-27 19:00:03 +01:00
SettingsWeak = settings ;
2020-08-21 13:14:27 +02:00
jvmManager - > InitializeControllers ( ) ;
2020-08-08 21:38:51 +02:00
2022-10-23 16:48:12 +02:00
skyline : : Logger : : DebugNoPrefix ( " Launching ROM {} " , skyline : : JniString ( env , romUriJstring ) ) ;
2020-04-26 01:34:35 +02:00
2020-11-03 10:44:09 +01:00
os - > Execute ( romFd , static_cast < skyline : : loader : : RomFormat > ( romType ) ) ;
2019-09-24 22:54:27 +02:00
} catch ( std : : exception & e ) {
2021-11-10 23:21:43 +01:00
skyline : : Logger : : ErrorNoPrefix ( " An uncaught exception has occurred: {} " , e . what ( ) ) ;
2020-12-05 18:41:52 +01:00
} catch ( const skyline : : signal : : SignalException & e ) {
2021-11-10 23:21:43 +01:00
skyline : : Logger : : ErrorNoPrefix ( " An uncaught exception has occurred: {} " , e . what ( ) ) ;
2019-09-24 22:54:27 +02:00
} catch ( . . . ) {
2021-11-10 23:21:43 +01:00
skyline : : Logger : : ErrorNoPrefix ( " An unknown uncaught exception has occurred " ) ;
2019-09-24 22:54:27 +02:00
}
2020-04-26 01:34:35 +02:00
2021-03-11 19:41:12 +01:00
perfetto : : TrackEvent : : Flush ( ) ;
2020-11-03 10:44:09 +01:00
InputWeak . reset ( ) ;
2020-08-20 20:31:32 +02:00
2020-09-26 07:17:57 +02:00
auto end { std : : chrono : : steady_clock : : now ( ) } ;
2021-11-10 23:21:43 +01:00
skyline : : Logger : : Write ( skyline : : Logger : : LogLevel : : Info , fmt : : format ( " Emulation has ended in {}ms " , std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( end - start ) . count ( ) ) ) ;
2020-09-29 14:46:17 +02:00
2021-10-27 23:27:44 +02:00
skyline : : Logger : : EmulationContext . Finalize ( ) ;
2020-09-29 14:46:17 +02:00
close ( romFd ) ;
2019-09-24 22:54:27 +02:00
}
2019-12-05 16:35:34 +01:00
2021-10-13 19:08:48 +02:00
extern " C " JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_stopEmulation ( JNIEnv * , jobject , jboolean join ) {
2020-11-03 10:44:09 +01:00
auto os { OsWeak . lock ( ) } ;
2020-11-18 20:47:09 +01:00
if ( ! os )
return false ;
2020-11-03 10:44:09 +01:00
auto process { os - > state . process } ;
2020-11-18 20:47:09 +01:00
if ( ! process )
return false ;
2021-10-13 19:08:48 +02:00
process - > Kill ( join , false , true ) ;
2020-11-18 20:47:09 +01:00
return true ;
2019-12-05 16:35:34 +01:00
}
2020-11-18 20:47:09 +01:00
extern " C " JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_setSurface ( JNIEnv * , jobject , jobject surface ) {
2020-11-03 10:44:09 +01:00
auto gpu { GpuWeak . lock ( ) } ;
2020-11-18 20:47:09 +01:00
if ( ! gpu )
return false ;
2020-10-28 17:00:39 +01:00
gpu - > presentation . UpdateSurface ( surface ) ;
2020-11-18 20:47:09 +01:00
return true ;
2019-12-05 16:35:34 +01:00
}
2020-04-18 02:16:09 +02:00
2021-10-13 19:08:48 +02:00
extern " C " JNIEXPORT void Java_emu_skyline_EmulationActivity_changeAudioStatus ( JNIEnv * , jobject , jboolean play ) {
auto audio { AudioWeak . lock ( ) } ;
if ( audio )
if ( play )
audio - > Resume ( ) ;
else
audio - > Pause ( ) ;
}
2020-11-18 20:47:09 +01:00
extern " C " JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceStatistics ( JNIEnv * env , jobject thiz ) {
static jclass clazz { } ;
if ( ! clazz )
clazz = env - > GetObjectClass ( thiz ) ;
static jfieldID fpsField { } ;
if ( ! fpsField )
fpsField = env - > GetFieldID ( clazz , " fps " , " I " ) ;
env - > SetIntField ( thiz , fpsField , Fps ) ;
2020-04-18 02:16:09 +02:00
2021-06-27 03:26:16 +02:00
static jfieldID averageFrametimeField { } ;
if ( ! averageFrametimeField )
averageFrametimeField = env - > GetFieldID ( clazz , " averageFrametime " , " F " ) ;
env - > SetFloatField ( thiz , averageFrametimeField , AverageFrametimeMs ) ;
static jfieldID averageFrametimeDeviationField { } ;
if ( ! averageFrametimeDeviationField )
averageFrametimeDeviationField = env - > GetFieldID ( clazz , " averageFrametimeDeviation " , " F " ) ;
env - > SetFloatField ( thiz , averageFrametimeDeviationField , AverageFrametimeDeviationMs ) ;
2020-08-08 21:38:51 +02:00
}
2020-04-26 01:34:35 +02:00
2022-10-28 12:15:48 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setController ( JNIEnv * , jobject , jint index , jint type , jint partnerIndex ) {
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-08-20 20:31:32 +02:00
std : : lock_guard guard ( input - > npad . mutex ) ;
2021-10-24 21:45:29 +02:00
input - > npad . controllers [ static_cast < size_t > ( index ) ] = skyline : : input : : GuestController { static_cast < skyline : : input : : NpadControllerType > ( type ) , static_cast < skyline : : i8 > ( partnerIndex ) } ;
2020-08-15 15:51:23 +02:00
}
2022-10-28 12:15:48 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_updateControllers ( JNIEnv * , jobject ) {
2020-11-03 10:44:09 +01:00
InputWeak . lock ( ) - > npad . Update ( ) ;
2020-08-15 15:51:23 +02:00
}
2022-10-28 12:15:48 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setButtonState ( JNIEnv * , jobject , jint index , jlong mask , jboolean pressed ) {
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-10-28 17:00:39 +01:00
if ( ! input )
return ; // We don't mind if we miss button updates while input hasn't been initialized
2021-10-24 21:45:29 +02:00
auto device { input - > npad . controllers [ static_cast < size_t > ( index ) ] . device } ;
2020-10-28 17:00:39 +01:00
if ( device )
device - > SetButtonState ( skyline : : input : : NpadButton { . raw = static_cast < skyline : : u64 > ( mask ) } , pressed ) ;
2020-04-26 01:34:35 +02:00
}
2022-10-28 12:15:48 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setAxisValue ( JNIEnv * , jobject , jint index , jint axis , jint value ) {
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-10-28 17:00:39 +01:00
if ( ! input )
return ; // We don't mind if we miss axis updates while input hasn't been initialized
2021-10-24 21:45:29 +02:00
auto device { input - > npad . controllers [ static_cast < size_t > ( index ) ] . device } ;
2020-10-28 17:00:39 +01:00
if ( device )
device - > SetAxisValue ( static_cast < skyline : : input : : NpadAxisId > ( axis ) , value ) ;
2020-04-26 01:34:35 +02:00
}
2020-09-07 18:39:05 +02:00
2022-11-03 16:22:49 +01:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setMotionState ( JNIEnv * env , jobject , jint index , jint motionId , jobject value ) {
auto input { InputWeak . lock ( ) } ;
if ( ! input )
return ; // We don't mind if we miss motion updates while input hasn't been initialized
const auto motionValue = reinterpret_cast < skyline : : input : : MotionSensorState * > ( env - > GetDirectBufferAddress ( value ) ) ;
auto device { input - > npad . controllers [ static_cast < size_t > ( index ) ] . device } ;
if ( device )
device - > SetMotionValue ( static_cast < skyline : : input : : MotionId > ( motionId ) , motionValue ) ;
}
2022-10-28 12:15:48 +02:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_input_InputHandler_00024Companion_setTouchState ( JNIEnv * env , jobject , jintArray pointsJni ) {
2020-10-28 17:00:39 +01:00
using Point = skyline : : input : : TouchScreenPoint ;
2020-09-07 18:39:05 +02:00
2020-11-03 10:44:09 +01:00
auto input { InputWeak . lock ( ) } ;
2020-10-28 17:00:39 +01:00
if ( ! input )
return ; // We don't mind if we miss touch updates while input hasn't been initialized
jboolean isCopy { false } ;
2020-09-07 18:39:05 +02:00
2021-10-24 21:45:29 +02:00
skyline : : span < Point > points ( reinterpret_cast < Point * > ( env - > GetIntArrayElements ( pointsJni , & isCopy ) ) ,
static_cast < size_t > ( env - > GetArrayLength ( pointsJni ) ) / ( sizeof ( Point ) / sizeof ( jint ) ) ) ;
2020-10-28 17:00:39 +01:00
input - > touch . SetState ( points ) ;
env - > ReleaseIntArrayElements ( pointsJni , reinterpret_cast < jint * > ( points . data ( ) ) , JNI_ABORT ) ;
2020-09-07 18:39:05 +02:00
}
2022-01-10 18:45:37 +01:00
2023-02-14 14:17:17 +01:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_settings_NativeSettings_updateNative ( JNIEnv * env , jobject ) {
2022-01-10 18:45:37 +01:00
auto settings { SettingsWeak . lock ( ) } ;
if ( ! settings )
return ; // We don't mind if we miss settings updates while settings haven't been initialized
2022-07-22 10:53:28 +02:00
settings - > Update ( ) ;
2022-01-10 18:45:37 +01:00
}
2022-02-18 13:20:56 +01:00
2023-02-14 14:17:17 +01:00
extern " C " JNIEXPORT void JNICALL Java_emu_skyline_settings_NativeSettings_00024Companion_setLogLevel ( JNIEnv * , jobject , jint logLevel ) {
2022-02-18 13:20:56 +01:00
skyline : : Logger : : configLevel = static_cast < skyline : : Logger : : LogLevel > ( logLevel ) ;
}