From a00c8c909ff4e7ecd2cb0780a5da1d7420813eb1 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Thu, 9 May 2024 16:15:41 +0000 Subject: [PATCH] android - replace jni with jna --- src/LibRyujinx/Android/JniExportedMethods.cs | 479 +++++++----------- .../PartitionFileSystemExtensions.cs | 3 + src/RyujinxAndroid/app/build.gradle | 5 +- .../org/ryujinx/android/BackendThreading.kt | 7 + .../java/org/ryujinx/android/BaseActivity.kt | 2 +- .../java/org/ryujinx/android/CrashHandler.kt | 6 +- .../org/ryujinx/android/GameController.kt | 109 ++-- .../main/java/org/ryujinx/android/GameHost.kt | 28 +- .../ryujinx/android/GamePadButtonInputId.kt | 1 + .../ryujinx/android/GraphicsConfiguration.kt | 22 - .../main/java/org/ryujinx/android/Helpers.kt | 7 +- .../main/java/org/ryujinx/android/Icons.kt | 11 +- .../main/java/org/ryujinx/android/Logging.kt | 19 +- .../java/org/ryujinx/android/MainActivity.kt | 20 +- .../ryujinx/android/MotionSensorManager.kt | 58 ++- .../java/org/ryujinx/android/NativeHelpers.kt | 27 +- .../java/org/ryujinx/android/NativeWindow.kt | 16 +- .../org/ryujinx/android/PerformanceMonitor.kt | 23 +- .../android/PhysicalControllerManager.kt | 112 ++-- .../org/ryujinx/android/RyujinxApplication.kt | 7 +- .../java/org/ryujinx/android/RyujinxNative.kt | 136 ++--- .../java/org/ryujinx/android/UiHandler.kt | 32 +- .../android/providers/DocumentProvider.kt | 90 ++-- .../org/ryujinx/android/ui/theme/Theme.kt | 1 - .../android/viewmodels/DlcViewModel.kt | 22 +- .../ryujinx/android/viewmodels/GameInfo.java | 20 + .../ryujinx/android/viewmodels/GameModel.kt | 30 +- .../android/viewmodels/HomeViewModel.kt | 25 +- .../android/viewmodels/MainViewModel.kt | 75 ++- .../android/viewmodels/SettingsViewModel.kt | 49 +- .../viewmodels/TitleUpdateViewModel.kt | 18 +- .../android/viewmodels/UserViewModel.kt | 36 +- .../viewmodels/VulkanDriverViewModel.kt | 66 +-- .../org/ryujinx/android/views/DlcViews.kt | 40 +- .../org/ryujinx/android/views/GameViews.kt | 19 +- .../org/ryujinx/android/views/HomeViews.kt | 32 +- .../org/ryujinx/android/views/MainView.kt | 2 +- .../org/ryujinx/android/views/SettingViews.kt | 16 +- .../org/ryujinx/android/views/UserViews.kt | 17 +- 39 files changed, 870 insertions(+), 818 deletions(-) create mode 100644 src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BackendThreading.kt delete mode 100644 src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GraphicsConfiguration.kt create mode 100644 src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameInfo.java diff --git a/src/LibRyujinx/Android/JniExportedMethods.cs b/src/LibRyujinx/Android/JniExportedMethods.cs index 7f9555674..b1ba17ab6 100644 --- a/src/LibRyujinx/Android/JniExportedMethods.cs +++ b/src/LibRyujinx/Android/JniExportedMethods.cs @@ -1,9 +1,5 @@ -using LibRyujinx.Jni; using LibRyujinx.Jni.Pointers; -using LibRyujinx.Jni.Primitives; using LibRyujinx.Jni.References; -using LibRyujinx.Jni.Values; -using Rxmxnx.PInvoke; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -18,6 +14,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -99,14 +96,8 @@ namespace LibRyujinx 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) + [UnmanagedCallersOnly(EntryPoint = "javaInitialize")] + public static bool JniInitialize(IntPtr jpathId) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); PlatformInfo.IsBionic = true; @@ -118,7 +109,7 @@ namespace LibRyujinx AsyncLogTargetOverflowAction.Block )); - var path = GetStoredString(jpathId); + var path = Marshal.PtrToStringAnsi(jpathId); var init = Initialize(path); @@ -138,70 +129,71 @@ namespace LibRyujinx return s; } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceReloadFilesystem")] - public static void JniReloadFileSystem() + [UnmanagedCallersOnly(EntryPoint = "deviceReloadFilesystem")] + public static void JnaReloadFileSystem() { 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) + [UnmanagedCallersOnly(EntryPoint = "deviceInitialize")] + public static bool JnaDeviceInitialize(bool isHostMapped, + bool useNce, + int systemLanguage, + int regionCode, + bool enableVsync, + bool enableDockedMode, + bool enablePtc, + bool enableInternetAccess, + IntPtr timeZonePtr, + bool ignoreMissingServices) { + debug_break(4); Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - AudioDriver = new OpenALHardwareDeviceDriver();//new OboeHardwareDeviceDriver(); + AudioDriver = new OpenALHardwareDeviceDriver(); + + var timezone = Marshal.PtrToStringAnsi(timeZonePtr); return InitializeDevice(isHostMapped, useNce, - (SystemLanguage)(int)systemLanguage, - (RegionCode)(int)regionCode, + (SystemLanguage)systemLanguage, + (RegionCode)regionCode, enableVsync, enableDockedMode, enablePtc, enableInternetAccess, - GetStoredString(timeZoneId), + timezone, ignoreMissingServices); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFifo")] - public static JDouble JniGetGameFifo(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "deviceGetGameFifo")] + public static double JnaGetGameFifo() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var stats = SwitchDevice.EmulationContext?.Statistics.GetFifoPercent() ?? 0; + 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) + [UnmanagedCallersOnly(EntryPoint = "deviceGetGameFrameTime")] + public static double JnaGetGameFrameTime() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var stats = SwitchDevice.EmulationContext?.Statistics.GetGameFrameTime() ?? 0; + 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) + [UnmanagedCallersOnly(EntryPoint = "deviceGetGameFrameRate")] + public static double JnaGetGameFrameRate() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var stats = SwitchDevice.EmulationContext?.Statistics.GetGameFrameRate() ?? 0; + 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) + public static bool JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef pathPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (SwitchDevice?.EmulationContext == null) @@ -214,8 +206,8 @@ namespace LibRyujinx return LoadApplication(path); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLaunchMiiEditor")] - public static JBoolean JniLaunchMiiEditApplet(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "deviceLaunchMiiEditor")] + public static bool JNALaunchMiiEditApplet() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (SwitchDevice?.EmulationContext == null) @@ -226,38 +218,38 @@ namespace LibRyujinx return LaunchMiiEditApplet(); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetDlcContentList")] - public static JArrayLocalRef JniGetDlcContentListNative(JEnvRef jEnv, JObjectLocalRef jObj, JLong pathPtr, JLong titleId) + [UnmanagedCallersOnly(EntryPoint = "deviceGetDlcContentList")] + public static IntPtr JniGetDlcContentListNative(IntPtr pathPtr, long titleId) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var list = GetDlcContentList(GetStoredString(pathPtr), (ulong)(long)titleId); + var list = GetDlcContentList(Marshal.PtrToStringAnsi(pathPtr) ?? "", (ulong)titleId); - return CreateStringArray(jEnv, list); + return CreateStringArray(list); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetDlcTitleId")] - public static JLong JniGetDlcTitleIdNative(JEnvRef jEnv, JObjectLocalRef jObj, JLong pathPtr, JLong ncaPath) + [UnmanagedCallersOnly(EntryPoint = "deviceGetDlcTitleId")] + public static long JniGetDlcTitleIdNative(IntPtr pathPtr, IntPtr ncaPath) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - return storeString(GetDlcTitleId(GetStoredString(pathPtr), GetStoredString(ncaPath))); + return Marshal.StringToHGlobalAnsi(GetDlcTitleId(Marshal.PtrToStringAnsi(pathPtr) ?? "", Marshal.PtrToStringAnsi(ncaPath) ?? "")); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceSignalEmulationClose")] - public static void JniSignalEmulationCloseNative(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "deviceSignalEmulationClose")] + public static void JniSignalEmulationCloseNative() { 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) + [UnmanagedCallersOnly(EntryPoint = "deviceCloseEmulation")] + public static void JniCloseEmulationNative() { 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, JInt updateDescriptor) + [UnmanagedCallersOnly(EntryPoint = "deviceLoadDescriptor")] + public static bool JnaLoadApplicationNative(int descriptor, int type, int updateDescriptor) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (SwitchDevice?.EmulationContext == null) @@ -268,17 +260,17 @@ namespace LibRyujinx var stream = OpenFile(descriptor); var update = updateDescriptor == -1 ? null : OpenFile(updateDescriptor); - return LoadApplication(stream, (FileType)(int)type, update); + return LoadApplication(stream, (FileType)type, update); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceVerifyFirmware")] - public static JLong JniVerifyFirmware(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci) + [UnmanagedCallersOnly(EntryPoint = "deviceVerifyFirmware")] + public static IntPtr JniVerifyFirmware(int descriptor, bool isXci) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var stream = OpenFile(descriptor); - long stringHandle = -1; + IntPtr stringHandle = 0; try { @@ -286,19 +278,19 @@ namespace LibRyujinx if (version != null) { - stringHandle = storeString(version.VersionString); + stringHandle = Marshal.StringToHGlobalAnsi(version.VersionString); } } catch(Exception _) { - + Logger.Error?.Print(LogClass.Service, $"Unable to verify firmware. Exception: {_}"); } return stringHandle; } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceInstallFirmware")] - public static void JniInstallFirmware(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci) + [UnmanagedCallersOnly(EntryPoint = "deviceInstallFirmware")] + public static void JniInstallFirmware(int descriptor, bool isXci) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); @@ -307,58 +299,47 @@ namespace LibRyujinx InstallFirmware(stream, isXci); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetInstalledFirmwareVersion")] - public static JLong JniGetInstalledFirmwareVersion(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "deviceGetInstalledFirmwareVersion")] + public static IntPtr JniGetInstalledFirmwareVersion() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var version = GetInstalledFirmwareVersion(); - long stringHandle = -1; + IntPtr stringHandle = 0; if (version != String.Empty) { - stringHandle = storeString(version); + stringHandle = Marshal.StringToHGlobalAnsi(version); } return stringHandle; } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsInitialize")] - public static JBoolean JniInitializeGraphicsNative(JEnvRef jEnv, JObjectLocalRef jObj, JObjectLocalRef graphicObject) + [UnmanagedCallersOnly(EntryPoint = "graphicsInitialize")] + public static bool JnaGraphicsInitialize(float resScale, + float maxAnisotropy, + bool fastGpuTime, + bool fast2DCopy, + bool enableMacroJit, + bool enableMacroHLE, + bool enableShaderCache, + bool enableTextureRecompression, + int backendThreading) { 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); + return InitializeGraphics(new GraphicsConfiguration() + { + ResScale = resScale, + MaxAnisotropy = maxAnisotropy, + FastGpuTime = fastGpuTime, + Fast2DCopy = fast2DCopy, + EnableMacroJit = enableMacroJit, + EnableMacroHLE = enableMacroHLE, + EnableShaderCache = enableShaderCache, + EnableTextureRecompression = enableTextureRecompression, + BackendThreading = (BackendThreading)backendThreading + }); } private static CCharSequence GetCCharSequence(string s) @@ -366,8 +347,8 @@ namespace LibRyujinx 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) + [UnmanagedCallersOnly(EntryPoint = "graphicsSetSurface")] + public static void JniSetSurface(long surfacePtr, long window) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); _surfacePtr = surfacePtr; @@ -376,11 +357,10 @@ namespace LibRyujinx _surfaceEvent.Set(); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsInitializeRenderer")] - public unsafe static JBoolean JniInitializeGraphicsRendererNative(JEnvRef jEnv, - JObjectLocalRef jObj, - JArrayLocalRef extensionsArray, - JLong driverHandle) + [UnmanagedCallersOnly(EntryPoint = "graphicsInitializeRenderer")] + public unsafe static bool JnaGraphicsInitializeRenderer(char** extensionsArray, + int extensionsLength, + long driverHandle) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); if (Renderer != null) @@ -388,37 +368,16 @@ namespace LibRyujinx 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++) + for (int i = 0; i < extensionsLength; i++) { - var obj = getObjectArrayElement(jEnv, extensionsArray, i); - var ext = obj.Transform(); - - extensions.Add(GetString(jEnv, ext)); + extensions.Add(Marshal.PtrToStringAnsi((IntPtr)extensionsArray[i])); } - if ((long)driverHandle != 0) + if (driverHandle != 0) { - VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle); + VulkanLoader = new VulkanLoader((IntPtr)driverHandle); } CreateSurface createSurfaceFunc = instance => @@ -446,36 +405,29 @@ namespace LibRyujinx return InitializeGraphicsRenderer(GraphicsBackend.Vulkan, createSurfaceFunc, extensions.ToArray()); } - private static JArrayLocalRef CreateStringArray(JEnvRef jEnv, List strings) + private unsafe static IntPtr CreateStringArray(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); + uint size = (uint)(Marshal.SizeOf() * (strings.Count + 1)); + var array = (char**)Marshal.AllocHGlobal((int)size); + Unsafe.InitBlockUnaligned(array, 0, size); for (int i = 0; i < strings.Count; i++) { - setObjectArrayElement(jEnv, array, i, CreateString(jEnv, strings[i])._value); + array[i] = (char*)Marshal.StringToHGlobalAnsi(strings[i]); } - return array; + return (nint)array; } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererSetSize")] - public static void JniSetRendererSizeNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height) + [UnmanagedCallersOnly(EntryPoint = "graphicsRendererSetSize")] + public static void JnaSetRendererSizeNative(int width, int 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) + [UnmanagedCallersOnly(EntryPoint = "graphicsRendererRunLoop")] + public static void JniRunLoopNative() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetSwapBuffersCallback(() => @@ -487,84 +439,32 @@ namespace LibRyujinx } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_loggingSetEnabled")] - public static void JniSetLoggingEnabledNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt logLevel, JBoolean enabled) + [UnmanagedCallersOnly(EntryPoint = "loggingSetEnabled")] + public static void JniSetLoggingEnabledNative(int logLevel, bool enabled) { - Logger.SetEnable((LogLevel)(int)logLevel, enabled); + Logger.SetEnable((LogLevel)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) + [UnmanagedCallersOnly(EntryPoint = "deviceGetGameInfo")] + public unsafe static void JniGetGameInfo(int fileDescriptor, IntPtr extension, IntPtr infoPtr) { 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); + var ext = Marshal.PtrToStringAnsi(extension); + var info = GetGameInfo(stream, ext.ToLower()) ?? GetDefaultInfo(stream); + var i = (GameInfoNative*)infoPtr; + var n = new GameInfoNative(info); + i->TitleId = n.TitleId; + i->TitleName = n.TitleName; + i->Version = n.Version; + i->FileSize = n.FileSize; + i->Icon = n.Icon; + i->Version = n.Version; + i->Developer = n.Developer; } - 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) + [UnmanagedCallersOnly(EntryPoint = "graphicsRendererSetVsync")] + public static void JnaSetVsyncStateNative(bool enabled) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); SetVsyncState(enabled); @@ -577,196 +477,197 @@ namespace LibRyujinx _swapBuffersCallback = Marshal.GetDelegateForFunctionPointer(swapBuffersCallback); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputInitialize")] - public static void JniInitializeInput(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height) + [UnmanagedCallersOnly(EntryPoint = "inputInitialize")] + public static void JnaInitializeInput(int width, int 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) + [UnmanagedCallersOnly(EntryPoint = "inputSetClientSize")] + public static void JnaSetClientSize(int width, int 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) + [UnmanagedCallersOnly(EntryPoint = "inputSetTouchPoint")] + public static void JnaSetTouchPoint(int x, int 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) + [UnmanagedCallersOnly(EntryPoint = "inputReleaseTouchPoint")] + public static void JnaReleaseTouchPoint() { 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) + [UnmanagedCallersOnly(EntryPoint = "inputUpdate")] + public static void JniUpdateInput() { 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) + [UnmanagedCallersOnly(EntryPoint = "inputSetButtonPressed")] + public static void JnaSetButtonPressed(int button, int id) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - SetButtonPressed((GamepadButtonInputId)(int)button, id); + SetButtonPressed((GamepadButtonInputId)button, id); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonReleased")] - public static void JniSetButtonReleased(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id) + [UnmanagedCallersOnly(EntryPoint = "inputSetButtonReleased")] + public static void JnaSetButtonReleased(int button, int id) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - SetButtonReleased((GamepadButtonInputId)(int)button, id); + SetButtonReleased((GamepadButtonInputId)button, id); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetAccelerometerData")] - public static void JniSetAccelerometerData(JEnvRef jEnv, JObjectLocalRef jObj, JFloat x, JFloat y, JFloat z, JInt id) + [UnmanagedCallersOnly(EntryPoint = "inputSetAccelerometerData")] + public static void JniSetAccelerometerData(float x, float y, float z, int id) { var accel = new Vector3(x, y, z); SetAccelerometerData(accel, id); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetGyroData")] - public static void JniSetGyroData(JEnvRef jEnv, JObjectLocalRef jObj, JFloat x, JFloat y, JFloat z, JInt id) + [UnmanagedCallersOnly(EntryPoint = "inputSetGyroData")] + public static void JniSetGyroData(float x, float y, float z, int id) { var gryo = new Vector3(x, y, z); SetGryoData(gryo, 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) + [UnmanagedCallersOnly(EntryPoint = "inputSetStickAxis")] + public static void JnaSetStickAxis(int stick, float x, float y, int 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); + SetStickAxis((StickInputId)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) + [UnmanagedCallersOnly(EntryPoint = "inputConnectGamepad")] + public static int JnaConnectGamepad(int 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) + [UnmanagedCallersOnly(EntryPoint = "userGetOpenedUser")] + public static IntPtr JniGetOpenedUser() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var userId = GetOpenedUser(); + var ptr = Marshal.StringToHGlobalAnsi(userId); - return storeString(userId); + return ptr; } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserPicture")] - public static JLong JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr) + [UnmanagedCallersOnly(EntryPoint = "userGetUserPicture")] + public static IntPtr JniGetUserPicture(IntPtr userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetStoredString(userIdPtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; - return storeString(GetUserPicture(userId)); + return Marshal.StringToHGlobalAnsi(GetUserPicture(userId)); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userSetUserPicture")] - public static void JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef picturePtr) + [UnmanagedCallersOnly(EntryPoint = "userSetUserPicture")] + public static void JniGetUserPicture(IntPtr userIdPtr, IntPtr picturePtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetString(jEnv, userIdPtr) ?? ""; - var picture = GetString(jEnv, picturePtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; + var picture = Marshal.PtrToStringAnsi(picturePtr) ?? ""; SetUserPicture(userId, picture); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserName")] - public static JLong JniGetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr) + [UnmanagedCallersOnly(EntryPoint = "userGetUserName")] + public static IntPtr JniGetUserName(IntPtr userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetStoredString(userIdPtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; - return storeString(GetUserName(userId)); + return Marshal.StringToHGlobalAnsi(GetUserName(userId)); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userSetUserName")] - public static void JniSetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef userNamePtr) + [UnmanagedCallersOnly(EntryPoint = "userSetUserName")] + public static void JniSetUserName(IntPtr userIdPtr, IntPtr userNamePtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetString(jEnv, userIdPtr) ?? ""; - var userName = GetString(jEnv, userNamePtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; + var userName = Marshal.PtrToStringAnsi(userNamePtr) ?? ""; SetUserName(userId, userName); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetAllUsers")] - public static JArrayLocalRef JniGetAllUsers(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "userGetAllUsers")] + public static IntPtr JniGetAllUsers() { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); var users = GetAllUsers(); - return CreateStringArray(jEnv, users.ToList()); + return CreateStringArray(users.ToList()); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userAddUser")] - public static void JniAddUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userNamePtr, JStringLocalRef picturePtr) + [UnmanagedCallersOnly(EntryPoint = "userAddUser")] + public static void JniAddUser(IntPtr userNamePtr, IntPtr picturePtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userName = GetString(jEnv, userNamePtr) ?? ""; - var picture = GetString(jEnv, picturePtr) ?? ""; + var userName = Marshal.PtrToStringAnsi(userNamePtr) ?? ""; + var picture = Marshal.PtrToStringAnsi(picturePtr) ?? ""; AddUser(userName, picture); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userDeleteUser")] - public static void JniDeleteUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr) + [UnmanagedCallersOnly(EntryPoint = "userDeleteUser")] + public static void JniDeleteUser(IntPtr userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetString(jEnv, userIdPtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; DeleteUser(userId); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerSetup")] - public static void JniSetupUiHandler(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "uiHandlerSetup")] + public static void JniSetupUiHandler() { SetupUiHandler(); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerWait")] - public static void JniWaitUiHandler(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "uiHandlerWait")] + public static void JniWaitUiHandler() { WaitUiHandler(); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerStopWait")] - public static void JniStopUiHandlerWait(JEnvRef jEnv, JObjectLocalRef jObj) + [UnmanagedCallersOnly(EntryPoint = "uiHandlerStopWait")] + public static void JniStopUiHandlerWait() { StopUiHandlerWait(); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerSetResponse")] - public static void JniSetUiHandlerResponse(JEnvRef jEnv, JObjectLocalRef jObj, JBoolean isOkPressed, JLong input) + [UnmanagedCallersOnly(EntryPoint = "uiHandlerSetResponse")] + public static void JniSetUiHandlerResponse(bool isOkPressed, long input) { SetUiHandlerResponse(isOkPressed, input); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userOpenUser")] - public static void JniOpenUser(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr) + [UnmanagedCallersOnly(EntryPoint = "userOpenUser")] + public static void JniOpenUser(IntPtr userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetStoredString(userIdPtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; OpenUser(userId); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userCloseUser")] - public static void JniCloseUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr) + [UnmanagedCallersOnly(EntryPoint = "userCloseUser")] + public static void JniCloseUser(IntPtr userIdPtr) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - var userId = GetString(jEnv, userIdPtr) ?? ""; + var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? ""; CloseUser(userId); } diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs index e8feb719e..ffa29162d 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs @@ -121,10 +121,13 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json"); if (File.Exists(addOnContentMetadataPath)) { + Logger.Info?.Print(LogClass.Loader, $"DLC Found : {addOnContentMetadataPath}"); List dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer); + Logger.Info?.Print(LogClass.Loader, $"DLC Found : {addOnContentMetadataPath}. Available: {dlcContainerList.Count}"); foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList) { + Logger.Info?.Print(LogClass.Loader, $"DLC Contents Available: {downloadableContentContainer.DownloadableContentNcaList.Count}"); foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) { if (File.Exists(downloadableContentContainer.ContainerPath)) diff --git a/src/RyujinxAndroid/app/build.gradle b/src/RyujinxAndroid/app/build.gradle index 1c461ac47..02861c9c4 100644 --- a/src/RyujinxAndroid/app/build.gradle +++ b/src/RyujinxAndroid/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "org.ryujinx.android" minSdk 30 targetSdk 34 - versionCode 10030 - versionName '1.0.30t' + versionCode 10031 + versionName '1.0.31' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -76,6 +76,7 @@ tasks.named("preBuild") { } dependencies { + implementation group: 'net.java.dev.jna', name: 'jna', version: '5.14.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.12.0' implementation platform('androidx.compose:compose-bom:2024.05.00') diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BackendThreading.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BackendThreading.kt new file mode 100644 index 000000000..f3dd5ed9c --- /dev/null +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BackendThreading.kt @@ -0,0 +1,7 @@ +package org.ryujinx.android + +enum class BackendThreading { + Auto, + Off, + On +} \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BaseActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BaseActivity.kt index e70053762..50e35ff2a 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BaseActivity.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/BaseActivity.kt @@ -3,7 +3,7 @@ package org.ryujinx.android import androidx.activity.ComponentActivity abstract class BaseActivity : ComponentActivity() { - companion object{ + companion object { val crashHandler = CrashHandler() } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/CrashHandler.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/CrashHandler.kt index f6c2eb656..be00d508e 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/CrashHandler.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/CrashHandler.kt @@ -4,10 +4,12 @@ import java.io.File import java.lang.Thread.UncaughtExceptionHandler class CrashHandler : UncaughtExceptionHandler { - var crashLog : String = "" + var crashLog: String = "" override fun uncaughtException(t: Thread, e: Throwable) { crashLog += e.toString() + "\n" - File(MainActivity.AppPath + "${File.separator}Logs${File.separator}crash.log").writeText(crashLog) + File(MainActivity.AppPath + "${File.separator}Logs${File.separator}crash.log").writeText( + crashLog + ) } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt index 204a58e6b..40460f0ef 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt @@ -34,9 +34,8 @@ typealias GamePadConfig = RadialGamePadConfig class GameController(var activity: Activity) { - companion object{ - private fun Create(context: Context, activity: Activity, controller: GameController) : View - { + companion object { + private fun Create(context: Context, controller: GameController): View { val inflator = LayoutInflater.from(context) val view = inflator.inflate(R.layout.game_layout, null) view.findViewById(R.id.leftcontainer)!!.addView(controller.leftGamePad) @@ -44,12 +43,13 @@ class GameController(var activity: Activity) { return view } + @Composable - fun Compose(viewModel: MainViewModel) : Unit { + fun Compose(viewModel: MainViewModel): Unit { AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> val controller = GameController(viewModel.activity) - val c = Create(context, viewModel.activity, controller) + val c = Create(context, controller) viewModel.activity.lifecycleScope.apply { viewModel.activity.lifecycleScope.launch { val events = merge( @@ -70,12 +70,11 @@ class GameController(var activity: Activity) { } } - private var ryujinxNative: RyujinxNative private var controllerView: View? = null var leftGamePad: GamePad var rightGamePad: GamePad var controllerId: Int = -1 - val isVisible : Boolean + val isVisible: Boolean get() { controllerView?.apply { return this.isVisible @@ -95,27 +94,25 @@ class GameController(var activity: Activity) { leftGamePad.gravityY = 1f rightGamePad.gravityX = 1f rightGamePad.gravityY = 1f - - ryujinxNative = RyujinxNative.instance } - fun setVisible(isVisible: Boolean){ + fun setVisible(isVisible: Boolean) { controllerView?.apply { this.isVisible = isVisible - if(isVisible) + if (isVisible) connect() } } - fun connect(){ - if(controllerId == -1) - controllerId = RyujinxNative.instance.inputConnectGamepad(0) + fun connect() { + if (controllerId == -1) + controllerId = RyujinxNative.jnaInstance.inputConnectGamepad(0) } private fun handleEvent(ev: Event) { - if(controllerId == -1) - controllerId = ryujinxNative.inputConnectGamepad(0) + if (controllerId == -1) + controllerId = RyujinxNative.jnaInstance.inputConnectGamepad(0) controllerId.apply { when (ev) { @@ -123,11 +120,11 @@ class GameController(var activity: Activity) { val action = ev.action when (action) { KeyEvent.ACTION_UP -> { - ryujinxNative.inputSetButtonReleased(ev.id, this) + RyujinxNative.jnaInstance.inputSetButtonReleased(ev.id, this) } KeyEvent.ACTION_DOWN -> { - ryujinxNative.inputSetButtonPressed(ev.id, this) + RyujinxNative.jnaInstance.inputSetButtonPressed(ev.id, this) } } } @@ -135,36 +132,82 @@ class GameController(var activity: Activity) { is Event.Direction -> { val direction = ev.id - when(direction) { + when (direction) { GamePadButtonInputId.DpadUp.ordinal -> { if (ev.xAxis > 0) { - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this) + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadRight.ordinal, + this + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadLeft.ordinal, + this + ) } else if (ev.xAxis < 0) { - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this) + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadLeft.ordinal, + this + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadRight.ordinal, + this + ) } else { - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadLeft.ordinal, + this + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadRight.ordinal, + this + ) } if (ev.yAxis < 0) { - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this) + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadUp.ordinal, + this + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadDown.ordinal, + this + ) } else if (ev.yAxis > 0) { - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this) + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadDown.ordinal, + this + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadUp.ordinal, + this + ) } else { - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadDown.ordinal, + this + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadUp.ordinal, + this + ) } } GamePadButtonInputId.LeftStick.ordinal -> { - ryujinxNative.inputSetStickAxis(1, ev.xAxis, -ev.yAxis ,this) + RyujinxNative.jnaInstance.inputSetStickAxis( + 1, + ev.xAxis, + -ev.yAxis, + this + ) } GamePadButtonInputId.RightStick.ordinal -> { - ryujinxNative.inputSetStickAxis(2, ev.xAxis, -ev.yAxis ,this) + RyujinxNative.jnaInstance.inputSetStickAxis( + 2, + ev.xAxis, + -ev.yAxis, + this + ) } } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt index 31f8adb3b..4a0741a1e 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt @@ -27,8 +27,6 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su private var _isStarted: Boolean = false private val nativeWindow: NativeWindow - private var _nativeRyujinx: RyujinxNative = RyujinxNative.instance - init { holder.addCallback(this) @@ -47,21 +45,21 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su if (_width != width || _height != height) { val window = nativeWindow.requeryWindowHandle() - _nativeRyujinx.graphicsSetSurface(window, nativeWindow.nativePointer) + RyujinxNative.jnaInstance.graphicsSetSurface(window, nativeWindow.nativePointer) - nativeWindow.swapInterval = 0; + nativeWindow.swapInterval = 0 } _width = width _height = height - _nativeRyujinx.graphicsRendererSetSize( + RyujinxNative.jnaInstance.graphicsRendererSetSize( width, height ) if (_isStarted) { - _nativeRyujinx.inputSetClientSize(width, height) + RyujinxNative.jnaInstance.inputSetClientSize(width, height) } } @@ -86,14 +84,14 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su _isStarted = true - game = if (mainViewModel.isMiiEditorLaunched) null else mainViewModel.gameModel; + game = if (mainViewModel.isMiiEditorLaunched) null else mainViewModel.gameModel - _nativeRyujinx.inputInitialize(width, height) + RyujinxNative.jnaInstance.inputInitialize(width, height) val id = mainViewModel.physicalControllerManager?.connect() mainViewModel.motionSensorManager?.setControllerId(id ?: -1) - _nativeRyujinx.graphicsRendererSetSize( + RyujinxNative.jnaInstance.graphicsRendererSetSize( surfaceHolder.surfaceFrame.width(), surfaceHolder.surfaceFrame.height() ) @@ -108,12 +106,12 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su var c = 0 val helper = NativeHelpers.instance while (_isStarted) { - _nativeRyujinx.inputUpdate() + RyujinxNative.jnaInstance.inputUpdate() Thread.sleep(1) showLoading?.apply { if (value) { - var value = helper.getProgressValue() + val value = helper.getProgressValue() if (value != -1f) progress?.apply { @@ -134,9 +132,9 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su } c = 0 mainViewModel.updateStats( - _nativeRyujinx.deviceGetGameFifo(), - _nativeRyujinx.deviceGetGameFrameRate(), - _nativeRyujinx.deviceGetGameFrameTime() + RyujinxNative.jnaInstance.deviceGetGameFifo(), + RyujinxNative.jnaInstance.deviceGetGameFrameRate(), + RyujinxNative.jnaInstance.deviceGetGameFrameTime() ) } } @@ -148,7 +146,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su thread { mainViewModel.activity.uiHandler.listen() } - _nativeRyujinx.graphicsRendererRunLoop() + RyujinxNative.jnaInstance.graphicsRendererRunLoop() game?.close() } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GamePadButtonInputId.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GamePadButtonInputId.kt index 05119474a..797be757c 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GamePadButtonInputId.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GamePadButtonInputId.kt @@ -2,6 +2,7 @@ package org.ryujinx.android enum class GamePadButtonInputId { None, + // Buttons A, B, diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GraphicsConfiguration.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GraphicsConfiguration.kt deleted file mode 100644 index a52048441..000000000 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GraphicsConfiguration.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.ryujinx.android - -import android.R.bool - -class GraphicsConfiguration { - var ResScale = 1f - var MaxAnisotropy = -1f - var FastGpuTime: Boolean = true - var Fast2DCopy: Boolean = true - var EnableMacroJit: Boolean = false - var EnableMacroHLE: Boolean = true - var EnableShaderCache: Boolean = true - var EnableTextureRecompression: Boolean = false - var BackendThreading: Int = org.ryujinx.android.BackendThreading.Auto.ordinal -} - -enum class BackendThreading -{ - Auto, - Off, - On -} \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Helpers.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Helpers.kt index 77dab8240..990cea899 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Helpers.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Helpers.kt @@ -80,9 +80,9 @@ class Helpers { currentProgressName: MutableState, finish: () -> Unit ) { - var fPath = path + "/${file.name}"; + var fPath = path + "/${file.name}" var callback: FileCallback? = object : FileCallback() { - override fun onFailed(errorCode: FileCallback.ErrorCode) { + override fun onFailed(errorCode: ErrorCode) { super.onFailed(errorCode) File(fPath).delete() finish() @@ -189,7 +189,6 @@ class Helpers { dir.mkdirs() } ZipInputStream(stream).use { zip -> - var count = 0 while (true) { val header = zip.nextEntry ?: break if (!folders.any { header.fileName.startsWith(it) }) { @@ -217,7 +216,7 @@ class Helpers { } } finally { isImporting.value = false - RyujinxNative.instance.deviceReloadFilesystem() + RyujinxNative.jnaInstance.deviceReloadFilesystem() } } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Icons.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Icons.kt index 8548efca6..674a4ebb1 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Icons.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Icons.kt @@ -20,7 +20,7 @@ import compose.icons.CssGgIcons import compose.icons.cssggicons.Games class Icons { - companion object{ + companion object { /// Icons exported from https://www.composables.com/icons @Composable fun listView(color: Color): ImageVector { @@ -216,6 +216,7 @@ class Icons { }.build() } } + @Composable fun applets(color: Color): ImageVector { return remember { @@ -331,6 +332,7 @@ class Icons { }.build() } } + @Composable fun playArrow(color: Color): ImageVector { return remember { @@ -372,6 +374,7 @@ class Icons { }.build() } } + @Composable fun folderOpen(color: Color): ImageVector { return remember { @@ -437,6 +440,7 @@ class Icons { }.build() } } + @Composable fun gameUpdate(): ImageVector { val primaryColor = MaterialTheme.colorScheme.primary @@ -514,6 +518,7 @@ class Icons { }.build() } } + @Composable fun download(): ImageVector { val primaryColor = MaterialTheme.colorScheme.primary @@ -583,6 +588,7 @@ class Icons { }.build() } } + @Composable fun vSync(): ImageVector { val primaryColor = MaterialTheme.colorScheme.primary @@ -653,6 +659,7 @@ class Icons { }.build() } } + @Composable fun videoGame(): ImageVector { val primaryColor = MaterialTheme.colorScheme.primary @@ -753,7 +760,7 @@ class Icons { @Preview @Composable -fun Preview(){ +fun Preview() { IconButton(modifier = Modifier.padding(4.dp), onClick = { }) { Icon( diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Logging.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Logging.kt index bc8c615ab..4ce4e9ff7 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Logging.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/Logging.kt @@ -9,11 +9,12 @@ import java.net.URLConnection class Logging(private var viewModel: MainViewModel) { val logPath = MainActivity.AppPath + "/Logs" - init{ + + init { File(logPath).mkdirs() } - fun requestExport(){ + fun requestExport() { val files = File(logPath).listFiles() files?.apply { val zipExportPath = MainActivity.AppPath + "/log.zip" @@ -22,7 +23,7 @@ class Logging(private var viewModel: MainViewModel) { if (files.isNotEmpty()) { val zipFile = ZipFile(zipExportPath) for (file in files) { - if(file.isFile) { + if (file.isFile) { zipFile.addFile(file) count++ } @@ -30,13 +31,17 @@ class Logging(private var viewModel: MainViewModel) { zipFile.close() } if (count > 0) { - val zip =File (zipExportPath) - val uri = FileProvider.getUriForFile(viewModel.activity, viewModel.activity.packageName + ".fileprovider", zip) + val zip = File(zipExportPath) + val uri = FileProvider.getUriForFile( + viewModel.activity, + viewModel.activity.packageName + ".fileprovider", + zip + ) val intent = Intent(Intent.ACTION_SEND) intent.putExtra(Intent.EXTRA_STREAM, uri) intent.setDataAndType(uri, URLConnection.guessContentTypeFromName(zip.name)) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - val chooser = Intent.createChooser(intent, "Share logs"); + val chooser = Intent.createChooser(intent, "Share logs") viewModel.activity.startActivity(chooser) } else { File(zipExportPath).delete() @@ -45,7 +50,7 @@ class Logging(private var viewModel: MainViewModel) { } fun clearLogs() { - if(File(logPath).exists()){ + if (File(logPath).exists()) { File(logPath).deleteRecursively() } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt index 06cf28edd..477656e44 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt @@ -42,7 +42,7 @@ class MainActivity : BaseActivity() { fun frameEnded(gameTime: Long) { mainViewModel?.activity?.apply { if (isActive && QuickSettings(this).enablePerformanceMode) { - mainViewModel?.performanceManager?.setTurboMode(true); + mainViewModel?.performanceManager?.setTurboMode(true) } } mainViewModel?.gameHost?.hideProgressIndicator() @@ -65,40 +65,40 @@ class MainActivity : BaseActivity() { val appPath: String = AppPath var quickSettings = QuickSettings(this) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Debug.ordinal, quickSettings.enableDebugLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Info.ordinal, quickSettings.enableInfoLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Stub.ordinal, quickSettings.enableStubLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Warning.ordinal, quickSettings.enableWarningLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Error.ordinal, quickSettings.enableErrorLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.AccessLog.ordinal, quickSettings.enableAccessLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Guest.ordinal, quickSettings.enableGuestLogs ) - RyujinxNative.instance.loggingSetEnabled( + RyujinxNative.jnaInstance.loggingSetEnabled( LogLevel.Trace.ordinal, quickSettings.enableTraceLogs ) val success = - RyujinxNative.instance.initialize(NativeHelpers.instance.storeStringJava(appPath)) + RyujinxNative.jnaInstance.javaInitialize(appPath) uiHandler = UiHandler() _isInit = success diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MotionSensorManager.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MotionSensorManager.kt index 1b34c985a..aaf2824f0 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MotionSensorManager.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MotionSensorManager.kt @@ -15,18 +15,20 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 { activity.getSystemService(Activity.SENSOR_SERVICE) as SensorManager private var controllerId: Int = -1 - private val motionGyroOrientation : FloatArray = FloatArray(3) - private val motionAcelOrientation : FloatArray = FloatArray(3) + private val motionGyroOrientation: FloatArray = FloatArray(3) + private val motionAcelOrientation: FloatArray = FloatArray(3) + init { accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) setOrientation90() - var orientationListener = object : OrientationEventListener(activity){ + var orientationListener = object : OrientationEventListener(activity) { override fun onOrientationChanged(orientation: Int) { - when{ + when { isWithinOrientationRange(orientation, 270) -> { setOrientation270() } + isWithinOrientationRange(orientation, 90) -> { setOrientation90() } @@ -34,10 +36,10 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 { } private fun isWithinOrientationRange( - currentOrientation : Int, targetOrientation : Int, epsilon : Int = 90 - ) : Boolean { + currentOrientation: Int, targetOrientation: Int, epsilon: Int = 90 + ): Boolean { return currentOrientation > targetOrientation - epsilon - && currentOrientation < targetOrientation + epsilon + && currentOrientation < targetOrientation + epsilon } } } @@ -50,6 +52,7 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 { motionAcelOrientation[1] = -1.0f motionAcelOrientation[2] = -1.0f } + fun setOrientation90() { motionGyroOrientation[0] = 1.0f motionGyroOrientation[1] = -1.0f @@ -59,30 +62,38 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 { motionAcelOrientation[2] = -1.0f } - fun setControllerId(id: Int){ + fun setControllerId(id: Int) { controllerId = id } - fun register(){ - if(isRegistered) + fun register() { + if (isRegistered) return gyro?.apply { - sensorManager.registerListener(this@MotionSensorManager, gyro, SensorManager.SENSOR_DELAY_GAME) + sensorManager.registerListener( + this@MotionSensorManager, + gyro, + SensorManager.SENSOR_DELAY_GAME + ) } accelerometer?.apply { - sensorManager.registerListener(this@MotionSensorManager, accelerometer, SensorManager.SENSOR_DELAY_GAME) + sensorManager.registerListener( + this@MotionSensorManager, + accelerometer, + SensorManager.SENSOR_DELAY_GAME + ) } - isRegistered = true; + isRegistered = true } - fun unregister(){ + fun unregister() { sensorManager.unregisterListener(this) isRegistered = false - if (controllerId != -1){ - RyujinxNative.instance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId) - RyujinxNative.instance.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId) + if (controllerId != -1) { + RyujinxNative.jnaInstance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId) + RyujinxNative.jnaInstance.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId) } } @@ -96,20 +107,25 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 { val y = motionAcelOrientation[1] * event.values[0] val z = motionAcelOrientation[2] * event.values[2] - RyujinxNative.instance.inputSetAccelerometerData(x, y, z, controllerId) + RyujinxNative.jnaInstance.inputSetAccelerometerData( + x, + y, + z, + controllerId + ) } Sensor.TYPE_GYROSCOPE -> { val x = motionGyroOrientation[0] * event.values[1] val y = motionGyroOrientation[1] * event.values[0] val z = motionGyroOrientation[2] * event.values[2] - RyujinxNative.instance.inputSetGyroData(x, y, z, controllerId) + RyujinxNative.jnaInstance.inputSetGyroData(x, y, z, controllerId) } } } else { - RyujinxNative.instance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId) - RyujinxNative.instance.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId) + RyujinxNative.jnaInstance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId) + RyujinxNative.jnaInstance.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId) } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt index 9518435f3..e3ffc258e 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt @@ -6,6 +6,7 @@ class NativeHelpers { companion object { val instance = NativeHelpers() + init { System.loadLibrary("ryujinxjni") } @@ -27,18 +28,18 @@ class NativeHelpers { external fun getMaxSwapInterval(nativeWindow: Long): Int external fun getMinSwapInterval(nativeWindow: Long): Int external fun setSwapInterval(nativeWindow: Long, swapInterval: Int): Int - external fun getProgressInfo() : String - external fun getProgressValue() : Float - external fun storeStringJava(string: String) : Long - external fun getStringJava(id: Long) : String + external fun getProgressInfo(): String + external fun getProgressValue(): Float + external fun storeStringJava(string: String): Long + external fun getStringJava(id: Long): String external fun setIsInitialOrientationFlipped(isFlipped: Boolean) - external fun getUiHandlerRequestType() : Int - external fun getUiHandlerRequestTitle() : Long - external fun getUiHandlerRequestMessage() : Long - external fun getUiHandlerMinLength() : Int - external fun getUiHandlerMaxLength() : Int - external fun getUiHandlerKeyboardMode() : Int - external fun getUiHandlerRequestWatermark() : Long - external fun getUiHandlerRequestInitialText() : Long - external fun getUiHandlerRequestSubtitle() : Long + external fun getUiHandlerRequestType(): Int + external fun getUiHandlerRequestTitle(): Long + external fun getUiHandlerRequestMessage(): Long + external fun getUiHandlerMinLength(): Int + external fun getUiHandlerMaxLength(): Int + external fun getUiHandlerKeyboardMode(): Int + external fun getUiHandlerRequestWatermark(): Long + external fun getUiHandlerRequestInitialText(): Long + external fun getUiHandlerRequestSubtitle(): Long } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeWindow.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeWindow.kt index dee3b4d61..0fd7b1f84 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeWindow.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeWindow.kt @@ -4,35 +4,35 @@ import android.view.SurfaceView class NativeWindow(val surface: SurfaceView) { var nativePointer: Long - var nativeHelpers: NativeHelpers = NativeHelpers.instance - private var _swapInterval : Int = 0 + private val nativeHelpers: NativeHelpers = NativeHelpers.instance + private var _swapInterval: Int = 0 - var maxSwapInterval : Int = 0 + var maxSwapInterval: Int = 0 get() { return if (nativePointer == -1L) 0 else nativeHelpers.getMaxSwapInterval(nativePointer) } - var minSwapInterval : Int = 0 + var minSwapInterval: Int = 0 get() { return if (nativePointer == -1L) 0 else nativeHelpers.getMinSwapInterval(nativePointer) } - var swapInterval : Int + var swapInterval: Int get() { return _swapInterval } set(value) { - if(nativePointer == -1L || nativeHelpers.setSwapInterval(nativePointer, value) == 0) + if (nativePointer == -1L || nativeHelpers.setSwapInterval(nativePointer, value) == 0) _swapInterval = value } init { - nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface); + nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface) swapInterval = maxOf(1, minSwapInterval) } - fun requeryWindowHandle() : Long { + fun requeryWindowHandle(): Long { nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface) swapInterval = swapInterval diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceMonitor.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceMonitor.kt index 7c3fdafc1..a461acf81 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceMonitor.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/PerformanceMonitor.kt @@ -15,10 +15,10 @@ import java.io.RandomAccessFile class PerformanceMonitor { val numberOfCores = Runtime.getRuntime().availableProcessors() - fun getFrequencies() : List { + fun getFrequencies(): List { val frequencies = mutableListOf() - for (i in 0.. { + fun getMemoryUsage(): List { val mem = mutableListOf() MainActivity.mainViewModel?.activity?.apply { val actManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager val memInfo = ActivityManager.MemoryInfo() actManager.getMemoryInfo(memInfo) - val availMemory = memInfo.availMem.toDouble()/(1024*1024) - val totalMemory= memInfo.totalMem.toDouble()/(1024*1024) + val availMemory = memInfo.availMem.toDouble() / (1024 * 1024) + val totalMemory = memInfo.totalMem.toDouble() / (1024 * 1024) mem.add((totalMemory - availMemory).toInt()) mem.add(totalMemory.toInt()) @@ -55,11 +54,11 @@ class PerformanceMonitor { @Composable fun RenderUsage() { - LazyColumn{ + LazyColumn { val frequencies = getFrequencies() val mem = getMemoryUsage() - for (i in 0.. { - ryujinxNative.inputSetButtonReleased(id.ordinal, controllerId) + RyujinxNative.jnaInstance.inputSetButtonReleased(id.ordinal, controllerId) } KeyEvent.ACTION_DOWN -> { - ryujinxNative.inputSetButtonPressed(id.ordinal, controllerId) + RyujinxNative.jnaInstance.inputSetButtonPressed(id.ordinal, controllerId) } } return true - } - else if(!isNotFallback){ + } else if (!isNotFallback) { return true } } - return false + return false } fun onMotionEvent(ev: MotionEvent) { - if(true) { - if(ev.action == MotionEvent.ACTION_MOVE) { + if (true) { + if (ev.action == MotionEvent.ACTION_MOVE) { val leftStickX = ev.getAxisValue(MotionEvent.AXIS_X) val leftStickY = ev.getAxisValue(MotionEvent.AXIS_Y) val rightStickX = ev.getAxisValue(MotionEvent.AXIS_Z) val rightStickY = ev.getAxisValue(MotionEvent.AXIS_RZ) - ryujinxNative.inputSetStickAxis(1, leftStickX, -leftStickY ,controllerId) - ryujinxNative.inputSetStickAxis(2, rightStickX, -rightStickY ,controllerId) + RyujinxNative.jnaInstance.inputSetStickAxis( + 1, + leftStickX, + -leftStickY, + controllerId + ) + RyujinxNative.jnaInstance.inputSetStickAxis( + 2, + rightStickX, + -rightStickY, + controllerId + ) ev.device?.apply { - if(sources and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD){ + if (sources and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD) { // Controller uses HAT val dPadHor = ev.getAxisValue(MotionEvent.AXIS_HAT_X) val dPadVert = ev.getAxisValue(MotionEvent.AXIS_HAT_Y) - if(dPadVert == 0.0f){ - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, controllerId) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, controllerId) + if (dPadVert == 0.0f) { + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadUp.ordinal, + controllerId + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadDown.ordinal, + controllerId + ) } - if(dPadHor == 0.0f){ - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, controllerId) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, controllerId) + if (dPadHor == 0.0f) { + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadLeft.ordinal, + controllerId + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadRight.ordinal, + controllerId + ) } - if(dPadVert < 0.0f){ - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, controllerId) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, controllerId) + if (dPadVert < 0.0f) { + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadUp.ordinal, + controllerId + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadDown.ordinal, + controllerId + ) } - if(dPadHor < 0.0f){ - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, controllerId) - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, controllerId) + if (dPadHor < 0.0f) { + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadLeft.ordinal, + controllerId + ) + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadRight.ordinal, + controllerId + ) } - if(dPadVert > 0.0f){ - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, controllerId) - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, controllerId) + if (dPadVert > 0.0f) { + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadUp.ordinal, + controllerId + ) + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadDown.ordinal, + controllerId + ) } - if(dPadHor > 0.0f){ - ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, controllerId) - ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, controllerId) + if (dPadHor > 0.0f) { + RyujinxNative.jnaInstance.inputSetButtonReleased( + GamePadButtonInputId.DpadLeft.ordinal, + controllerId + ) + RyujinxNative.jnaInstance.inputSetButtonPressed( + GamePadButtonInputId.DpadRight.ordinal, + controllerId + ) } } } @@ -80,12 +124,12 @@ class PhysicalControllerManager(val activity: MainActivity) { } } - fun connect() : Int { - controllerId = ryujinxNative.inputConnectGamepad(0) + fun connect(): Int { + controllerId = RyujinxNative.jnaInstance.inputConnectGamepad(0) return controllerId } - fun disconnect(){ + fun disconnect() { controllerId = -1 } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxApplication.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxApplication.kt index 69a71d5c5..79142be34 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxApplication.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxApplication.kt @@ -9,11 +9,12 @@ class RyujinxApplication : Application() { instance = this } - fun getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir + fun getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir + companion object { - lateinit var instance : RyujinxApplication + lateinit var instance: RyujinxApplication private set - val context : Context get() = instance.applicationContext + val context: Context get() = instance.applicationContext } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt index a01d75332..bcb32efa4 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt @@ -1,20 +1,11 @@ package org.ryujinx.android +import com.sun.jna.Library +import com.sun.jna.Native import org.ryujinx.android.viewmodels.GameInfo -@Suppress("KotlinJniMissingFunction") -class RyujinxNative { - external fun initialize(appPath: Long): Boolean - - companion object { - val instance: RyujinxNative = RyujinxNative() - - init { - System.loadLibrary("ryujinx") - } - } - - external fun deviceInitialize( +interface RyujinxNativeJna : Library { + fun deviceInitialize( isHostMapped: Boolean, useNce: Boolean, systemLanguage: Int, regionCode: Int, @@ -22,60 +13,81 @@ class RyujinxNative { enableDockedMode: Boolean, enablePtc: Boolean, enableInternetAccess: Boolean, - timeZone: Long, + timeZone: String, ignoreMissingServices: Boolean ): Boolean - external fun graphicsInitialize(configuration: GraphicsConfiguration): Boolean - external fun graphicsInitializeRenderer( + fun graphicsInitialize( + rescale: Float = 1f, + maxAnisotropy: Float = 1f, + fastGpuTime: Boolean = true, + fast2DCopy: Boolean = true, + enableMacroJit: Boolean = false, + enableMacroHLE: Boolean = true, + enableShaderCache: Boolean = true, + enableTextureRecompression: Boolean = false, + backendThreading: Int = BackendThreading.Auto.ordinal + ): Boolean + + fun graphicsInitializeRenderer( extensions: Array, + extensionsLength: Int, driver: Long ): Boolean - external fun deviceLoad(game: String): Boolean - external fun deviceLaunchMiiEditor(): Boolean - external fun deviceGetGameFrameRate(): Double - external fun deviceGetGameFrameTime(): Double - external fun deviceGetGameFifo(): Double - external fun deviceGetGameInfo(fileDescriptor: Int, extension: Long): GameInfo - external fun deviceGetGameInfoFromPath(path: String): GameInfo - external fun deviceLoadDescriptor(fileDescriptor: Int, gameType: Int, updateDescriptor: Int): Boolean - external fun graphicsRendererSetSize(width: Int, height: Int) - external fun graphicsRendererSetVsync(enabled: Boolean) - external fun graphicsRendererRunLoop() - external fun deviceReloadFilesystem() - external fun inputInitialize(width: Int, height: Int) - external fun inputSetClientSize(width: Int, height: Int) - external fun inputSetTouchPoint(x: Int, y: Int) - external fun inputReleaseTouchPoint() - external fun inputUpdate() - external fun inputSetButtonPressed(button: Int, id: Int) - external fun inputSetButtonReleased(button: Int, id: Int) - external fun inputConnectGamepad(index: Int): Int - external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int) - external fun inputSetAccelerometerData(x: Float, y: Float, z: Float, id: Int) - external fun inputSetGyroData(x: Float, y: Float, z: Float, id: Int) - external fun graphicsSetSurface(surface: Long, window: Long) - external fun deviceCloseEmulation() - external fun deviceSignalEmulationClose() - external fun deviceGetDlcTitleId(path: Long, ncaPath: Long): Long - external fun deviceGetDlcContentList(path: Long, titleId: Long): Array - external fun userGetOpenedUser(): Long - external fun userGetUserPicture(userId: Long): Long - external fun userSetUserPicture(userId: String, picture: String) - external fun userGetUserName(userId: Long): Long - external fun userSetUserName(userId: String, userName: String) - external fun userGetAllUsers(): Array - external fun userAddUser(username: String, picture: String) - external fun userDeleteUser(userId: String) - external fun userOpenUser(userId: Long) - external fun userCloseUser(userId: String) - external fun loggingSetEnabled(logLevel: Int, enabled: Boolean) - external fun deviceVerifyFirmware(fileDescriptor: Int, isXci: Boolean): Long - external fun deviceInstallFirmware(fileDescriptor: Int, isXci: Boolean) - external fun deviceGetInstalledFirmwareVersion() : Long - external fun uiHandlerSetup() - external fun uiHandlerWait() - external fun uiHandlerStopWait() - external fun uiHandlerSetResponse(isOkPressed: Boolean, input: Long) + fun javaInitialize(appPath: String): Boolean + fun deviceLaunchMiiEditor(): Boolean + fun deviceGetGameFrameRate(): Double + fun deviceGetGameFrameTime(): Double + fun deviceGetGameFifo(): Double + fun deviceLoadDescriptor(fileDescriptor: Int, gameType: Int, updateDescriptor: Int): Boolean + fun graphicsRendererSetSize(width: Int, height: Int) + fun graphicsRendererSetVsync(enabled: Boolean) + fun graphicsRendererRunLoop() + fun deviceReloadFilesystem() + fun inputInitialize(width: Int, height: Int) + fun inputSetClientSize(width: Int, height: Int) + fun inputSetTouchPoint(x: Int, y: Int) + fun inputReleaseTouchPoint() + fun inputUpdate() + fun inputSetButtonPressed(button: Int, id: Int) + fun inputSetButtonReleased(button: Int, id: Int) + fun inputConnectGamepad(index: Int): Int + fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int) + fun inputSetAccelerometerData(x: Float, y: Float, z: Float, id: Int) + fun inputSetGyroData(x: Float, y: Float, z: Float, id: Int) + fun graphicsSetSurface(surface: Long, window: Long) + fun deviceCloseEmulation() + fun deviceSignalEmulationClose() + fun userGetOpenedUser(): String + fun userGetUserPicture(userId: String): String + fun userSetUserPicture(userId: String, picture: String) + fun userGetUserName(userId: String): String + fun userSetUserName(userId: String, userName: String) + fun userAddUser(username: String, picture: String) + fun userDeleteUser(userId: String) + fun userOpenUser(userId: String) + fun userCloseUser(userId: String) + fun loggingSetEnabled(logLevel: Int, enabled: Boolean) + fun deviceVerifyFirmware(fileDescriptor: Int, isXci: Boolean): String + fun deviceInstallFirmware(fileDescriptor: Int, isXci: Boolean) + fun deviceGetInstalledFirmwareVersion(): String + fun uiHandlerSetup() + fun uiHandlerWait() + fun uiHandlerStopWait() + fun uiHandlerSetResponse(isOkPressed: Boolean, input: Long) + fun deviceGetDlcTitleId(path: String, ncaPath: String): String + fun deviceGetGameInfo(fileDescriptor: Int, extension: String, info: GameInfo) + fun userGetAllUsers(): Array + fun deviceGetDlcContentList(path: String, titleId: Long): Array +} + +class RyujinxNative { + + companion object { + val jnaInstance: RyujinxNativeJna = Native.load( + "ryujinx", + RyujinxNativeJna::class.java + ) + } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt index b7eaf0cfb..a2ab87ec4 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -50,13 +51,13 @@ class UiHandler { var shouldListen = true init { - RyujinxNative.instance.uiHandlerSetup() + RyujinxNative.jnaInstance.uiHandlerSetup() } fun listen() { showMessage.value = false while (shouldListen) { - RyujinxNative.instance.uiHandlerWait() + RyujinxNative.jnaInstance.uiHandlerWait() title = NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestTitle()) @@ -79,7 +80,7 @@ class UiHandler { fun stop() { shouldListen = false - RyujinxNative.instance.uiHandlerStopWait() + RyujinxNative.jnaInstance.uiHandlerStopWait() } @OptIn(ExperimentalMaterial3Api::class) @@ -96,23 +97,24 @@ class UiHandler { mutableStateOf("") } - fun validate() : Boolean{ - if(inputText.value.isEmpty()){ + fun validate(): Boolean { + if (inputText.value.isEmpty()) { validation.value = "Must be between ${minLength} and ${maxLength} characters" - } - else{ + } else { return inputText.value.length < minLength || inputText.value.length > maxLength } - return false; + return false } fun getInputType(): KeyboardType { - return when(mode){ + return when (mode) { KeyboardMode.Default -> KeyboardType.Text - KeyboardMode.Numeric -> KeyboardType.Decimal - KeyboardMode.ASCII -> KeyboardType.Ascii - else -> { KeyboardType.Text} + KeyboardMode.Numeric -> KeyboardType.Decimal + KeyboardMode.ASCII -> KeyboardType.Ascii + else -> { + KeyboardType.Text + } } } @@ -125,15 +127,15 @@ class UiHandler { NativeHelpers.instance.storeStringJava(inputListener.value) } showMessageListener.value = false - RyujinxNative.instance.uiHandlerSetResponse(true, input) + RyujinxNative.jnaInstance.uiHandlerSetResponse(true, input) } if (showMessageListener.value) { - AlertDialog( + BasicAlertDialog( + onDismissRequest = { }, modifier = Modifier .wrapContentWidth() .wrapContentHeight(), - onDismissRequest = { }, properties = DialogProperties(dismissOnBackPress = false, false) ) { Column { diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/providers/DocumentProvider.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/providers/DocumentProvider.kt index dd2316a7e..13d5f66ec 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/providers/DocumentProvider.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/providers/DocumentProvider.kt @@ -21,7 +21,7 @@ class DocumentProvider : DocumentsProvider() { private val applicationName = "Ryujinx" companion object { - private val DEFAULT_ROOT_PROJECTION : Array = arrayOf( + private val DEFAULT_ROOT_PROJECTION: Array = arrayOf( DocumentsContract.Root.COLUMN_ROOT_ID, DocumentsContract.Root.COLUMN_MIME_TYPES, DocumentsContract.Root.COLUMN_FLAGS, @@ -32,7 +32,7 @@ class DocumentProvider : DocumentsProvider() { DocumentsContract.Root.COLUMN_AVAILABLE_BYTES ) - private val DEFAULT_DOCUMENT_PROJECTION : Array = arrayOf( + private val DEFAULT_DOCUMENT_PROJECTION: Array = arrayOf( DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_MIME_TYPE, DocumentsContract.Document.COLUMN_DISPLAY_NAME, @@ -41,19 +41,19 @@ class DocumentProvider : DocumentsProvider() { DocumentsContract.Document.COLUMN_SIZE ) - const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".providers" + const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".providers" - const val ROOT_ID : String = "root" + const val ROOT_ID: String = "root" } - override fun onCreate() : Boolean { + override fun onCreate(): Boolean { return true } /** * @return The [File] that corresponds to the document ID supplied by [getDocumentId] */ - private fun getFile(documentId : String) : File { + private fun getFile(documentId: String): File { if (documentId.startsWith(ROOT_ID)) { val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1)) if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found") @@ -66,17 +66,20 @@ class DocumentProvider : DocumentsProvider() { /** * @return A unique ID for the provided [File] */ - private fun getDocumentId(file : File) : String { + private fun getDocumentId(file: File): String { return "$ROOT_ID/${file.toRelativeString(baseDirectory)}" } - override fun queryRoots(projection : Array?) : Cursor { + override fun queryRoots(projection: Array?): Cursor { val cursor = MatrixCursor(projection ?: DEFAULT_ROOT_PROJECTION) cursor.newRow().apply { add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT_ID) add(DocumentsContract.Root.COLUMN_SUMMARY, null) - add(DocumentsContract.Root.COLUMN_FLAGS, DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD) + add( + DocumentsContract.Root.COLUMN_FLAGS, + DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD + ) add(DocumentsContract.Root.COLUMN_TITLE, applicationName) add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory)) add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*") @@ -87,22 +90,23 @@ class DocumentProvider : DocumentsProvider() { return cursor } - override fun queryDocument(documentId : String?, projection : Array?) : Cursor { + override fun queryDocument(documentId: String?, projection: Array?): Cursor { val cursor = MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION) return includeFile(cursor, documentId, null) } - override fun isChildDocument(parentDocumentId : String?, documentId : String?) : Boolean { + override fun isChildDocument(parentDocumentId: String?, documentId: String?): Boolean { return documentId?.startsWith(parentDocumentId!!) ?: false } /** * @return A new [File] with a unique name based off the supplied [name], not conflicting with any existing file */ - fun File.resolveWithoutConflict(name : String) : File { + fun File.resolveWithoutConflict(name: String): File { var file = resolve(name) if (file.exists()) { - var noConflictId = 1 // Makes sure two files don't have the same name by adding a number to the end + var noConflictId = + 1 // Makes sure two files don't have the same name by adding a number to the end val extension = name.substringAfterLast('.') val baseName = name.substringBeforeLast('.') while (file.exists()) @@ -111,7 +115,11 @@ class DocumentProvider : DocumentsProvider() { return file } - override fun createDocument(parentDocumentId : String?, mimeType : String?, displayName : String) : String? { + override fun createDocument( + parentDocumentId: String?, + mimeType: String?, + displayName: String + ): String { val parentFile = getFile(parentDocumentId!!) val newFile = parentFile.resolveWithoutConflict(displayName) @@ -123,20 +131,20 @@ class DocumentProvider : DocumentsProvider() { if (!newFile.createNewFile()) throw IOException("Failed to create file") } - } catch (e : IOException) { + } catch (e: IOException) { throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}") } return getDocumentId(newFile) } - override fun deleteDocument(documentId : String?) { + override fun deleteDocument(documentId: String?) { val file = getFile(documentId!!) if (!file.delete()) throw FileNotFoundException("Couldn't delete document with ID '$documentId'") } - override fun removeDocument(documentId : String, parentDocumentId : String?) { + override fun removeDocument(documentId: String, parentDocumentId: String?) { val parent = getFile(parentDocumentId!!) val file = getFile(documentId) @@ -148,18 +156,19 @@ class DocumentProvider : DocumentsProvider() { } } - override fun renameDocument(documentId : String?, displayName : String?) : String? { + override fun renameDocument(documentId: String?, displayName: String?): String { if (displayName == null) throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null") val sourceFile = getFile(documentId!!) - val sourceParentFile = sourceFile.parentFile ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent") + val sourceParentFile = sourceFile.parentFile + ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent") val destFile = sourceParentFile.resolve(displayName) try { if (!sourceFile.renameTo(destFile)) throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'") - } catch (e : Exception) { + } catch (e: Exception) { throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}") } @@ -167,16 +176,16 @@ class DocumentProvider : DocumentsProvider() { } private fun copyDocument( - sourceDocumentId : String, sourceParentDocumentId : String, - targetParentDocumentId : String? - ) : String? { + sourceDocumentId: String, sourceParentDocumentId: String, + targetParentDocumentId: String? + ): String? { if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'") return copyDocument(sourceDocumentId, targetParentDocumentId) } - override fun copyDocument(sourceDocumentId : String, targetParentDocumentId : String?) : String? { + override fun copyDocument(sourceDocumentId: String, targetParentDocumentId: String?): String { val parent = getFile(targetParentDocumentId!!) val oldFile = getFile(sourceDocumentId) val newFile = parent.resolveWithoutConflict(oldFile.name) @@ -190,7 +199,7 @@ class DocumentProvider : DocumentsProvider() { inStream.copyTo(outStream) } } - } catch (e : IOException) { + } catch (e: IOException) { throw FileNotFoundException("Couldn't copy document '$sourceDocumentId': ${e.message}") } @@ -198,9 +207,9 @@ class DocumentProvider : DocumentsProvider() { } override fun moveDocument( - sourceDocumentId : String, sourceParentDocumentId : String?, - targetParentDocumentId : String? - ) : String? { + sourceDocumentId: String, sourceParentDocumentId: String?, + targetParentDocumentId: String? + ): String? { try { val newDocumentId = copyDocument( sourceDocumentId, sourceParentDocumentId!!, @@ -208,12 +217,12 @@ class DocumentProvider : DocumentsProvider() { ) removeDocument(sourceDocumentId, sourceParentDocumentId) return newDocumentId - } catch (e : FileNotFoundException) { + } catch (e: FileNotFoundException) { throw FileNotFoundException("Couldn't move document '$sourceDocumentId'") } } - private fun includeFile(cursor : MatrixCursor, documentId : String?, file : File?) : MatrixCursor { + private fun includeFile(cursor: MatrixCursor, documentId: String?, file: File?): MatrixCursor { val localDocumentId = documentId ?: file?.let { getDocumentId(it) } val localFile = file ?: getFile(documentId!!) @@ -232,7 +241,10 @@ class DocumentProvider : DocumentsProvider() { cursor.newRow().apply { add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId) - add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, if (localFile == baseDirectory) applicationName else localFile.name) + add( + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + if (localFile == baseDirectory) applicationName else localFile.name + ) add(DocumentsContract.Document.COLUMN_SIZE, localFile.length()) add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile)) add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified()) @@ -244,14 +256,14 @@ class DocumentProvider : DocumentsProvider() { return cursor } - private fun getTypeForFile(file : File) : Any? { + private fun getTypeForFile(file: File): Any? { return if (file.isDirectory) DocumentsContract.Document.MIME_TYPE_DIR else getTypeForName(file.name) } - private fun getTypeForName(name : String) : Any? { + private fun getTypeForName(name: String): Any { val lastDot = name.lastIndexOf('.') if (lastDot >= 0) { val extension = name.substring(lastDot + 1) @@ -262,7 +274,11 @@ class DocumentProvider : DocumentsProvider() { return "application/octect-stream" } - override fun queryChildDocuments(parentDocumentId : String?, projection : Array?, sortOrder : String?) : Cursor { + override fun queryChildDocuments( + parentDocumentId: String?, + projection: Array?, + sortOrder: String? + ): Cursor { var cursor = MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION) val parent = getFile(parentDocumentId!!) @@ -272,7 +288,11 @@ class DocumentProvider : DocumentsProvider() { return cursor } - override fun openDocument(documentId : String?, mode : String?, signal : CancellationSignal?) : ParcelFileDescriptor { + override fun openDocument( + documentId: String?, + mode: String?, + signal: CancellationSignal? + ): ParcelFileDescriptor { val file = documentId?.let { getFile(it) } val accessMode = ParcelFileDescriptor.parseMode(mode) return ParcelFileDescriptor.open(file, accessMode) diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/ui/theme/Theme.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/ui/theme/Theme.kt index 38fbf2513..cf5976536 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/ui/theme/Theme.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/ui/theme/Theme.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/DlcViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/DlcViewModel.kt index 7d95ec594..b1d78b810 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/DlcViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/DlcViewModel.kt @@ -11,7 +11,6 @@ import com.anggrayudi.storage.file.getAbsolutePath import com.google.gson.Gson import com.google.gson.reflect.TypeToken import org.ryujinx.android.MainActivity -import org.ryujinx.android.NativeHelpers import org.ryujinx.android.RyujinxNative import java.io.File @@ -40,14 +39,14 @@ class DlcViewModel(val titleId: String) { val path = file.getAbsolutePath(storageHelper.storage.context) if (path.isNotEmpty()) { data?.apply { - val contents = RyujinxNative.instance.deviceGetDlcContentList( - NativeHelpers.instance.storeStringJava(path), + val contents = RyujinxNative.jnaInstance.deviceGetDlcContentList( + path, titleId.toLong(16) ) if (contents.isNotEmpty()) { val contentPath = path - val container = DlcContainerList(contentPath); + val container = DlcContainerList(contentPath) for (content in contents) container.dlc_nca_list.add( @@ -90,7 +89,7 @@ class DlcViewModel(val titleId: String) { val containerPath = container.path if (!File(containerPath).exists()) - continue; + continue for (dlc in container.dlc_nca_list) { val enabled = remember { @@ -102,7 +101,10 @@ class DlcViewModel(val titleId: String) { enabled, containerPath, dlc.fullPath, - NativeHelpers.instance.getStringJava(RyujinxNative.instance.deviceGetDlcTitleId(NativeHelpers.instance.storeStringJava(containerPath), NativeHelpers.instance.storeStringJava(dlc.fullPath))) + RyujinxNative.jnaInstance.deviceGetDlcTitleId( + containerPath, + dlc.fullPath + ) ) ) } @@ -143,11 +145,13 @@ data class DlcContainerList( data class DlcContainer( var enabled: Boolean = false, var titleId: String = "", - var fullPath: String = "") + var fullPath: String = "" +) data class DlcItem( - var name:String = "", + var name: String = "", var isEnabled: MutableState = mutableStateOf(false), var containerPath: String = "", var fullPath: String = "", - var titleId: String = "") + var titleId: String = "" +) diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameInfo.java b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameInfo.java new file mode 100644 index 000000000..1f62ba81c --- /dev/null +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameInfo.java @@ -0,0 +1,20 @@ +package org.ryujinx.android.viewmodels; + +import com.sun.jna.Structure; + +import java.util.List; + + +public class GameInfo extends Structure { + public double FileSize = 0.0; + public String TitleName; + public String TitleId; + public String Developer; + public String Version; + public String Icon; + + @Override + protected List getFieldOrder() { + return List.of("FileSize", "TitleName", "TitleId", "Developer", "Version", "Icon"); + } +} \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt index 017809ea0..16c695e5a 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/GameModel.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.os.ParcelFileDescriptor import androidx.documentfile.provider.DocumentFile import com.anggrayudi.storage.file.extension -import org.ryujinx.android.NativeHelpers import org.ryujinx.android.RyujinxNative @@ -24,8 +23,8 @@ class GameModel(var file: DocumentFile, val context: Context) { init { fileName = file.name val pid = open() - val ext = NativeHelpers.instance.storeStringJava(file.extension) - val gameInfo = RyujinxNative.instance.deviceGetGameInfo(pid, ext) + val gameInfo = GameInfo() + RyujinxNative.jnaInstance.deviceGetGameInfo(pid, file.extension, gameInfo) close() fileSize = gameInfo.FileSize @@ -46,28 +45,28 @@ class GameModel(var file: DocumentFile, val context: Context) { } } - fun open() : Int { + fun open(): Int { descriptor = context.contentResolver.openFileDescriptor(file.uri, "rw") return descriptor?.fd ?: 0 } - fun openUpdate() : Int { - if(titleId?.isNotEmpty() == true) { + fun openUpdate(): Int { + if (titleId?.isNotEmpty() == true) { val vm = TitleUpdateViewModel(titleId ?: "") - if(vm.data?.selected?.isNotEmpty() == true){ + if (vm.data?.selected?.isNotEmpty() == true) { val uri = Uri.parse(vm.data?.selected) val file = DocumentFile.fromSingleUri(context, uri) - if(file?.exists() == true){ + if (file?.exists() == true) { updateDescriptor = context.contentResolver.openFileDescriptor(file.uri, "rw") - return updateDescriptor ?.fd ?: -1; + return updateDescriptor?.fd ?: -1 } } } - return -1; + return -1 } fun close() { @@ -78,16 +77,7 @@ class GameModel(var file: DocumentFile, val context: Context) { } } -class GameInfo { - var FileSize = 0.0 - var TitleName: String? = null - var TitleId: String? = null - var Developer: String? = null - var Version: String? = null - var Icon: String? = null -} - -enum class FileType{ +enum class FileType { None, Nsp, Xci, diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt index f98b66fb4..6ccd80d3b 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/HomeViewModel.kt @@ -46,37 +46,38 @@ class HomeViewModel( } } - fun filter(query : String){ + fun filter(query: String) { gameList.clear() - gameList.addAll(loadedCache.filter { it.titleName != null && it.titleName!!.isNotEmpty() && (query.trim() - .isEmpty() || it.titleName!!.lowercase(Locale.getDefault()) - .contains(query)) }) + gameList.addAll(loadedCache.filter { + it.titleName != null && it.titleName!!.isNotEmpty() && (query.trim() + .isEmpty() || it.titleName!!.lowercase(Locale.getDefault()) + .contains(query)) + }) } - fun requestReload(){ + fun requestReload() { shouldReload = true } - fun reloadGameList() { - var storage = activity?.storageHelper ?: return - - if(isLoading) + private fun reloadGameList() { + activity?.storageHelper ?: return + + if (isLoading) return val folder = gameFolderPath ?: return gameList.clear() - + isLoading = true thread { try { loadedCache.clear() - val files = mutableListOf() for (file in folder.search(false, DocumentFileType.FILE)) { if (file.extension == "xci" || file.extension == "nsp" || file.extension == "nro") activity.let { val item = GameModel(file, it) - if(item.titleId?.isNotEmpty() == true && item.titleName?.isNotEmpty() == true) { + if (item.titleId?.isNotEmpty() == true && item.titleName?.isNotEmpty() == true && item.titleName != "Unknown") { loadedCache.add(item) gameList.add(item) } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt index fad3402ce..fc7eae122 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Semaphore import org.ryujinx.android.GameController import org.ryujinx.android.GameHost -import org.ryujinx.android.GraphicsConfiguration import org.ryujinx.android.Logging import org.ryujinx.android.MainActivity import org.ryujinx.android.MotionSensorManager @@ -55,24 +54,19 @@ class MainViewModel(val activity: MainActivity) { } fun closeGame() { - RyujinxNative.instance.deviceSignalEmulationClose() + RyujinxNative.jnaInstance.deviceSignalEmulationClose() gameHost?.close() - RyujinxNative.instance.deviceCloseEmulation() + RyujinxNative.jnaInstance.deviceCloseEmulation() motionSensorManager?.unregister() physicalControllerManager?.disconnect() motionSensorManager?.setControllerId(-1) } fun refreshFirmwareVersion() { - var handle = RyujinxNative.instance.deviceGetInstalledFirmwareVersion() - if (handle != -1L) { - firmwareVersion = NativeHelpers.instance.getStringJava(handle) - } + firmwareVersion = RyujinxNative.jnaInstance.deviceGetInstalledFirmwareVersion() } fun loadGame(game: GameModel): Boolean { - val nativeRyujinx = RyujinxNative.instance - val descriptor = game.open() if (descriptor == 0) @@ -85,12 +79,12 @@ class MainViewModel(val activity: MainActivity) { val settings = QuickSettings(activity) - var success = nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply { - EnableShaderCache = settings.enableShaderCache - EnableTextureRecompression = settings.enableTextureRecompression - ResScale = settings.resScale - BackendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal - }) + var success = RyujinxNative.jnaInstance.graphicsInitialize( + enableShaderCache = settings.enableShaderCache, + enableTextureRecompression = settings.enableTextureRecompression, + rescale = settings.resScale, + backendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal + ) if (!success) return false @@ -139,8 +133,11 @@ class MainViewModel(val activity: MainActivity) { } - success = nativeRyujinx.graphicsInitializeRenderer( - nativeInterop.VkRequiredExtensions!!, + val extensions = nativeInterop.VkRequiredExtensions + + success = RyujinxNative.jnaInstance.graphicsInitializeRenderer( + extensions!!, + extensions.size, driverHandle ) if (!success) @@ -151,7 +148,7 @@ class MainViewModel(val activity: MainActivity) { semaphore.acquire() launchOnUiThread { // We are only able to initialize the emulation context on the main thread - success = nativeRyujinx.deviceInitialize( + success = RyujinxNative.jnaInstance.deviceInitialize( settings.isHostMapped, settings.useNce, SystemLanguage.AmericanEnglish.ordinal, @@ -160,7 +157,7 @@ class MainViewModel(val activity: MainActivity) { settings.enableDocked, settings.enablePtc, false, - NativeHelpers.instance.storeStringJava("UTC"), + "UTC", settings.ignoreMissingServices ) @@ -173,28 +170,24 @@ class MainViewModel(val activity: MainActivity) { if (!success) return false - success = nativeRyujinx.deviceLoadDescriptor(descriptor, game.type.ordinal, update) + success = + RyujinxNative.jnaInstance.deviceLoadDescriptor(descriptor, game.type.ordinal, update) - if (!success) - return false - - return true + return success } fun loadMiiEditor(): Boolean { - val nativeRyujinx = RyujinxNative.instance - gameModel = null isMiiEditorLaunched = true val settings = QuickSettings(activity) - var success = nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply { - EnableShaderCache = settings.enableShaderCache - EnableTextureRecompression = settings.enableTextureRecompression - ResScale = settings.resScale - BackendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal - }) + var success = RyujinxNative.jnaInstance.graphicsInitialize( + enableShaderCache = settings.enableShaderCache, + enableTextureRecompression = settings.enableTextureRecompression, + rescale = settings.resScale, + backendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal + ) if (!success) return false @@ -243,8 +236,11 @@ class MainViewModel(val activity: MainActivity) { } - success = nativeRyujinx.graphicsInitializeRenderer( - nativeInterop.VkRequiredExtensions!!, + val extensions = nativeInterop.VkRequiredExtensions + + success = RyujinxNative.jnaInstance.graphicsInitializeRenderer( + extensions!!, + extensions.size, driverHandle ) if (!success) @@ -255,7 +251,7 @@ class MainViewModel(val activity: MainActivity) { semaphore.acquire() launchOnUiThread { // We are only able to initialize the emulation context on the main thread - success = nativeRyujinx.deviceInitialize( + success = RyujinxNative.jnaInstance.deviceInitialize( settings.isHostMapped, settings.useNce, SystemLanguage.AmericanEnglish.ordinal, @@ -264,7 +260,7 @@ class MainViewModel(val activity: MainActivity) { settings.enableDocked, settings.enablePtc, false, - NativeHelpers.instance.storeStringJava("UTC"), + "UTC", settings.ignoreMissingServices ) @@ -277,12 +273,9 @@ class MainViewModel(val activity: MainActivity) { if (!success) return false - success = nativeRyujinx.deviceLaunchMiiEditor() + success = RyujinxNative.jnaInstance.deviceLaunchMiiEditor() - if (!success) - return false - - return true + return success } fun clearPptcCache(titleId: String) { diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt index 574267894..5e4fd5c07 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/SettingsViewModel.kt @@ -12,7 +12,6 @@ import com.anggrayudi.storage.file.extension import com.anggrayudi.storage.file.getAbsolutePath import org.ryujinx.android.LogLevel import org.ryujinx.android.MainActivity -import org.ryujinx.android.NativeHelpers import org.ryujinx.android.RyujinxNative import java.io.File import kotlin.concurrent.thread @@ -28,7 +27,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main sharedPref = getPreferences() previousFolderCallback = activity.storageHelper!!.onFolderSelected previousFileCallback = activity.storageHelper!!.onFileSelected - activity.storageHelper!!.onFolderSelected = { requestCode, folder -> + activity.storageHelper!!.onFolderSelected = { _, folder -> run { val p = folder.getAbsolutePath(activity) val editor = sharedPref.edit() @@ -146,18 +145,24 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main editor.apply() activity.storageHelper!!.onFolderSelected = previousFolderCallback - RyujinxNative.instance.loggingSetEnabled(LogLevel.Debug.ordinal, enableDebugLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Info.ordinal, enableInfoLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Stub.ordinal, enableStubLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Warning.ordinal, enableWarningLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Error.ordinal, enableErrorLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.AccessLog.ordinal, enableAccessLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Guest.ordinal, enableGuestLogs.value) - RyujinxNative.instance.loggingSetEnabled(LogLevel.Trace.ordinal, enableTraceLogs.value) + RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Debug.ordinal, enableDebugLogs.value) + RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Info.ordinal, enableInfoLogs.value) + RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Stub.ordinal, enableStubLogs.value) + RyujinxNative.jnaInstance.loggingSetEnabled( + LogLevel.Warning.ordinal, + enableWarningLogs.value + ) + RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Error.ordinal, enableErrorLogs.value) + RyujinxNative.jnaInstance.loggingSetEnabled( + LogLevel.AccessLog.ordinal, + enableAccessLogs.value + ) + RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Guest.ordinal, enableGuestLogs.value) + RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Trace.ordinal, enableTraceLogs.value) } fun openGameFolder() { - val path = sharedPref?.getString("gameFolder", "") ?: "" + val path = sharedPref.getString("gameFolder", "") ?: "" if (path.isEmpty()) activity.storageHelper?.storage?.openFolderPicker() @@ -169,13 +174,13 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main } fun importProdKeys() { - activity.storageHelper!!.onFileSelected = { requestCode, files -> + activity.storageHelper!!.onFileSelected = { _, files -> run { activity.storageHelper!!.onFileSelected = previousFileCallback val file = files.firstOrNull() file?.apply { if (name == "prod.keys") { - val outputFile = File(MainActivity.AppPath + "/system"); + val outputFile = File(MainActivity.AppPath + "/system") outputFile.delete() thread { @@ -183,9 +188,6 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main activity, outputFile, callback = object : FileCallback() { - override fun onCompleted(result: Any) { - super.onCompleted(result) - } }) } } @@ -209,14 +211,13 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main val descriptor = activity.contentResolver.openFileDescriptor(file.uri, "rw") descriptor?.use { d -> - val version = RyujinxNative.instance.deviceVerifyFirmware( - d.fd, - extension == "xci" - ) + selectedFirmwareVersion = + RyujinxNative.jnaInstance.deviceVerifyFirmware( + d.fd, + extension == "xci" + ) selectedFirmwareFile = file - if (version != -1L) { - selectedFirmwareVersion = - NativeHelpers.instance.getStringJava(version) + if (selectedFirmwareVersion.isEmpty()) { installState.value = FirmwareInstallState.Query } else { installState.value = FirmwareInstallState.Cancelled @@ -246,7 +247,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main installState.value = FirmwareInstallState.Install thread { try { - RyujinxNative.instance.deviceInstallFirmware( + RyujinxNative.jnaInstance.deviceInstallFirmware( d.fd, extension == "xci" ) diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt index 2f3df1b08..a5dc5b9be 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt @@ -53,8 +53,11 @@ class TitleUpdateViewModel(val titleId: String) { if (requestCode == UpdateRequestCode) { val file = files.firstOrNull() file?.apply { - if(file.extension == "nsp"){ - storageHelper.storage.context.contentResolver.takePersistableUriPermission(file.uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + if (file.extension == "nsp") { + storageHelper.storage.context.contentResolver.takePersistableUriPermission( + file.uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION + ) currentPaths.add(file.uri.toString()) } } @@ -72,7 +75,7 @@ class TitleUpdateViewModel(val titleId: String) { currentPaths.forEach { val uri = Uri.parse(it) val file = DocumentFile.fromSingleUri(storageHelper.storage.context, uri) - if(file?.exists() == true){ + if (file?.exists() == true) { existingPaths.add(it) } } @@ -108,20 +111,19 @@ class TitleUpdateViewModel(val titleId: String) { currentPaths.forEach { val uri = Uri.parse(it) val file = DocumentFile.fromSingleUri(storageHelper.storage.context, uri) - if(file?.exists() == true){ + if (file?.exists() == true) { savedUpdates.add(it) } } metadata.paths = savedUpdates - if(selected.isNotEmpty()){ + if (selected.isNotEmpty()) { val uri = Uri.parse(selected) val file = DocumentFile.fromSingleUri(storageHelper.storage.context, uri) - if(file?.exists() == true){ + if (file?.exists() == true) { metadata.selected = selected } - } - else { + } else { metadata.selected = selected } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/UserViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/UserViewModel.kt index 6cb5cdcb7..50cccb6c2 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/UserViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/UserViewModel.kt @@ -1,6 +1,5 @@ package org.ryujinx.android.viewmodels -import org.ryujinx.android.NativeHelpers import org.ryujinx.android.RyujinxNative import java.util.Base64 @@ -14,52 +13,45 @@ class UserViewModel { fun refreshUsers() { userList.clear() - val native = RyujinxNative.instance - val helper = NativeHelpers.instance val decoder = Base64.getDecoder() openedUser = UserModel() - openedUser.id = helper.getStringJava(native.userGetOpenedUser()) + openedUser.id = RyujinxNative.jnaInstance.userGetOpenedUser() if (openedUser.id.isNotEmpty()) { - openedUser.username = - helper.getStringJava(native.userGetUserName(helper.storeStringJava(openedUser.id))) + openedUser.username = RyujinxNative.jnaInstance.userGetUserName(openedUser.id) openedUser.userPicture = decoder.decode( - helper.getStringJava( - native.userGetUserPicture( - helper.storeStringJava(openedUser.id) - ) + RyujinxNative.jnaInstance.userGetUserPicture( + openedUser.id ) ) } - val users = native.userGetAllUsers() + val users = RyujinxNative.jnaInstance.userGetAllUsers() for (user in users) { userList.add( UserModel( user, - helper.getStringJava(native.userGetUserName(helper.storeStringJava(user))), + RyujinxNative.jnaInstance.userGetUserName(user), decoder.decode( - helper.getStringJava( - native.userGetUserPicture( - helper.storeStringJava(user) - ) - ) + RyujinxNative.jnaInstance.userGetUserPicture(user) ) ) ) } } - fun openUser(userModel: UserModel){ - val native = RyujinxNative.instance - val helper = NativeHelpers.instance - native.userOpenUser(helper.storeStringJava(userModel.id)) + fun openUser(userModel: UserModel) { + RyujinxNative.jnaInstance.userOpenUser(userModel.id) refreshUsers() } } -data class UserModel(var id : String = "", var username: String = "", var userPicture: ByteArray? = null) { +data class UserModel( + var id: String = "", + var username: String = "", + var userPicture: ByteArray? = null +) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt index bc0a58a43..40eb56f59 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt @@ -18,7 +18,7 @@ class VulkanDriverViewModel(val activity: MainActivity) { const val DriverFolder: String = "drivers" } - private fun getAppPath() : String { + private fun getAppPath(): String { var appPath = MainActivity.AppPath appPath += "/" @@ -26,29 +26,29 @@ class VulkanDriverViewModel(val activity: MainActivity) { return appPath } - fun ensureDriverPath() : File { + fun ensureDriverPath(): File { val driverPath = getAppPath() + DriverFolder val driverFolder = File(driverPath) - if(!driverFolder.exists()) + if (!driverFolder.exists()) driverFolder.mkdirs() return driverFolder } - fun getAvailableDrivers() : MutableList { + fun getAvailableDrivers(): MutableList { val driverFolder = ensureDriverPath() val folders = driverFolder.walkTopDown() val drivers = mutableListOf() - + val selectedDriverFile = File(driverFolder.absolutePath + "/selected") - if(selectedDriverFile.exists()){ + if (selectedDriverFile.exists()) { selected = selectedDriverFile.readText() - if(!File(selected).exists()) { + if (!File(selected).exists()) { selected = "" saveSelected() } @@ -56,13 +56,13 @@ class VulkanDriverViewModel(val activity: MainActivity) { val gson = Gson() - for (folder in folders){ - if(folder.isDirectory && folder.parent == driverFolder.absolutePath){ + for (folder in folders) { + if (folder.isDirectory && folder.parent == driverFolder.absolutePath) { val meta = File(folder.absolutePath + "/meta.json") - if(meta.exists()){ + if (meta.exists()) { val metadata = gson.fromJson(meta.readText(), DriverMetadata::class.java) - if(metadata.name.isNotEmpty()) { + if (metadata.name.isNotEmpty()) { val driver = folder.absolutePath + "/${metadata.libraryName}" metadata.driverPath = driver if (File(driver).exists()) @@ -82,10 +82,10 @@ class VulkanDriverViewModel(val activity: MainActivity) { selectedDriverFile.writeText(selected) } - fun removeSelected(){ - if(selected.isNotEmpty()){ + fun removeSelected() { + if (selected.isNotEmpty()) { val sel = File(selected) - if(sel.exists()) { + if (sel.exists()) { sel.parentFile?.deleteRecursively() } selected = "" @@ -102,12 +102,11 @@ class VulkanDriverViewModel(val activity: MainActivity) { onFileSelected = { requestCode, files -> run { onFileSelected = callBack - if(requestCode == DriverRequestCode) - { + if (requestCode == DriverRequestCode) { val file = files.firstOrNull() file?.apply { val path = file.getAbsolutePath(storage.context) - if (path.isNotEmpty()){ + if (path.isNotEmpty()) { val name = file.name?.removeSuffix("." + file.extension) ?: "" val driverFolder = ensureDriverPath() val extractionFolder = File(driverFolder.absolutePath + "/${name}") @@ -117,14 +116,18 @@ class VulkanDriverViewModel(val activity: MainActivity) { zip.entries().asSequence().forEach { entry -> zip.getInputStream(entry).use { input -> - val filePath = extractionFolder.absolutePath + File.separator + entry.name + val filePath = + extractionFolder.absolutePath + File.separator + entry.name if (!entry.isDirectory) { File(filePath).delete() - val bos = BufferedOutputStream(FileOutputStream(filePath)) + val bos = + BufferedOutputStream(FileOutputStream(filePath)) val bytesIn = ByteArray(4096) var read: Int - while (input.read(bytesIn).also { read = it } != -1) { + while (input.read(bytesIn) + .also { read = it } != -1 + ) { bos.write(bytesIn, 0, read) } bos.close() @@ -144,7 +147,8 @@ class VulkanDriverViewModel(val activity: MainActivity) { } } } - openFilePicker(DriverRequestCode, + openFilePicker( + DriverRequestCode, filterMimeTypes = arrayOf("application/zip") ) } @@ -152,14 +156,14 @@ class VulkanDriverViewModel(val activity: MainActivity) { } data class DriverMetadata( - var schemaVersion : Int = 0, - var name : String = "", - var description : String = "", - var author : String = "", - var packageVersion : String = "", - var vendor : String = "", - var driverVersion : String = "", - var minApi : Int = 0, - var libraryName : String = "", - var driverPath : String = "" + var schemaVersion: Int = 0, + var name: String = "", + var description: String = "", + var author: String = "", + var packageVersion: String = "", + var vendor: String = "", + var driverVersion: String = "", + var minApi: Int = 0, + var libraryName: String = "", + var driverPath: String = "" ) \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/DlcViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/DlcViews.kt index ab442f93d..529033c70 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/DlcViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/DlcViews.kt @@ -50,13 +50,19 @@ class DlcViews { Column(modifier = Modifier.padding(16.dp)) { Column { - Row(modifier = Modifier - .padding(8.dp) - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween) { - Text(text = "DLC for ${name}", textAlign = TextAlign.Center, modifier = Modifier.align( - Alignment.CenterVertically - )) + Row( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "DLC for ${name}", + textAlign = TextAlign.Center, + modifier = Modifier.align( + Alignment.CenterVertically + ) + ) } Surface( modifier = Modifier @@ -65,19 +71,22 @@ class DlcViews { shape = MaterialTheme.shapes.medium ) { - if(refresh.value) { + if (refresh.value) { dlcList.clear() dlcList.addAll(viewModel.getDlc()) refresh.value = false } - LazyColumn(modifier = Modifier - .fillMaxWidth() - .height(400.dp)){ + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .height(400.dp) + ) { items(dlcList) { dlcItem -> dlcItem.apply { - Row(modifier = Modifier - .padding(8.dp) - .fillMaxWidth() + Row( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth() ) { Checkbox( checked = (dlcItem.isEnabled.value), @@ -94,7 +103,8 @@ class DlcViews { viewModel.remove(dlcItem) refresh.value = true }) { - Icon(Icons.Filled.Delete, + Icon( + Icons.Filled.Delete, contentDescription = "remove" ) } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/GameViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/GameViews.kt index d7a883a76..28ece4bc8 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/GameViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/GameViews.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api @@ -80,8 +81,6 @@ class GameViews { Box(modifier = Modifier.fillMaxSize()) { GameStats(mainViewModel) - val ryujinxNative = RyujinxNative.instance - val showController = remember { mutableStateOf(QuickSettings(mainViewModel.activity).useVirtualController) } @@ -128,19 +127,19 @@ class GameViews { when (event.type) { PointerEventType.Press -> { - ryujinxNative.inputSetTouchPoint( + RyujinxNative.jnaInstance.inputSetTouchPoint( position.x.roundToInt(), position.y.roundToInt() ) } PointerEventType.Release -> { - ryujinxNative.inputReleaseTouchPoint() + RyujinxNative.jnaInstance.inputReleaseTouchPoint() } PointerEventType.Move -> { - ryujinxNative.inputSetTouchPoint( + RyujinxNative.jnaInstance.inputSetTouchPoint( position.x.roundToInt(), position.y.roundToInt() ) @@ -209,7 +208,7 @@ class GameViews { IconButton(modifier = Modifier.padding(4.dp), onClick = { showMore.value = false showController.value = !showController.value - ryujinxNative.inputReleaseTouchPoint() + RyujinxNative.jnaInstance.inputReleaseTouchPoint() mainViewModel.controller?.setVisible(showController.value) }) { Icon( @@ -220,7 +219,7 @@ class GameViews { IconButton(modifier = Modifier.padding(4.dp), onClick = { showMore.value = false enableVsync.value = !enableVsync.value - RyujinxNative.instance.graphicsRendererSetVsync( + RyujinxNative.jnaInstance.graphicsRendererSetVsync( enableVsync.value ) }) { @@ -262,10 +261,12 @@ class GameViews { if (progressValue.value > -1) LinearProgressIndicator( + progress = { + progressValue.value + }, modifier = Modifier .fillMaxWidth() .padding(top = 16.dp), - progress = progressValue.value ) else LinearProgressIndicator( @@ -279,7 +280,7 @@ class GameViews { } if (showBackNotice.value) { - AlertDialog(onDismissRequest = { showBackNotice.value = false }) { + BasicAlertDialog(onDismissRequest = { showBackNotice.value = false }) { Column { Surface( modifier = Modifier diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt index ac5c26cf2..51cba4fcc 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/HomeViews.kt @@ -3,8 +3,6 @@ package org.ryujinx.android.views import android.content.res.Resources import android.graphics.BitmapFactory import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.expandVertically -import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.basicMarquee @@ -38,25 +36,22 @@ import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Card -import androidx.compose.material3.CardElevation import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue @@ -69,14 +64,10 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.layout.HorizontalAlignmentLine -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Popup import androidx.navigation.NavHostController import com.anggrayudi.storage.extension.launchOnUiThread import org.ryujinx.android.R @@ -377,7 +368,7 @@ class HomeViews { } if (showLoading.value) { - AlertDialog(onDismissRequest = { }) { + BasicAlertDialog(onDismissRequest = { }) { Card( modifier = Modifier .padding(16.dp) @@ -401,7 +392,7 @@ class HomeViews { } } if (openTitleUpdateDialog.value) { - AlertDialog(onDismissRequest = { + BasicAlertDialog(onDismissRequest = { openTitleUpdateDialog.value = false }) { Surface( @@ -419,7 +410,7 @@ class HomeViews { } } if (openDlcDialog.value) { - AlertDialog(onDismissRequest = { + BasicAlertDialog(onDismissRequest = { openDlcDialog.value = false }) { Surface( @@ -451,14 +442,13 @@ class HomeViews { thread { showLoading.value = true val success = - viewModel.mainViewModel?.loadGame(viewModel.mainViewModel.selected!!) - ?: false + viewModel.mainViewModel.loadGame(viewModel.mainViewModel.selected!!) if (success) { launchOnUiThread { - viewModel.mainViewModel?.navigateToGame() + viewModel.mainViewModel.navigateToGame() } } else { - viewModel.mainViewModel?.selected!!.close() + viewModel.mainViewModel.selected!!.close() } showLoading.value = false } @@ -487,7 +477,7 @@ class HomeViews { }, onClick = { showAppMenu.value = false viewModel.mainViewModel?.clearPptcCache( - viewModel.mainViewModel?.selected?.titleId ?: "" + viewModel.mainViewModel.selected?.titleId ?: "" ) }) DropdownMenuItem(text = { @@ -495,7 +485,7 @@ class HomeViews { }, onClick = { showAppMenu.value = false viewModel.mainViewModel?.purgeShaderCache( - viewModel.mainViewModel?.selected?.titleId ?: "" + viewModel.mainViewModel.selected?.titleId ?: "" ) }) DropdownMenuItem(text = { @@ -548,7 +538,7 @@ class HomeViews { onClick = { if (viewModel.mainViewModel?.selected != null) { showAppActions.value = false - viewModel.mainViewModel?.apply { + viewModel.mainViewModel.apply { selected = null } selectedModel.value = null @@ -639,7 +629,7 @@ class HomeViews { onClick = { if (viewModel.mainViewModel?.selected != null) { showAppActions.value = false - viewModel.mainViewModel?.apply { + viewModel.mainViewModel.apply { selected = null } selectedModel.value = null diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt index e4243a67c..cd994de6f 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt @@ -16,7 +16,7 @@ class MainView { NavHost(navController = navController, startDestination = "home") { composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) } - composable("user") { UserViews.Main(mainViewModel, navController) } + composable("user") { UserViews.Main(mainViewModel) } composable("game") { GameViews.Main() } composable("settings") { SettingViews.Main( diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt index acd93f2b3..b841efb10 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt @@ -29,10 +29,11 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api @@ -192,7 +193,7 @@ class SettingViews { ) settingsViewModel.navController.popBackStack() }) { - Icon(Icons.Filled.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }) }) { contentPadding -> @@ -310,7 +311,7 @@ class SettingViews { } if (showFirwmareDialog.value) { - AlertDialog(onDismissRequest = { + BasicAlertDialog(onDismissRequest = { if (firmwareInstallState.value != FirmwareInstallState.Install) { showFirwmareDialog.value = false settingsViewModel.clearFirmwareSelection(firmwareInstallState) @@ -552,7 +553,6 @@ class SettingViews { Button(onClick = { val storage = MainActivity.StorageHelper storage?.apply { - val s = this.storage val callBack = this.onFileSelected onFileSelected = { requestCode, files -> run { @@ -578,7 +578,7 @@ class SettingViews { } if (showImportWarning.value) { - AlertDialog(onDismissRequest = { + BasicAlertDialog(onDismissRequest = { showImportWarning.value = false importFile.value = null }) { @@ -626,7 +626,7 @@ class SettingViews { } if (showImportCompletion.value) { - AlertDialog(onDismissRequest = { + BasicAlertDialog(onDismissRequest = { showImportCompletion.value = false importFile.value = null mainViewModel.userViewModel.refreshUsers() @@ -739,7 +739,7 @@ class SettingViews { } if (isDriverSelectorOpen.value) { - AlertDialog(onDismissRequest = { + BasicAlertDialog(onDismissRequest = { isDriverSelectorOpen.value = false if (isChanged.value) { @@ -1064,7 +1064,7 @@ class SettingViews { } } - BackHandler() { + BackHandler { settingsViewModel.save( isHostMapped, useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices, diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/UserViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/UserViews.kt index 3cd8591d9..a16d57599 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/UserViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/UserViews.kt @@ -17,7 +17,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -43,10 +43,11 @@ class UserViews { companion object { @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable - fun Main(viewModel: MainViewModel? = null, navController: NavHostController? = null) { + fun Main(viewModel: MainViewModel? = null) { val reload = remember { mutableStateOf(true) } + fun refresh() { viewModel?.userViewModel?.refreshUsers() reload.value = true @@ -64,7 +65,7 @@ class UserViews { IconButton(onClick = { viewModel?.navController?.popBackStack() }) { - Icon(Icons.Filled.ArrowBack, contentDescription = "Back") + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }) }) { contentPadding -> @@ -133,10 +134,14 @@ class UserViews { .fillMaxSize() .padding(4.dp) ) { - if(viewModel?.userViewModel?.userList?.isNotEmpty() == true) { + if (viewModel?.userViewModel?.userList?.isNotEmpty() == true) { items(viewModel.userViewModel.userList) { user -> Image( - bitmap = BitmapFactory.decodeByteArray(user.userPicture, 0, user.userPicture?.size ?: 0) + bitmap = BitmapFactory.decodeByteArray( + user.userPicture, + 0, + user.userPicture?.size ?: 0 + ) .asImageBitmap(), contentDescription = "selected image", contentScale = ContentScale.Crop, @@ -165,6 +170,6 @@ class UserViews { @Preview @Composable fun Preview() { - UserViews.Main() + Main() } }