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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user