forked from MeloNX/MeloNX
android - replace jni with jna
This commit is contained in:
parent
8888edde5c
commit
a00c8c909f
@ -1,9 +1,5 @@
|
|||||||
using LibRyujinx.Jni;
|
|
||||||
using LibRyujinx.Jni.Pointers;
|
using LibRyujinx.Jni.Pointers;
|
||||||
using LibRyujinx.Jni.Primitives;
|
|
||||||
using LibRyujinx.Jni.References;
|
using LibRyujinx.Jni.References;
|
||||||
using LibRyujinx.Jni.Values;
|
|
||||||
using Rxmxnx.PInvoke;
|
|
||||||
using Ryujinx.Audio.Backends.OpenAL;
|
using Ryujinx.Audio.Backends.OpenAL;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
@ -18,6 +14,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -99,14 +96,8 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
public delegate IntPtr JniCreateSurface(IntPtr native_surface, IntPtr instance);
|
public delegate IntPtr JniCreateSurface(IntPtr native_surface, IntPtr instance);
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "JNI_OnLoad")]
|
[UnmanagedCallersOnly(EntryPoint = "javaInitialize")]
|
||||||
internal static int LoadLibrary(JavaVMRef vm, IntPtr unknown)
|
public static bool JniInitialize(IntPtr jpathId)
|
||||||
{
|
|
||||||
return 0x00010006; //JNI_VERSION_1_6
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_initialize")]
|
|
||||||
public static JBoolean JniInitialize(JEnvRef jEnv, JObjectLocalRef jObj, JLong jpathId)
|
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
PlatformInfo.IsBionic = true;
|
PlatformInfo.IsBionic = true;
|
||||||
@ -118,7 +109,7 @@ namespace LibRyujinx
|
|||||||
AsyncLogTargetOverflowAction.Block
|
AsyncLogTargetOverflowAction.Block
|
||||||
));
|
));
|
||||||
|
|
||||||
var path = GetStoredString(jpathId);
|
var path = Marshal.PtrToStringAnsi(jpathId);
|
||||||
|
|
||||||
var init = Initialize(path);
|
var init = Initialize(path);
|
||||||
|
|
||||||
@ -138,70 +129,71 @@ namespace LibRyujinx
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceReloadFilesystem")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceReloadFilesystem")]
|
||||||
public static void JniReloadFileSystem()
|
public static void JnaReloadFileSystem()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
SwitchDevice?.ReloadFileSystem();
|
SwitchDevice?.ReloadFileSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceInitialize")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceInitialize")]
|
||||||
public static JBoolean JniInitializeDeviceNative(JEnvRef jEnv,
|
public static bool JnaDeviceInitialize(bool isHostMapped,
|
||||||
JObjectLocalRef jObj,
|
bool useNce,
|
||||||
JBoolean isHostMapped,
|
int systemLanguage,
|
||||||
JBoolean useNce,
|
int regionCode,
|
||||||
JInt systemLanguage,
|
bool enableVsync,
|
||||||
JInt regionCode,
|
bool enableDockedMode,
|
||||||
JBoolean enableVsync,
|
bool enablePtc,
|
||||||
JBoolean enableDockedMode,
|
bool enableInternetAccess,
|
||||||
JBoolean enablePtc,
|
IntPtr timeZonePtr,
|
||||||
JBoolean enableInternetAccess,
|
bool ignoreMissingServices)
|
||||||
JLong timeZoneId,
|
|
||||||
JBoolean ignoreMissingServices)
|
|
||||||
{
|
{
|
||||||
|
debug_break(4);
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
AudioDriver = new OpenALHardwareDeviceDriver();//new OboeHardwareDeviceDriver();
|
AudioDriver = new OpenALHardwareDeviceDriver();
|
||||||
|
|
||||||
|
var timezone = Marshal.PtrToStringAnsi(timeZonePtr);
|
||||||
return InitializeDevice(isHostMapped,
|
return InitializeDevice(isHostMapped,
|
||||||
useNce,
|
useNce,
|
||||||
(SystemLanguage)(int)systemLanguage,
|
(SystemLanguage)systemLanguage,
|
||||||
(RegionCode)(int)regionCode,
|
(RegionCode)regionCode,
|
||||||
enableVsync,
|
enableVsync,
|
||||||
enableDockedMode,
|
enableDockedMode,
|
||||||
enablePtc,
|
enablePtc,
|
||||||
enableInternetAccess,
|
enableInternetAccess,
|
||||||
GetStoredString(timeZoneId),
|
timezone,
|
||||||
ignoreMissingServices);
|
ignoreMissingServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFifo")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetGameFifo")]
|
||||||
public static JDouble JniGetGameFifo(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static double JnaGetGameFifo()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var stats = SwitchDevice.EmulationContext?.Statistics.GetFifoPercent() ?? 0;
|
var stats = SwitchDevice?.EmulationContext?.Statistics.GetFifoPercent() ?? 0;
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFrameTime")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetGameFrameTime")]
|
||||||
public static JDouble JniGetGameFrameTime(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static double JnaGetGameFrameTime()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var stats = SwitchDevice.EmulationContext?.Statistics.GetGameFrameTime() ?? 0;
|
var stats = SwitchDevice?.EmulationContext?.Statistics.GetGameFrameTime() ?? 0;
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameFrameRate")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetGameFrameRate")]
|
||||||
public static JDouble JniGetGameFrameRate(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static double JnaGetGameFrameRate()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var stats = SwitchDevice.EmulationContext?.Statistics.GetGameFrameRate() ?? 0;
|
var stats = SwitchDevice?.EmulationContext?.Statistics.GetGameFrameRate() ?? 0;
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoad")]
|
[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");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
if (SwitchDevice?.EmulationContext == null)
|
if (SwitchDevice?.EmulationContext == null)
|
||||||
@ -214,8 +206,8 @@ namespace LibRyujinx
|
|||||||
return LoadApplication(path);
|
return LoadApplication(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLaunchMiiEditor")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceLaunchMiiEditor")]
|
||||||
public static JBoolean JniLaunchMiiEditApplet(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static bool JNALaunchMiiEditApplet()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
if (SwitchDevice?.EmulationContext == null)
|
if (SwitchDevice?.EmulationContext == null)
|
||||||
@ -226,38 +218,38 @@ namespace LibRyujinx
|
|||||||
return LaunchMiiEditApplet();
|
return LaunchMiiEditApplet();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetDlcContentList")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetDlcContentList")]
|
||||||
public static JArrayLocalRef JniGetDlcContentListNative(JEnvRef jEnv, JObjectLocalRef jObj, JLong pathPtr, JLong titleId)
|
public static IntPtr JniGetDlcContentListNative(IntPtr pathPtr, long titleId)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetDlcTitleId")]
|
||||||
public static JLong JniGetDlcTitleIdNative(JEnvRef jEnv, JObjectLocalRef jObj, JLong pathPtr, JLong ncaPath)
|
public static long JniGetDlcTitleIdNative(IntPtr pathPtr, IntPtr ncaPath)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceSignalEmulationClose")]
|
||||||
public static void JniSignalEmulationCloseNative(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniSignalEmulationCloseNative()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
SignalEmulationClose();
|
SignalEmulationClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceCloseEmulation")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceCloseEmulation")]
|
||||||
public static void JniCloseEmulationNative(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniCloseEmulationNative()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
CloseEmulation();
|
CloseEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoadDescriptor")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceLoadDescriptor")]
|
||||||
public static JBoolean JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JInt type, JInt updateDescriptor)
|
public static bool JnaLoadApplicationNative(int descriptor, int type, int updateDescriptor)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
if (SwitchDevice?.EmulationContext == null)
|
if (SwitchDevice?.EmulationContext == null)
|
||||||
@ -268,17 +260,17 @@ namespace LibRyujinx
|
|||||||
var stream = OpenFile(descriptor);
|
var stream = OpenFile(descriptor);
|
||||||
var update = updateDescriptor == -1 ? null : OpenFile(updateDescriptor);
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceVerifyFirmware")]
|
||||||
public static JLong JniVerifyFirmware(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci)
|
public static IntPtr JniVerifyFirmware(int descriptor, bool isXci)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
|
|
||||||
var stream = OpenFile(descriptor);
|
var stream = OpenFile(descriptor);
|
||||||
|
|
||||||
long stringHandle = -1;
|
IntPtr stringHandle = 0;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -286,19 +278,19 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
if (version != null)
|
if (version != null)
|
||||||
{
|
{
|
||||||
stringHandle = storeString(version.VersionString);
|
stringHandle = Marshal.StringToHGlobalAnsi(version.VersionString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception _)
|
catch(Exception _)
|
||||||
{
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Service, $"Unable to verify firmware. Exception: {_}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return stringHandle;
|
return stringHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceInstallFirmware")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceInstallFirmware")]
|
||||||
public static void JniInstallFirmware(JEnvRef jEnv, JObjectLocalRef jObj, JInt descriptor, JBoolean isXci)
|
public static void JniInstallFirmware(int descriptor, bool isXci)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
|
|
||||||
@ -307,58 +299,47 @@ namespace LibRyujinx
|
|||||||
InstallFirmware(stream, isXci);
|
InstallFirmware(stream, isXci);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetInstalledFirmwareVersion")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetInstalledFirmwareVersion")]
|
||||||
public static JLong JniGetInstalledFirmwareVersion(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static IntPtr JniGetInstalledFirmwareVersion()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
|
|
||||||
var version = GetInstalledFirmwareVersion();
|
var version = GetInstalledFirmwareVersion();
|
||||||
long stringHandle = -1;
|
IntPtr stringHandle = 0;
|
||||||
|
|
||||||
if (version != String.Empty)
|
if (version != String.Empty)
|
||||||
{
|
{
|
||||||
stringHandle = storeString(version);
|
stringHandle = Marshal.StringToHGlobalAnsi(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
return stringHandle;
|
return stringHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsInitialize")]
|
[UnmanagedCallersOnly(EntryPoint = "graphicsInitialize")]
|
||||||
public static JBoolean JniInitializeGraphicsNative(JEnvRef jEnv, JObjectLocalRef jObj, JObjectLocalRef graphicObject)
|
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");
|
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<GetObjectClassDelegate>();
|
|
||||||
var getFieldId = getFieldIdPtr.GetUnsafeDelegate<GetFieldIdDelegate>();
|
|
||||||
var getLongField = getLongFieldPtr.GetUnsafeDelegate<GetLongFieldDelegate>();
|
|
||||||
var getIntField = getIntFieldPtr.GetUnsafeDelegate<GetIntFieldDelegate>();
|
|
||||||
var getBooleanField = getBooleanFieldPtr.GetUnsafeDelegate<GetBooleanFieldDelegate>();
|
|
||||||
var getFloatField = getFloatFieldPtr.GetUnsafeDelegate<GetFloatFieldDelegate>();
|
|
||||||
|
|
||||||
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;
|
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)
|
private static CCharSequence GetCCharSequence(string s)
|
||||||
@ -366,8 +347,8 @@ namespace LibRyujinx
|
|||||||
return Encoding.UTF8.GetBytes(s).AsSpan();
|
return Encoding.UTF8.GetBytes(s).AsSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsSetSurface")]
|
[UnmanagedCallersOnly(EntryPoint = "graphicsSetSurface")]
|
||||||
public static void JniSetSurface(JEnvRef jEnv, JObjectLocalRef jObj, JLong surfacePtr, JLong window)
|
public static void JniSetSurface(long surfacePtr, long window)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
_surfacePtr = surfacePtr;
|
_surfacePtr = surfacePtr;
|
||||||
@ -376,11 +357,10 @@ namespace LibRyujinx
|
|||||||
_surfaceEvent.Set();
|
_surfaceEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsInitializeRenderer")]
|
[UnmanagedCallersOnly(EntryPoint = "graphicsInitializeRenderer")]
|
||||||
public unsafe static JBoolean JniInitializeGraphicsRendererNative(JEnvRef jEnv,
|
public unsafe static bool JnaGraphicsInitializeRenderer(char** extensionsArray,
|
||||||
JObjectLocalRef jObj,
|
int extensionsLength,
|
||||||
JArrayLocalRef extensionsArray,
|
long driverHandle)
|
||||||
JLong driverHandle)
|
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
if (Renderer != null)
|
if (Renderer != null)
|
||||||
@ -388,37 +368,16 @@ namespace LibRyujinx
|
|||||||
return false;
|
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<GetObjectClassDelegate>();
|
|
||||||
var getFieldId = getFieldIdPtr.GetUnsafeDelegate<GetFieldIdDelegate>();
|
|
||||||
var getArrayLength = getArrayLengthPtr.GetUnsafeDelegate<GetArrayLengthDelegate>();
|
|
||||||
var getObjectArrayElement = getObjectArrayElementPtr.GetUnsafeDelegate<GetObjectArrayElementDelegate>();
|
|
||||||
var getLongField = getLongFieldPtr.GetUnsafeDelegate<GetLongFieldDelegate>();
|
|
||||||
var getObjectField = getObjectFieldPtr.GetUnsafeDelegate<GetObjectFieldDelegate>();
|
|
||||||
|
|
||||||
List<string?> extensions = new();
|
List<string?> extensions = new();
|
||||||
|
|
||||||
var count = getArrayLength(jEnv, extensionsArray);
|
for (int i = 0; i < extensionsLength; i++)
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
{
|
||||||
var obj = getObjectArrayElement(jEnv, extensionsArray, i);
|
extensions.Add(Marshal.PtrToStringAnsi((IntPtr)extensionsArray[i]));
|
||||||
var ext = obj.Transform<JObjectLocalRef, JStringLocalRef>();
|
|
||||||
|
|
||||||
extensions.Add(GetString(jEnv, ext));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((long)driverHandle != 0)
|
if (driverHandle != 0)
|
||||||
{
|
{
|
||||||
VulkanLoader = new VulkanLoader((IntPtr)(long)driverHandle);
|
VulkanLoader = new VulkanLoader((IntPtr)driverHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateSurface createSurfaceFunc = instance =>
|
CreateSurface createSurfaceFunc = instance =>
|
||||||
@ -446,36 +405,29 @@ namespace LibRyujinx
|
|||||||
return InitializeGraphicsRenderer(GraphicsBackend.Vulkan, createSurfaceFunc, extensions.ToArray());
|
return InitializeGraphicsRenderer(GraphicsBackend.Vulkan, createSurfaceFunc, extensions.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JArrayLocalRef CreateStringArray(JEnvRef jEnv, List<string> strings)
|
private unsafe static IntPtr CreateStringArray(List<string> strings)
|
||||||
{
|
{
|
||||||
JEnvValue value = jEnv.Environment;
|
uint size = (uint)(Marshal.SizeOf<IntPtr>() * (strings.Count + 1));
|
||||||
ref JNativeInterface jInterface = ref value.Functions;
|
var array = (char**)Marshal.AllocHGlobal((int)size);
|
||||||
IntPtr newObjectArrayPtr = jInterface.NewObjectArrayPointer;
|
Unsafe.InitBlockUnaligned(array, 0, size);
|
||||||
IntPtr findClassPtr = jInterface.FindClassPointer;
|
|
||||||
IntPtr setObjectArrayElementPtr = jInterface.SetObjectArrayElementPointer;
|
|
||||||
|
|
||||||
var newObjectArray = newObjectArrayPtr.GetUnsafeDelegate<NewObjectArrayDelegate>();
|
|
||||||
var findClass = findClassPtr.GetUnsafeDelegate<FindClassDelegate>();
|
|
||||||
var setObjectArrayElement = setObjectArrayElementPtr.GetUnsafeDelegate<SetObjectArrayElementDelegate>();
|
|
||||||
var array = newObjectArray(jEnv, strings.Count, findClass(jEnv, GetCCharSequence("java/lang/String")), CreateString(jEnv, "")._value);
|
|
||||||
|
|
||||||
for (int i = 0; i < strings.Count; i++)
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "graphicsRendererSetSize")]
|
||||||
public static void JniSetRendererSizeNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height)
|
public static void JnaSetRendererSizeNative(int width, int height)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
Renderer?.Window?.SetSize(width, height);
|
Renderer?.Window?.SetSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererRunLoop")]
|
[UnmanagedCallersOnly(EntryPoint = "graphicsRendererRunLoop")]
|
||||||
public static void JniRunLoopNative(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniRunLoopNative()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
SetSwapBuffersCallback(() =>
|
SetSwapBuffersCallback(() =>
|
||||||
@ -487,84 +439,32 @@ namespace LibRyujinx
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_loggingSetEnabled")]
|
[UnmanagedCallersOnly(EntryPoint = "loggingSetEnabled")]
|
||||||
public static void JniSetLoggingEnabledNative(JEnvRef jEnv, JObjectLocalRef jObj, JInt logLevel, JBoolean enabled)
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "deviceGetGameInfo")]
|
||||||
public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef path)
|
public unsafe static void JniGetGameInfo(int fileDescriptor, IntPtr extension, IntPtr infoPtr)
|
||||||
{
|
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
|
||||||
var info = GetGameInfo(GetString(jEnv, path));
|
|
||||||
return GetInfo(jEnv, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfo")]
|
|
||||||
public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JInt fileDescriptor, JLong extension)
|
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
using var stream = OpenFile(fileDescriptor);
|
using var stream = OpenFile(fileDescriptor);
|
||||||
var ext = GetStoredString(extension);
|
var ext = Marshal.PtrToStringAnsi(extension);
|
||||||
var info = GetGameInfo(stream, ext.ToLower());
|
var info = GetGameInfo(stream, ext.ToLower()) ?? GetDefaultInfo(stream);
|
||||||
return GetInfo(jEnv, info);
|
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)
|
[UnmanagedCallersOnly(EntryPoint = "graphicsRendererSetVsync")]
|
||||||
{
|
public static void JnaSetVsyncStateNative(bool enabled)
|
||||||
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<FindClassDelegate>();
|
|
||||||
var newGlobalRef = newGlobalRefPtr.GetUnsafeDelegate<NewGlobalRefDelegate>();
|
|
||||||
var getFieldId = getFieldIdPtr.GetUnsafeDelegate<GetFieldIdDelegate>();
|
|
||||||
var getMethod = getMethodPtr.GetUnsafeDelegate<GetMethodIdDelegate>();
|
|
||||||
var newObject = newObjectPtr.GetUnsafeDelegate<NewObjectDelegate>();
|
|
||||||
var setObjectField = setObjectFieldPtr.GetUnsafeDelegate<SetObjectFieldDelegate>();
|
|
||||||
var setDoubleField = setDoubleFieldPtr.GetUnsafeDelegate<SetDoubleFieldDelegate>();
|
|
||||||
|
|
||||||
var javaClass = findClass(jEnv, javaClassName);
|
|
||||||
var newGlobal = newGlobalRef(jEnv, javaClass._value);
|
|
||||||
var constructor = getMethod(jEnv, javaClass, GetCCharSequence("<init>"), 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<byte>()))._value);
|
|
||||||
setDoubleField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("FileSize"), GetCCharSequence("D")), info?.FileSize ?? 0d);
|
|
||||||
|
|
||||||
return newObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JStringLocalRef CreateString(JEnvRef jEnv, string? s)
|
|
||||||
{
|
|
||||||
s ??= string.Empty;
|
|
||||||
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(s);
|
|
||||||
|
|
||||||
var str = createString(jEnv, ptr);
|
|
||||||
|
|
||||||
Marshal.FreeHGlobal(ptr);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererSetVsync")]
|
|
||||||
public static void JniSetVsyncStateNative(JEnvRef jEnv, JObjectLocalRef jObj, JBoolean enabled)
|
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
SetVsyncState(enabled);
|
SetVsyncState(enabled);
|
||||||
@ -577,196 +477,197 @@ namespace LibRyujinx
|
|||||||
_swapBuffersCallback = Marshal.GetDelegateForFunctionPointer<SwapBuffersCallback>(swapBuffersCallback);
|
_swapBuffersCallback = Marshal.GetDelegateForFunctionPointer<SwapBuffersCallback>(swapBuffersCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputInitialize")]
|
[UnmanagedCallersOnly(EntryPoint = "inputInitialize")]
|
||||||
public static void JniInitializeInput(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height)
|
public static void JnaInitializeInput(int width, int height)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
InitializeInput(width, height);
|
InitializeInput(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetClientSize")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetClientSize")]
|
||||||
public static void JniSetClientSize(JEnvRef jEnv, JObjectLocalRef jObj, JInt width, JInt height)
|
public static void JnaSetClientSize(int width, int height)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
SetClientSize(width, height);
|
SetClientSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetTouchPoint")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetTouchPoint")]
|
||||||
public static void JniSetTouchPoint(JEnvRef jEnv, JObjectLocalRef jObj, JInt x, JInt y)
|
public static void JnaSetTouchPoint(int x, int y)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
SetTouchPoint(x, y);
|
SetTouchPoint(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputReleaseTouchPoint")]
|
[UnmanagedCallersOnly(EntryPoint = "inputReleaseTouchPoint")]
|
||||||
public static void JniReleaseTouchPoint(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JnaReleaseTouchPoint()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
ReleaseTouchPoint();
|
ReleaseTouchPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputUpdate")]
|
[UnmanagedCallersOnly(EntryPoint = "inputUpdate")]
|
||||||
public static void JniUpdateInput(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniUpdateInput()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
UpdateInput();
|
UpdateInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonPressed")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetButtonPressed")]
|
||||||
public static void JniSetButtonPressed(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id)
|
public static void JnaSetButtonPressed(int button, int id)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetButtonReleased")]
|
||||||
public static void JniSetButtonReleased(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id)
|
public static void JnaSetButtonReleased(int button, int id)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetAccelerometerData")]
|
||||||
public static void JniSetAccelerometerData(JEnvRef jEnv, JObjectLocalRef jObj, JFloat x, JFloat y, JFloat z, JInt id)
|
public static void JniSetAccelerometerData(float x, float y, float z, int id)
|
||||||
{
|
{
|
||||||
var accel = new Vector3(x, y, z);
|
var accel = new Vector3(x, y, z);
|
||||||
SetAccelerometerData(accel, id);
|
SetAccelerometerData(accel, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetGyroData")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetGyroData")]
|
||||||
public static void JniSetGyroData(JEnvRef jEnv, JObjectLocalRef jObj, JFloat x, JFloat y, JFloat z, JInt id)
|
public static void JniSetGyroData(float x, float y, float z, int id)
|
||||||
{
|
{
|
||||||
var gryo = new Vector3(x, y, z);
|
var gryo = new Vector3(x, y, z);
|
||||||
SetGryoData(gryo, id);
|
SetGryoData(gryo, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetStickAxis")]
|
[UnmanagedCallersOnly(EntryPoint = "inputSetStickAxis")]
|
||||||
public static void JniSetStickAxis(JEnvRef jEnv, JObjectLocalRef jObj, JInt stick, JFloat x, JFloat y, JInt id)
|
public static void JnaSetStickAxis(int stick, float x, float y, int id)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "inputConnectGamepad")]
|
||||||
public static JInt JniConnectGamepad(JEnvRef jEnv, JObjectLocalRef jObj, JInt index)
|
public static int JnaConnectGamepad(int index)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
return ConnectGamepad(index);
|
return ConnectGamepad(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetOpenedUser")]
|
[UnmanagedCallersOnly(EntryPoint = "userGetOpenedUser")]
|
||||||
public static JLong JniGetOpenedUser(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static IntPtr JniGetOpenedUser()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userId = GetOpenedUser();
|
var userId = GetOpenedUser();
|
||||||
|
var ptr = Marshal.StringToHGlobalAnsi(userId);
|
||||||
|
|
||||||
return storeString(userId);
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserPicture")]
|
[UnmanagedCallersOnly(EntryPoint = "userGetUserPicture")]
|
||||||
public static JLong JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr)
|
public static IntPtr JniGetUserPicture(IntPtr userIdPtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "userSetUserPicture")]
|
||||||
public static void JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef picturePtr)
|
public static void JniGetUserPicture(IntPtr userIdPtr, IntPtr picturePtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userId = GetString(jEnv, userIdPtr) ?? "";
|
var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? "";
|
||||||
var picture = GetString(jEnv, picturePtr) ?? "";
|
var picture = Marshal.PtrToStringAnsi(picturePtr) ?? "";
|
||||||
|
|
||||||
SetUserPicture(userId, picture);
|
SetUserPicture(userId, picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserName")]
|
[UnmanagedCallersOnly(EntryPoint = "userGetUserName")]
|
||||||
public static JLong JniGetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr)
|
public static IntPtr JniGetUserName(IntPtr userIdPtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
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")]
|
[UnmanagedCallersOnly(EntryPoint = "userSetUserName")]
|
||||||
public static void JniSetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef userNamePtr)
|
public static void JniSetUserName(IntPtr userIdPtr, IntPtr userNamePtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userId = GetString(jEnv, userIdPtr) ?? "";
|
var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? "";
|
||||||
var userName = GetString(jEnv, userNamePtr) ?? "";
|
var userName = Marshal.PtrToStringAnsi(userNamePtr) ?? "";
|
||||||
|
|
||||||
SetUserName(userId, userName);
|
SetUserName(userId, userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetAllUsers")]
|
[UnmanagedCallersOnly(EntryPoint = "userGetAllUsers")]
|
||||||
public static JArrayLocalRef JniGetAllUsers(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static IntPtr JniGetAllUsers()
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var users = GetAllUsers();
|
var users = GetAllUsers();
|
||||||
|
|
||||||
return CreateStringArray(jEnv, users.ToList());
|
return CreateStringArray(users.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userAddUser")]
|
[UnmanagedCallersOnly(EntryPoint = "userAddUser")]
|
||||||
public static void JniAddUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userNamePtr, JStringLocalRef picturePtr)
|
public static void JniAddUser(IntPtr userNamePtr, IntPtr picturePtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userName = GetString(jEnv, userNamePtr) ?? "";
|
var userName = Marshal.PtrToStringAnsi(userNamePtr) ?? "";
|
||||||
var picture = GetString(jEnv, picturePtr) ?? "";
|
var picture = Marshal.PtrToStringAnsi(picturePtr) ?? "";
|
||||||
|
|
||||||
AddUser(userName, picture);
|
AddUser(userName, picture);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userDeleteUser")]
|
[UnmanagedCallersOnly(EntryPoint = "userDeleteUser")]
|
||||||
public static void JniDeleteUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
public static void JniDeleteUser(IntPtr userIdPtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userId = GetString(jEnv, userIdPtr) ?? "";
|
var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? "";
|
||||||
|
|
||||||
DeleteUser(userId);
|
DeleteUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerSetup")]
|
[UnmanagedCallersOnly(EntryPoint = "uiHandlerSetup")]
|
||||||
public static void JniSetupUiHandler(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniSetupUiHandler()
|
||||||
{
|
{
|
||||||
SetupUiHandler();
|
SetupUiHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerWait")]
|
[UnmanagedCallersOnly(EntryPoint = "uiHandlerWait")]
|
||||||
public static void JniWaitUiHandler(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniWaitUiHandler()
|
||||||
{
|
{
|
||||||
WaitUiHandler();
|
WaitUiHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerStopWait")]
|
[UnmanagedCallersOnly(EntryPoint = "uiHandlerStopWait")]
|
||||||
public static void JniStopUiHandlerWait(JEnvRef jEnv, JObjectLocalRef jObj)
|
public static void JniStopUiHandlerWait()
|
||||||
{
|
{
|
||||||
StopUiHandlerWait();
|
StopUiHandlerWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_uiHandlerSetResponse")]
|
[UnmanagedCallersOnly(EntryPoint = "uiHandlerSetResponse")]
|
||||||
public static void JniSetUiHandlerResponse(JEnvRef jEnv, JObjectLocalRef jObj, JBoolean isOkPressed, JLong input)
|
public static void JniSetUiHandlerResponse(bool isOkPressed, long input)
|
||||||
{
|
{
|
||||||
SetUiHandlerResponse(isOkPressed, input);
|
SetUiHandlerResponse(isOkPressed, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userOpenUser")]
|
[UnmanagedCallersOnly(EntryPoint = "userOpenUser")]
|
||||||
public static void JniOpenUser(JEnvRef jEnv, JObjectLocalRef jObj, JLong userIdPtr)
|
public static void JniOpenUser(IntPtr userIdPtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userId = GetStoredString(userIdPtr) ?? "";
|
var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? "";
|
||||||
|
|
||||||
OpenUser(userId);
|
OpenUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userCloseUser")]
|
[UnmanagedCallersOnly(EntryPoint = "userCloseUser")]
|
||||||
public static void JniCloseUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
public static void JniCloseUser(IntPtr userIdPtr)
|
||||||
{
|
{
|
||||||
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
Logger.Trace?.Print(LogClass.Application, "Jni Function Call");
|
||||||
var userId = GetString(jEnv, userIdPtr) ?? "";
|
var userId = Marshal.PtrToStringAnsi(userIdPtr) ?? "";
|
||||||
|
|
||||||
CloseUser(userId);
|
CloseUser(userId);
|
||||||
}
|
}
|
||||||
|
@ -121,10 +121,13 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json");
|
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json");
|
||||||
if (File.Exists(addOnContentMetadataPath))
|
if (File.Exists(addOnContentMetadataPath))
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Loader, $"DLC Found : {addOnContentMetadataPath}");
|
||||||
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer);
|
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer);
|
||||||
|
Logger.Info?.Print(LogClass.Loader, $"DLC Found : {addOnContentMetadataPath}. Available: {dlcContainerList.Count}");
|
||||||
|
|
||||||
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Loader, $"DLC Contents Available: {downloadableContentContainer.DownloadableContentNcaList.Count}");
|
||||||
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
|
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
|
||||||
{
|
{
|
||||||
if (File.Exists(downloadableContentContainer.ContainerPath))
|
if (File.Exists(downloadableContentContainer.ContainerPath))
|
||||||
|
@ -11,8 +11,8 @@ android {
|
|||||||
applicationId "org.ryujinx.android"
|
applicationId "org.ryujinx.android"
|
||||||
minSdk 30
|
minSdk 30
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 10030
|
versionCode 10031
|
||||||
versionName '1.0.30t'
|
versionName '1.0.31'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
@ -76,6 +76,7 @@ tasks.named("preBuild") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.14.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'com.google.android.material:material:1.12.0'
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
implementation platform('androidx.compose:compose-bom:2024.05.00')
|
implementation platform('androidx.compose:compose-bom:2024.05.00')
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.ryujinx.android
|
||||||
|
|
||||||
|
enum class BackendThreading {
|
||||||
|
Auto,
|
||||||
|
Off,
|
||||||
|
On
|
||||||
|
}
|
@ -8,6 +8,8 @@ class CrashHandler : UncaughtExceptionHandler {
|
|||||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||||
crashLog += e.toString() + "\n"
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,7 @@ typealias GamePadConfig = RadialGamePadConfig
|
|||||||
class GameController(var activity: Activity) {
|
class GameController(var activity: Activity) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun Create(context: Context, activity: Activity, controller: GameController) : View
|
private fun Create(context: Context, controller: GameController): View {
|
||||||
{
|
|
||||||
val inflator = LayoutInflater.from(context)
|
val inflator = LayoutInflater.from(context)
|
||||||
val view = inflator.inflate(R.layout.game_layout, null)
|
val view = inflator.inflate(R.layout.game_layout, null)
|
||||||
view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(controller.leftGamePad)
|
view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(controller.leftGamePad)
|
||||||
@ -44,12 +43,13 @@ class GameController(var activity: Activity) {
|
|||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Compose(viewModel: MainViewModel): Unit {
|
fun Compose(viewModel: MainViewModel): Unit {
|
||||||
AndroidView(
|
AndroidView(
|
||||||
modifier = Modifier.fillMaxSize(), factory = { context ->
|
modifier = Modifier.fillMaxSize(), factory = { context ->
|
||||||
val controller = GameController(viewModel.activity)
|
val controller = GameController(viewModel.activity)
|
||||||
val c = Create(context, viewModel.activity, controller)
|
val c = Create(context, controller)
|
||||||
viewModel.activity.lifecycleScope.apply {
|
viewModel.activity.lifecycleScope.apply {
|
||||||
viewModel.activity.lifecycleScope.launch {
|
viewModel.activity.lifecycleScope.launch {
|
||||||
val events = merge(
|
val events = merge(
|
||||||
@ -70,7 +70,6 @@ class GameController(var activity: Activity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var ryujinxNative: RyujinxNative
|
|
||||||
private var controllerView: View? = null
|
private var controllerView: View? = null
|
||||||
var leftGamePad: GamePad
|
var leftGamePad: GamePad
|
||||||
var rightGamePad: GamePad
|
var rightGamePad: GamePad
|
||||||
@ -95,8 +94,6 @@ class GameController(var activity: Activity) {
|
|||||||
leftGamePad.gravityY = 1f
|
leftGamePad.gravityY = 1f
|
||||||
rightGamePad.gravityX = 1f
|
rightGamePad.gravityX = 1f
|
||||||
rightGamePad.gravityY = 1f
|
rightGamePad.gravityY = 1f
|
||||||
|
|
||||||
ryujinxNative = RyujinxNative.instance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setVisible(isVisible: Boolean) {
|
fun setVisible(isVisible: Boolean) {
|
||||||
@ -110,12 +107,12 @@ class GameController(var activity: Activity) {
|
|||||||
|
|
||||||
fun connect() {
|
fun connect() {
|
||||||
if (controllerId == -1)
|
if (controllerId == -1)
|
||||||
controllerId = RyujinxNative.instance.inputConnectGamepad(0)
|
controllerId = RyujinxNative.jnaInstance.inputConnectGamepad(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEvent(ev: Event) {
|
private fun handleEvent(ev: Event) {
|
||||||
if (controllerId == -1)
|
if (controllerId == -1)
|
||||||
controllerId = ryujinxNative.inputConnectGamepad(0)
|
controllerId = RyujinxNative.jnaInstance.inputConnectGamepad(0)
|
||||||
|
|
||||||
controllerId.apply {
|
controllerId.apply {
|
||||||
when (ev) {
|
when (ev) {
|
||||||
@ -123,11 +120,11 @@ class GameController(var activity: Activity) {
|
|||||||
val action = ev.action
|
val action = ev.action
|
||||||
when (action) {
|
when (action) {
|
||||||
KeyEvent.ACTION_UP -> {
|
KeyEvent.ACTION_UP -> {
|
||||||
ryujinxNative.inputSetButtonReleased(ev.id, this)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(ev.id, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.ACTION_DOWN -> {
|
KeyEvent.ACTION_DOWN -> {
|
||||||
ryujinxNative.inputSetButtonPressed(ev.id, this)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(ev.id, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,33 +135,79 @@ class GameController(var activity: Activity) {
|
|||||||
when (direction) {
|
when (direction) {
|
||||||
GamePadButtonInputId.DpadUp.ordinal -> {
|
GamePadButtonInputId.DpadUp.ordinal -> {
|
||||||
if (ev.xAxis > 0) {
|
if (ev.xAxis > 0) {
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
GamePadButtonInputId.DpadRight.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadLeft.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
} else if (ev.xAxis < 0) {
|
} else if (ev.xAxis < 0) {
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
GamePadButtonInputId.DpadLeft.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadRight.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
|
GamePadButtonInputId.DpadLeft.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadRight.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (ev.yAxis < 0) {
|
if (ev.yAxis < 0) {
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadDown.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
} else if (ev.yAxis > 0) {
|
} else if (ev.yAxis > 0) {
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
GamePadButtonInputId.DpadDown.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
|
GamePadButtonInputId.DpadDown.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GamePadButtonInputId.LeftStick.ordinal -> {
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
ryujinxNative.inputSetStickAxis(1, ev.xAxis, -ev.yAxis ,this)
|
RyujinxNative.jnaInstance.inputSetStickAxis(
|
||||||
|
1,
|
||||||
|
ev.xAxis,
|
||||||
|
-ev.yAxis,
|
||||||
|
this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
GamePadButtonInputId.RightStick.ordinal -> {
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
ryujinxNative.inputSetStickAxis(2, ev.xAxis, -ev.yAxis ,this)
|
RyujinxNative.jnaInstance.inputSetStickAxis(
|
||||||
|
2,
|
||||||
|
ev.xAxis,
|
||||||
|
-ev.yAxis,
|
||||||
|
this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,6 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||||||
private var _isStarted: Boolean = false
|
private var _isStarted: Boolean = false
|
||||||
private val nativeWindow: NativeWindow
|
private val nativeWindow: NativeWindow
|
||||||
|
|
||||||
private var _nativeRyujinx: RyujinxNative = RyujinxNative.instance
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
holder.addCallback(this)
|
holder.addCallback(this)
|
||||||
|
|
||||||
@ -47,21 +45,21 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||||||
|
|
||||||
if (_width != width || _height != height) {
|
if (_width != width || _height != height) {
|
||||||
val window = nativeWindow.requeryWindowHandle()
|
val window = nativeWindow.requeryWindowHandle()
|
||||||
_nativeRyujinx.graphicsSetSurface(window, nativeWindow.nativePointer)
|
RyujinxNative.jnaInstance.graphicsSetSurface(window, nativeWindow.nativePointer)
|
||||||
|
|
||||||
nativeWindow.swapInterval = 0;
|
nativeWindow.swapInterval = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
_width = width
|
_width = width
|
||||||
_height = height
|
_height = height
|
||||||
|
|
||||||
_nativeRyujinx.graphicsRendererSetSize(
|
RyujinxNative.jnaInstance.graphicsRendererSetSize(
|
||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
)
|
)
|
||||||
|
|
||||||
if (_isStarted) {
|
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
|
_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()
|
val id = mainViewModel.physicalControllerManager?.connect()
|
||||||
mainViewModel.motionSensorManager?.setControllerId(id ?: -1)
|
mainViewModel.motionSensorManager?.setControllerId(id ?: -1)
|
||||||
|
|
||||||
_nativeRyujinx.graphicsRendererSetSize(
|
RyujinxNative.jnaInstance.graphicsRendererSetSize(
|
||||||
surfaceHolder.surfaceFrame.width(),
|
surfaceHolder.surfaceFrame.width(),
|
||||||
surfaceHolder.surfaceFrame.height()
|
surfaceHolder.surfaceFrame.height()
|
||||||
)
|
)
|
||||||
@ -108,12 +106,12 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||||||
var c = 0
|
var c = 0
|
||||||
val helper = NativeHelpers.instance
|
val helper = NativeHelpers.instance
|
||||||
while (_isStarted) {
|
while (_isStarted) {
|
||||||
_nativeRyujinx.inputUpdate()
|
RyujinxNative.jnaInstance.inputUpdate()
|
||||||
Thread.sleep(1)
|
Thread.sleep(1)
|
||||||
|
|
||||||
showLoading?.apply {
|
showLoading?.apply {
|
||||||
if (value) {
|
if (value) {
|
||||||
var value = helper.getProgressValue()
|
val value = helper.getProgressValue()
|
||||||
|
|
||||||
if (value != -1f)
|
if (value != -1f)
|
||||||
progress?.apply {
|
progress?.apply {
|
||||||
@ -134,9 +132,9 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||||||
}
|
}
|
||||||
c = 0
|
c = 0
|
||||||
mainViewModel.updateStats(
|
mainViewModel.updateStats(
|
||||||
_nativeRyujinx.deviceGetGameFifo(),
|
RyujinxNative.jnaInstance.deviceGetGameFifo(),
|
||||||
_nativeRyujinx.deviceGetGameFrameRate(),
|
RyujinxNative.jnaInstance.deviceGetGameFrameRate(),
|
||||||
_nativeRyujinx.deviceGetGameFrameTime()
|
RyujinxNative.jnaInstance.deviceGetGameFrameTime()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +146,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su
|
|||||||
thread {
|
thread {
|
||||||
mainViewModel.activity.uiHandler.listen()
|
mainViewModel.activity.uiHandler.listen()
|
||||||
}
|
}
|
||||||
_nativeRyujinx.graphicsRendererRunLoop()
|
RyujinxNative.jnaInstance.graphicsRendererRunLoop()
|
||||||
|
|
||||||
game?.close()
|
game?.close()
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package org.ryujinx.android
|
|||||||
|
|
||||||
enum class GamePadButtonInputId {
|
enum class GamePadButtonInputId {
|
||||||
None,
|
None,
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -80,9 +80,9 @@ class Helpers {
|
|||||||
currentProgressName: MutableState<String>,
|
currentProgressName: MutableState<String>,
|
||||||
finish: () -> Unit
|
finish: () -> Unit
|
||||||
) {
|
) {
|
||||||
var fPath = path + "/${file.name}";
|
var fPath = path + "/${file.name}"
|
||||||
var callback: FileCallback? = object : FileCallback() {
|
var callback: FileCallback? = object : FileCallback() {
|
||||||
override fun onFailed(errorCode: FileCallback.ErrorCode) {
|
override fun onFailed(errorCode: ErrorCode) {
|
||||||
super.onFailed(errorCode)
|
super.onFailed(errorCode)
|
||||||
File(fPath).delete()
|
File(fPath).delete()
|
||||||
finish()
|
finish()
|
||||||
@ -189,7 +189,6 @@ class Helpers {
|
|||||||
dir.mkdirs()
|
dir.mkdirs()
|
||||||
}
|
}
|
||||||
ZipInputStream(stream).use { zip ->
|
ZipInputStream(stream).use { zip ->
|
||||||
var count = 0
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val header = zip.nextEntry ?: break
|
val header = zip.nextEntry ?: break
|
||||||
if (!folders.any { header.fileName.startsWith(it) }) {
|
if (!folders.any { header.fileName.startsWith(it) }) {
|
||||||
@ -217,7 +216,7 @@ class Helpers {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isImporting.value = false
|
isImporting.value = false
|
||||||
RyujinxNative.instance.deviceReloadFilesystem()
|
RyujinxNative.jnaInstance.deviceReloadFilesystem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun applets(color: Color): ImageVector {
|
fun applets(color: Color): ImageVector {
|
||||||
return remember {
|
return remember {
|
||||||
@ -331,6 +332,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun playArrow(color: Color): ImageVector {
|
fun playArrow(color: Color): ImageVector {
|
||||||
return remember {
|
return remember {
|
||||||
@ -372,6 +374,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun folderOpen(color: Color): ImageVector {
|
fun folderOpen(color: Color): ImageVector {
|
||||||
return remember {
|
return remember {
|
||||||
@ -437,6 +440,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun gameUpdate(): ImageVector {
|
fun gameUpdate(): ImageVector {
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
@ -514,6 +518,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun download(): ImageVector {
|
fun download(): ImageVector {
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
@ -583,6 +588,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun vSync(): ImageVector {
|
fun vSync(): ImageVector {
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
@ -653,6 +659,7 @@ class Icons {
|
|||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun videoGame(): ImageVector {
|
fun videoGame(): ImageVector {
|
||||||
val primaryColor = MaterialTheme.colorScheme.primary
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
@ -9,6 +9,7 @@ import java.net.URLConnection
|
|||||||
|
|
||||||
class Logging(private var viewModel: MainViewModel) {
|
class Logging(private var viewModel: MainViewModel) {
|
||||||
val logPath = MainActivity.AppPath + "/Logs"
|
val logPath = MainActivity.AppPath + "/Logs"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
File(logPath).mkdirs()
|
File(logPath).mkdirs()
|
||||||
}
|
}
|
||||||
@ -31,12 +32,16 @@ class Logging(private var viewModel: MainViewModel) {
|
|||||||
}
|
}
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
val zip = File(zipExportPath)
|
val zip = File(zipExportPath)
|
||||||
val uri = FileProvider.getUriForFile(viewModel.activity, viewModel.activity.packageName + ".fileprovider", zip)
|
val uri = FileProvider.getUriForFile(
|
||||||
|
viewModel.activity,
|
||||||
|
viewModel.activity.packageName + ".fileprovider",
|
||||||
|
zip
|
||||||
|
)
|
||||||
val intent = Intent(Intent.ACTION_SEND)
|
val intent = Intent(Intent.ACTION_SEND)
|
||||||
intent.putExtra(Intent.EXTRA_STREAM, uri)
|
intent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
intent.setDataAndType(uri, URLConnection.guessContentTypeFromName(zip.name))
|
intent.setDataAndType(uri, URLConnection.guessContentTypeFromName(zip.name))
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
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)
|
viewModel.activity.startActivity(chooser)
|
||||||
} else {
|
} else {
|
||||||
File(zipExportPath).delete()
|
File(zipExportPath).delete()
|
||||||
|
@ -42,7 +42,7 @@ class MainActivity : BaseActivity() {
|
|||||||
fun frameEnded(gameTime: Long) {
|
fun frameEnded(gameTime: Long) {
|
||||||
mainViewModel?.activity?.apply {
|
mainViewModel?.activity?.apply {
|
||||||
if (isActive && QuickSettings(this).enablePerformanceMode) {
|
if (isActive && QuickSettings(this).enablePerformanceMode) {
|
||||||
mainViewModel?.performanceManager?.setTurboMode(true);
|
mainViewModel?.performanceManager?.setTurboMode(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mainViewModel?.gameHost?.hideProgressIndicator()
|
mainViewModel?.gameHost?.hideProgressIndicator()
|
||||||
@ -65,40 +65,40 @@ class MainActivity : BaseActivity() {
|
|||||||
val appPath: String = AppPath
|
val appPath: String = AppPath
|
||||||
|
|
||||||
var quickSettings = QuickSettings(this)
|
var quickSettings = QuickSettings(this)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Debug.ordinal,
|
LogLevel.Debug.ordinal,
|
||||||
quickSettings.enableDebugLogs
|
quickSettings.enableDebugLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Info.ordinal,
|
LogLevel.Info.ordinal,
|
||||||
quickSettings.enableInfoLogs
|
quickSettings.enableInfoLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Stub.ordinal,
|
LogLevel.Stub.ordinal,
|
||||||
quickSettings.enableStubLogs
|
quickSettings.enableStubLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Warning.ordinal,
|
LogLevel.Warning.ordinal,
|
||||||
quickSettings.enableWarningLogs
|
quickSettings.enableWarningLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Error.ordinal,
|
LogLevel.Error.ordinal,
|
||||||
quickSettings.enableErrorLogs
|
quickSettings.enableErrorLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.AccessLog.ordinal,
|
LogLevel.AccessLog.ordinal,
|
||||||
quickSettings.enableAccessLogs
|
quickSettings.enableAccessLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Guest.ordinal,
|
LogLevel.Guest.ordinal,
|
||||||
quickSettings.enableGuestLogs
|
quickSettings.enableGuestLogs
|
||||||
)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
LogLevel.Trace.ordinal,
|
LogLevel.Trace.ordinal,
|
||||||
quickSettings.enableTraceLogs
|
quickSettings.enableTraceLogs
|
||||||
)
|
)
|
||||||
val success =
|
val success =
|
||||||
RyujinxNative.instance.initialize(NativeHelpers.instance.storeStringJava(appPath))
|
RyujinxNative.jnaInstance.javaInitialize(appPath)
|
||||||
|
|
||||||
uiHandler = UiHandler()
|
uiHandler = UiHandler()
|
||||||
_isInit = success
|
_isInit = success
|
||||||
|
@ -17,6 +17,7 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
|
|||||||
|
|
||||||
private val motionGyroOrientation: FloatArray = FloatArray(3)
|
private val motionGyroOrientation: FloatArray = FloatArray(3)
|
||||||
private val motionAcelOrientation: FloatArray = FloatArray(3)
|
private val motionAcelOrientation: FloatArray = FloatArray(3)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
|
||||||
gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
|
gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
|
||||||
@ -27,6 +28,7 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
|
|||||||
isWithinOrientationRange(orientation, 270) -> {
|
isWithinOrientationRange(orientation, 270) -> {
|
||||||
setOrientation270()
|
setOrientation270()
|
||||||
}
|
}
|
||||||
|
|
||||||
isWithinOrientationRange(orientation, 90) -> {
|
isWithinOrientationRange(orientation, 90) -> {
|
||||||
setOrientation90()
|
setOrientation90()
|
||||||
}
|
}
|
||||||
@ -50,6 +52,7 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
|
|||||||
motionAcelOrientation[1] = -1.0f
|
motionAcelOrientation[1] = -1.0f
|
||||||
motionAcelOrientation[2] = -1.0f
|
motionAcelOrientation[2] = -1.0f
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOrientation90() {
|
fun setOrientation90() {
|
||||||
motionGyroOrientation[0] = 1.0f
|
motionGyroOrientation[0] = 1.0f
|
||||||
motionGyroOrientation[1] = -1.0f
|
motionGyroOrientation[1] = -1.0f
|
||||||
@ -67,13 +70,21 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
|
|||||||
if (isRegistered)
|
if (isRegistered)
|
||||||
return
|
return
|
||||||
gyro?.apply {
|
gyro?.apply {
|
||||||
sensorManager.registerListener(this@MotionSensorManager, gyro, SensorManager.SENSOR_DELAY_GAME)
|
sensorManager.registerListener(
|
||||||
|
this@MotionSensorManager,
|
||||||
|
gyro,
|
||||||
|
SensorManager.SENSOR_DELAY_GAME
|
||||||
|
)
|
||||||
}
|
}
|
||||||
accelerometer?.apply {
|
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() {
|
||||||
@ -81,8 +92,8 @@ class MotionSensorManager(val activity: MainActivity) : SensorEventListener2 {
|
|||||||
isRegistered = false
|
isRegistered = false
|
||||||
|
|
||||||
if (controllerId != -1) {
|
if (controllerId != -1) {
|
||||||
RyujinxNative.instance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId)
|
RyujinxNative.jnaInstance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
RyujinxNative.instance.inputSetGyroData(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 y = motionAcelOrientation[1] * event.values[0]
|
||||||
val z = motionAcelOrientation[2] * event.values[2]
|
val z = motionAcelOrientation[2] * event.values[2]
|
||||||
|
|
||||||
RyujinxNative.instance.inputSetAccelerometerData(x, y, z, controllerId)
|
RyujinxNative.jnaInstance.inputSetAccelerometerData(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Sensor.TYPE_GYROSCOPE -> {
|
Sensor.TYPE_GYROSCOPE -> {
|
||||||
val x = motionGyroOrientation[0] * event.values[1]
|
val x = motionGyroOrientation[0] * event.values[1]
|
||||||
val y = motionGyroOrientation[1] * event.values[0]
|
val y = motionGyroOrientation[1] * event.values[0]
|
||||||
val z = motionGyroOrientation[2] * event.values[2]
|
val z = motionGyroOrientation[2] * event.values[2]
|
||||||
RyujinxNative.instance.inputSetGyroData(x, y, z, controllerId)
|
RyujinxNative.jnaInstance.inputSetGyroData(x, y, z, controllerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RyujinxNative.instance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId)
|
RyujinxNative.jnaInstance.inputSetAccelerometerData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
RyujinxNative.instance.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId)
|
RyujinxNative.jnaInstance.inputSetGyroData(0.0F, 0.0F, 0.0F, controllerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ class NativeHelpers {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val instance = NativeHelpers()
|
val instance = NativeHelpers()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
System.loadLibrary("ryujinxjni")
|
System.loadLibrary("ryujinxjni")
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import android.view.SurfaceView
|
|||||||
|
|
||||||
class NativeWindow(val surface: SurfaceView) {
|
class NativeWindow(val surface: SurfaceView) {
|
||||||
var nativePointer: Long
|
var nativePointer: Long
|
||||||
var nativeHelpers: NativeHelpers = NativeHelpers.instance
|
private val nativeHelpers: NativeHelpers = NativeHelpers.instance
|
||||||
private var _swapInterval: Int = 0
|
private var _swapInterval: Int = 0
|
||||||
|
|
||||||
var maxSwapInterval: Int = 0
|
var maxSwapInterval: Int = 0
|
||||||
@ -27,7 +27,7 @@ class NativeWindow(val surface: SurfaceView) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface);
|
nativePointer = nativeHelpers.getNativeWindow(surface.holder.surface)
|
||||||
|
|
||||||
swapInterval = maxOf(1, minSwapInterval)
|
swapInterval = maxOf(1, minSwapInterval)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class PerformanceMonitor {
|
|||||||
fun getFrequencies(): List<Double> {
|
fun getFrequencies(): List<Double> {
|
||||||
val frequencies = mutableListOf<Double>()
|
val frequencies = mutableListOf<Double>()
|
||||||
for (i in 0..<numberOfCores) {
|
for (i in 0..<numberOfCores) {
|
||||||
var freq = 0.0;
|
var freq = 0.0
|
||||||
try {
|
try {
|
||||||
val reader = RandomAccessFile(
|
val reader = RandomAccessFile(
|
||||||
"/sys/devices/system/cpu/cpu${i}/cpufreq/scaling_cur_freq",
|
"/sys/devices/system/cpu/cpu${i}/cpufreq/scaling_cur_freq",
|
||||||
@ -27,8 +27,7 @@ class PerformanceMonitor {
|
|||||||
val f = reader.readLine()
|
val f = reader.readLine()
|
||||||
reader.close()
|
reader.close()
|
||||||
freq = f.toDouble() / 1000.0
|
freq = f.toDouble() / 1000.0
|
||||||
}
|
} catch (e: Exception) {
|
||||||
catch (e:Exception){
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import org.ryujinx.android.viewmodels.QuickSettings
|
|||||||
|
|
||||||
class PhysicalControllerManager(val activity: MainActivity) {
|
class PhysicalControllerManager(val activity: MainActivity) {
|
||||||
private var controllerId: Int = -1
|
private var controllerId: Int = -1
|
||||||
private var ryujinxNative: RyujinxNative = RyujinxNative.instance
|
|
||||||
|
|
||||||
fun onKeyEvent(event: KeyEvent): Boolean {
|
fun onKeyEvent(event: KeyEvent): Boolean {
|
||||||
val id = getGamePadButtonInputId(event.keyCode)
|
val id = getGamePadButtonInputId(event.keyCode)
|
||||||
@ -16,16 +15,15 @@ class PhysicalControllerManager(val activity: MainActivity) {
|
|||||||
if (/*controllerId != -1 &&*/ isNotFallback) {
|
if (/*controllerId != -1 &&*/ isNotFallback) {
|
||||||
when (event.action) {
|
when (event.action) {
|
||||||
KeyEvent.ACTION_UP -> {
|
KeyEvent.ACTION_UP -> {
|
||||||
ryujinxNative.inputSetButtonReleased(id.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(id.ordinal, controllerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.ACTION_DOWN -> {
|
KeyEvent.ACTION_DOWN -> {
|
||||||
ryujinxNative.inputSetButtonPressed(id.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(id.ordinal, controllerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
} else if (!isNotFallback) {
|
||||||
else if(!isNotFallback){
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,8 +38,18 @@ class PhysicalControllerManager(val activity: MainActivity) {
|
|||||||
val leftStickY = ev.getAxisValue(MotionEvent.AXIS_Y)
|
val leftStickY = ev.getAxisValue(MotionEvent.AXIS_Y)
|
||||||
val rightStickX = ev.getAxisValue(MotionEvent.AXIS_Z)
|
val rightStickX = ev.getAxisValue(MotionEvent.AXIS_Z)
|
||||||
val rightStickY = ev.getAxisValue(MotionEvent.AXIS_RZ)
|
val rightStickY = ev.getAxisValue(MotionEvent.AXIS_RZ)
|
||||||
ryujinxNative.inputSetStickAxis(1, leftStickX, -leftStickY ,controllerId)
|
RyujinxNative.jnaInstance.inputSetStickAxis(
|
||||||
ryujinxNative.inputSetStickAxis(2, rightStickX, -rightStickY ,controllerId)
|
1,
|
||||||
|
leftStickX,
|
||||||
|
-leftStickY,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetStickAxis(
|
||||||
|
2,
|
||||||
|
rightStickX,
|
||||||
|
-rightStickY,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
|
||||||
ev.device?.apply {
|
ev.device?.apply {
|
||||||
if (sources and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD) {
|
if (sources and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD) {
|
||||||
@ -49,30 +57,66 @@ class PhysicalControllerManager(val activity: MainActivity) {
|
|||||||
val dPadHor = ev.getAxisValue(MotionEvent.AXIS_HAT_X)
|
val dPadHor = ev.getAxisValue(MotionEvent.AXIS_HAT_X)
|
||||||
val dPadVert = ev.getAxisValue(MotionEvent.AXIS_HAT_Y)
|
val dPadVert = ev.getAxisValue(MotionEvent.AXIS_HAT_Y)
|
||||||
if (dPadVert == 0.0f) {
|
if (dPadVert == 0.0f) {
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, controllerId)
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadDown.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (dPadHor == 0.0f) {
|
if (dPadHor == 0.0f) {
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, controllerId)
|
GamePadButtonInputId.DpadLeft.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadRight.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dPadVert < 0.0f) {
|
if (dPadVert < 0.0f) {
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, controllerId)
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadDown.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (dPadHor < 0.0f) {
|
if (dPadHor < 0.0f) {
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, controllerId)
|
GamePadButtonInputId.DpadLeft.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
|
GamePadButtonInputId.DpadRight.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dPadVert > 0.0f) {
|
if (dPadVert > 0.0f) {
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, controllerId)
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
|
GamePadButtonInputId.DpadDown.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (dPadHor > 0.0f) {
|
if (dPadHor > 0.0f) {
|
||||||
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, controllerId)
|
RyujinxNative.jnaInstance.inputSetButtonReleased(
|
||||||
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, controllerId)
|
GamePadButtonInputId.DpadLeft.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
|
RyujinxNative.jnaInstance.inputSetButtonPressed(
|
||||||
|
GamePadButtonInputId.DpadRight.ordinal,
|
||||||
|
controllerId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +125,7 @@ class PhysicalControllerManager(val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun connect(): Int {
|
fun connect(): Int {
|
||||||
controllerId = ryujinxNative.inputConnectGamepad(0)
|
controllerId = RyujinxNative.jnaInstance.inputConnectGamepad(0)
|
||||||
return controllerId
|
return controllerId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ class RyujinxApplication : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
fun getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: RyujinxApplication
|
lateinit var instance: RyujinxApplication
|
||||||
private set
|
private set
|
||||||
|
@ -1,20 +1,11 @@
|
|||||||
package org.ryujinx.android
|
package org.ryujinx.android
|
||||||
|
|
||||||
|
import com.sun.jna.Library
|
||||||
|
import com.sun.jna.Native
|
||||||
import org.ryujinx.android.viewmodels.GameInfo
|
import org.ryujinx.android.viewmodels.GameInfo
|
||||||
|
|
||||||
@Suppress("KotlinJniMissingFunction")
|
interface RyujinxNativeJna : Library {
|
||||||
class RyujinxNative {
|
fun deviceInitialize(
|
||||||
external fun initialize(appPath: Long): Boolean
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val instance: RyujinxNative = RyujinxNative()
|
|
||||||
|
|
||||||
init {
|
|
||||||
System.loadLibrary("ryujinx")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
external fun deviceInitialize(
|
|
||||||
isHostMapped: Boolean, useNce: Boolean,
|
isHostMapped: Boolean, useNce: Boolean,
|
||||||
systemLanguage: Int,
|
systemLanguage: Int,
|
||||||
regionCode: Int,
|
regionCode: Int,
|
||||||
@ -22,60 +13,81 @@ class RyujinxNative {
|
|||||||
enableDockedMode: Boolean,
|
enableDockedMode: Boolean,
|
||||||
enablePtc: Boolean,
|
enablePtc: Boolean,
|
||||||
enableInternetAccess: Boolean,
|
enableInternetAccess: Boolean,
|
||||||
timeZone: Long,
|
timeZone: String,
|
||||||
ignoreMissingServices: Boolean
|
ignoreMissingServices: Boolean
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
external fun graphicsInitialize(configuration: GraphicsConfiguration): Boolean
|
fun graphicsInitialize(
|
||||||
external fun graphicsInitializeRenderer(
|
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<String>,
|
extensions: Array<String>,
|
||||||
|
extensionsLength: Int,
|
||||||
driver: Long
|
driver: Long
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
external fun deviceLoad(game: String): Boolean
|
fun javaInitialize(appPath: String): Boolean
|
||||||
external fun deviceLaunchMiiEditor(): Boolean
|
fun deviceLaunchMiiEditor(): Boolean
|
||||||
external fun deviceGetGameFrameRate(): Double
|
fun deviceGetGameFrameRate(): Double
|
||||||
external fun deviceGetGameFrameTime(): Double
|
fun deviceGetGameFrameTime(): Double
|
||||||
external fun deviceGetGameFifo(): Double
|
fun deviceGetGameFifo(): Double
|
||||||
external fun deviceGetGameInfo(fileDescriptor: Int, extension: Long): GameInfo
|
fun deviceLoadDescriptor(fileDescriptor: Int, gameType: Int, updateDescriptor: Int): Boolean
|
||||||
external fun deviceGetGameInfoFromPath(path: String): GameInfo
|
fun graphicsRendererSetSize(width: Int, height: Int)
|
||||||
external fun deviceLoadDescriptor(fileDescriptor: Int, gameType: Int, updateDescriptor: Int): Boolean
|
fun graphicsRendererSetVsync(enabled: Boolean)
|
||||||
external fun graphicsRendererSetSize(width: Int, height: Int)
|
fun graphicsRendererRunLoop()
|
||||||
external fun graphicsRendererSetVsync(enabled: Boolean)
|
fun deviceReloadFilesystem()
|
||||||
external fun graphicsRendererRunLoop()
|
fun inputInitialize(width: Int, height: Int)
|
||||||
external fun deviceReloadFilesystem()
|
fun inputSetClientSize(width: Int, height: Int)
|
||||||
external fun inputInitialize(width: Int, height: Int)
|
fun inputSetTouchPoint(x: Int, y: Int)
|
||||||
external fun inputSetClientSize(width: Int, height: Int)
|
fun inputReleaseTouchPoint()
|
||||||
external fun inputSetTouchPoint(x: Int, y: Int)
|
fun inputUpdate()
|
||||||
external fun inputReleaseTouchPoint()
|
fun inputSetButtonPressed(button: Int, id: Int)
|
||||||
external fun inputUpdate()
|
fun inputSetButtonReleased(button: Int, id: Int)
|
||||||
external fun inputSetButtonPressed(button: Int, id: Int)
|
fun inputConnectGamepad(index: Int): Int
|
||||||
external fun inputSetButtonReleased(button: Int, id: Int)
|
fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int)
|
||||||
external fun inputConnectGamepad(index: Int): Int
|
fun inputSetAccelerometerData(x: Float, y: Float, z: Float, id: Int)
|
||||||
external fun inputSetStickAxis(stick: Int, x: Float, y: Float, id: Int)
|
fun inputSetGyroData(x: Float, y: Float, z: Float, id: Int)
|
||||||
external fun inputSetAccelerometerData(x: Float, y: Float, z: Float, id: Int)
|
fun graphicsSetSurface(surface: Long, window: Long)
|
||||||
external fun inputSetGyroData(x: Float, y: Float, z: Float, id: Int)
|
fun deviceCloseEmulation()
|
||||||
external fun graphicsSetSurface(surface: Long, window: Long)
|
fun deviceSignalEmulationClose()
|
||||||
external fun deviceCloseEmulation()
|
fun userGetOpenedUser(): String
|
||||||
external fun deviceSignalEmulationClose()
|
fun userGetUserPicture(userId: String): String
|
||||||
external fun deviceGetDlcTitleId(path: Long, ncaPath: Long): Long
|
fun userSetUserPicture(userId: String, picture: String)
|
||||||
external fun deviceGetDlcContentList(path: Long, titleId: Long): Array<String>
|
fun userGetUserName(userId: String): String
|
||||||
external fun userGetOpenedUser(): Long
|
fun userSetUserName(userId: String, userName: String)
|
||||||
external fun userGetUserPicture(userId: Long): Long
|
fun userAddUser(username: String, picture: String)
|
||||||
external fun userSetUserPicture(userId: String, picture: String)
|
fun userDeleteUser(userId: String)
|
||||||
external fun userGetUserName(userId: Long): Long
|
fun userOpenUser(userId: String)
|
||||||
external fun userSetUserName(userId: String, userName: String)
|
fun userCloseUser(userId: String)
|
||||||
external fun userGetAllUsers(): Array<String>
|
fun loggingSetEnabled(logLevel: Int, enabled: Boolean)
|
||||||
external fun userAddUser(username: String, picture: String)
|
fun deviceVerifyFirmware(fileDescriptor: Int, isXci: Boolean): String
|
||||||
external fun userDeleteUser(userId: String)
|
fun deviceInstallFirmware(fileDescriptor: Int, isXci: Boolean)
|
||||||
external fun userOpenUser(userId: Long)
|
fun deviceGetInstalledFirmwareVersion(): String
|
||||||
external fun userCloseUser(userId: String)
|
fun uiHandlerSetup()
|
||||||
external fun loggingSetEnabled(logLevel: Int, enabled: Boolean)
|
fun uiHandlerWait()
|
||||||
external fun deviceVerifyFirmware(fileDescriptor: Int, isXci: Boolean): Long
|
fun uiHandlerStopWait()
|
||||||
external fun deviceInstallFirmware(fileDescriptor: Int, isXci: Boolean)
|
fun uiHandlerSetResponse(isOkPressed: Boolean, input: Long)
|
||||||
external fun deviceGetInstalledFirmwareVersion() : Long
|
fun deviceGetDlcTitleId(path: String, ncaPath: String): String
|
||||||
external fun uiHandlerSetup()
|
fun deviceGetGameInfo(fileDescriptor: Int, extension: String, info: GameInfo)
|
||||||
external fun uiHandlerWait()
|
fun userGetAllUsers(): Array<String>
|
||||||
external fun uiHandlerStopWait()
|
fun deviceGetDlcContentList(path: String, titleId: Long): Array<String>
|
||||||
external fun uiHandlerSetResponse(isOkPressed: Boolean, input: Long)
|
}
|
||||||
|
|
||||||
|
class RyujinxNative {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val jnaInstance: RyujinxNativeJna = Native.load(
|
||||||
|
"ryujinx",
|
||||||
|
RyujinxNativeJna::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@ -50,13 +51,13 @@ class UiHandler {
|
|||||||
var shouldListen = true
|
var shouldListen = true
|
||||||
|
|
||||||
init {
|
init {
|
||||||
RyujinxNative.instance.uiHandlerSetup()
|
RyujinxNative.jnaInstance.uiHandlerSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun listen() {
|
fun listen() {
|
||||||
showMessage.value = false
|
showMessage.value = false
|
||||||
while (shouldListen) {
|
while (shouldListen) {
|
||||||
RyujinxNative.instance.uiHandlerWait()
|
RyujinxNative.jnaInstance.uiHandlerWait()
|
||||||
|
|
||||||
title =
|
title =
|
||||||
NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestTitle())
|
NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestTitle())
|
||||||
@ -79,7 +80,7 @@ class UiHandler {
|
|||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
shouldListen = false
|
shouldListen = false
|
||||||
RyujinxNative.instance.uiHandlerStopWait()
|
RyujinxNative.jnaInstance.uiHandlerStopWait()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@ -99,12 +100,11 @@ class UiHandler {
|
|||||||
fun validate(): Boolean {
|
fun validate(): Boolean {
|
||||||
if (inputText.value.isEmpty()) {
|
if (inputText.value.isEmpty()) {
|
||||||
validation.value = "Must be between ${minLength} and ${maxLength} characters"
|
validation.value = "Must be between ${minLength} and ${maxLength} characters"
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
return inputText.value.length < minLength || inputText.value.length > maxLength
|
return inputText.value.length < minLength || inputText.value.length > maxLength
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInputType(): KeyboardType {
|
fun getInputType(): KeyboardType {
|
||||||
@ -112,7 +112,9 @@ class UiHandler {
|
|||||||
KeyboardMode.Default -> KeyboardType.Text
|
KeyboardMode.Default -> KeyboardType.Text
|
||||||
KeyboardMode.Numeric -> KeyboardType.Decimal
|
KeyboardMode.Numeric -> KeyboardType.Decimal
|
||||||
KeyboardMode.ASCII -> KeyboardType.Ascii
|
KeyboardMode.ASCII -> KeyboardType.Ascii
|
||||||
else -> { KeyboardType.Text}
|
else -> {
|
||||||
|
KeyboardType.Text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,15 +127,15 @@ class UiHandler {
|
|||||||
NativeHelpers.instance.storeStringJava(inputListener.value)
|
NativeHelpers.instance.storeStringJava(inputListener.value)
|
||||||
}
|
}
|
||||||
showMessageListener.value = false
|
showMessageListener.value = false
|
||||||
RyujinxNative.instance.uiHandlerSetResponse(true, input)
|
RyujinxNative.jnaInstance.uiHandlerSetResponse(true, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showMessageListener.value) {
|
if (showMessageListener.value) {
|
||||||
AlertDialog(
|
BasicAlertDialog(
|
||||||
|
onDismissRequest = { },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.wrapContentWidth()
|
.wrapContentWidth()
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
onDismissRequest = { },
|
|
||||||
properties = DialogProperties(dismissOnBackPress = false, false)
|
properties = DialogProperties(dismissOnBackPress = false, false)
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
|
@ -76,7 +76,10 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
cursor.newRow().apply {
|
cursor.newRow().apply {
|
||||||
add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT_ID)
|
add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT_ID)
|
||||||
add(DocumentsContract.Root.COLUMN_SUMMARY, null)
|
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_TITLE, applicationName)
|
||||||
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
|
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
|
||||||
add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*")
|
add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*")
|
||||||
@ -102,7 +105,8 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
fun File.resolveWithoutConflict(name: String): File {
|
fun File.resolveWithoutConflict(name: String): File {
|
||||||
var file = resolve(name)
|
var file = resolve(name)
|
||||||
if (file.exists()) {
|
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 extension = name.substringAfterLast('.')
|
||||||
val baseName = name.substringBeforeLast('.')
|
val baseName = name.substringBeforeLast('.')
|
||||||
while (file.exists())
|
while (file.exists())
|
||||||
@ -111,7 +115,11 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
return file
|
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 parentFile = getFile(parentDocumentId!!)
|
||||||
val newFile = parentFile.resolveWithoutConflict(displayName)
|
val newFile = parentFile.resolveWithoutConflict(displayName)
|
||||||
|
|
||||||
@ -148,12 +156,13 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun renameDocument(documentId : String?, displayName : String?) : String? {
|
override fun renameDocument(documentId: String?, displayName: String?): String {
|
||||||
if (displayName == null)
|
if (displayName == null)
|
||||||
throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
|
throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
|
||||||
|
|
||||||
val sourceFile = getFile(documentId!!)
|
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)
|
val destFile = sourceParentFile.resolve(displayName)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -176,7 +185,7 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
return copyDocument(sourceDocumentId, targetParentDocumentId)
|
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 parent = getFile(targetParentDocumentId!!)
|
||||||
val oldFile = getFile(sourceDocumentId)
|
val oldFile = getFile(sourceDocumentId)
|
||||||
val newFile = parent.resolveWithoutConflict(oldFile.name)
|
val newFile = parent.resolveWithoutConflict(oldFile.name)
|
||||||
@ -232,7 +241,10 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
|
|
||||||
cursor.newRow().apply {
|
cursor.newRow().apply {
|
||||||
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
|
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_SIZE, localFile.length())
|
||||||
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
|
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
|
||||||
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
|
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
|
||||||
@ -251,7 +263,7 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
getTypeForName(file.name)
|
getTypeForName(file.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTypeForName(name : String) : Any? {
|
private fun getTypeForName(name: String): Any {
|
||||||
val lastDot = name.lastIndexOf('.')
|
val lastDot = name.lastIndexOf('.')
|
||||||
if (lastDot >= 0) {
|
if (lastDot >= 0) {
|
||||||
val extension = name.substring(lastDot + 1)
|
val extension = name.substring(lastDot + 1)
|
||||||
@ -262,7 +274,11 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
return "application/octect-stream"
|
return "application/octect-stream"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun queryChildDocuments(parentDocumentId : String?, projection : Array<out String>?, sortOrder : String?) : Cursor {
|
override fun queryChildDocuments(
|
||||||
|
parentDocumentId: String?,
|
||||||
|
projection: Array<out String>?,
|
||||||
|
sortOrder: String?
|
||||||
|
): Cursor {
|
||||||
var cursor = MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION)
|
var cursor = MatrixCursor(projection ?: DEFAULT_DOCUMENT_PROJECTION)
|
||||||
|
|
||||||
val parent = getFile(parentDocumentId!!)
|
val parent = getFile(parentDocumentId!!)
|
||||||
@ -272,7 +288,11 @@ class DocumentProvider : DocumentsProvider() {
|
|||||||
return cursor
|
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 file = documentId?.let { getFile(it) }
|
||||||
val accessMode = ParcelFileDescriptor.parseMode(mode)
|
val accessMode = ParcelFileDescriptor.parseMode(mode)
|
||||||
return ParcelFileDescriptor.open(file, accessMode)
|
return ParcelFileDescriptor.open(file, accessMode)
|
||||||
|
@ -16,7 +16,6 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import com.anggrayudi.storage.file.getAbsolutePath
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import org.ryujinx.android.NativeHelpers
|
|
||||||
import org.ryujinx.android.RyujinxNative
|
import org.ryujinx.android.RyujinxNative
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -40,14 +39,14 @@ class DlcViewModel(val titleId: String) {
|
|||||||
val path = file.getAbsolutePath(storageHelper.storage.context)
|
val path = file.getAbsolutePath(storageHelper.storage.context)
|
||||||
if (path.isNotEmpty()) {
|
if (path.isNotEmpty()) {
|
||||||
data?.apply {
|
data?.apply {
|
||||||
val contents = RyujinxNative.instance.deviceGetDlcContentList(
|
val contents = RyujinxNative.jnaInstance.deviceGetDlcContentList(
|
||||||
NativeHelpers.instance.storeStringJava(path),
|
path,
|
||||||
titleId.toLong(16)
|
titleId.toLong(16)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (contents.isNotEmpty()) {
|
if (contents.isNotEmpty()) {
|
||||||
val contentPath = path
|
val contentPath = path
|
||||||
val container = DlcContainerList(contentPath);
|
val container = DlcContainerList(contentPath)
|
||||||
|
|
||||||
for (content in contents)
|
for (content in contents)
|
||||||
container.dlc_nca_list.add(
|
container.dlc_nca_list.add(
|
||||||
@ -90,7 +89,7 @@ class DlcViewModel(val titleId: String) {
|
|||||||
val containerPath = container.path
|
val containerPath = container.path
|
||||||
|
|
||||||
if (!File(containerPath).exists())
|
if (!File(containerPath).exists())
|
||||||
continue;
|
continue
|
||||||
|
|
||||||
for (dlc in container.dlc_nca_list) {
|
for (dlc in container.dlc_nca_list) {
|
||||||
val enabled = remember {
|
val enabled = remember {
|
||||||
@ -102,7 +101,10 @@ class DlcViewModel(val titleId: String) {
|
|||||||
enabled,
|
enabled,
|
||||||
containerPath,
|
containerPath,
|
||||||
dlc.fullPath,
|
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(
|
data class DlcContainer(
|
||||||
var enabled: Boolean = false,
|
var enabled: Boolean = false,
|
||||||
var titleId: String = "",
|
var titleId: String = "",
|
||||||
var fullPath: String = "")
|
var fullPath: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
data class DlcItem(
|
data class DlcItem(
|
||||||
var name: String = "",
|
var name: String = "",
|
||||||
var isEnabled: MutableState<Boolean> = mutableStateOf(false),
|
var isEnabled: MutableState<Boolean> = mutableStateOf(false),
|
||||||
var containerPath: String = "",
|
var containerPath: String = "",
|
||||||
var fullPath: String = "",
|
var fullPath: String = "",
|
||||||
var titleId: String = "")
|
var titleId: String = ""
|
||||||
|
)
|
||||||
|
@ -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<String> getFieldOrder() {
|
||||||
|
return List.of("FileSize", "TitleName", "TitleId", "Developer", "Version", "Icon");
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,6 @@ import android.net.Uri
|
|||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import com.anggrayudi.storage.file.extension
|
import com.anggrayudi.storage.file.extension
|
||||||
import org.ryujinx.android.NativeHelpers
|
|
||||||
import org.ryujinx.android.RyujinxNative
|
import org.ryujinx.android.RyujinxNative
|
||||||
|
|
||||||
|
|
||||||
@ -24,8 +23,8 @@ class GameModel(var file: DocumentFile, val context: Context) {
|
|||||||
init {
|
init {
|
||||||
fileName = file.name
|
fileName = file.name
|
||||||
val pid = open()
|
val pid = open()
|
||||||
val ext = NativeHelpers.instance.storeStringJava(file.extension)
|
val gameInfo = GameInfo()
|
||||||
val gameInfo = RyujinxNative.instance.deviceGetGameInfo(pid, ext)
|
RyujinxNative.jnaInstance.deviceGetGameInfo(pid, file.extension, gameInfo)
|
||||||
close()
|
close()
|
||||||
|
|
||||||
fileSize = gameInfo.FileSize
|
fileSize = gameInfo.FileSize
|
||||||
@ -62,12 +61,12 @@ class GameModel(var file: DocumentFile, val context: Context) {
|
|||||||
if (file?.exists() == true) {
|
if (file?.exists() == true) {
|
||||||
updateDescriptor = context.contentResolver.openFileDescriptor(file.uri, "rw")
|
updateDescriptor = context.contentResolver.openFileDescriptor(file.uri, "rw")
|
||||||
|
|
||||||
return updateDescriptor ?.fd ?: -1;
|
return updateDescriptor?.fd ?: -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun close() {
|
fun close() {
|
||||||
@ -78,15 +77,6 @@ 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,
|
None,
|
||||||
Nsp,
|
Nsp,
|
||||||
|
@ -48,17 +48,19 @@ class HomeViewModel(
|
|||||||
|
|
||||||
fun filter(query: String) {
|
fun filter(query: String) {
|
||||||
gameList.clear()
|
gameList.clear()
|
||||||
gameList.addAll(loadedCache.filter { it.titleName != null && it.titleName!!.isNotEmpty() && (query.trim()
|
gameList.addAll(loadedCache.filter {
|
||||||
|
it.titleName != null && it.titleName!!.isNotEmpty() && (query.trim()
|
||||||
.isEmpty() || it.titleName!!.lowercase(Locale.getDefault())
|
.isEmpty() || it.titleName!!.lowercase(Locale.getDefault())
|
||||||
.contains(query)) })
|
.contains(query))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestReload() {
|
fun requestReload() {
|
||||||
shouldReload = true
|
shouldReload = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reloadGameList() {
|
private fun reloadGameList() {
|
||||||
var storage = activity?.storageHelper ?: return
|
activity?.storageHelper ?: return
|
||||||
|
|
||||||
if (isLoading)
|
if (isLoading)
|
||||||
return
|
return
|
||||||
@ -70,13 +72,12 @@ class HomeViewModel(
|
|||||||
thread {
|
thread {
|
||||||
try {
|
try {
|
||||||
loadedCache.clear()
|
loadedCache.clear()
|
||||||
val files = mutableListOf<GameModel>()
|
|
||||||
for (file in folder.search(false, DocumentFileType.FILE)) {
|
for (file in folder.search(false, DocumentFileType.FILE)) {
|
||||||
if (file.extension == "xci" || file.extension == "nsp" || file.extension == "nro")
|
if (file.extension == "xci" || file.extension == "nsp" || file.extension == "nro")
|
||||||
activity.let {
|
activity.let {
|
||||||
val item = GameModel(file, it)
|
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)
|
loadedCache.add(item)
|
||||||
gameList.add(item)
|
gameList.add(item)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.coroutines.sync.Semaphore
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
import org.ryujinx.android.GameController
|
import org.ryujinx.android.GameController
|
||||||
import org.ryujinx.android.GameHost
|
import org.ryujinx.android.GameHost
|
||||||
import org.ryujinx.android.GraphicsConfiguration
|
|
||||||
import org.ryujinx.android.Logging
|
import org.ryujinx.android.Logging
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import org.ryujinx.android.MotionSensorManager
|
import org.ryujinx.android.MotionSensorManager
|
||||||
@ -55,24 +54,19 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun closeGame() {
|
fun closeGame() {
|
||||||
RyujinxNative.instance.deviceSignalEmulationClose()
|
RyujinxNative.jnaInstance.deviceSignalEmulationClose()
|
||||||
gameHost?.close()
|
gameHost?.close()
|
||||||
RyujinxNative.instance.deviceCloseEmulation()
|
RyujinxNative.jnaInstance.deviceCloseEmulation()
|
||||||
motionSensorManager?.unregister()
|
motionSensorManager?.unregister()
|
||||||
physicalControllerManager?.disconnect()
|
physicalControllerManager?.disconnect()
|
||||||
motionSensorManager?.setControllerId(-1)
|
motionSensorManager?.setControllerId(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshFirmwareVersion() {
|
fun refreshFirmwareVersion() {
|
||||||
var handle = RyujinxNative.instance.deviceGetInstalledFirmwareVersion()
|
firmwareVersion = RyujinxNative.jnaInstance.deviceGetInstalledFirmwareVersion()
|
||||||
if (handle != -1L) {
|
|
||||||
firmwareVersion = NativeHelpers.instance.getStringJava(handle)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadGame(game: GameModel): Boolean {
|
fun loadGame(game: GameModel): Boolean {
|
||||||
val nativeRyujinx = RyujinxNative.instance
|
|
||||||
|
|
||||||
val descriptor = game.open()
|
val descriptor = game.open()
|
||||||
|
|
||||||
if (descriptor == 0)
|
if (descriptor == 0)
|
||||||
@ -85,12 +79,12 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
|
|
||||||
val settings = QuickSettings(activity)
|
val settings = QuickSettings(activity)
|
||||||
|
|
||||||
var success = nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply {
|
var success = RyujinxNative.jnaInstance.graphicsInitialize(
|
||||||
EnableShaderCache = settings.enableShaderCache
|
enableShaderCache = settings.enableShaderCache,
|
||||||
EnableTextureRecompression = settings.enableTextureRecompression
|
enableTextureRecompression = settings.enableTextureRecompression,
|
||||||
ResScale = settings.resScale
|
rescale = settings.resScale,
|
||||||
BackendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal
|
backendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal
|
||||||
})
|
)
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return false
|
||||||
@ -139,8 +133,11 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
success = nativeRyujinx.graphicsInitializeRenderer(
|
val extensions = nativeInterop.VkRequiredExtensions
|
||||||
nativeInterop.VkRequiredExtensions!!,
|
|
||||||
|
success = RyujinxNative.jnaInstance.graphicsInitializeRenderer(
|
||||||
|
extensions!!,
|
||||||
|
extensions.size,
|
||||||
driverHandle
|
driverHandle
|
||||||
)
|
)
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -151,7 +148,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
semaphore.acquire()
|
semaphore.acquire()
|
||||||
launchOnUiThread {
|
launchOnUiThread {
|
||||||
// We are only able to initialize the emulation context on the main thread
|
// We are only able to initialize the emulation context on the main thread
|
||||||
success = nativeRyujinx.deviceInitialize(
|
success = RyujinxNative.jnaInstance.deviceInitialize(
|
||||||
settings.isHostMapped,
|
settings.isHostMapped,
|
||||||
settings.useNce,
|
settings.useNce,
|
||||||
SystemLanguage.AmericanEnglish.ordinal,
|
SystemLanguage.AmericanEnglish.ordinal,
|
||||||
@ -160,7 +157,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
settings.enableDocked,
|
settings.enableDocked,
|
||||||
settings.enablePtc,
|
settings.enablePtc,
|
||||||
false,
|
false,
|
||||||
NativeHelpers.instance.storeStringJava("UTC"),
|
"UTC",
|
||||||
settings.ignoreMissingServices
|
settings.ignoreMissingServices
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -173,28 +170,24 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
success = nativeRyujinx.deviceLoadDescriptor(descriptor, game.type.ordinal, update)
|
success =
|
||||||
|
RyujinxNative.jnaInstance.deviceLoadDescriptor(descriptor, game.type.ordinal, update)
|
||||||
|
|
||||||
if (!success)
|
return success
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadMiiEditor(): Boolean {
|
fun loadMiiEditor(): Boolean {
|
||||||
val nativeRyujinx = RyujinxNative.instance
|
|
||||||
|
|
||||||
gameModel = null
|
gameModel = null
|
||||||
isMiiEditorLaunched = true
|
isMiiEditorLaunched = true
|
||||||
|
|
||||||
val settings = QuickSettings(activity)
|
val settings = QuickSettings(activity)
|
||||||
|
|
||||||
var success = nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply {
|
var success = RyujinxNative.jnaInstance.graphicsInitialize(
|
||||||
EnableShaderCache = settings.enableShaderCache
|
enableShaderCache = settings.enableShaderCache,
|
||||||
EnableTextureRecompression = settings.enableTextureRecompression
|
enableTextureRecompression = settings.enableTextureRecompression,
|
||||||
ResScale = settings.resScale
|
rescale = settings.resScale,
|
||||||
BackendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal
|
backendThreading = org.ryujinx.android.BackendThreading.Auto.ordinal
|
||||||
})
|
)
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return false
|
||||||
@ -243,8 +236,11 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
success = nativeRyujinx.graphicsInitializeRenderer(
|
val extensions = nativeInterop.VkRequiredExtensions
|
||||||
nativeInterop.VkRequiredExtensions!!,
|
|
||||||
|
success = RyujinxNative.jnaInstance.graphicsInitializeRenderer(
|
||||||
|
extensions!!,
|
||||||
|
extensions.size,
|
||||||
driverHandle
|
driverHandle
|
||||||
)
|
)
|
||||||
if (!success)
|
if (!success)
|
||||||
@ -255,7 +251,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
semaphore.acquire()
|
semaphore.acquire()
|
||||||
launchOnUiThread {
|
launchOnUiThread {
|
||||||
// We are only able to initialize the emulation context on the main thread
|
// We are only able to initialize the emulation context on the main thread
|
||||||
success = nativeRyujinx.deviceInitialize(
|
success = RyujinxNative.jnaInstance.deviceInitialize(
|
||||||
settings.isHostMapped,
|
settings.isHostMapped,
|
||||||
settings.useNce,
|
settings.useNce,
|
||||||
SystemLanguage.AmericanEnglish.ordinal,
|
SystemLanguage.AmericanEnglish.ordinal,
|
||||||
@ -264,7 +260,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
settings.enableDocked,
|
settings.enableDocked,
|
||||||
settings.enablePtc,
|
settings.enablePtc,
|
||||||
false,
|
false,
|
||||||
NativeHelpers.instance.storeStringJava("UTC"),
|
"UTC",
|
||||||
settings.ignoreMissingServices
|
settings.ignoreMissingServices
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -277,12 +273,9 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
success = nativeRyujinx.deviceLaunchMiiEditor()
|
success = RyujinxNative.jnaInstance.deviceLaunchMiiEditor()
|
||||||
|
|
||||||
if (!success)
|
return success
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearPptcCache(titleId: String) {
|
fun clearPptcCache(titleId: String) {
|
||||||
|
@ -12,7 +12,6 @@ import com.anggrayudi.storage.file.extension
|
|||||||
import com.anggrayudi.storage.file.getAbsolutePath
|
import com.anggrayudi.storage.file.getAbsolutePath
|
||||||
import org.ryujinx.android.LogLevel
|
import org.ryujinx.android.LogLevel
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import org.ryujinx.android.NativeHelpers
|
|
||||||
import org.ryujinx.android.RyujinxNative
|
import org.ryujinx.android.RyujinxNative
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
@ -28,7 +27,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
sharedPref = getPreferences()
|
sharedPref = getPreferences()
|
||||||
previousFolderCallback = activity.storageHelper!!.onFolderSelected
|
previousFolderCallback = activity.storageHelper!!.onFolderSelected
|
||||||
previousFileCallback = activity.storageHelper!!.onFileSelected
|
previousFileCallback = activity.storageHelper!!.onFileSelected
|
||||||
activity.storageHelper!!.onFolderSelected = { requestCode, folder ->
|
activity.storageHelper!!.onFolderSelected = { _, folder ->
|
||||||
run {
|
run {
|
||||||
val p = folder.getAbsolutePath(activity)
|
val p = folder.getAbsolutePath(activity)
|
||||||
val editor = sharedPref.edit()
|
val editor = sharedPref.edit()
|
||||||
@ -146,18 +145,24 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
editor.apply()
|
editor.apply()
|
||||||
activity.storageHelper!!.onFolderSelected = previousFolderCallback
|
activity.storageHelper!!.onFolderSelected = previousFolderCallback
|
||||||
|
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Debug.ordinal, enableDebugLogs.value)
|
RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Debug.ordinal, enableDebugLogs.value)
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Info.ordinal, enableInfoLogs.value)
|
RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Info.ordinal, enableInfoLogs.value)
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Stub.ordinal, enableStubLogs.value)
|
RyujinxNative.jnaInstance.loggingSetEnabled(LogLevel.Stub.ordinal, enableStubLogs.value)
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Warning.ordinal, enableWarningLogs.value)
|
RyujinxNative.jnaInstance.loggingSetEnabled(
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Error.ordinal, enableErrorLogs.value)
|
LogLevel.Warning.ordinal,
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.AccessLog.ordinal, enableAccessLogs.value)
|
enableWarningLogs.value
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Guest.ordinal, enableGuestLogs.value)
|
)
|
||||||
RyujinxNative.instance.loggingSetEnabled(LogLevel.Trace.ordinal, enableTraceLogs.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() {
|
fun openGameFolder() {
|
||||||
val path = sharedPref?.getString("gameFolder", "") ?: ""
|
val path = sharedPref.getString("gameFolder", "") ?: ""
|
||||||
|
|
||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
activity.storageHelper?.storage?.openFolderPicker()
|
activity.storageHelper?.storage?.openFolderPicker()
|
||||||
@ -169,13 +174,13 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun importProdKeys() {
|
fun importProdKeys() {
|
||||||
activity.storageHelper!!.onFileSelected = { requestCode, files ->
|
activity.storageHelper!!.onFileSelected = { _, files ->
|
||||||
run {
|
run {
|
||||||
activity.storageHelper!!.onFileSelected = previousFileCallback
|
activity.storageHelper!!.onFileSelected = previousFileCallback
|
||||||
val file = files.firstOrNull()
|
val file = files.firstOrNull()
|
||||||
file?.apply {
|
file?.apply {
|
||||||
if (name == "prod.keys") {
|
if (name == "prod.keys") {
|
||||||
val outputFile = File(MainActivity.AppPath + "/system");
|
val outputFile = File(MainActivity.AppPath + "/system")
|
||||||
outputFile.delete()
|
outputFile.delete()
|
||||||
|
|
||||||
thread {
|
thread {
|
||||||
@ -183,9 +188,6 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
activity,
|
activity,
|
||||||
outputFile,
|
outputFile,
|
||||||
callback = object : FileCallback() {
|
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 =
|
val descriptor =
|
||||||
activity.contentResolver.openFileDescriptor(file.uri, "rw")
|
activity.contentResolver.openFileDescriptor(file.uri, "rw")
|
||||||
descriptor?.use { d ->
|
descriptor?.use { d ->
|
||||||
val version = RyujinxNative.instance.deviceVerifyFirmware(
|
selectedFirmwareVersion =
|
||||||
|
RyujinxNative.jnaInstance.deviceVerifyFirmware(
|
||||||
d.fd,
|
d.fd,
|
||||||
extension == "xci"
|
extension == "xci"
|
||||||
)
|
)
|
||||||
selectedFirmwareFile = file
|
selectedFirmwareFile = file
|
||||||
if (version != -1L) {
|
if (selectedFirmwareVersion.isEmpty()) {
|
||||||
selectedFirmwareVersion =
|
|
||||||
NativeHelpers.instance.getStringJava(version)
|
|
||||||
installState.value = FirmwareInstallState.Query
|
installState.value = FirmwareInstallState.Query
|
||||||
} else {
|
} else {
|
||||||
installState.value = FirmwareInstallState.Cancelled
|
installState.value = FirmwareInstallState.Cancelled
|
||||||
@ -246,7 +247,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
installState.value = FirmwareInstallState.Install
|
installState.value = FirmwareInstallState.Install
|
||||||
thread {
|
thread {
|
||||||
try {
|
try {
|
||||||
RyujinxNative.instance.deviceInstallFirmware(
|
RyujinxNative.jnaInstance.deviceInstallFirmware(
|
||||||
d.fd,
|
d.fd,
|
||||||
extension == "xci"
|
extension == "xci"
|
||||||
)
|
)
|
||||||
|
@ -54,7 +54,10 @@ class TitleUpdateViewModel(val titleId: String) {
|
|||||||
val file = files.firstOrNull()
|
val file = files.firstOrNull()
|
||||||
file?.apply {
|
file?.apply {
|
||||||
if (file.extension == "nsp") {
|
if (file.extension == "nsp") {
|
||||||
storageHelper.storage.context.contentResolver.takePersistableUriPermission(file.uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
storageHelper.storage.context.contentResolver.takePersistableUriPermission(
|
||||||
|
file.uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
currentPaths.add(file.uri.toString())
|
currentPaths.add(file.uri.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,8 +123,7 @@ class TitleUpdateViewModel(val titleId: String) {
|
|||||||
if (file?.exists() == true) {
|
if (file?.exists() == true) {
|
||||||
metadata.selected = selected
|
metadata.selected = selected
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
metadata.selected = selected
|
metadata.selected = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.ryujinx.android.viewmodels
|
package org.ryujinx.android.viewmodels
|
||||||
|
|
||||||
import org.ryujinx.android.NativeHelpers
|
|
||||||
import org.ryujinx.android.RyujinxNative
|
import org.ryujinx.android.RyujinxNative
|
||||||
import java.util.Base64
|
import java.util.Base64
|
||||||
|
|
||||||
@ -14,35 +13,26 @@ class UserViewModel {
|
|||||||
|
|
||||||
fun refreshUsers() {
|
fun refreshUsers() {
|
||||||
userList.clear()
|
userList.clear()
|
||||||
val native = RyujinxNative.instance
|
|
||||||
val helper = NativeHelpers.instance
|
|
||||||
val decoder = Base64.getDecoder()
|
val decoder = Base64.getDecoder()
|
||||||
openedUser = UserModel()
|
openedUser = UserModel()
|
||||||
openedUser.id = helper.getStringJava(native.userGetOpenedUser())
|
openedUser.id = RyujinxNative.jnaInstance.userGetOpenedUser()
|
||||||
if (openedUser.id.isNotEmpty()) {
|
if (openedUser.id.isNotEmpty()) {
|
||||||
openedUser.username =
|
openedUser.username = RyujinxNative.jnaInstance.userGetUserName(openedUser.id)
|
||||||
helper.getStringJava(native.userGetUserName(helper.storeStringJava(openedUser.id)))
|
|
||||||
openedUser.userPicture = decoder.decode(
|
openedUser.userPicture = decoder.decode(
|
||||||
helper.getStringJava(
|
RyujinxNative.jnaInstance.userGetUserPicture(
|
||||||
native.userGetUserPicture(
|
openedUser.id
|
||||||
helper.storeStringJava(openedUser.id)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val users = native.userGetAllUsers()
|
val users = RyujinxNative.jnaInstance.userGetAllUsers()
|
||||||
for (user in users) {
|
for (user in users) {
|
||||||
userList.add(
|
userList.add(
|
||||||
UserModel(
|
UserModel(
|
||||||
user,
|
user,
|
||||||
helper.getStringJava(native.userGetUserName(helper.storeStringJava(user))),
|
RyujinxNative.jnaInstance.userGetUserName(user),
|
||||||
decoder.decode(
|
decoder.decode(
|
||||||
helper.getStringJava(
|
RyujinxNative.jnaInstance.userGetUserPicture(user)
|
||||||
native.userGetUserPicture(
|
|
||||||
helper.storeStringJava(user)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -50,16 +40,18 @@ class UserViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openUser(userModel: UserModel) {
|
fun openUser(userModel: UserModel) {
|
||||||
val native = RyujinxNative.instance
|
RyujinxNative.jnaInstance.userOpenUser(userModel.id)
|
||||||
val helper = NativeHelpers.instance
|
|
||||||
native.userOpenUser(helper.storeStringJava(userModel.id))
|
|
||||||
|
|
||||||
refreshUsers()
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
|
@ -102,8 +102,7 @@ class VulkanDriverViewModel(val activity: MainActivity) {
|
|||||||
onFileSelected = { requestCode, files ->
|
onFileSelected = { requestCode, files ->
|
||||||
run {
|
run {
|
||||||
onFileSelected = callBack
|
onFileSelected = callBack
|
||||||
if(requestCode == DriverRequestCode)
|
if (requestCode == DriverRequestCode) {
|
||||||
{
|
|
||||||
val file = files.firstOrNull()
|
val file = files.firstOrNull()
|
||||||
file?.apply {
|
file?.apply {
|
||||||
val path = file.getAbsolutePath(storage.context)
|
val path = file.getAbsolutePath(storage.context)
|
||||||
@ -117,14 +116,18 @@ class VulkanDriverViewModel(val activity: MainActivity) {
|
|||||||
zip.entries().asSequence().forEach { entry ->
|
zip.entries().asSequence().forEach { entry ->
|
||||||
|
|
||||||
zip.getInputStream(entry).use { input ->
|
zip.getInputStream(entry).use { input ->
|
||||||
val filePath = extractionFolder.absolutePath + File.separator + entry.name
|
val filePath =
|
||||||
|
extractionFolder.absolutePath + File.separator + entry.name
|
||||||
|
|
||||||
if (!entry.isDirectory) {
|
if (!entry.isDirectory) {
|
||||||
File(filePath).delete()
|
File(filePath).delete()
|
||||||
val bos = BufferedOutputStream(FileOutputStream(filePath))
|
val bos =
|
||||||
|
BufferedOutputStream(FileOutputStream(filePath))
|
||||||
val bytesIn = ByteArray(4096)
|
val bytesIn = ByteArray(4096)
|
||||||
var read: Int
|
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.write(bytesIn, 0, read)
|
||||||
}
|
}
|
||||||
bos.close()
|
bos.close()
|
||||||
@ -144,7 +147,8 @@ class VulkanDriverViewModel(val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
openFilePicker(DriverRequestCode,
|
openFilePicker(
|
||||||
|
DriverRequestCode,
|
||||||
filterMimeTypes = arrayOf("application/zip")
|
filterMimeTypes = arrayOf("application/zip")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,19 @@ class DlcViews {
|
|||||||
|
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
Column {
|
Column {
|
||||||
Row(modifier = Modifier
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween) {
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
Text(text = "DLC for ${name}", textAlign = TextAlign.Center, modifier = Modifier.align(
|
) {
|
||||||
|
Text(
|
||||||
|
text = "DLC for ${name}",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.align(
|
||||||
Alignment.CenterVertically
|
Alignment.CenterVertically
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -70,12 +76,15 @@ class DlcViews {
|
|||||||
dlcList.addAll(viewModel.getDlc())
|
dlcList.addAll(viewModel.getDlc())
|
||||||
refresh.value = false
|
refresh.value = false
|
||||||
}
|
}
|
||||||
LazyColumn(modifier = Modifier
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(400.dp)){
|
.height(400.dp)
|
||||||
|
) {
|
||||||
items(dlcList) { dlcItem ->
|
items(dlcList) { dlcItem ->
|
||||||
dlcItem.apply {
|
dlcItem.apply {
|
||||||
Row(modifier = Modifier
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
@ -94,7 +103,8 @@ class DlcViews {
|
|||||||
viewModel.remove(dlcItem)
|
viewModel.remove(dlcItem)
|
||||||
refresh.value = true
|
refresh.value = true
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Filled.Delete,
|
Icon(
|
||||||
|
Icons.Filled.Delete,
|
||||||
contentDescription = "remove"
|
contentDescription = "remove"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
|||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@ -80,8 +81,6 @@ class GameViews {
|
|||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
GameStats(mainViewModel)
|
GameStats(mainViewModel)
|
||||||
|
|
||||||
val ryujinxNative = RyujinxNative.instance
|
|
||||||
|
|
||||||
val showController = remember {
|
val showController = remember {
|
||||||
mutableStateOf(QuickSettings(mainViewModel.activity).useVirtualController)
|
mutableStateOf(QuickSettings(mainViewModel.activity).useVirtualController)
|
||||||
}
|
}
|
||||||
@ -128,19 +127,19 @@ class GameViews {
|
|||||||
|
|
||||||
when (event.type) {
|
when (event.type) {
|
||||||
PointerEventType.Press -> {
|
PointerEventType.Press -> {
|
||||||
ryujinxNative.inputSetTouchPoint(
|
RyujinxNative.jnaInstance.inputSetTouchPoint(
|
||||||
position.x.roundToInt(),
|
position.x.roundToInt(),
|
||||||
position.y.roundToInt()
|
position.y.roundToInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PointerEventType.Release -> {
|
PointerEventType.Release -> {
|
||||||
ryujinxNative.inputReleaseTouchPoint()
|
RyujinxNative.jnaInstance.inputReleaseTouchPoint()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PointerEventType.Move -> {
|
PointerEventType.Move -> {
|
||||||
ryujinxNative.inputSetTouchPoint(
|
RyujinxNative.jnaInstance.inputSetTouchPoint(
|
||||||
position.x.roundToInt(),
|
position.x.roundToInt(),
|
||||||
position.y.roundToInt()
|
position.y.roundToInt()
|
||||||
)
|
)
|
||||||
@ -209,7 +208,7 @@ class GameViews {
|
|||||||
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
||||||
showMore.value = false
|
showMore.value = false
|
||||||
showController.value = !showController.value
|
showController.value = !showController.value
|
||||||
ryujinxNative.inputReleaseTouchPoint()
|
RyujinxNative.jnaInstance.inputReleaseTouchPoint()
|
||||||
mainViewModel.controller?.setVisible(showController.value)
|
mainViewModel.controller?.setVisible(showController.value)
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
@ -220,7 +219,7 @@ class GameViews {
|
|||||||
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
IconButton(modifier = Modifier.padding(4.dp), onClick = {
|
||||||
showMore.value = false
|
showMore.value = false
|
||||||
enableVsync.value = !enableVsync.value
|
enableVsync.value = !enableVsync.value
|
||||||
RyujinxNative.instance.graphicsRendererSetVsync(
|
RyujinxNative.jnaInstance.graphicsRendererSetVsync(
|
||||||
enableVsync.value
|
enableVsync.value
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
@ -262,10 +261,12 @@ class GameViews {
|
|||||||
|
|
||||||
if (progressValue.value > -1)
|
if (progressValue.value > -1)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
|
progress = {
|
||||||
|
progressValue.value
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 16.dp),
|
.padding(top = 16.dp),
|
||||||
progress = progressValue.value
|
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
@ -279,7 +280,7 @@ class GameViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showBackNotice.value) {
|
if (showBackNotice.value) {
|
||||||
AlertDialog(onDismissRequest = { showBackNotice.value = false }) {
|
BasicAlertDialog(onDismissRequest = { showBackNotice.value = false }) {
|
||||||
Column {
|
Column {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -3,8 +3,6 @@ package org.ryujinx.android.views
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandVertically
|
|
||||||
import androidx.compose.animation.shrinkVertically
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.basicMarquee
|
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.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardElevation
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ElevatedCard
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.OutlinedCard
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SearchBar
|
import androidx.compose.material3.SearchBar
|
||||||
import androidx.compose.material3.SearchBarDefaults
|
import androidx.compose.material3.SearchBarDefaults
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
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.Color
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.layout.ContentScale
|
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.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Popup
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.anggrayudi.storage.extension.launchOnUiThread
|
import com.anggrayudi.storage.extension.launchOnUiThread
|
||||||
import org.ryujinx.android.R
|
import org.ryujinx.android.R
|
||||||
@ -377,7 +368,7 @@ class HomeViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showLoading.value) {
|
if (showLoading.value) {
|
||||||
AlertDialog(onDismissRequest = { }) {
|
BasicAlertDialog(onDismissRequest = { }) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
@ -401,7 +392,7 @@ class HomeViews {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (openTitleUpdateDialog.value) {
|
if (openTitleUpdateDialog.value) {
|
||||||
AlertDialog(onDismissRequest = {
|
BasicAlertDialog(onDismissRequest = {
|
||||||
openTitleUpdateDialog.value = false
|
openTitleUpdateDialog.value = false
|
||||||
}) {
|
}) {
|
||||||
Surface(
|
Surface(
|
||||||
@ -419,7 +410,7 @@ class HomeViews {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (openDlcDialog.value) {
|
if (openDlcDialog.value) {
|
||||||
AlertDialog(onDismissRequest = {
|
BasicAlertDialog(onDismissRequest = {
|
||||||
openDlcDialog.value = false
|
openDlcDialog.value = false
|
||||||
}) {
|
}) {
|
||||||
Surface(
|
Surface(
|
||||||
@ -451,14 +442,13 @@ class HomeViews {
|
|||||||
thread {
|
thread {
|
||||||
showLoading.value = true
|
showLoading.value = true
|
||||||
val success =
|
val success =
|
||||||
viewModel.mainViewModel?.loadGame(viewModel.mainViewModel.selected!!)
|
viewModel.mainViewModel.loadGame(viewModel.mainViewModel.selected!!)
|
||||||
?: false
|
|
||||||
if (success) {
|
if (success) {
|
||||||
launchOnUiThread {
|
launchOnUiThread {
|
||||||
viewModel.mainViewModel?.navigateToGame()
|
viewModel.mainViewModel.navigateToGame()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewModel.mainViewModel?.selected!!.close()
|
viewModel.mainViewModel.selected!!.close()
|
||||||
}
|
}
|
||||||
showLoading.value = false
|
showLoading.value = false
|
||||||
}
|
}
|
||||||
@ -487,7 +477,7 @@ class HomeViews {
|
|||||||
}, onClick = {
|
}, onClick = {
|
||||||
showAppMenu.value = false
|
showAppMenu.value = false
|
||||||
viewModel.mainViewModel?.clearPptcCache(
|
viewModel.mainViewModel?.clearPptcCache(
|
||||||
viewModel.mainViewModel?.selected?.titleId ?: ""
|
viewModel.mainViewModel.selected?.titleId ?: ""
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
DropdownMenuItem(text = {
|
DropdownMenuItem(text = {
|
||||||
@ -495,7 +485,7 @@ class HomeViews {
|
|||||||
}, onClick = {
|
}, onClick = {
|
||||||
showAppMenu.value = false
|
showAppMenu.value = false
|
||||||
viewModel.mainViewModel?.purgeShaderCache(
|
viewModel.mainViewModel?.purgeShaderCache(
|
||||||
viewModel.mainViewModel?.selected?.titleId ?: ""
|
viewModel.mainViewModel.selected?.titleId ?: ""
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
DropdownMenuItem(text = {
|
DropdownMenuItem(text = {
|
||||||
@ -548,7 +538,7 @@ class HomeViews {
|
|||||||
onClick = {
|
onClick = {
|
||||||
if (viewModel.mainViewModel?.selected != null) {
|
if (viewModel.mainViewModel?.selected != null) {
|
||||||
showAppActions.value = false
|
showAppActions.value = false
|
||||||
viewModel.mainViewModel?.apply {
|
viewModel.mainViewModel.apply {
|
||||||
selected = null
|
selected = null
|
||||||
}
|
}
|
||||||
selectedModel.value = null
|
selectedModel.value = null
|
||||||
@ -639,7 +629,7 @@ class HomeViews {
|
|||||||
onClick = {
|
onClick = {
|
||||||
if (viewModel.mainViewModel?.selected != null) {
|
if (viewModel.mainViewModel?.selected != null) {
|
||||||
showAppActions.value = false
|
showAppActions.value = false
|
||||||
viewModel.mainViewModel?.apply {
|
viewModel.mainViewModel.apply {
|
||||||
selected = null
|
selected = null
|
||||||
}
|
}
|
||||||
selectedModel.value = null
|
selectedModel.value = null
|
||||||
|
@ -16,7 +16,7 @@ class MainView {
|
|||||||
|
|
||||||
NavHost(navController = navController, startDestination = "home") {
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
|
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
|
||||||
composable("user") { UserViews.Main(mainViewModel, navController) }
|
composable("user") { UserViews.Main(mainViewModel) }
|
||||||
composable("game") { GameViews.Main() }
|
composable("game") { GameViews.Main() }
|
||||||
composable("settings") {
|
composable("settings") {
|
||||||
SettingViews.Main(
|
SettingViews.Main(
|
||||||
|
@ -29,10 +29,11 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
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.material.icons.filled.KeyboardArrowUp
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@ -192,7 +193,7 @@ class SettingViews {
|
|||||||
)
|
)
|
||||||
settingsViewModel.navController.popBackStack()
|
settingsViewModel.navController.popBackStack()
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}) { contentPadding ->
|
}) { contentPadding ->
|
||||||
@ -310,7 +311,7 @@ class SettingViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showFirwmareDialog.value) {
|
if (showFirwmareDialog.value) {
|
||||||
AlertDialog(onDismissRequest = {
|
BasicAlertDialog(onDismissRequest = {
|
||||||
if (firmwareInstallState.value != FirmwareInstallState.Install) {
|
if (firmwareInstallState.value != FirmwareInstallState.Install) {
|
||||||
showFirwmareDialog.value = false
|
showFirwmareDialog.value = false
|
||||||
settingsViewModel.clearFirmwareSelection(firmwareInstallState)
|
settingsViewModel.clearFirmwareSelection(firmwareInstallState)
|
||||||
@ -552,7 +553,6 @@ class SettingViews {
|
|||||||
Button(onClick = {
|
Button(onClick = {
|
||||||
val storage = MainActivity.StorageHelper
|
val storage = MainActivity.StorageHelper
|
||||||
storage?.apply {
|
storage?.apply {
|
||||||
val s = this.storage
|
|
||||||
val callBack = this.onFileSelected
|
val callBack = this.onFileSelected
|
||||||
onFileSelected = { requestCode, files ->
|
onFileSelected = { requestCode, files ->
|
||||||
run {
|
run {
|
||||||
@ -578,7 +578,7 @@ class SettingViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showImportWarning.value) {
|
if (showImportWarning.value) {
|
||||||
AlertDialog(onDismissRequest = {
|
BasicAlertDialog(onDismissRequest = {
|
||||||
showImportWarning.value = false
|
showImportWarning.value = false
|
||||||
importFile.value = null
|
importFile.value = null
|
||||||
}) {
|
}) {
|
||||||
@ -626,7 +626,7 @@ class SettingViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showImportCompletion.value) {
|
if (showImportCompletion.value) {
|
||||||
AlertDialog(onDismissRequest = {
|
BasicAlertDialog(onDismissRequest = {
|
||||||
showImportCompletion.value = false
|
showImportCompletion.value = false
|
||||||
importFile.value = null
|
importFile.value = null
|
||||||
mainViewModel.userViewModel.refreshUsers()
|
mainViewModel.userViewModel.refreshUsers()
|
||||||
@ -739,7 +739,7 @@ class SettingViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isDriverSelectorOpen.value) {
|
if (isDriverSelectorOpen.value) {
|
||||||
AlertDialog(onDismissRequest = {
|
BasicAlertDialog(onDismissRequest = {
|
||||||
isDriverSelectorOpen.value = false
|
isDriverSelectorOpen.value = false
|
||||||
|
|
||||||
if (isChanged.value) {
|
if (isChanged.value) {
|
||||||
@ -1064,7 +1064,7 @@ class SettingViews {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BackHandler() {
|
BackHandler {
|
||||||
settingsViewModel.save(
|
settingsViewModel.save(
|
||||||
isHostMapped,
|
isHostMapped,
|
||||||
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices,
|
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices,
|
||||||
|
@ -17,7 +17,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
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.material.icons.filled.Refresh
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@ -43,10 +43,11 @@ class UserViews {
|
|||||||
companion object {
|
companion object {
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Main(viewModel: MainViewModel? = null, navController: NavHostController? = null) {
|
fun Main(viewModel: MainViewModel? = null) {
|
||||||
val reload = remember {
|
val reload = remember {
|
||||||
mutableStateOf(true)
|
mutableStateOf(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
viewModel?.userViewModel?.refreshUsers()
|
viewModel?.userViewModel?.refreshUsers()
|
||||||
reload.value = true
|
reload.value = true
|
||||||
@ -64,7 +65,7 @@ class UserViews {
|
|||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
viewModel?.navController?.popBackStack()
|
viewModel?.navController?.popBackStack()
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}) { contentPadding ->
|
}) { contentPadding ->
|
||||||
@ -136,7 +137,11 @@ class UserViews {
|
|||||||
if (viewModel?.userViewModel?.userList?.isNotEmpty() == true) {
|
if (viewModel?.userViewModel?.userList?.isNotEmpty() == true) {
|
||||||
items(viewModel.userViewModel.userList) { user ->
|
items(viewModel.userViewModel.userList) { user ->
|
||||||
Image(
|
Image(
|
||||||
bitmap = BitmapFactory.decodeByteArray(user.userPicture, 0, user.userPicture?.size ?: 0)
|
bitmap = BitmapFactory.decodeByteArray(
|
||||||
|
user.userPicture,
|
||||||
|
0,
|
||||||
|
user.userPicture?.size ?: 0
|
||||||
|
)
|
||||||
.asImageBitmap(),
|
.asImageBitmap(),
|
||||||
contentDescription = "selected image",
|
contentDescription = "selected image",
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
@ -165,6 +170,6 @@ class UserViews {
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun Preview() {
|
fun Preview() {
|
||||||
UserViews.Main()
|
Main()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user