forked from MeloNX/MeloNX
Cleanup LibRyujinx and add more verbose logging
This commit is contained in:
parent
ee4e18ff0d
commit
300b23cf9b
@ -1,10 +1,7 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Common.Logging.Formatters;
|
using Ryujinx.Common.Logging.Formatters;
|
||||||
using Ryujinx.Common.Logging.Targets;
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibRyujinx
|
namespace LibRyujinx
|
||||||
{
|
{
|
||||||
@ -26,7 +23,7 @@ namespace LibRyujinx
|
|||||||
Logcat.AndroidLogPrint(GetLogLevel(args.Level), _name, _formatter.Format(args));
|
Logcat.AndroidLogPrint(GetLogLevel(args.Level), _name, _formatter.Format(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Logcat.LogLevel GetLogLevel(LogLevel logLevel)
|
private static Logcat.LogLevel GetLogLevel(LogLevel logLevel)
|
||||||
{
|
{
|
||||||
return logLevel switch
|
return logLevel switch
|
||||||
{
|
{
|
||||||
@ -39,13 +36,14 @@ namespace LibRyujinx
|
|||||||
LogLevel.AccessLog => Logcat.LogLevel.Info,
|
LogLevel.AccessLog => Logcat.LogLevel.Info,
|
||||||
LogLevel.Notice => Logcat.LogLevel.Info,
|
LogLevel.Notice => Logcat.LogLevel.Info,
|
||||||
LogLevel.Trace => Logcat.LogLevel.Verbose,
|
LogLevel.Trace => Logcat.LogLevel.Verbose,
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException(),
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
using System;
|
using LibRyujinx.Jni;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
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 LibRyujinx.Jni.Values;
|
||||||
using LibRyujinx.Jni.Primitives;
|
using LibRyujinx.Shared.Audio.Oboe;
|
||||||
using LibRyujinx.Jni;
|
using Microsoft.Win32.SafeHandles;
|
||||||
using Rxmxnx.PInvoke;
|
using Rxmxnx.PInvoke;
|
||||||
using System.Text;
|
using Ryujinx.Common.Configuration;
|
||||||
using LibRyujinx.Jni.Internal.Pointers;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Logging.Targets;
|
using Ryujinx.Common.Logging.Targets;
|
||||||
|
using Ryujinx.Common.SystemInfo;
|
||||||
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
using Ryujinx.Input;
|
||||||
|
using Silk.NET.Core.Loader;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using LibRyujinx.Shared.Audio.Oboe;
|
using System;
|
||||||
using System.Threading;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Win32.SafeHandles;
|
using System.Numerics;
|
||||||
using Newtonsoft.Json.Linq;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using LibHac.Tools.Fs;
|
using System.Text;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using System.Threading;
|
||||||
|
|
||||||
namespace LibRyujinx
|
namespace LibRyujinx
|
||||||
{
|
{
|
||||||
@ -53,9 +54,16 @@ namespace LibRyujinx
|
|||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_initialize")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_initialize")]
|
||||||
public static JBoolean JniInitialize(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef jpath, JBoolean enableDebugLogs)
|
public static JBoolean JniInitialize(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef jpath, JBoolean enableDebugLogs)
|
||||||
{
|
{
|
||||||
var path = GetString(jEnv, jpath);
|
SystemInfo.IsBionic = true;
|
||||||
|
|
||||||
Ryujinx.Common.SystemInfo.SystemInfo.IsBionic = true;
|
Logger.AddTarget(
|
||||||
|
new AsyncLogTargetWrapper(
|
||||||
|
new AndroidLogTarget("Ryujinx"),
|
||||||
|
1000,
|
||||||
|
AsyncLogTargetOverflowAction.Block
|
||||||
|
));
|
||||||
|
|
||||||
|
var path = GetString(jEnv, jpath);
|
||||||
|
|
||||||
var init = Initialize(path, enableDebugLogs);
|
var init = Initialize(path, enableDebugLogs);
|
||||||
|
|
||||||
@ -63,17 +71,10 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
_surfaceEvent = new ManualResetEvent(false);
|
_surfaceEvent = new ManualResetEvent(false);
|
||||||
|
|
||||||
Logger.AddTarget(
|
|
||||||
new AsyncLogTargetWrapper(
|
|
||||||
new AndroidLogTarget("Ryujinx"),
|
|
||||||
1000,
|
|
||||||
AsyncLogTargetOverflowAction.Block
|
|
||||||
));
|
|
||||||
|
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetString(JEnvRef jEnv, JStringLocalRef jString)
|
private static string? GetString(JEnvRef jEnv, JStringLocalRef jString)
|
||||||
{
|
{
|
||||||
var stringPtr = getStringPointer(jEnv, jString);
|
var stringPtr = getStringPointer(jEnv, jString);
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
var jobject = getObjectClass(jEnv, graphicObject);
|
var jobject = getObjectClass(jEnv, graphicObject);
|
||||||
|
|
||||||
GraphicsConfiguration graphicsConfiguration = new GraphicsConfiguration()
|
GraphicsConfiguration graphicsConfiguration = new()
|
||||||
{
|
{
|
||||||
EnableShaderCache = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableShaderCache"), GetCCharSequence("Z"))),
|
EnableShaderCache = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableShaderCache"), GetCCharSequence("Z"))),
|
||||||
EnableMacroHLE = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableMacroHLE"), GetCCharSequence("Z"))),
|
EnableMacroHLE = getBooleanField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("EnableMacroHLE"), GetCCharSequence("Z"))),
|
||||||
@ -191,17 +192,17 @@ namespace LibRyujinx
|
|||||||
MaxAnisotropy = getFloatField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("MaxAnisotropy"), 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")))
|
BackendThreading = (BackendThreading)(int)getIntField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("BackendThreading"), GetCCharSequence("I")))
|
||||||
};
|
};
|
||||||
Silk.NET.Core.Loader.SearchPathContainer.Platform = Silk.NET.Core.Loader.UnderlyingPlatform.Android;
|
SearchPathContainer.Platform = UnderlyingPlatform.Android;
|
||||||
return InitializeGraphics(graphicsConfiguration);
|
return InitializeGraphics(graphicsConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CCharSequence GetCCharSequence(string s)
|
private static CCharSequence GetCCharSequence(string s)
|
||||||
{
|
{
|
||||||
return (CCharSequence)Encoding.UTF8.GetBytes(s).AsSpan();
|
return Encoding.UTF8.GetBytes(s).AsSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsSetSurface")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsSetSurface")]
|
||||||
public unsafe static void JniSetSurface(JEnvRef jEnv, JObjectLocalRef jObj, JLong surfacePtr)
|
public static void JniSetSurface(JEnvRef jEnv, JObjectLocalRef jObj, JLong surfacePtr)
|
||||||
{
|
{
|
||||||
_surfacePtr = surfacePtr;
|
_surfacePtr = surfacePtr;
|
||||||
|
|
||||||
@ -235,7 +236,7 @@ namespace LibRyujinx
|
|||||||
var getLongField = getLongFieldPtr.GetUnsafeDelegate<GetLongFieldDelegate>();
|
var getLongField = getLongFieldPtr.GetUnsafeDelegate<GetLongFieldDelegate>();
|
||||||
var getObjectField = getObjectFieldPtr.GetUnsafeDelegate<GetObjectFieldDelegate>();
|
var getObjectField = getObjectFieldPtr.GetUnsafeDelegate<GetObjectFieldDelegate>();
|
||||||
|
|
||||||
List<string> extensions = new List<string>();
|
List<string?> extensions = new();
|
||||||
|
|
||||||
var count = getArrayLength(jEnv, extensionsArray);
|
var count = getArrayLength(jEnv, extensionsArray);
|
||||||
|
|
||||||
@ -249,9 +250,9 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
_surfaceEvent.Set();
|
_surfaceEvent.Set();
|
||||||
|
|
||||||
_surfacePtr = (long)surfacePtr;
|
_surfacePtr = surfacePtr;
|
||||||
|
|
||||||
CreateSurface createSurfaceFunc = (IntPtr instance) =>
|
CreateSurface createSurfaceFunc = instance =>
|
||||||
{
|
{
|
||||||
_surfaceEvent.WaitOne();
|
_surfaceEvent.WaitOne();
|
||||||
_surfaceEvent.Reset();
|
_surfaceEvent.Reset();
|
||||||
@ -259,10 +260,10 @@ namespace LibRyujinx
|
|||||||
var api = Vk.GetApi();
|
var api = Vk.GetApi();
|
||||||
if (api.TryGetInstanceExtension(new Instance(instance), out KhrAndroidSurface surfaceExtension))
|
if (api.TryGetInstanceExtension(new Instance(instance), out KhrAndroidSurface surfaceExtension))
|
||||||
{
|
{
|
||||||
var createInfo = new AndroidSurfaceCreateInfoKHR()
|
var createInfo = new AndroidSurfaceCreateInfoKHR
|
||||||
{
|
{
|
||||||
SType = StructureType.AndroidSurfaceCreateInfoKhr,
|
SType = StructureType.AndroidSurfaceCreateInfoKhr,
|
||||||
Window = (nint*)_surfacePtr
|
Window = (nint*)_surfacePtr,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = surfaceExtension.CreateAndroidSurface(new Instance(instance), createInfo, null, out var surface);
|
var result = surfaceExtension.CreateAndroidSurface(new Instance(instance), createInfo, null, out var surface);
|
||||||
@ -296,9 +297,8 @@ namespace LibRyujinx
|
|||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfoFromPath")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfoFromPath")]
|
||||||
public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef path)
|
public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef path)
|
||||||
{
|
{
|
||||||
var info = GetGameInfo(GetString(jEnv, path)) ?? new GameInfo();
|
var info = GetGameInfo(GetString(jEnv, path));
|
||||||
SHA256 sha;
|
return GetInfo(jEnv, info, out SHA256 _);
|
||||||
return GetInfo(jEnv, info, out sha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfo")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfo")]
|
||||||
@ -306,12 +306,11 @@ namespace LibRyujinx
|
|||||||
{
|
{
|
||||||
using var stream = OpenFile(fileDescriptor);
|
using var stream = OpenFile(fileDescriptor);
|
||||||
|
|
||||||
var info = GetGameInfo(stream, isXci) ?? new GameInfo();
|
var info = GetGameInfo(stream, isXci);
|
||||||
SHA256 sha;
|
return GetInfo(jEnv, info, out SHA256 _);
|
||||||
return GetInfo(jEnv, info, out sha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JObjectLocalRef GetInfo(JEnvRef jEnv, GameInfo info, out SHA256 sha)
|
private static JObjectLocalRef GetInfo(JEnvRef jEnv, GameInfo? info, out SHA256 sha)
|
||||||
{
|
{
|
||||||
var javaClassName = GetCCharSequence("org/ryujinx/android/viewmodels/GameInfo");
|
var javaClassName = GetCCharSequence("org/ryujinx/android/viewmodels/GameInfo");
|
||||||
|
|
||||||
@ -339,7 +338,7 @@ namespace LibRyujinx
|
|||||||
var constructor = getMethod(jEnv, javaClass, GetCCharSequence("<init>"), GetCCharSequence("()V"));
|
var constructor = getMethod(jEnv, javaClass, GetCCharSequence("<init>"), GetCCharSequence("()V"));
|
||||||
var newObj = newObject(jEnv, javaClass, constructor, 0);
|
var newObj = newObject(jEnv, javaClass, constructor, 0);
|
||||||
sha = SHA256.Create();
|
sha = SHA256.Create();
|
||||||
var iconCacheByte = sha.ComputeHash(info.Icon ?? new byte[0]);
|
var iconCacheByte = sha.ComputeHash(info?.Icon ?? Array.Empty<byte>());
|
||||||
var iconCache = BitConverter.ToString(iconCacheByte).Replace("-", "");
|
var iconCache = BitConverter.ToString(iconCacheByte).Replace("-", "");
|
||||||
|
|
||||||
var cacheDirectory = Path.Combine(AppDataManager.BaseDirPath, "iconCache");
|
var cacheDirectory = Path.Combine(AppDataManager.BaseDirPath, "iconCache");
|
||||||
@ -348,21 +347,23 @@ namespace LibRyujinx
|
|||||||
var cachePath = Path.Combine(cacheDirectory, iconCache);
|
var cachePath = Path.Combine(cacheDirectory, iconCache);
|
||||||
if (!File.Exists(cachePath))
|
if (!File.Exists(cachePath))
|
||||||
{
|
{
|
||||||
File.WriteAllBytes(cachePath, info.Icon ?? new byte[0]);
|
File.WriteAllBytes(cachePath, info?.Icon ?? Array.Empty<byte>());
|
||||||
}
|
}
|
||||||
|
|
||||||
setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("TitleName"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, info.TitleName)._value);
|
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("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("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("Version"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, info?.Version)._value);
|
||||||
setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("IconCache"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, iconCache)._value);
|
setObjectField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("IconCache"), GetCCharSequence("Ljava/lang/String;")), CreateString(jEnv, iconCache)._value);
|
||||||
setDoubleField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("FileSize"), GetCCharSequence("D")), info.FileSize);
|
setDoubleField(jEnv, newObj, getFieldId(jEnv, javaClass, GetCCharSequence("FileSize"), GetCCharSequence("D")), info?.FileSize ?? 0d);
|
||||||
|
|
||||||
return newObj;
|
return newObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JStringLocalRef CreateString(JEnvRef jEnv, string s)
|
private static JStringLocalRef CreateString(JEnvRef jEnv, string? s)
|
||||||
{
|
{
|
||||||
|
s ??= string.Empty;
|
||||||
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(s);
|
var ptr = Marshal.StringToHGlobalAnsi(s);
|
||||||
|
|
||||||
var str = createString(jEnv, ptr);
|
var str = createString(jEnv, ptr);
|
||||||
@ -417,19 +418,19 @@ namespace LibRyujinx
|
|||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonPressed")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonPressed")]
|
||||||
public static void JniSetButtonPressed(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id)
|
public static void JniSetButtonPressed(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id)
|
||||||
{
|
{
|
||||||
SetButtonPressed((Ryujinx.Input.GamepadButtonInputId)(int)button, id);
|
SetButtonPressed((GamepadButtonInputId)(int)button, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonReleased")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonReleased")]
|
||||||
public static void JniSetButtonReleased(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id)
|
public static void JniSetButtonReleased(JEnvRef jEnv, JObjectLocalRef jObj, JInt button, JInt id)
|
||||||
{
|
{
|
||||||
SetButtonReleased((Ryujinx.Input.GamepadButtonInputId)(int)button, id);
|
SetButtonReleased((GamepadButtonInputId)(int)button, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetStickAxis")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetStickAxis")]
|
||||||
public static void JniSetStickAxis(JEnvRef jEnv, JObjectLocalRef jObj, JInt stick, JFloat x, JFloat y, JInt id)
|
public static void JniSetStickAxis(JEnvRef jEnv, JObjectLocalRef jObj, JInt stick, JFloat x, JFloat y, JInt id)
|
||||||
{
|
{
|
||||||
SetStickAxis((Ryujinx.Input.StickInputId)(int)stick, new System.Numerics.Vector2(x, y), id);
|
SetStickAxis((StickInputId)(int)stick, new Vector2(x, y), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputConnectGamepad")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputConnectGamepad")]
|
||||||
@ -438,7 +439,7 @@ namespace LibRyujinx
|
|||||||
return ConnectGamepad(index);
|
return ConnectGamepad(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream OpenFile(int descriptor)
|
private static FileStream OpenFile(int descriptor)
|
||||||
{
|
{
|
||||||
var safeHandle = new SafeFileHandle(descriptor, false);
|
var safeHandle = new SafeFileHandle(descriptor, false);
|
||||||
|
|
||||||
@ -464,7 +465,7 @@ namespace LibRyujinx
|
|||||||
Warn = 0x05,
|
Warn = 0x05,
|
||||||
Error = 0x06,
|
Error = 0x06,
|
||||||
Fatal = 0x07,
|
Fatal = 0x07,
|
||||||
Silent = 0x08
|
Silent = 0x08,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,8 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LibRyujinx
|
namespace LibRyujinx
|
||||||
{
|
{
|
||||||
@ -27,7 +23,7 @@ namespace LibRyujinx
|
|||||||
bool enableDockedMode,
|
bool enableDockedMode,
|
||||||
bool enablePtc,
|
bool enablePtc,
|
||||||
bool enableInternetAccess,
|
bool enableInternetAccess,
|
||||||
string timeZone,
|
string? timeZone,
|
||||||
bool ignoreMissingServices)
|
bool ignoreMissingServices)
|
||||||
{
|
{
|
||||||
if (SwitchDevice == null)
|
if (SwitchDevice == null)
|
||||||
@ -66,7 +62,7 @@ namespace LibRyujinx
|
|||||||
return (isXci ? emulationContext?.LoadXci(stream) : emulationContext.LoadNsp(stream)) ?? false;
|
return (isXci ? emulationContext?.LoadXci(stream) : emulationContext.LoadNsp(stream)) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool LoadApplication(string path)
|
public static bool LoadApplication(string? path)
|
||||||
{
|
{
|
||||||
var emulationContext = SwitchDevice.EmulationContext;
|
var emulationContext = SwitchDevice.EmulationContext;
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ namespace LibRyujinx
|
|||||||
return InitializeGraphicsRenderer(graphicsBackend, createSurfaceFunc, extensions.ToArray());
|
return InitializeGraphicsRenderer(graphicsBackend, createSurfaceFunc, extensions.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool InitializeGraphicsRenderer(GraphicsBackend graphicsBackend, CreateSurface createSurfaceFunc, string[] requiredExtensions)
|
public static bool InitializeGraphicsRenderer(GraphicsBackend graphicsBackend, CreateSurface createSurfaceFunc, string?[] requiredExtensions)
|
||||||
{
|
{
|
||||||
if (Renderer != null)
|
if (Renderer != null)
|
||||||
{
|
{
|
||||||
|
@ -21,9 +21,6 @@ using LibHac.Common;
|
|||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
@ -34,7 +31,6 @@ using Ryujinx.Common.Utilities;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Ryujinx.Ui.Common.Configuration.System;
|
using Ryujinx.Ui.Common.Configuration.System;
|
||||||
using Ryujinx.Common.Logging.Targets;
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using Ryujinx.Common;
|
|
||||||
|
|
||||||
namespace LibRyujinx
|
namespace LibRyujinx
|
||||||
{
|
{
|
||||||
@ -42,7 +38,7 @@ namespace LibRyujinx
|
|||||||
{
|
{
|
||||||
internal static IHardwareDeviceDriver AudioDriver { get; set; } = new DummyHardwareDeviceDriver();
|
internal static IHardwareDeviceDriver AudioDriver { get; set; } = new DummyHardwareDeviceDriver();
|
||||||
|
|
||||||
private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
public static SwitchDevice? SwitchDevice { get; set; }
|
public static SwitchDevice? SwitchDevice { get; set; }
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "initialize")]
|
[UnmanagedCallersOnly(EntryPoint = "initialize")]
|
||||||
@ -71,8 +67,6 @@ namespace LibRyujinx
|
|||||||
ConfigurationState.Initialize();
|
ConfigurationState.Initialize();
|
||||||
LoggerModule.Initialize();
|
LoggerModule.Initialize();
|
||||||
|
|
||||||
SwitchDevice = new SwitchDevice();
|
|
||||||
|
|
||||||
Logger.SetEnable(LogLevel.Debug, enableDebugLogs);
|
Logger.SetEnable(LogLevel.Debug, enableDebugLogs);
|
||||||
Logger.SetEnable(LogLevel.Stub, false);
|
Logger.SetEnable(LogLevel.Stub, false);
|
||||||
Logger.SetEnable(LogLevel.Info, true);
|
Logger.SetEnable(LogLevel.Info, true);
|
||||||
@ -80,18 +74,27 @@ namespace LibRyujinx
|
|||||||
Logger.SetEnable(LogLevel.Error, true);
|
Logger.SetEnable(LogLevel.Error, true);
|
||||||
Logger.SetEnable(LogLevel.Trace, false);
|
Logger.SetEnable(LogLevel.Trace, false);
|
||||||
Logger.SetEnable(LogLevel.Guest, true);
|
Logger.SetEnable(LogLevel.Guest, true);
|
||||||
Logger.SetEnable(LogLevel.AccessLog, false);
|
Logger.SetEnable(LogLevel.AccessLog, false);
|
||||||
|
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
new FileLogTarget(AppDataManager.BaseDirPath, "file"),
|
new FileLogTarget(AppDataManager.BaseDirPath, "file"),
|
||||||
1000,
|
1000,
|
||||||
AsyncLogTargetOverflowAction.Block
|
AsyncLogTargetOverflowAction.Block
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Logger.Notice.Print(LogClass.Application, "Initializing...");
|
||||||
|
Logger.Notice.Print(LogClass.Application, $"Using base path: {AppDataManager.BaseDirPath}");
|
||||||
|
|
||||||
|
SwitchDevice = new SwitchDevice();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.Notice.Print(LogClass.Application, "RyujinxAndroid is ready!");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +110,7 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
var context = SwitchDevice.EmulationContext;
|
var context = SwitchDevice.EmulationContext;
|
||||||
|
|
||||||
return new GameStats()
|
return new GameStats
|
||||||
{
|
{
|
||||||
Fifo = context.Statistics.GetFifoPercent(),
|
Fifo = context.Statistics.GetFifoPercent(),
|
||||||
GameFps = context.Statistics.GetGameFrameRate(),
|
GameFps = context.Statistics.GetGameFrameRate(),
|
||||||
@ -116,24 +119,37 @@ namespace LibRyujinx
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static GameInfo GetGameInfo(string file)
|
public static GameInfo? GetGameInfo(string? file)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(file))
|
||||||
|
{
|
||||||
|
return new GameInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Getting game info for file: {file}");
|
||||||
|
|
||||||
using var stream = File.Open(file, FileMode.Open);
|
using var stream = File.Open(file, FileMode.Open);
|
||||||
|
|
||||||
return GetGameInfo(stream, file.ToLower().EndsWith("xci"));
|
return GetGameInfo(stream, file.ToLower().EndsWith("xci"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameInfo GetGameInfo(Stream gameStream, bool isXci)
|
public static GameInfo? GetGameInfo(Stream gameStream, bool isXci)
|
||||||
{
|
{
|
||||||
var gameInfo = new GameInfo();
|
if (SwitchDevice == null)
|
||||||
gameInfo.FileSize = gameStream.Length * 0.000000000931;
|
{
|
||||||
gameInfo.TitleName = "Unknown";
|
Logger.Error?.Print(LogClass.Application, "SwitchDevice is not initialized.");
|
||||||
gameInfo.TitleId = "0000000000000000";
|
return null;
|
||||||
gameInfo.Developer = "Unknown";
|
}
|
||||||
gameInfo.Version = "0";
|
|
||||||
gameInfo.Icon = null;
|
|
||||||
|
|
||||||
Language titleLanguage = Language.AmericanEnglish;
|
var gameInfo = new GameInfo
|
||||||
|
{
|
||||||
|
FileSize = gameStream.Length * 0.000000000931, TitleName = "Unknown", TitleId = "0000000000000000",
|
||||||
|
Developer = "Unknown",
|
||||||
|
Version = "0",
|
||||||
|
Icon = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Language TitleLanguage = Language.AmericanEnglish;
|
||||||
|
|
||||||
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
|
||||||
|
|
||||||
@ -205,13 +221,12 @@ namespace LibRyujinx
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var id = gameInfo.TitleId;
|
GetControlFsAndTitleId(pfs, out IFileSystem? controlFs, out string? id);
|
||||||
GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out id);
|
|
||||||
|
|
||||||
gameInfo.TitleId = id;
|
gameInfo.TitleId = id;
|
||||||
|
|
||||||
// Check if there is an update available.
|
// Check if there is an update available.
|
||||||
if (IsUpdateApplied(gameInfo.TitleId, out IFileSystem updatedControlFs))
|
if (IsUpdateApplied(gameInfo.TitleId, out IFileSystem? updatedControlFs))
|
||||||
{
|
{
|
||||||
// Replace the original ControlFs by the updated one.
|
// Replace the original ControlFs by the updated one.
|
||||||
controlFs = updatedControlFs;
|
controlFs = updatedControlFs;
|
||||||
@ -226,7 +241,7 @@ namespace LibRyujinx
|
|||||||
{
|
{
|
||||||
using UniqueRef<IFile> icon = new();
|
using UniqueRef<IFile> icon = new();
|
||||||
|
|
||||||
controlFs.OpenFile(ref icon.Ref, $"/icon_{titleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
controlFs?.OpenFile(ref icon.Ref, $"/icon_{TitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
using MemoryStream stream = new();
|
using MemoryStream stream = new();
|
||||||
|
|
||||||
@ -244,7 +259,7 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
using var icon = new UniqueRef<IFile>();
|
using var icon = new UniqueRef<IFile>();
|
||||||
|
|
||||||
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
controlFs?.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
using MemoryStream stream = new();
|
using MemoryStream stream = new();
|
||||||
|
|
||||||
@ -264,10 +279,9 @@ namespace LibRyujinx
|
|||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
|
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
|
||||||
}
|
}
|
||||||
catch (InvalidDataException)
|
catch (InvalidDataException exception)
|
||||||
{
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. {exception}");
|
||||||
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. ");
|
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@ -281,17 +295,17 @@ namespace LibRyujinx
|
|||||||
Logger.Warning?.Print(LogClass.Application, exception.Message);
|
Logger.Warning?.Print(LogClass.Application, exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
|
void ReadControlData(IFileSystem? controlFs, Span<byte> outProperty)
|
||||||
{
|
{
|
||||||
using UniqueRef<IFile> controlFile = new();
|
using UniqueRef<IFile> controlFile = new();
|
||||||
|
|
||||||
controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
controlFs?.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
|
controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version)
|
void GetGameInformation(ref ApplicationControlProperty controlData, out string? titleName, out string titleId, out string? publisher, out string? version)
|
||||||
{
|
{
|
||||||
_ = Enum.TryParse(titleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
_ = Enum.TryParse(TitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
||||||
|
|
||||||
if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage)
|
if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage)
|
||||||
{
|
{
|
||||||
@ -350,20 +364,28 @@ namespace LibRyujinx
|
|||||||
version = controlData.DisplayVersionString.ToString();
|
version = controlData.DisplayVersionString.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId)
|
void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem? controlFs, out string? titleId)
|
||||||
{
|
{
|
||||||
(_, _, Nca controlNca) = GetGameData(SwitchDevice.VirtualFileSystem, pfs, 0);
|
if (SwitchDevice == null)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "SwitchDevice is not initialized.");
|
||||||
|
|
||||||
|
controlFs = null;
|
||||||
|
titleId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(_, _, Nca? controlNca) = GetGameData(SwitchDevice.VirtualFileSystem, pfs, 0);
|
||||||
|
|
||||||
// Return the ControlFS
|
// Return the ControlFS
|
||||||
controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
|
controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
|
||||||
titleId = controlNca?.Header.TitleId.ToString("x16");
|
titleId = controlNca?.Header.TitleId.ToString("x16");
|
||||||
}
|
}
|
||||||
|
|
||||||
(Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
|
(Nca? mainNca, Nca? patchNca, Nca? controlNca) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
|
||||||
{
|
{
|
||||||
Nca mainNca = null;
|
Nca? mainNca = null;
|
||||||
Nca patchNca = null;
|
Nca? patchNca = null;
|
||||||
Nca controlNca = null;
|
Nca? controlNca = null;
|
||||||
|
|
||||||
fileSystem.ImportTickets(pfs);
|
fileSystem.ImportTickets(pfs);
|
||||||
|
|
||||||
@ -371,9 +393,11 @@ namespace LibRyujinx
|
|||||||
{
|
{
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Loading file from PFS: {fileEntry.FullPath}");
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
||||||
|
|
||||||
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
||||||
|
|
||||||
@ -404,22 +428,28 @@ namespace LibRyujinx
|
|||||||
return (mainNca, patchNca, controlNca);
|
return (mainNca, patchNca, controlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs)
|
bool IsUpdateApplied(string? titleId, out IFileSystem? updatedControlFs)
|
||||||
{
|
{
|
||||||
updatedControlFs = null;
|
updatedControlFs = null;
|
||||||
|
|
||||||
string updatePath = "(unknown)";
|
string? updatePath = "(unknown)";
|
||||||
|
|
||||||
|
if (SwitchDevice?.VirtualFileSystem == null)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "SwitchDevice was not initialized.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(Nca patchNca, Nca controlNca) = GetGameUpdateData(SwitchDevice.VirtualFileSystem, titleId, 0, out updatePath);
|
(Nca? patchNca, Nca? controlNca) = GetGameUpdateData(SwitchDevice.VirtualFileSystem, titleId, 0, out updatePath);
|
||||||
|
|
||||||
if (patchNca != null && controlNca != null)
|
if (patchNca != null && controlNca != null)
|
||||||
{
|
{
|
||||||
updatedControlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
|
updatedControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (InvalidDataException)
|
catch (InvalidDataException)
|
||||||
{
|
{
|
||||||
@ -433,7 +463,7 @@ namespace LibRyujinx
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
(Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath)
|
(Nca? patch, Nca? control) GetGameUpdateData(VirtualFileSystem fileSystem, string? titleId, int programIndex, out string? updatePath)
|
||||||
{
|
{
|
||||||
updatePath = null;
|
updatePath = null;
|
||||||
|
|
||||||
@ -447,12 +477,12 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
if (File.Exists(titleUpdateMetadataPath))
|
||||||
{
|
{
|
||||||
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected;
|
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
|
||||||
|
|
||||||
if (File.Exists(updatePath))
|
if (File.Exists(updatePath))
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
|
FileStream file = new(updatePath, FileMode.Open, FileAccess.Read);
|
||||||
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
PartitionFileSystem nsp = new(file.AsStorage());
|
||||||
|
|
||||||
return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex);
|
return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex);
|
||||||
}
|
}
|
||||||
@ -462,10 +492,10 @@ namespace LibRyujinx
|
|||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex)
|
(Nca? patchNca, Nca? controlNca) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex)
|
||||||
{
|
{
|
||||||
Nca patchNca = null;
|
Nca? patchNca = null;
|
||||||
Nca controlNca = null;
|
Nca? controlNca = null;
|
||||||
|
|
||||||
fileSystem.ImportTickets(pfs);
|
fileSystem.ImportTickets(pfs);
|
||||||
|
|
||||||
@ -475,7 +505,7 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage());
|
||||||
|
|
||||||
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
|
||||||
|
|
||||||
@ -518,7 +548,9 @@ namespace LibRyujinx
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
VirtualFileSystem?.Dispose();
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
VirtualFileSystem.Dispose();
|
||||||
InputManager?.Dispose();
|
InputManager?.Dispose();
|
||||||
EmulationContext?.Dispose();
|
EmulationContext?.Dispose();
|
||||||
}
|
}
|
||||||
@ -546,7 +578,7 @@ namespace LibRyujinx
|
|||||||
bool enableDockedMode,
|
bool enableDockedMode,
|
||||||
bool enablePtc,
|
bool enablePtc,
|
||||||
bool enableInternetAccess,
|
bool enableInternetAccess,
|
||||||
string timeZone,
|
string? timeZone,
|
||||||
bool ignoreMissingServices)
|
bool ignoreMissingServices)
|
||||||
{
|
{
|
||||||
if (LibRyujinx.Renderer == null)
|
if (LibRyujinx.Renderer == null)
|
||||||
@ -605,11 +637,11 @@ namespace LibRyujinx
|
|||||||
public class GameInfo
|
public class GameInfo
|
||||||
{
|
{
|
||||||
public double FileSize;
|
public double FileSize;
|
||||||
public string TitleName;
|
public string? TitleName;
|
||||||
public string TitleId;
|
public string? TitleId;
|
||||||
public string Developer;
|
public string? Developer;
|
||||||
public string Version;
|
public string? Version;
|
||||||
public byte[] Icon;
|
public byte[]? Icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GameStats
|
public class GameStats
|
||||||
@ -618,4 +650,4 @@ namespace LibRyujinx
|
|||||||
public double GameFps;
|
public double GameFps;
|
||||||
public double GameTime;
|
public double GameTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if existing old baseDirPath is a symlink, to prevent possible errors.
|
// Check if existing old baseDirPath is a symlink, to prevent possible errors.
|
||||||
// Should be removed, when the existance of the old directory isn't checked anymore.
|
// Should be removed, when the existence of the old directory isn't checked anymore.
|
||||||
private static bool IsPathSymlink(string path)
|
private static bool IsPathSymlink(string path)
|
||||||
{
|
{
|
||||||
FileAttributes attributes = File.GetAttributes(path);
|
FileAttributes attributes = File.GetAttributes(path);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user