using LibRyujinx.Jni; using LibRyujinx.Jni.Pointers; using LibRyujinx.Jni.Primitives; using LibRyujinx.Jni.References; using LibRyujinx.Jni.Values; using LibRyujinx.Shared.Audio.Oboe; using Microsoft.Win32.SafeHandles; using Rxmxnx.PInvoke; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Logging.Targets; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.Input; using Silk.NET.Core.Loader; using Silk.NET.Vulkan; using Silk.NET.Vulkan.Extensions.KHR; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; using System.Text; using System.Threading; namespace LibRyujinx { public static partial class LibRyujinx { private static ManualResetEvent _surfaceEvent; private static long _surfacePtr; private static long _window = 0; public static VulkanLoader? VulkanLoader { get; private set; } [DllImport("libryujinxjni")] private extern static IntPtr getStringPointer(JEnvRef jEnv, JStringLocalRef s); [DllImport("libryujinxjni")] private extern static JStringLocalRef createString(JEnvRef jEnv, IntPtr ch); [DllImport("libryujinxjni")] private extern static long storeString(string ch); [DllImport("libryujinxjni")] private extern static IntPtr getString(long id); private static string GetStoredString(long id) { var pointer = getString(id); if (pointer != IntPtr.Zero) { var str = Marshal.PtrToStringAnsi(pointer) ?? ""; Marshal.FreeHGlobal(pointer); return str; } return ""; } [DllImport("libryujinxjni")] internal extern static void setRenderingThread(); [DllImport("libryujinxjni")] internal extern static void debug_break(int code); [DllImport("libryujinxjni")] internal extern static void onFrameEnd(double time); [DllImport("libryujinxjni")] internal extern static void setProgressInfo(IntPtr info, float progress); [DllImport("libryujinxjni")] internal extern static void setCurrentTransform(long native_window, int transform); public delegate IntPtr JniCreateSurface(IntPtr native_surface, IntPtr instance); [UnmanagedCallersOnly(EntryPoint = "JNI_OnLoad")] internal static int LoadLibrary(JavaVMRef vm, IntPtr unknown) { return 0x00010006; //JNI_VERSION_1_6 } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_initialize")] public static JBoolean JniInitialize(JEnvRef jEnv, JObjectLocalRef jObj, JLong jpathId) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); PlatformInfo.IsBionic = true; Logger.AddTarget( new AsyncLogTargetWrapper( new AndroidLogTarget("Ryujinx"), 1000, AsyncLogTargetOverflowAction.Block )); var path = GetStoredString(jpathId); var init = Initialize(path); _surfaceEvent?.Set(); _surfaceEvent = new ManualResetEvent(false); return init; } private static string? GetString(JEnvRef jEnv, JStringLocalRef jString) { var stringPtr = getStringPointer(jEnv, jString); var s = Marshal.PtrToStringAnsi(stringPtr); Marshal.FreeHGlobal(stringPtr); return s; } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceReloadFilesystem")] public static void JniReloadFileSystem() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SwitchDevice?.ReloadFileSystem(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceInitialize")] public static JBoolean JniInitializeDeviceNative(JEnvRef jEnv, JObjectLocalRef jObj, JBoolean isHostMapped, JBoolean useNce, JInt systemLanguage, JInt regionCode, JBoolean enableVsync, JBoolean enableDockedMode, JBoolean enablePtc, JBoolean enableInternetAccess, JLong timeZoneId, JBoolean ignoreMissingServices) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); AudioDriver = new OpenALHardwareDeviceDriver();//new OboeHardwareDeviceDriver(); return InitializeDevice(isHostMapped, useNce, (SystemLanguage)(int)systemLanguage, (RegionCode)(int)regionCode, enableVsync, enableDockedMode, enablePtc, enableInternetAccess, GetStoredString(timeZoneId), ignoreMissingServices); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFifo")] public static JDouble JniGetGameFifo(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var stats = SwitchDevice.EmulationContext?.Statistics.GetFifoPercent() ?? 0; return stats; } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFrameTime")] public static JDouble JniGetGameFrameTime(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var stats = SwitchDevice.EmulationContext?.Statistics.GetGameFrameTime() ?? 0; return stats; } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFrameRate")] public static JDouble JniGetGameFrameRate(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var stats = SwitchDevice.EmulationContext?.Statistics.GetGameFrameRate() ?? 0; return stats; } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoad")] public static JBoolean JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef pathPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (SwitchDevice?.EmulationContext == null) { return false; } var path = GetString(jEnv, pathPtr); return LoadApplication(path); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLaunchMiiEditor")] public static JBoolean JniLaunchMiiEditApplet(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (SwitchDevice?.EmulationContext == null) { return false; } return LaunchMiiEditApplet(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetDlcContentList")] public static JArrayLocalRef JniGetDlcContentListNative(JEnvRef jEnv, JObjectLocalRef jObj, JLong pathPtr, JLong titleId) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var list = GetDlcContentList(GetStoredString(pathPtr), (ulong)(long)titleId); debug_break(4); return CreateStringArray(jEnv, list); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetDlcTitleId")] public static JLong JniGetDlcTitleIdNative(JEnvRef jEnv, JObjectLocalRef jObj, JLong pathPtr, JLong ncaPath) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); return storeString(GetDlcTitleId(GetStoredString(pathPtr), GetStoredString(ncaPath))); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceSignalEmulationClose")] public static void JniSignalEmulationCloseNative(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SignalEmulationClose(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceCloseEmulation")] public static void JniCloseEmulationNative(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); CloseEmulation(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoadDescriptor")] public static JBoolean JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JInt type) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (SwitchDevice?.EmulationContext == null) { return false; } var stream = OpenFile(descriptor); return LoadApplication(stream, (FileType)(int)type); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsInitialize")] public static JBoolean JniInitializeGraphicsNative(JEnvRef jEnv, JObjectLocalRef jObj, JObjectLocalRef graphicObject) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); JEnvValue value = jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr getObjectClassPtr = jInterface.GetObjectClassPointer; IntPtr getFieldIdPtr = jInterface.GetFieldIdPointer; IntPtr getIntFieldPtr = jInterface.GetIntFieldPointer; IntPtr getLongFieldPtr = jInterface.GetLongFieldPointer; IntPtr getFloatFieldPtr = jInterface.GetFloatFieldPointer; IntPtr getBooleanFieldPtr = jInterface.GetBooleanFieldPointer; var getObjectClass = getObjectClassPtr.GetUnsafeDelegate(); var getFieldId = getFieldIdPtr.GetUnsafeDelegate(); var getLongField = getLongFieldPtr.GetUnsafeDelegate(); var getIntField = getIntFieldPtr.GetUnsafeDelegate(); var getBooleanField = getBooleanFieldPtr.GetUnsafeDelegate(); var getFloatField = getFloatFieldPtr.GetUnsafeDelegate(); var jobject = getObjectClass(jEnv, graphicObject); GraphicsConfiguration graphicsConfiguration = new() { EnableShaderCache = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableShaderCache"), GetCCharSequence("Z"))), EnableMacroHLE = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableMacroHLE"), GetCCharSequence("Z"))), EnableMacroJit = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableMacroJit"), GetCCharSequence("Z"))), EnableTextureRecompression = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableTextureRecompression"), GetCCharSequence("Z"))), Fast2DCopy = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("Fast2DCopy"), GetCCharSequence("Z"))), FastGpuTime = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("FastGpuTime"), GetCCharSequence("Z"))), ResScale = getFloatField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("ResScale"), GetCCharSequence("F"))), MaxAnisotropy = getFloatField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("MaxAnisotropy"), GetCCharSequence("F"))), BackendThreading = (BackendThreading)(int)getIntField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("BackendThreading"), GetCCharSequence("I"))) }; SearchPathContainer.Platform = UnderlyingPlatform.Android; return InitializeGraphics(graphicsConfiguration); } private static CCharSequence GetCCharSequence(string s) { return Encoding.UTF8.GetBytes(s).AsSpan(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsSetSurface")] public static void JniSetSurface(JEnvRef jEnv, JObjectLocalRef jObj, JLong surfacePtr, JLong window) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); _surfacePtr = surfacePtr; _window = window; _surfaceEvent.Set(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsInitializeRenderer")] public unsafe static JBoolean JniInitializeGraphicsRendererNative(JEnvRef jEnv, JObjectLocalRef jObj, JArrayLocalRef extensionsArray, JLong driverHandle) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (Renderer != null) { return false; } JEnvValue value = jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr getObjectClassPtr = jInterface.GetObjectClassPointer; IntPtr getFieldIdPtr = jInterface.GetFieldIdPointer; IntPtr getLongFieldPtr = jInterface.GetLongFieldPointer; IntPtr getArrayLengthPtr = jInterface.GetArrayLengthPointer; IntPtr getObjectArrayElementPtr = jInterface.GetObjectArrayElementPointer; IntPtr getObjectFieldPtr = jInterface.GetObjectFieldPointer; var getObjectClass = getObjectClassPtr.GetUnsafeDelegate(); var getFieldId = getFieldIdPtr.GetUnsafeDelegate(); var getArrayLength = getArrayLengthPtr.GetUnsafeDelegate(); var getObjectArrayElement = getObjectArrayElementPtr.GetUnsafeDelegate(); var getLongField = getLongFieldPtr.GetUnsafeDelegate(); var getObjectField = getObjectFieldPtr.GetUnsafeDelegate(); List extensions = new(); var count = getArrayLength(jEnv, extensionsArray); for (int i = 0; i < count; i++) { var obj = getObjectArrayElement(jEnv, extensionsArray, i); var ext = obj.Transform(); extensions.Add(GetString(jEnv, ext)); } if ((long)driverHandle != 0) { VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle); } CreateSurface createSurfaceFunc = instance => { _surfaceEvent.WaitOne(); _surfaceEvent.Reset(); var api = VulkanLoader?.GetApi() ?? Vk.GetApi(); if (api.TryGetInstanceExtension(new Instance(instance), out KhrAndroidSurface surfaceExtension)) { var createInfo = new AndroidSurfaceCreateInfoKHR { SType = StructureType.AndroidSurfaceCreateInfoKhr, Window = (nint*)_surfacePtr, }; var result = surfaceExtension.CreateAndroidSurface(new Instance(instance), createInfo, null, out var surface); return (nint)surface.Handle; } return IntPtr.Zero; }; return InitializeGraphicsRenderer(GraphicsBackend.Vulkan, createSurfaceFunc, extensions.ToArray()); } private static JArrayLocalRef CreateStringArray(JEnvRef jEnv, List strings) { JEnvValue value = jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr newObjectArrayPtr = jInterface.NewObjectArrayPointer; IntPtr findClassPtr = jInterface.FindClassPointer; IntPtr setObjectArrayElementPtr = jInterface.SetObjectArrayElementPointer; var newObjectArray = newObjectArrayPtr.GetUnsafeDelegate(); var findClass = findClassPtr.GetUnsafeDelegate(); var setObjectArrayElement = setObjectArrayElementPtr.GetUnsafeDelegate(); var array = newObjectArray(jEnv, strings.Count, findClass(jEnv, GetCCharSequence("java/lang/String")), CreateString(jEnv, "")._value); for (int i = 0; i < strings.Count; i++) { setObjectArrayElement(jEnv, array, i, CreateString(jEnv, strings[i])._value); } return array; } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererSetSize")] public static void JniSetRendererSizeNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); Renderer?.Window?.SetSize(width, height); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererRunLoop")] public static void JniRunLoopNative(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetSwapBuffersCallback(() => { var time = SwitchDevice.EmulationContext.Statistics.GetGameFrameTime(); onFrameEnd(time); }); RunLoop(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_loggingSetEnabled")] public static void JniSetLoggingEnabledNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt logLevel, JBoolean enabled) { Logger.SetEnable((LogLevel)(int)logLevel, enabled); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfoFromPath")] public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef path) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var info = GetGameInfo(GetString(jEnv, path)); return GetInfo(jEnv, info); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfo")] public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JInt fileDescriptor, JLong extension) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); using var stream = OpenFile(fileDescriptor); var ext = GetStoredString(extension); var info = GetGameInfo(stream, ext.ToLower()); return GetInfo(jEnv, info); } private static JObjectLocalRef GetInfo(JEnvRef jEnv, GameInfo? info) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var javaClassName = GetCCharSequence("org/ryujinx/android/viewmodels/GameInfo"); JEnvValue value = jEnv.Environment; ref JNativeInterface jInterface = ref value.Functions; IntPtr findClassPtr = jInterface.FindClassPointer; IntPtr newGlobalRefPtr = jInterface.NewGlobalRefPointer; IntPtr getFieldIdPtr = jInterface.GetFieldIdPointer; IntPtr getMethodPtr = jInterface.GetMethodIdPointer; IntPtr newObjectPtr = jInterface.NewObjectPointer; IntPtr setObjectFieldPtr = jInterface.SetObjectFieldPointer; IntPtr setDoubleFieldPtr = jInterface.SetDoubleFieldPointer; var findClass = findClassPtr.GetUnsafeDelegate(); var newGlobalRef = newGlobalRefPtr.GetUnsafeDelegate(); var getFieldId = getFieldIdPtr.GetUnsafeDelegate(); var getMethod = getMethodPtr.GetUnsafeDelegate(); var newObject = newObjectPtr.GetUnsafeDelegate(); var setObjectField = setObjectFieldPtr.GetUnsafeDelegate(); var setDoubleField = setDoubleFieldPtr.GetUnsafeDelegate(); var javaClass = findClass(jEnv, javaClassName); var newGlobal = newGlobalRef(jEnv, javaClass._value); var constructor = getMethod(jEnv, javaClass, GetCCharSequence(""), GetCCharSequence("()V")); var newObj = newObject(jEnv, javaClass, constructor, 0); setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("TitleName"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, info?.TitleName)._value); setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("TitleId"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, info?.TitleId)._value); setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("Developer"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, info?.Developer)._value); setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("Version"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, info?.Version)._value); setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("Icon"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, Convert.ToBase64String(info?.Icon ?? Array.Empty()))._value); setDoubleField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("FileSize"), GetCCharSequence("D")), info?.FileSize ?? 0d); return newObj; } private static JStringLocalRef CreateString(JEnvRef jEnv, string? s) { s ??= string.Empty; var ptr = Marshal.StringToHGlobalAnsi(s); var str = createString(jEnv, ptr); Marshal.FreeHGlobal(ptr); return str; } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererSetVsync")] public static void JniSetVsyncStateNative(JEnvRef jEnv, JObjectLocalRef jObj, JBoolean enabled) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetVsyncState(enabled); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererSetSwapBufferCallback")] public static void JniSetSwapBuffersCallbackNative(JEnvRef jEnv, JObjectLocalRef jObj, IntPtr swapBuffersCallback) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); _swapBuffersCallback = Marshal.GetDelegateForFunctionPointer(swapBuffersCallback); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputInitialize")] public static void JniInitializeInput(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); InitializeInput(width, height); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetClientSize")] public static void JniSetClientSize(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetClientSize(width, height); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetTouchPoint")] public static void JniSetTouchPoint(JEnvRef jEnv, JObjectLocalRef jObj, JInt x, JInt y) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetTouchPoint(x, y); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputReleaseTouchPoint")] public static void JniReleaseTouchPoint(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); ReleaseTouchPoint(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputUpdate")] public static void JniUpdateInput(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); UpdateInput(); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonPressed")] public static void JniSetButtonPressed(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetButtonPressed((GamepadButtonInputId)(int)button, id); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonReleased")] public static void JniSetButtonReleased(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetButtonReleased((GamepadButtonInputId)(int)button, id); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetStickAxis")] public static void JniSetStickAxis(JEnvRef jEnv, JObjectLocalRef jObj, JInt stick, JFloat x, JFloat y, JInt id) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetStickAxis((StickInputId)(int)stick, new Vector2(float.IsNaN(x) ? 0 : x, float.IsNaN(y) ? 0 : y), id); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputConnectGamepad")] public static JInt JniConnectGamepad(JEnvRef jEnv, JObjectLocalRef jObj, JInt index) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); return ConnectGamepad(index); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetOpenedUser")] public static JLong JniGetOpenedUser(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetOpenedUser(); return storeString(userId); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserPicture")] public static JLong JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetStoredString(userIdPtr) ?? ""; return storeString(GetUserPicture(userId)); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userSetUserPicture")] public static void JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef picturePtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetString(jEnv, userIdPtr) ?? ""; var picture = GetString(jEnv, picturePtr) ?? ""; SetUserPicture(userId, picture); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserName")] public static JLong JniGetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetStoredString(userIdPtr) ?? ""; return storeString(GetUserName(userId)); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userSetUserName")] public static void JniSetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef userNamePtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetString(jEnv, userIdPtr) ?? ""; var userName = GetString(jEnv, userNamePtr) ?? ""; SetUserName(userId, userName); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetAllUsers")] public static JArrayLocalRef JniGetAllUsers(JEnvRef jEnv, JObjectLocalRef jObj) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var users = GetAllUsers(); return CreateStringArray(jEnv, users.ToList()); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userAddUser")] public static void JniAddUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userNamePtr, JStringLocalRef picturePtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userName = GetString(jEnv, userNamePtr) ?? ""; var picture = GetString(jEnv, picturePtr) ?? ""; AddUser(userName, picture); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userDeleteUser")] public static void JniDeleteUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetString(jEnv, userIdPtr) ?? ""; DeleteUser(userId); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userOpenUser")] public static void JniOpenUser(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetStoredString(userIdPtr) ?? ""; OpenUser(userId); } [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userCloseUser")] public static void JniCloseUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetString(jEnv, userIdPtr) ?? ""; CloseUser(userId); } private static FileStream OpenFile(int descriptor) { var safeHandle = new SafeFileHandle(descriptor, false); return new FileStream(safeHandle, FileAccess.ReadWrite); } } internal static partial class Logcat { [LibraryImport("liblog", StringMarshalling = StringMarshalling.Utf8)] private static partial void __android_log_print(LogLevel level, string? tag, string format, string args, IntPtr ptr); internal static void AndroidLogPrint(LogLevel level, string? tag, string message) => __android_log_print(level, tag, "%s", message, IntPtr.Zero); internal enum LogLevel { Unknown = 0x00, Default = 0x01, Verbose = 0x02, Debug = 0x03, Info = 0x04, Warn = 0x05, Error = 0x06, Fatal = 0x07, Silent = 0x08, } } }