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.Targets;
using System;
using System.IO;
using System.Linq;
namespace LibRyujinx
{
@ -26,7 +23,7 @@ namespace LibRyujinx
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
{
@ -39,13 +36,14 @@ namespace LibRyujinx
LogLevel.AccessLog => Logcat.LogLevel.Info,
LogLevel.Notice => Logcat.LogLevel.Info,
LogLevel.Trace => Logcat.LogLevel.Verbose,
_ => throw new NotImplementedException()
_ => throw new NotImplementedException(),
};
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@ -1,27 +1,28 @@
using System;
using System.Runtime.InteropServices;
using Ryujinx.Common.Configuration;
using System.Collections.Generic;
using LibRyujinx.Jni;
using LibRyujinx.Jni.Pointers;
using LibRyujinx.Jni.Primitives;
using LibRyujinx.Jni.References;
using LibRyujinx.Jni.Values;
using LibRyujinx.Jni.Primitives;
using LibRyujinx.Jni;
using LibRyujinx.Shared.Audio.Oboe;
using Microsoft.Win32.SafeHandles;
using Rxmxnx.PInvoke;
using System.Text;
using LibRyujinx.Jni.Internal.Pointers;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
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.Extensions.KHR;
using LibRyujinx.Shared.Audio.Oboe;
using System.Threading;
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Win32.SafeHandles;
using Newtonsoft.Json.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using LibHac.Tools.Fs;
using Ryujinx.HLE.HOS.SystemState;
using System.Text;
using System.Threading;
namespace LibRyujinx
{
@ -53,9 +54,16 @@ namespace LibRyujinx
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_initialize")]
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);
@ -63,17 +71,10 @@ namespace LibRyujinx
_surfaceEvent = new ManualResetEvent(false);
Logger.AddTarget(
new AsyncLogTargetWrapper(
new AndroidLogTarget("Ryujinx"),
1000,
AsyncLogTargetOverflowAction.Block
));
return init;
}
private static string GetString(JEnvRef jEnv, JStringLocalRef jString)
private static string? GetString(JEnvRef jEnv, JStringLocalRef jString)
{
var stringPtr = getStringPointer(jEnv, jString);
@ -179,7 +180,7 @@ namespace LibRyujinx
var jobject = getObjectClass(jEnv, graphicObject);
GraphicsConfiguration graphicsConfiguration = new GraphicsConfiguration()
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"))),
@ -191,17 +192,17 @@ namespace LibRyujinx
MaxAnisotropy = getFloatField(jEnv, graphicObject, getFieldId(jEnv, jobject, GetCCharSequence("MaxAnisotropy"), GetCCharSequence("F"))),
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);
}
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")]
public unsafe static void JniSetSurface(JEnvRef jEnv, JObjectLocalRef jObj, JLong surfacePtr)
public static void JniSetSurface(JEnvRef jEnv, JObjectLocalRef jObj, JLong surfacePtr)
{
_surfacePtr = surfacePtr;
@ -235,7 +236,7 @@ namespace LibRyujinx
var getLongField = getLongFieldPtr.GetUnsafeDelegate<GetLongFieldDelegate>();
var getObjectField = getObjectFieldPtr.GetUnsafeDelegate<GetObjectFieldDelegate>();
List<string> extensions = new List<string>();
List<string?> extensions = new();
var count = getArrayLength(jEnv, extensionsArray);
@ -249,9 +250,9 @@ namespace LibRyujinx
_surfaceEvent.Set();
_surfacePtr = (long)surfacePtr;
_surfacePtr = surfacePtr;
CreateSurface createSurfaceFunc = (IntPtr instance) =>
CreateSurface createSurfaceFunc = instance =>
{
_surfaceEvent.WaitOne();
_surfaceEvent.Reset();
@ -259,10 +260,10 @@ namespace LibRyujinx
var api = Vk.GetApi();
if (api.TryGetInstanceExtension(new Instance(instance), out KhrAndroidSurface surfaceExtension))
{
var createInfo = new AndroidSurfaceCreateInfoKHR()
var createInfo = new AndroidSurfaceCreateInfoKHR
{
SType = StructureType.AndroidSurfaceCreateInfoKhr,
Window = (nint*)_surfacePtr
Window = (nint*)_surfacePtr,
};
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")]
public static JObjectLocalRef JniGetGameInfo(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef path)
{
var info = GetGameInfo(GetString(jEnv, path)) ?? new GameInfo();
SHA256 sha;
return GetInfo(jEnv, info, out sha);
var info = GetGameInfo(GetString(jEnv, path));
return GetInfo(jEnv, info, out SHA256 _);
}
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceGetGameInfo")]
@ -306,12 +306,11 @@ namespace LibRyujinx
{
using var stream = OpenFile(fileDescriptor);
var info = GetGameInfo(stream, isXci) ?? new GameInfo();
SHA256 sha;
return GetInfo(jEnv, info, out sha);
var info = GetGameInfo(stream, isXci);
return GetInfo(jEnv, info, out SHA256 _);
}
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");
@ -339,7 +338,7 @@ namespace LibRyujinx
var constructor = getMethod(jEnv, javaClass, GetCCharSequence("<init>"), GetCCharSequence("()V"));
var newObj = newObject(jEnv, javaClass, constructor, 0);
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 cacheDirectory = Path.Combine(AppDataManager.BaseDirPath, "iconCache");
@ -348,21 +347,23 @@ namespace LibRyujinx
var cachePath = Path.Combine(cacheDirectory, iconCache);
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("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("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("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;
}
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 str = createString(jEnv, ptr);
@ -417,19 +418,19 @@ namespace LibRyujinx
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_inputSetButtonPressed")]
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")]
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")]
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")]
@ -438,7 +439,7 @@ namespace LibRyujinx
return ConnectGamepad(index);
}
private static Stream OpenFile(int descriptor)
private static FileStream OpenFile(int descriptor)
{
var safeHandle = new SafeFileHandle(descriptor, false);
@ -464,7 +465,7 @@ namespace LibRyujinx
Warn = 0x05,
Error = 0x06,
Fatal = 0x07,
Silent = 0x08
Silent = 0x08,
}
}
}

View File

@ -2,12 +2,8 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.SystemState;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace LibRyujinx
{
@ -27,7 +23,7 @@ namespace LibRyujinx
bool enableDockedMode,
bool enablePtc,
bool enableInternetAccess,
string timeZone,
string? timeZone,
bool ignoreMissingServices)
{
if (SwitchDevice == null)
@ -66,7 +62,7 @@ namespace LibRyujinx
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;

View File

@ -78,7 +78,7 @@ namespace LibRyujinx
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)
{

View File

@ -21,9 +21,6 @@ using LibHac.Common;
using LibHac.Ns;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ui.App.Common;
using System.Text;
using System.Threading;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Fs;
@ -34,7 +31,6 @@ using Ryujinx.Common.Utilities;
using System.Globalization;
using Ryujinx.Ui.Common.Configuration.System;
using Ryujinx.Common.Logging.Targets;
using Ryujinx.Common;
namespace LibRyujinx
{
@ -42,7 +38,7 @@ namespace LibRyujinx
{
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; }
[UnmanagedCallersOnly(EntryPoint = "initialize")]
@ -71,8 +67,6 @@ namespace LibRyujinx
ConfigurationState.Initialize();
LoggerModule.Initialize();
SwitchDevice = new SwitchDevice();
Logger.SetEnable(LogLevel.Debug, enableDebugLogs);
Logger.SetEnable(LogLevel.Stub, false);
Logger.SetEnable(LogLevel.Info, true);
@ -80,18 +74,27 @@ namespace LibRyujinx
Logger.SetEnable(LogLevel.Error, true);
Logger.SetEnable(LogLevel.Trace, false);
Logger.SetEnable(LogLevel.Guest, true);
Logger.SetEnable(LogLevel.AccessLog, false);
Logger.SetEnable(LogLevel.AccessLog, false);
Logger.AddTarget(new AsyncLogTargetWrapper(
new FileLogTarget(AppDataManager.BaseDirPath, "file"),
1000,
AsyncLogTargetOverflowAction.Block
));
Logger.Notice.Print(LogClass.Application, "Initializing...");
Logger.Notice.Print(LogClass.Application, $"Using base path: {AppDataManager.BaseDirPath}");
SwitchDevice = new SwitchDevice();
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
Logger.Notice.Print(LogClass.Application, "RyujinxAndroid is ready!");
return true;
}
@ -107,7 +110,7 @@ namespace LibRyujinx
var context = SwitchDevice.EmulationContext;
return new GameStats()
return new GameStats
{
Fifo = context.Statistics.GetFifoPercent(),
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);
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();
gameInfo.FileSize = gameStream.Length * 0.000000000931;
gameInfo.TitleName = "Unknown";
gameInfo.TitleId = "0000000000000000";
gameInfo.Developer = "Unknown";
gameInfo.Version = "0";
gameInfo.Icon = null;
if (SwitchDevice == null)
{
Logger.Error?.Print(LogClass.Application, "SwitchDevice is not initialized.");
return 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);
@ -205,13 +221,12 @@ namespace LibRyujinx
}
else
{
var id = gameInfo.TitleId;
GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out id);
GetControlFsAndTitleId(pfs, out IFileSystem? controlFs, out string? id);
gameInfo.TitleId = id;
// 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.
controlFs = updatedControlFs;
@ -226,7 +241,7 @@ namespace LibRyujinx
{
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();
@ -244,7 +259,7 @@ namespace LibRyujinx
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();
@ -264,10 +279,9 @@ namespace LibRyujinx
{
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. ");
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. {exception}");
}
catch (Exception exception)
{
@ -281,17 +295,17 @@ namespace LibRyujinx
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();
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();
}
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)
{
@ -350,20 +364,28 @@ namespace LibRyujinx
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
controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
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 patchNca = null;
Nca controlNca = null;
Nca? mainNca = null;
Nca? patchNca = null;
Nca? controlNca = null;
fileSystem.ImportTickets(pfs);
@ -371,9 +393,11 @@ namespace LibRyujinx
{
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();
Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage());
Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage());
int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
@ -404,22 +428,28 @@ namespace LibRyujinx
return (mainNca, patchNca, controlNca);
}
bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs)
bool IsUpdateApplied(string? titleId, out IFileSystem? updatedControlFs)
{
updatedControlFs = null;
string updatePath = "(unknown)";
string? updatePath = "(unknown)";
if (SwitchDevice?.VirtualFileSystem == null)
{
Logger.Error?.Print(LogClass.Application, "SwitchDevice was not initialized.");
return false;
}
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)
{
updatedControlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
return true;
updatedControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
}
return true;
}
catch (InvalidDataException)
{
@ -433,7 +463,7 @@ namespace LibRyujinx
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;
@ -447,12 +477,12 @@ namespace LibRyujinx
if (File.Exists(titleUpdateMetadataPath))
{
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected;
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
if (File.Exists(updatePath))
{
FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
FileStream file = new(updatePath, FileMode.Open, FileAccess.Read);
PartitionFileSystem nsp = new(file.AsStorage());
return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex);
}
@ -462,10 +492,10 @@ namespace LibRyujinx
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 controlNca = null;
Nca? patchNca = null;
Nca? controlNca = null;
fileSystem.ImportTickets(pfs);
@ -475,7 +505,7 @@ namespace LibRyujinx
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);
@ -518,7 +548,9 @@ namespace LibRyujinx
public void Dispose()
{
VirtualFileSystem?.Dispose();
GC.SuppressFinalize(this);
VirtualFileSystem.Dispose();
InputManager?.Dispose();
EmulationContext?.Dispose();
}
@ -546,7 +578,7 @@ namespace LibRyujinx
bool enableDockedMode,
bool enablePtc,
bool enableInternetAccess,
string timeZone,
string? timeZone,
bool ignoreMissingServices)
{
if (LibRyujinx.Renderer == null)
@ -605,11 +637,11 @@ namespace LibRyujinx
public class GameInfo
{
public double FileSize;
public string TitleName;
public string TitleId;
public string Developer;
public string Version;
public byte[] Icon;
public string? TitleName;
public string? TitleId;
public string? Developer;
public string? Version;
public byte[]? Icon;
}
public class GameStats
@ -618,4 +650,4 @@ namespace LibRyujinx
public double GameFps;
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.
// 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)
{
FileAttributes attributes = File.GetAttributes(path);