Cleanup LibRyujinx and add more verbose logging

This commit is contained in:
TSR Berry 2023-07-22 07:27:13 +02:00 committed by Emmanuel Hansen
parent ee4e18ff0d
commit 300b23cf9b
6 changed files with 155 additions and 128 deletions

View File

@ -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);
} }
} }
} }

View File

@ -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,
} }
} }
} }

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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;
} }
} }

View File

@ -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);