LibKenjinx: Fix possible NullReferenceExceptions

This commit is contained in:
KeatonTheBot
2026-03-09 18:00:40 -05:00
parent ce2f2a14af
commit 72955d1aa4
5 changed files with 57 additions and 48 deletions
+3 -3
View File
@@ -61,7 +61,7 @@ namespace LibKenjinx
}; };
// Try call method with single bool // Try call method with single bool
private static bool TryCallBool(object target, string[] names, bool arg) private static bool TryCallBool(object? target, string[] names, bool arg)
{ {
if (target == null) return false; if (target == null) return false;
var t = target.GetType(); var t = target.GetType();
@@ -77,7 +77,7 @@ namespace LibKenjinx
} }
// Try call method with single float // Try call method with single float
private static bool TryCallFloat(object target, string[] names, float arg) private static bool TryCallFloat(object? target, string[] names, float arg)
{ {
if (target == null) return false; if (target == null) return false;
var t = target.GetType(); var t = target.GetType();
@@ -93,7 +93,7 @@ namespace LibKenjinx
} }
// Try set property (float/double) // Try set property (float/double)
private static bool TrySetFloatProp(object target, string[] names, float value) private static bool TrySetFloatProp(object? target, string[] names, float value)
{ {
if (target == null) return false; if (target == null) return false;
var t = target.GetType(); var t = target.GetType();
+3 -3
View File
@@ -54,12 +54,12 @@ namespace LibKenjinx
public static void InstallFirmware(Stream stream, bool isXci) public static void InstallFirmware(Stream stream, bool isXci)
{ {
SwitchDevice?.ContentManager.InstallFirmware(stream, isXci); SwitchDevice?.ContentManager?.InstallFirmware(stream, isXci);
} }
public static string GetInstalledFirmwareVersion() public static string GetInstalledFirmwareVersion()
{ {
var version = SwitchDevice?.ContentManager.GetCurrentFirmwareVersion(); var version = SwitchDevice?.ContentManager?.GetCurrentFirmwareVersion();
if (version != null) if (version != null)
{ {
@@ -71,7 +71,7 @@ namespace LibKenjinx
public static SystemVersion? VerifyFirmware(Stream stream, bool isXci) public static SystemVersion? VerifyFirmware(Stream stream, bool isXci)
{ {
return SwitchDevice?.ContentManager.VerifyFirmwarePackage(stream, isXci) ?? null; return SwitchDevice?.ContentManager?.VerifyFirmwarePackage(stream, isXci) ?? null;
} }
public static bool LoadApplication(Stream stream, FileType type, Stream? updateStream = null) public static bool LoadApplication(Stream stream, FileType type, Stream? updateStream = null)
+10 -10
View File
@@ -8,7 +8,7 @@ namespace LibKenjinx
{ {
public static string GetOpenedUser() public static string GetOpenedUser()
{ {
var lastProfile = SwitchDevice?.AccountManager.LastOpenedUser; var lastProfile = SwitchDevice?.AccountManager?.LastOpenedUser;
return lastProfile?.UserId.ToString() ?? ""; return lastProfile?.UserId.ToString() ?? "";
} }
@@ -17,7 +17,7 @@ namespace LibKenjinx
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
var user = SwitchDevice?.AccountManager.GetAllUsers().FirstOrDefault(x => x.UserId == uid); var user = SwitchDevice?.AccountManager?.GetAllUsers().FirstOrDefault(x => x.UserId == uid);
if (user == null) if (user == null)
return ""; return "";
@@ -31,14 +31,14 @@ namespace LibKenjinx
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
SwitchDevice?.AccountManager.SetUserImage(uid, Convert.FromBase64String(picture)); SwitchDevice?.AccountManager?.SetUserImage(uid, Convert.FromBase64String(picture));
} }
public static string GetUserName(string userId) public static string GetUserName(string userId)
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
var user = SwitchDevice?.AccountManager.GetAllUsers().FirstOrDefault(x => x.UserId == uid); var user = SwitchDevice?.AccountManager?.GetAllUsers().FirstOrDefault(x => x.UserId == uid);
return user?.Name ?? ""; return user?.Name ?? "";
} }
@@ -47,36 +47,36 @@ namespace LibKenjinx
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
SwitchDevice?.AccountManager.SetUserName(uid, name); SwitchDevice?.AccountManager?.SetUserName(uid, name);
} }
public static string[] GetAllUsers() public static string[] GetAllUsers()
{ {
return SwitchDevice?.AccountManager.GetAllUsers().Select(x => x.UserId.ToString()).ToArray() ?? return SwitchDevice?.AccountManager?.GetAllUsers().Select(x => x.UserId.ToString()).ToArray() ??
[]; [];
} }
public static void AddUser(string userName, string picture) public static void AddUser(string userName, string picture)
{ {
SwitchDevice?.AccountManager.AddUser(userName, Convert.FromBase64String(picture)); SwitchDevice?.AccountManager?.AddUser(userName, Convert.FromBase64String(picture));
} }
public static void DeleteUser(string userId) public static void DeleteUser(string userId)
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
SwitchDevice?.AccountManager.DeleteUser(uid); SwitchDevice?.AccountManager?.DeleteUser(uid);
} }
public static void OpenUser(string userId) public static void OpenUser(string userId)
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
SwitchDevice?.AccountManager.OpenUser(uid); SwitchDevice?.AccountManager?.OpenUser(uid);
} }
public static void CloseUser(string userId) public static void CloseUser(string userId)
{ {
var uid = new UserId(userId); var uid = new UserId(userId);
SwitchDevice?.AccountManager.CloseUser(uid); SwitchDevice?.AccountManager?.CloseUser(uid);
} }
} }
} }
+37 -28
View File
@@ -523,7 +523,7 @@ namespace LibKenjinx
if (File.Exists(updatePath)) if (File.Exists(updatePath))
{ {
FileStream file = new(updatePath, FileMode.Open, FileAccess.Read); FileStream file = new(updatePath, FileMode.Open, FileAccess.Read);
IFileSystem pfs = null; IFileSystem pfs;
if (Path.GetExtension(updatePath).ToLower() == ".xci") if (Path.GetExtension(updatePath).ToLower() == ".xci")
{ {
@@ -616,8 +616,7 @@ namespace LibKenjinx
partitionFileSystem.OpenFile(ref ncaFile.Ref, ncaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); partitionFileSystem.OpenFile(ref ncaFile.Ref, ncaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), ncaPath); if (TryOpenNca(ncaFile.Get.AsStorage(), ncaPath) is { } nca)
if (nca != null)
{ {
return nca.Header.TitleId.ToString("X16"); return nca.Header.TitleId.ToString("X16");
} }
@@ -627,7 +626,7 @@ namespace LibKenjinx
} }
private static Nca TryOpenNca(IStorage ncaStorage, string containerPath) private static Nca? TryOpenNca(IStorage ncaStorage, string containerPath)
{ {
try try
{ {
@@ -661,7 +660,7 @@ namespace LibKenjinx
partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); Nca? nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
if (nca == null) if (nca == null)
{ {
continue; continue;
@@ -698,7 +697,7 @@ namespace LibKenjinx
} }
// ===== Amiibo Helpers (Kenjinx) ===== // ===== Amiibo Helpers (Kenjinx) =====
public static bool AmiiboLoadFromBytes(byte[] data) public static bool AmiiboLoadFromBytes(byte[]? data)
{ {
if (data == null || data.Length == 0) if (data == null || data.Length == 0)
{ {
@@ -755,13 +754,13 @@ namespace LibKenjinx
public class SwitchDevice : IDisposable public class SwitchDevice : IDisposable
{ {
private readonly SystemVersion _firmwareVersion; private readonly SystemVersion? _firmwareVersion;
private int _contextDisposeState; private int _contextDisposeState;
public VirtualFileSystem VirtualFileSystem { get; set; } public VirtualFileSystem VirtualFileSystem { get; set; }
public ContentManager ContentManager { get; set; } public ContentManager? ContentManager { get; set; }
public AccountManager AccountManager { get; set; } public AccountManager? AccountManager { get; set; }
public LibHacHorizonManager LibHacHorizonManager { get; set; } public LibHacHorizonManager? LibHacHorizonManager { get; set; }
public UserChannelPersistence UserChannelPersistence { get; set; } public UserChannelPersistence? UserChannelPersistence { get; set; }
public InputManager? InputManager { get; set; } public InputManager? InputManager { get; set; }
public Switch? EmulationContext { get; set; } public Switch? EmulationContext { get; set; }
public IHostUIHandler? HostUiHandler { get; set; } public IHostUIHandler? HostUiHandler { get; set; }
@@ -949,7 +948,7 @@ namespace LibKenjinx
// --- Prepare paths for the physical save directory (Android sandbox) // --- Prepare paths for the physical save directory (Android sandbox)
string savesRoot = Path.Combine( string savesRoot = Path.Combine(
AppDataManager.BaseDirPath, AppDataManager.BaseDirPath,
Ryujinx.HLE.FileSystem.VirtualFileSystem.UserNandPath, VirtualFileSystem.UserNandPath,
"save" "save"
); );
@@ -963,17 +962,24 @@ namespace LibKenjinx
catch { /* ignore */ } catch { /* ignore */ }
// Call existing Horizon APIs to create/secure the saves // Call existing Horizon APIs to create/secure the saves
var rc = LibHacHorizonManager.RyujinxClient.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control); if (LibHacHorizonManager != null)
if (rc.IsFailure())
{ {
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {rc.ToStringWithName()}"); var rc = LibHacHorizonManager.RyujinxClient.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control);
} if (rc.IsFailure())
{
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationCacheStorage. Result code {rc.ToStringWithName()}");
}
Uid userId = AccountManager.LastOpenedUser.UserId.ToLibHacUid(); if (AccountManager != null)
rc = LibHacHorizonManager.RyujinxClient.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in userId); {
if (rc.IsFailure()) Uid userId = AccountManager.LastOpenedUser.UserId.ToLibHacUid();
{ rc = LibHacHorizonManager.RyujinxClient.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in userId);
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {rc.ToStringWithName()}"); }
if (rc.IsFailure())
{
Logger.Error?.Print(LogClass.Application, $"Error calling EnsureApplicationSaveData. Result code {rc.ToStringWithName()}");
}
} }
// Get the after-list of save dirs and calculate the difference // Get the after-list of save dirs and calculate the difference
@@ -1013,7 +1019,10 @@ namespace LibKenjinx
File.WriteAllText(markerFile, $"{titleIdHex}\n{titleName}"); File.WriteAllText(markerFile, $"{titleIdHex}\n{titleName}");
} }
UpsertTitleMapNdjson(savesRoot, titleIdHex, titleName, createdSaveDirName); if (createdSaveDirName != null)
{
UpsertTitleMapNdjson(savesRoot, titleIdHex, titleName, createdSaveDirName);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -1038,7 +1047,7 @@ namespace LibKenjinx
/// - Completely rewrites the file (no unlimited size increase) /// - Completely rewrites the file (no unlimited size increase)
/// - NEVER overwrites the folder with an empty value; attempts to determine it via markers /// - NEVER overwrites the folder with an empty value; attempts to determine it via markers
/// </summary> /// </summary>
private static void UpsertTitleMapNdjson(string savesRoot, string titleIdHex, string titleName, string createdFolder) private static void UpsertTitleMapNdjson(string savesRoot, string? titleIdHex, string? titleName, string createdFolder)
{ {
Directory.CreateDirectory(savesRoot); Directory.CreateDirectory(savesRoot);
string mapPath = Path.Combine(savesRoot, "titleid_map.ndjson"); string mapPath = Path.Combine(savesRoot, "titleid_map.ndjson");
@@ -1092,7 +1101,7 @@ namespace LibKenjinx
string effectiveFolder = createdFolder; string effectiveFolder = createdFolder;
if (string.IsNullOrWhiteSpace(effectiveFolder)) if (string.IsNullOrWhiteSpace(effectiveFolder))
{ {
effectiveFolder = ResolveSaveFolderByMarker(savesRoot, titleIdLc); effectiveFolder = ResolveSaveFolderByMarker(savesRoot, titleIdLc) ?? string.Empty;
} }
if (string.IsNullOrWhiteSpace(effectiveFolder) && !string.IsNullOrWhiteSpace(existingFolder)) if (string.IsNullOrWhiteSpace(effectiveFolder) && !string.IsNullOrWhiteSpace(existingFolder))
{ {
@@ -1122,7 +1131,7 @@ namespace LibKenjinx
string finalFolder = string.IsNullOrWhiteSpace(effectiveFolder) ? (existing.Folder ?? "") : effectiveFolder; string finalFolder = string.IsNullOrWhiteSpace(effectiveFolder) ? (existing.Folder ?? "") : effectiveFolder;
string finalTs = nowIso; string finalTs = nowIso;
byTitleId[titleIdLc] = (finalName ?? "", finalFolder ?? "", finalTs); byTitleId[titleIdLc] = (finalName, finalFolder, finalTs);
// 5) rewrite file (stable: sort by titleId) // 5) rewrite file (stable: sort by titleId)
try try
@@ -1145,7 +1154,7 @@ namespace LibKenjinx
/// <summary> /// <summary>
/// Scans save subfolders for a TITLEID.txt whose first line equals the titleId; returns folder name or null. /// Scans save subfolders for a TITLEID.txt whose first line equals the titleId; returns folder name or null.
/// </summary> /// </summary>
private static string ResolveSaveFolderByMarker(string savesRoot, string titleIdLc) private static string? ResolveSaveFolderByMarker(string savesRoot, string titleIdLc)
{ {
try try
{ {
@@ -1159,7 +1168,7 @@ namespace LibKenjinx
try try
{ {
using var sr = new StreamReader(marker, Encoding.UTF8, true); using var sr = new StreamReader(marker, Encoding.UTF8, true);
string first = sr.ReadLine()?.Trim()?.ToLowerInvariant(); string? first = sr.ReadLine()?.Trim().ToLowerInvariant();
if (first == titleIdLc) if (first == titleIdLc)
{ {
return Path.GetFileName(dir); return Path.GetFileName(dir);
@@ -1210,7 +1219,7 @@ namespace LibKenjinx
{ {
VirtualFileSystem.ReloadKeySet(); VirtualFileSystem.ReloadKeySet();
ContentManager = new ContentManager(VirtualFileSystem); ContentManager = new ContentManager(VirtualFileSystem);
AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient); AccountManager = new AccountManager(LibHacHorizonManager?.RyujinxClient);
} }
} }
+4 -4
View File
@@ -55,14 +55,14 @@ namespace LibKenjinx
byte* xp = (byte*)xPtr; byte* xp = (byte*)xPtr;
try try
{ {
nint ptr = default; nint ptr;
ptr = _getInstanceProcAddr(ret.CurrentInstance.GetValueOrDefault().Handle, xPtr); ptr = _getInstanceProcAddr(ret.CurrentInstance.GetValueOrDefault().Handle, xPtr);
if (ptr == default) if (ptr == 0)
{ {
ptr = _getInstanceProcAddr(nint.Zero, xPtr); ptr = _getInstanceProcAddr(nint.Zero, xPtr);
if (ptr == default) if (ptr == 0)
{ {
var currentDevice = ret.CurrentDevice.GetValueOrDefault().Handle; var currentDevice = ret.CurrentDevice.GetValueOrDefault().Handle;
if (currentDevice != nint.Zero) if (currentDevice != nint.Zero)
@@ -70,7 +70,7 @@ namespace LibKenjinx
ptr = _getDeviceProcAddr(currentDevice, xPtr); ptr = _getDeviceProcAddr(currentDevice, xPtr);
} }
if (ptr == default) if (ptr == 0)
{ {
Logger.Warning?.Print(LogClass.Gpu, $"Failed to get function pointer: {x}"); Logger.Warning?.Print(LogClass.Gpu, $"Failed to get function pointer: {x}");
} }