mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-06-07 12:59:15 +00:00
Compare commits
3 Commits
d1205dc95d
...
Canary-1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00dad0a5e2 | ||
|
|
b70e2e44cb | ||
|
|
012d1d6886 |
@@ -56,7 +56,6 @@
|
|||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.2" />
|
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -700,6 +700,56 @@
|
|||||||
"zh_TW": "掃描 Amiibo"
|
"zh_TW": "掃描 Amiibo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "MenuBarActionsScanSkylander",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "فحص Skylander",
|
||||||
|
"de_DE": "Skylander scannen",
|
||||||
|
"el_GR": "Σάρωση Skylander",
|
||||||
|
"en_US": "Scan A Skylander",
|
||||||
|
"es_ES": "Escanear Skylander",
|
||||||
|
"fr_FR": "Scanner un Skylander",
|
||||||
|
"he_IL": "סרוק אמיבו",
|
||||||
|
"it_IT": "Scansiona un Skylander",
|
||||||
|
"ja_JP": "Skylander をスキャン",
|
||||||
|
"ko_KR": "Skylander 스캔",
|
||||||
|
"no_NO": "Skann en Skylander",
|
||||||
|
"pl_PL": "Skanuj Skylander",
|
||||||
|
"pt_BR": "Escanear um Skylander",
|
||||||
|
"ru_RU": "Сканировать Skylander",
|
||||||
|
"sv_SE": "Skanna en Skylander",
|
||||||
|
"th_TH": "สแกนหา Skylander",
|
||||||
|
"tr_TR": "Bir Skylander Tara",
|
||||||
|
"uk_UA": "Сканувати Skylander",
|
||||||
|
"zh_CN": "扫描 Skylander",
|
||||||
|
"zh_TW": "掃描 Skylander"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "MenuBarActionsRemoveSkylander",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "إزالة Skylander",
|
||||||
|
"de_DE": "Skylander entfernen",
|
||||||
|
"el_GR": "Αφαίρεση Skylander",
|
||||||
|
"en_US": "Remove Skylander",
|
||||||
|
"es_ES": "Eliminar Skylander",
|
||||||
|
"fr_FR": "Supprimer un Skylander",
|
||||||
|
"he_IL": "הסר Skylander",
|
||||||
|
"it_IT": "Rimuovi Skylander",
|
||||||
|
"ja_JP": "Skylander を削除",
|
||||||
|
"ko_KR": "Skylander 제거",
|
||||||
|
"no_NO": "Fjern Skylander",
|
||||||
|
"pl_PL": "Usuń Skylander",
|
||||||
|
"pt_BR": "Remover um Skylander",
|
||||||
|
"ru_RU": "Удалить Skylander",
|
||||||
|
"sv_SE": "Ta bort Skylander",
|
||||||
|
"th_TH": "ลบ Skylander",
|
||||||
|
"tr_TR": "Skylander'ı Kaldır",
|
||||||
|
"uk_UA": "Видалити Skylander",
|
||||||
|
"zh_CN": "移除 Skylander",
|
||||||
|
"zh_TW": "移除 Skylander"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarActionsScanAmiiboBin",
|
"ID": "MenuBarActionsScanAmiiboBin",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
|
|||||||
@@ -107,12 +107,12 @@ namespace Ryujinx.BuildValidationTasks
|
|||||||
{
|
{
|
||||||
locale.Translations[langCode] = string.Empty;
|
locale.Translations[langCode] = string.Empty;
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
$"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
$"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
||||||
<PackageReference Include="MsgPack.Cli" />
|
<PackageReference Include="MsgPack.Cli" />
|
||||||
<PackageReference Include="System.Management" />
|
|
||||||
<PackageReference Include="Humanizer" />
|
<PackageReference Include="Humanizer" />
|
||||||
<PackageReference Include="Gommon" />
|
<PackageReference Include="Gommon" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using Ryujinx.HLE.HOS.Services.Mii;
|
|||||||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
|
||||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv;
|
using Ryujinx.HLE.HOS.Services.Nv;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
||||||
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
|
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
|
||||||
@@ -66,6 +67,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
internal List<NfpDevice> NfpDevices { get; private set; }
|
internal List<NfpDevice> NfpDevices { get; private set; }
|
||||||
|
|
||||||
|
internal List<NfcDevice> NfcDevices { get; private set; }
|
||||||
|
|
||||||
internal SmRegistry SmRegistry { get; private set; }
|
internal SmRegistry SmRegistry { get; private set; }
|
||||||
|
|
||||||
internal ServerBase SmServer { get; private set; }
|
internal ServerBase SmServer { get; private set; }
|
||||||
@@ -132,6 +135,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
PerformanceState = new PerformanceState();
|
PerformanceState = new PerformanceState();
|
||||||
|
|
||||||
NfpDevices = [];
|
NfpDevices = [];
|
||||||
|
NfcDevices = [];
|
||||||
|
|
||||||
// Note: This is not really correct, but with HLE of services, the only memory
|
// Note: This is not really correct, but with HLE of services, the only memory
|
||||||
// region used that is used is Application, so we can use the other ones for anything.
|
// region used that is used is Application, so we can use the other ones for anything.
|
||||||
@@ -372,6 +376,15 @@ namespace Ryujinx.HLE.HOS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ScanSkylander(int nfcDeviceId, byte[] data)
|
||||||
|
{
|
||||||
|
if (NfcDevices[nfcDeviceId].State == NfcDeviceState.SearchingForTag)
|
||||||
|
{
|
||||||
|
NfcDevices[nfcDeviceId].State = NfcDeviceState.TagFound;
|
||||||
|
NfcDevices[nfcDeviceId].Data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool SearchingForAmiibo(out int nfpDeviceId)
|
public bool SearchingForAmiibo(out int nfpDeviceId)
|
||||||
{
|
{
|
||||||
nfpDeviceId = default;
|
nfpDeviceId = default;
|
||||||
@@ -389,6 +402,53 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SearchingForSkylander(out int nfcDeviceId)
|
||||||
|
{
|
||||||
|
nfcDeviceId = default;
|
||||||
|
|
||||||
|
for (int i = 0; i < NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (NfcDevices[i].State == NfcDeviceState.SearchingForTag)
|
||||||
|
{
|
||||||
|
nfcDeviceId = i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasSkylander(out int nfcDeviceId)
|
||||||
|
{
|
||||||
|
nfcDeviceId = default;
|
||||||
|
|
||||||
|
for (int i = 0; i < NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (NfcDevices[i].State == NfcDeviceState.TagFound)
|
||||||
|
{
|
||||||
|
nfcDeviceId = i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveSkylander()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (NfcDevices[i].State == NfcDeviceState.TagFound)
|
||||||
|
{
|
||||||
|
NfcDevices[i].State = NfcDeviceState.Initialized;
|
||||||
|
NfcDevices[i].SignalDeactivate();
|
||||||
|
Thread.Sleep(100); // NOTE: Simulate skylander scanning delay.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SignalDisplayResolutionChange()
|
public void SignalDisplayResolutionChange()
|
||||||
{
|
{
|
||||||
DisplayResolutionChangeEvent.ReadableEvent.Signal();
|
DisplayResolutionChangeEvent.ReadableEvent.Signal();
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
|
||||||
{
|
{
|
||||||
[Service("nfc:mf:u")]
|
[Service("nfc:mf:u")]
|
||||||
class IUserManager : IpcService
|
class IUserManager : IpcService
|
||||||
{
|
{
|
||||||
public IUserManager(ServiceCtx context) { }
|
public IUserManager(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandCmif(0)]
|
||||||
|
// CreateUserInterface() -> object<nn::nfc::mf::IUser>
|
||||||
|
public ResultCode CreateUserInterface(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IMifare());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
477
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs
Normal file
477
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
class IMifare : IpcService
|
||||||
|
{
|
||||||
|
private State _state;
|
||||||
|
|
||||||
|
private KEvent _availabilityChangeEvent;
|
||||||
|
|
||||||
|
private CancellationTokenSource _cancelTokenSource;
|
||||||
|
|
||||||
|
public IMifare()
|
||||||
|
{
|
||||||
|
_state = State.NonInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(0)]
|
||||||
|
public ResultCode Initialize(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_state = State.Initialized;
|
||||||
|
|
||||||
|
NfcDevice devicePlayer1 = new()
|
||||||
|
{
|
||||||
|
NpadIdType = NpadIdType.Player1,
|
||||||
|
Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1),
|
||||||
|
State = NfcDeviceState.Initialized,
|
||||||
|
};
|
||||||
|
|
||||||
|
context.Device.System.NfcDevices.Add(devicePlayer1);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
public ResultCode Finalize(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (_state == State.Initialized)
|
||||||
|
{
|
||||||
|
_cancelTokenSource?.Cancel();
|
||||||
|
|
||||||
|
// NOTE: All events are destroyed here.
|
||||||
|
context.Device.System.NfcDevices.Clear();
|
||||||
|
|
||||||
|
_state = State.NonInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(2)]
|
||||||
|
public ResultCode GetListDevices(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (context.Request.RecvListBuff.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.WrongArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong outputPosition = context.Request.RecvListBuff[0].Position;
|
||||||
|
ulong outputSize = context.Request.RecvListBuff[0].Size;
|
||||||
|
|
||||||
|
if (context.Device.System.NfcDevices.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfcDevices[i].Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ResponseData.Write(context.Device.System.NfcDevices.Count);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(3)]
|
||||||
|
public ResultCode StartDetection(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
|
||||||
|
{
|
||||||
|
context.Device.System.NfcDevices[i].State = NfcDeviceState.SearchingForTag;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancelTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (_cancelTokenSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagFound)
|
||||||
|
{
|
||||||
|
context.Device.System.NfcDevices[i].SignalActivate();
|
||||||
|
Thread.Sleep(125); // NOTE: Simulate skylander scanning delay.
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, _cancelTokenSource.Token);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(4)]
|
||||||
|
public ResultCode StopDetection(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_cancelTokenSource?.Cancel();
|
||||||
|
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
|
||||||
|
{
|
||||||
|
context.Device.System.NfcDevices[i].State = NfcDeviceState.Initialized;
|
||||||
|
Array.Clear(context.Device.System.NfcDevices[i].Data);
|
||||||
|
context.Device.System.NfcDevices[i].SignalDeactivate();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(5)]
|
||||||
|
public ResultCode ReadMifare(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (context.Request.ReceiveBuff.Count == 0 || context.Request.SendBuff.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.WrongArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
if (context.Device.System.NfcDevices.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
||||||
|
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
||||||
|
|
||||||
|
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
|
||||||
|
|
||||||
|
ulong inputPosition = context.Request.SendBuff[0].Position;
|
||||||
|
ulong inputSize = context.Request.SendBuff[0].Size;
|
||||||
|
|
||||||
|
byte[] readBlockParameter = new byte[inputSize];
|
||||||
|
|
||||||
|
context.Memory.Read(inputPosition, readBlockParameter);
|
||||||
|
|
||||||
|
var span = MemoryMarshal.Cast<byte, NfcMifareReadBlockParameter>(readBlockParameter);
|
||||||
|
var list = new List<NfcMifareReadBlockParameter>(span.Length);
|
||||||
|
|
||||||
|
foreach (var item in span)
|
||||||
|
list.Add(item);
|
||||||
|
|
||||||
|
Thread.Sleep(125 * list.Count); // NOTE: Simulate skylander scanning delay.
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
|
||||||
|
{
|
||||||
|
return ResultCode.TagNotFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int p = 0; p < list.Count; p++)
|
||||||
|
{
|
||||||
|
NfcMifareReadBlockData blockData = new()
|
||||||
|
{
|
||||||
|
SectorNumber = list[p].SectorNumber,
|
||||||
|
Reserved = new Array7<byte>(),
|
||||||
|
};
|
||||||
|
byte[] data = new byte[16];
|
||||||
|
|
||||||
|
switch (list[p].SectorKey.MifareCommand)
|
||||||
|
{
|
||||||
|
case NfcMifareCommand.NfcMifareCommand_Read:
|
||||||
|
case NfcMifareCommand.NfcMifareCommand_AuthA:
|
||||||
|
if (IsCurrentBlockKeyBlock(list[p].SectorNumber))
|
||||||
|
{
|
||||||
|
Array.Copy(context.Device.System.NfcDevices[i].Data, (16 * list[p].SectorNumber) + 6, data, 6, 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Array.Copy(context.Device.System.NfcDevices[i].Data, 16 * list[p].SectorNumber, data, 0, 16);
|
||||||
|
}
|
||||||
|
data.CopyTo(blockData.Data.AsSpan());
|
||||||
|
context.Memory.Write(outputPosition + ((uint)(p * Unsafe.SizeOf<NfcMifareReadBlockData>())), blockData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(6)]
|
||||||
|
public ResultCode WriteMifare(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (context.Request.SendBuff.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.WrongArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
if (context.Device.System.NfcDevices.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong inputPosition = context.Request.SendBuff[0].Position;
|
||||||
|
ulong inputSize = context.Request.SendBuff[0].Size;
|
||||||
|
|
||||||
|
byte[] writeBlockParameter = new byte[inputSize];
|
||||||
|
|
||||||
|
context.Memory.Read(inputPosition, writeBlockParameter);
|
||||||
|
|
||||||
|
var span = MemoryMarshal.Cast<byte, NfcMifareWriteBlockParameter>(writeBlockParameter);
|
||||||
|
var list = new List<NfcMifareWriteBlockParameter>(span.Length);
|
||||||
|
|
||||||
|
foreach (var item in span)
|
||||||
|
list.Add(item);
|
||||||
|
|
||||||
|
Thread.Sleep(125 * list.Count); // NOTE: Simulate skylander scanning delay.
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
|
||||||
|
{
|
||||||
|
return ResultCode.TagNotFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int p = 0; p < list.Count; p++)
|
||||||
|
{
|
||||||
|
switch (list[p].SectorKey.MifareCommand)
|
||||||
|
{
|
||||||
|
case NfcMifareCommand.NfcMifareCommand_Write:
|
||||||
|
case NfcMifareCommand.NfcMifareCommand_AuthA:
|
||||||
|
list[p].Data.AsSpan().CopyTo(context.Device.System.NfcDevices[i].Data.AsSpan(list[p].SectorNumber * 16, 16));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(7)]
|
||||||
|
public ResultCode GetTagInfo(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ResultCode resultCode = ResultCode.Success;
|
||||||
|
|
||||||
|
if (context.Request.RecvListBuff.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.WrongArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong outputPosition = context.Request.RecvListBuff[0].Position;
|
||||||
|
|
||||||
|
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<TagInfo>());
|
||||||
|
|
||||||
|
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf<TagInfo>());
|
||||||
|
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
if (context.Device.System.NfcDevices.Count == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
|
||||||
|
{
|
||||||
|
resultCode = ResultCode.TagNotFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagMounted || context.Device.System.NfcDevices[i].State == NfcDeviceState.TagFound)
|
||||||
|
{
|
||||||
|
TagInfo tagInfo = new()
|
||||||
|
{
|
||||||
|
UuidLength = 4,
|
||||||
|
Reserved1 = new Array21<byte>(),
|
||||||
|
Protocol = (uint)NfcProtocol.NfcProtocol_TypeA, // Type A Protocol
|
||||||
|
TagType = (uint)NfcTagType.NfcTagType_Mifare, // Mifare Type
|
||||||
|
Reserved2 = new Array6<byte>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
byte[] uuid = new byte[4];
|
||||||
|
|
||||||
|
Array.Copy(context.Device.System.NfcDevices[i].Data, 0, uuid, 0, 4);
|
||||||
|
|
||||||
|
uuid.CopyTo(tagInfo.Uuid.AsSpan());
|
||||||
|
|
||||||
|
context.Memory.Write(outputPosition, tagInfo);
|
||||||
|
|
||||||
|
resultCode = ResultCode.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resultCode = ResultCode.WrongDeviceState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8)]
|
||||||
|
public ResultCode AttachActivateEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
|
||||||
|
{
|
||||||
|
context.Device.System.NfcDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfcDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(9)]
|
||||||
|
public ResultCode AttachDeactivateEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
|
||||||
|
{
|
||||||
|
context.Device.System.NfcDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfcDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(10)]
|
||||||
|
public ResultCode GetState(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((int)_state);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(11)]
|
||||||
|
public ResultCode GetDeviceState(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
|
||||||
|
{
|
||||||
|
if (context.Device.System.NfcDevices[i].State > NfcDeviceState.Finalized)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"{nameof(context.Device.System.NfcDevices)} contains an invalid state for device {i}: {context.Device.System.NfcDevices[i].State}");
|
||||||
|
}
|
||||||
|
context.ResponseData.Write((uint)context.Device.System.NfcDevices[i].State);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ResponseData.Write((uint)NfcDeviceState.Unavailable);
|
||||||
|
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(12)]
|
||||||
|
public ResultCode GetNpadId(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
|
||||||
|
{
|
||||||
|
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfcDevices[i].Handle));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.DeviceNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(13)]
|
||||||
|
public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_availabilityChangeEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsCurrentBlockKeyBlock(byte block)
|
||||||
|
{
|
||||||
|
return ((block + 1) % 4) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
class NfcDevice
|
||||||
|
{
|
||||||
|
public KEvent ActivateEvent;
|
||||||
|
public KEvent DeactivateEvent;
|
||||||
|
|
||||||
|
public void SignalActivate() => ActivateEvent.ReadableEvent.Signal();
|
||||||
|
public void SignalDeactivate() => DeactivateEvent.ReadableEvent.Signal();
|
||||||
|
|
||||||
|
public NfcDeviceState State = NfcDeviceState.Unavailable;
|
||||||
|
|
||||||
|
public PlayerIndex Handle;
|
||||||
|
public NpadIdType NpadIdType;
|
||||||
|
|
||||||
|
public byte[] Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcMifareCommand : byte
|
||||||
|
{
|
||||||
|
NfcMifareCommand_Read = 0x30,
|
||||||
|
NfcMifareCommand_AuthA = 0x60,
|
||||||
|
NfcMifareCommand_AuthB = 0x61,
|
||||||
|
NfcMifareCommand_Write = 0xA0,
|
||||||
|
NfcMifareCommand_Transfer = 0xB0,
|
||||||
|
NfcMifareCommand_Decrement = 0xC0,
|
||||||
|
NfcMifareCommand_Increment = 0xC1,
|
||||||
|
NfcMifareCommand_Store = 0xC2,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x18)]
|
||||||
|
struct NfcMifareReadBlockData
|
||||||
|
{
|
||||||
|
public Array16<byte> Data;
|
||||||
|
public byte SectorNumber;
|
||||||
|
public Array7<byte> Reserved;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x18)]
|
||||||
|
struct NfcMifareReadBlockParameter
|
||||||
|
{
|
||||||
|
public byte SectorNumber;
|
||||||
|
public Array7<byte> Reserved;
|
||||||
|
public NfcSectorKey SectorKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x28)]
|
||||||
|
struct NfcMifareWriteBlockParameter
|
||||||
|
{
|
||||||
|
public Array16<byte> Data;
|
||||||
|
public byte SectorNumber;
|
||||||
|
public Array7<byte> Reserved;
|
||||||
|
public NfcSectorKey SectorKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcProtocol : byte
|
||||||
|
{
|
||||||
|
NfcProtocol_None = 0b_0000_0000,
|
||||||
|
NfcProtocol_TypeA = 0b_0000_0001, ///< ISO14443A
|
||||||
|
NfcProtocol_TypeB = 0b_0000_0010, ///< ISO14443B
|
||||||
|
NfcProtocol_TypeF = 0b_0000_0100, ///< Sony FeliCa
|
||||||
|
NfcProtocol_All = 0xFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
struct NfcSectorKey
|
||||||
|
{
|
||||||
|
public NfcMifareCommand MifareCommand;
|
||||||
|
public byte Unknown;
|
||||||
|
public Array6<byte> Reserved1;
|
||||||
|
public Array6<byte> SectorKey;
|
||||||
|
public Array2<byte> Reserved2;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcTagType : byte
|
||||||
|
{
|
||||||
|
NfcTagType_None = 0b_0000_0000,
|
||||||
|
NfcTagType_Type1 = 0b_0000_0001, ///< ISO14443A RW. Topaz
|
||||||
|
NfcTagType_Type2 = 0b_0000_0010, ///< ISO14443A RW. Ultralight, NTAGX, ST25TN
|
||||||
|
NfcTagType_Type3 = 0b_0000_0100, ///< ISO14443A RW/RO. Sony FeliCa
|
||||||
|
NfcTagType_Type4A = 0b_0000_1000, ///< ISO14443A RW/RO. DESFire
|
||||||
|
NfcTagType_Type4B = 0b_0001_0000, ///< ISO14443B RW/RO. DESFire
|
||||||
|
NfcTagType_Type5 = 0b_0010_0000, ///< ISO15693 RW/RO. SLI, SLIX, ST25TV
|
||||||
|
NfcTagType_Mifare = 0b_0100_0000, ///< Mifare clasic. Skylanders
|
||||||
|
NfcTagType_All = 0xFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcDeviceState : byte
|
||||||
|
{
|
||||||
|
Initialized = 0,
|
||||||
|
SearchingForTag = 1,
|
||||||
|
TagFound = 2,
|
||||||
|
TagRemoved = 3,
|
||||||
|
TagMounted = 4,
|
||||||
|
Unavailable = 5,
|
||||||
|
Finalized = 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
NonInitialized,
|
||||||
|
Initialized,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x58)]
|
||||||
|
struct TagInfo
|
||||||
|
{
|
||||||
|
public Array10<byte> Uuid;
|
||||||
|
public byte UuidLength;
|
||||||
|
public Array21<byte> Reserved1;
|
||||||
|
public uint Protocol;
|
||||||
|
public uint TagType;
|
||||||
|
public Array6<byte> Reserved2;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs
Normal file
17
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
|
||||||
|
{
|
||||||
|
public enum ResultCode
|
||||||
|
{
|
||||||
|
ModuleId = 161,
|
||||||
|
ErrorCodeShift = 9,
|
||||||
|
|
||||||
|
Success = 0,
|
||||||
|
|
||||||
|
DeviceNotFound = (64 << ErrorCodeShift) | ModuleId, // 0x80A1
|
||||||
|
WrongArgument = (65 << ErrorCodeShift) | ModuleId, // 0x82A1
|
||||||
|
WrongDeviceState = (73 << ErrorCodeShift) | ModuleId, // 0x92A1
|
||||||
|
NfcDisabled = (80 << ErrorCodeShift) | ModuleId, // 0xA0A1
|
||||||
|
TagNotFound = (97 << ErrorCodeShift) | ModuleId, // 0xC2A1
|
||||||
|
MifareAccessError = (288 << ErrorCodeShift) | ModuleId, // 0x240a1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,11 +29,6 @@
|
|||||||
<TrimMode>partial</TrimMode>
|
<TrimMode>partial</TrimMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
|
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
|
||||||
<PublishTrimmed>false</PublishTrimmed>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
|
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
|
||||||
See:
|
See:
|
||||||
|
|||||||
@@ -370,6 +370,39 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile;
|
public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile;
|
||||||
|
|
||||||
|
public bool IsSkylanderRequested
|
||||||
|
{
|
||||||
|
get => field && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasSkylander
|
||||||
|
{
|
||||||
|
get => field && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowSkylanderActions
|
||||||
|
{
|
||||||
|
get => field && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool ShowLoadProgress
|
public bool ShowLoadProgress
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -1864,6 +1897,46 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task OpenSkylanderWindow()
|
||||||
|
{
|
||||||
|
if (AppHost.Device.System.SearchingForSkylander(out int deviceId))
|
||||||
|
{
|
||||||
|
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(
|
||||||
|
new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
|
||||||
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
|
{
|
||||||
|
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||||
|
{
|
||||||
|
Patterns = ["*.sky", "*.bin", "*.dmp", "*.dump"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.HasValue)
|
||||||
|
{
|
||||||
|
// Open reading stream from the first file.
|
||||||
|
await using var stream = await result.Value.OpenReadAsync();
|
||||||
|
using var streamReader = new BinaryReader(stream);
|
||||||
|
// Reads all the content of file as a text.
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
var count = streamReader.Read(data, 0, 1024);
|
||||||
|
if (count < 1024)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppHost.Device.System.ScanSkylander(deviceId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveSkylander()
|
||||||
|
{
|
||||||
|
AppHost.Device.System.RemoveSkylander();
|
||||||
|
}
|
||||||
|
|
||||||
public void ReloadRenderDocApi()
|
public void ReloadRenderDocApi()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -184,6 +184,22 @@
|
|||||||
IsVisible="{Binding CanScanAmiiboBinaries}"
|
IsVisible="{Binding CanScanAmiiboBinaries}"
|
||||||
InputGesture="Ctrl + B"
|
InputGesture="Ctrl + B"
|
||||||
IsEnabled="{Binding IsAmiiboBinRequested}" />
|
IsEnabled="{Binding IsAmiiboBinRequested}" />
|
||||||
|
<MenuItem
|
||||||
|
Command="{Binding OpenSkylanderWindow}"
|
||||||
|
AttachedToVisualTree="ScanSkylanderMenuItem_AttachedToVisualTree"
|
||||||
|
Header="{ext:Locale MenuBarActionsScanSkylander}"
|
||||||
|
Icon="{ext:Icon fa-solid fa-cube}"
|
||||||
|
IsVisible="{Binding ShowSkylanderActions}"
|
||||||
|
InputGesture="Ctrl + S"
|
||||||
|
IsEnabled="{Binding IsSkylanderRequested}" />
|
||||||
|
<MenuItem
|
||||||
|
Command="{Binding RemoveSkylander}"
|
||||||
|
AttachedToVisualTree="RemoveSkylanderMenuItem_AttachedToVisualTree"
|
||||||
|
Header="{ext:Locale MenuBarActionsRemoveSkylander}"
|
||||||
|
Icon="{ext:Icon fa-solid fa-cube}"
|
||||||
|
IsVisible="{Binding ShowSkylanderActions}"
|
||||||
|
InputGesture="Ctrl + D"
|
||||||
|
IsEnabled="{Binding HasSkylander}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding TakeScreenshot}"
|
Command="{Binding TakeScreenshot}"
|
||||||
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
|
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
|
||||||
|
|||||||
@@ -193,6 +193,20 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
|
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ScanSkylanderMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem)
|
||||||
|
ViewModel.IsSkylanderRequested = ViewModel.AppHost.Device.System.SearchingForSkylander(out _);
|
||||||
|
ViewModel.ShowSkylanderActions = string.Equals(ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(), "0100CCC0002E6000");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveSkylanderMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem)
|
||||||
|
ViewModel.HasSkylander = ViewModel.AppHost.Device.System.HasSkylander(out _);
|
||||||
|
ViewModel.ShowSkylanderActions = string.Equals(ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(), "0100CCC0002E6000");
|
||||||
|
}
|
||||||
|
|
||||||
private async Task InstallFileTypes()
|
private async Task InstallFileTypes()
|
||||||
{
|
{
|
||||||
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Management;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ namespace Ryujinx.Ava.Utilities.SystemInfo
|
|||||||
{
|
{
|
||||||
internal WindowsSystemInfo()
|
internal WindowsSystemInfo()
|
||||||
{
|
{
|
||||||
CpuName = $"{GetCpuidCpuName() ?? GetCpuNameWMI()} ; {LogicalCoreCount} logical"; // WMI is very slow
|
CpuName = $"{GetCpuidCpuName() ?? GetCpuNameFromRegistry()} ; {LogicalCoreCount} logical";
|
||||||
(RamTotal, RamAvailable) = GetMemoryStats();
|
(RamTotal, RamAvailable) = GetMemoryStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,25 +27,26 @@ namespace Ryujinx.Ava.Utilities.SystemInfo
|
|||||||
return (0, 0);
|
return (0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetCpuNameWMI()
|
private static string GetCpuNameFromRegistry()
|
||||||
{
|
{
|
||||||
ManagementObjectCollection cpuObjs = GetWMIObjects("root\\CIMV2", "SELECT * FROM Win32_Processor");
|
try
|
||||||
|
{
|
||||||
|
using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
|
||||||
|
|
||||||
if (cpuObjs != null)
|
return key?.GetValue("ProcessorNameString")?.ToString()?.Trim();
|
||||||
{
|
|
||||||
foreach (ManagementBaseObject cpuObj in cpuObjs)
|
|
||||||
{
|
|
||||||
return cpuObj["Name"].ToString().Trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Registry CPU name lookup failed: {ex.Message}");
|
||||||
|
|
||||||
return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER")?.Trim();
|
return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER")?.Trim();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
private struct MemoryStatusEx
|
private struct MemoryStatusEx()
|
||||||
{
|
{
|
||||||
public uint Length;
|
public uint Length = (uint)Marshal.SizeOf<MemoryStatusEx>();
|
||||||
public uint MemoryLoad;
|
public uint MemoryLoad;
|
||||||
public ulong TotalPhys;
|
public ulong TotalPhys;
|
||||||
public ulong AvailPhys;
|
public ulong AvailPhys;
|
||||||
@@ -55,33 +55,10 @@ namespace Ryujinx.Ava.Utilities.SystemInfo
|
|||||||
public ulong TotalVirtual;
|
public ulong TotalVirtual;
|
||||||
public ulong AvailVirtual;
|
public ulong AvailVirtual;
|
||||||
public ulong AvailExtendedVirtual;
|
public ulong AvailExtendedVirtual;
|
||||||
|
|
||||||
public MemoryStatusEx()
|
|
||||||
{
|
|
||||||
Length = (uint)Marshal.SizeOf<MemoryStatusEx>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);
|
private static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);
|
||||||
|
|
||||||
private static ManagementObjectCollection GetWMIObjects(string scope, string query)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return new ManagementObjectSearcher(scope, query).Get();
|
|
||||||
}
|
|
||||||
catch (PlatformNotSupportedException ex)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"WMI isn't available : {ex.Message}");
|
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"WMI isn't available : {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user