mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-05 09:21:05 +02:00
single MR with all patches i did, compared to the 2.0.5 release:
See merge request kenji-nx/ryujinx!18
This commit is contained in:
@@ -293,6 +293,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
KThread selectedThread = _state.SelectedThread;
|
||||
|
||||
// TODO fix building error CS1061
|
||||
//if (!currentThread.IsThreadNamed && currentThread.GetThreadName() != "")
|
||||
//{
|
||||
// currentThread.HostThread.Name = $"<{currentThread.GetThreadName()}>";
|
||||
// currentThread.IsThreadNamed = true;
|
||||
//}
|
||||
|
||||
// If the thread is already scheduled and running on the core, we have nothing to do.
|
||||
if (currentThread == selectedThread)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,9 @@ using Ryujinx.HLE.HOS.Services.Hid;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.HLE.Kenjinx;
|
||||
using System.Security.Cryptography;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Globalization;
|
||||
@@ -142,7 +145,28 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
{
|
||||
context.Device.System.NfpDevices[i].State = NfpDeviceState.SearchingForTag;
|
||||
|
||||
// ▼ If a tag has already been injected via Bridge, use it immediately.
|
||||
if (KenjinxAmiiboShim.TryConsume(out var tagBytes))
|
||||
{
|
||||
var dev = context.Device.System.NfpDevices[i];
|
||||
|
||||
if (TryGetFigureIdFrom1Dc(tagBytes, out var fid, out var raw))
|
||||
{
|
||||
dev.AmiiboId = fid;
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceNfp, $"Kenjinx: raw@1DC={raw} → FigureID={fid}");
|
||||
}
|
||||
else
|
||||
{
|
||||
dev.AmiiboId = MakeAmiiboIdFromBytes(tagBytes); // Fallback (SHA1)
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceNfp, $"Kenjinx: FigureID={dev.AmiiboId} (fallback, keine 1DC-Bytes)");
|
||||
}
|
||||
|
||||
dev.UseRandomUuid = false; // stabile UUID über die Session
|
||||
dev.State = NfpDeviceState.TagFound;
|
||||
}
|
||||
// ▲
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
@@ -155,7 +179,32 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
break;
|
||||
}
|
||||
|
||||
// ▼ Poll during the search for newly injected tags.
|
||||
for (int d = 0; d < context.Device.System.NfpDevices.Count; d++)
|
||||
{
|
||||
var dev = context.Device.System.NfpDevices[d];
|
||||
|
||||
if (dev.State == NfpDeviceState.SearchingForTag && KenjinxAmiiboShim.TryConsume(out var tagBytesLoop))
|
||||
{
|
||||
if (TryGetFigureIdFrom1Dc(tagBytesLoop, out var fid, out var raw))
|
||||
{
|
||||
dev.AmiiboId = fid;
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceNfp, $"Kenjinx: raw@1DC={raw} → FigureID={fid}");
|
||||
}
|
||||
else
|
||||
{
|
||||
dev.AmiiboId = MakeAmiiboIdFromBytes(tagBytesLoop); // Fallback (SHA1)
|
||||
Logger.Info?.PrintMsg(LogClass.ServiceNfp, $"Kenjinx: FigureID={dev.AmiiboId} (fallback, keine 1DC-Bytes)");
|
||||
}
|
||||
|
||||
dev.UseRandomUuid = false;
|
||||
dev.State = NfpDeviceState.TagFound;
|
||||
}
|
||||
}
|
||||
// ▲
|
||||
|
||||
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
|
||||
|
||||
{
|
||||
if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound)
|
||||
{
|
||||
@@ -196,9 +245,62 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ▼ Discard the injected day
|
||||
KenjinxAmiiboShim.Clear();
|
||||
// ▲
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
// ▼▼▼ FigureID directly from 0x1DC (8 bytes) without 16-bit swapping
|
||||
private static bool TryGetFigureIdFrom1Dc(byte[] data, out string figureId, out string debugRaw)
|
||||
{
|
||||
figureId = string.Empty;
|
||||
debugRaw = string.Empty;
|
||||
|
||||
const int off = 0x1DC;
|
||||
const int len = 8;
|
||||
|
||||
if (data == null || data.Length < off + len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var s = data.AsSpan(off, len);
|
||||
|
||||
// Debug string "AA-BB-..." in uppercase letters
|
||||
debugRaw = $"{s[0]:X2}-{s[1]:X2}-{s[2]:X2}-{s[3]:X2}-{s[4]:X2}-{s[5]:X2}-{s[6]:X2}-{s[7]:X2}";
|
||||
|
||||
// FigureID as a 16-digit lowercase hexadecimal string in the **same order**
|
||||
// Layout: [0..1]=CharId(BE), [2]=Variant, [3]=Type, [4..5]=Model(BE), [6]=Series, [7]=unknown
|
||||
figureId =
|
||||
$"{s[0]:x2}{s[1]:x2}{s[2]:x2}{s[3]:x2}{s[4]:x2}{s[5]:x2}{s[6]:x2}{s[7]:x2}";
|
||||
|
||||
return true;
|
||||
}
|
||||
// ▲▲▲
|
||||
|
||||
// Fallback: Deterministic 16 hexadecimal characters from SHA1 (first 8 bytes)
|
||||
private static string MakeAmiiboIdFromBytes(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0) return "0000000000000000";
|
||||
|
||||
using var sha = SHA1.Create();
|
||||
var hash = sha.ComputeHash(data); // 20 Bytes
|
||||
char[] buf = new char[16];
|
||||
for (int i = 0, k = 0; i < 8; i++)
|
||||
{
|
||||
byte b = hash[i];
|
||||
buf[k++] = GetHex((b >> 4) & 0xF);
|
||||
buf[k++] = GetHex(b & 0xF);
|
||||
}
|
||||
return new string(buf);
|
||||
}
|
||||
|
||||
private static char GetHex(int v) => (char)(v < 10 ? ('0' + v) : ('a' + (v - 10)));
|
||||
|
||||
|
||||
[CommandCmif(5)]
|
||||
// Mount(bytes<8, 4>, u32, u32)
|
||||
public ResultCode Mount(ServiceCtx context)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
{
|
||||
/// <summary>
|
||||
/// A small static buffer + an API that we access from the Android side via reflection.
|
||||
/// </summary>
|
||||
public static class KenjinxAmiiboShim
|
||||
{
|
||||
private static byte[]? s_tag;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool InjectAmiibo(byte[] tagBytes)
|
||||
{
|
||||
if (tagBytes is null || tagBytes.Length == 0) return false;
|
||||
s_tag = (byte[])tagBytes.Clone();
|
||||
System.Diagnostics.Debug.WriteLine($"[Kenjinx] KenjinxAmiiboShim.InjectAmiibo bytes={tagBytes.Length}");
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ClearAmiibo()
|
||||
{
|
||||
s_tag = null;
|
||||
System.Diagnostics.Debug.WriteLine("[Kenjinx] KenjinxAmiiboShim.ClearAmiibo");
|
||||
}
|
||||
|
||||
// ▼ Used by INfp – retrieves the data exactly once a day and empties the buffer
|
||||
public static bool TryConsume(out byte[] data)
|
||||
{
|
||||
if (s_tag is null)
|
||||
{
|
||||
data = Array.Empty<byte>();
|
||||
return false;
|
||||
}
|
||||
data = s_tag;
|
||||
s_tag = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ▼ Convenient alias
|
||||
public static void Clear() => ClearAmiibo();
|
||||
|
||||
public static bool HasInjectedAmiibo => s_tag is not null;
|
||||
|
||||
public static ReadOnlySpan<byte> PeekInjectedAmiibo()
|
||||
=> s_tag is null ? ReadOnlySpan<byte>.Empty : new ReadOnlySpan<byte>(s_tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Ryujinx.HLE;
|
||||
|
||||
namespace Ryujinx.HLE.Kenjinx
|
||||
{
|
||||
public static class AmiiboBridge
|
||||
{
|
||||
public static bool TryLoadVirtualAmiibo(Switch device, byte[] data, out string message)
|
||||
{
|
||||
message = "NFP bridge not wired.";
|
||||
if (device?.System is null)
|
||||
{
|
||||
message = "No System on Switch device.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- TRY 0: Our conflict-free shim class first (recommended) ---
|
||||
// Type name including assembly: Ryujinx.HLE
|
||||
var shimType = Type.GetType(
|
||||
"Ryujinx.HLE.HOS.Services.Nfc.Nfp.KenjinxAmiiboShim, Ryujinx.HLE",
|
||||
throwOnError: false
|
||||
);
|
||||
if (shimType != null)
|
||||
{
|
||||
var m = shimType.GetMethod(
|
||||
"InjectAmiibo",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
binder: null,
|
||||
types: new[] { typeof(byte[]) },
|
||||
modifiers: null
|
||||
);
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
var ok = (bool)(m.Invoke(null, new object[] { data }) ?? false);
|
||||
if (ok)
|
||||
{
|
||||
message = "Injected via KenjinxAmiiboShim.InjectAmiibo";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TRY 0.5: If available – static API directly on NfpManager ---
|
||||
// (Only if you later add a static InjectAmiibo(...) method to NfpManager.)
|
||||
var nfpManagerType = Type.GetType(
|
||||
"Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager, Ryujinx.HLE",
|
||||
throwOnError: false
|
||||
);
|
||||
if (nfpManagerType != null)
|
||||
{
|
||||
var m = nfpManagerType.GetMethod(
|
||||
"InjectAmiibo",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
binder: null,
|
||||
types: new[] { typeof(byte[]) },
|
||||
modifiers: null
|
||||
);
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
var ok = m.Invoke(null, new object[] { data });
|
||||
message = "Injected via NfpManager.InjectAmiibo(static)";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// --- TRY 1: Direct access to an existing manager in the system object (your existing code) ---
|
||||
var sys = device.System;
|
||||
var nfpMgr =
|
||||
GetField(sys, "NfpManager") ??
|
||||
GetField(sys, "_nfpManager") ??
|
||||
GetField(sys, "NfcManager") ??
|
||||
GetField(sys, "_nfcManager");
|
||||
|
||||
if (nfpMgr != null)
|
||||
{
|
||||
// Possible method identifiers depending on the tree:
|
||||
var mi =
|
||||
GetMethod(nfpMgr, "InjectAmiibo", new[] { typeof(byte[]) }) ??
|
||||
GetMethod(nfpMgr, "LoadAmiiboFromBytes", new[] { typeof(byte[]) }) ??
|
||||
GetMethod(nfpMgr, "LoadVirtualAmiibo", new[] { typeof(byte[]) }) ??
|
||||
GetMethod(nfpMgr, "ScanAmiiboFromBuffer", new[] { typeof(byte[]) });
|
||||
|
||||
if (mi != null)
|
||||
{
|
||||
mi.Invoke(nfpMgr, new object[] { data });
|
||||
message = $"Injected via {mi.DeclaringType?.Name}.{mi.Name}";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
message = "No NFP manager API found. Add a fixed InjectAmiibo(byte[]) to your NFP layer (see TODO in AmiiboBridge comments).";
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ClearVirtualAmiibo(Switch device)
|
||||
{
|
||||
if (device?.System is null) return;
|
||||
|
||||
// --- Clear via Shim (if present) ---
|
||||
var shimType = Type.GetType(
|
||||
"Ryujinx.HLE.HOS.Services.Nfc.Nfp.KenjinxAmiiboShim, Ryujinx.HLE",
|
||||
throwOnError: false
|
||||
);
|
||||
shimType?
|
||||
.GetMethod("ClearAmiibo", BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null)
|
||||
?.Invoke(null, null);
|
||||
|
||||
// --- Optional: Static clear API on NfpManager (if available) ---
|
||||
var nfpManagerType = Type.GetType(
|
||||
"Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager, Ryujinx.HLE",
|
||||
throwOnError: false
|
||||
);
|
||||
nfpManagerType?
|
||||
.GetMethod("ClearAmiibo", BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null)
|
||||
?.Invoke(null, null);
|
||||
|
||||
// --- Your existing instance fallback ---
|
||||
var sys = device.System;
|
||||
var nfpMgr =
|
||||
GetField(sys, "NfpManager") ??
|
||||
GetField(sys, "_nfpManager") ??
|
||||
GetField(sys, "NfcManager") ??
|
||||
GetField(sys, "_nfcManager");
|
||||
|
||||
if (nfpMgr != null)
|
||||
{
|
||||
var mi =
|
||||
GetMethod(nfpMgr, "ClearAmiibo", Type.EmptyTypes) ??
|
||||
GetMethod(nfpMgr, "ResetVirtualAmiibo", Type.EmptyTypes);
|
||||
|
||||
mi?.Invoke(nfpMgr, Array.Empty<object>());
|
||||
}
|
||||
}
|
||||
|
||||
private static object? GetField(object target, string name)
|
||||
{
|
||||
var t = target.GetType();
|
||||
var f = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
|
||||
return f?.GetValue(target);
|
||||
}
|
||||
|
||||
private static MethodInfo? GetMethod(object target, string name, Type[]? sig)
|
||||
{
|
||||
var t = target.GetType();
|
||||
return t.GetMethod(
|
||||
name,
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy,
|
||||
binder: null,
|
||||
types: sig ?? Type.EmptyTypes,
|
||||
modifiers: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user