Reduce usage of Marshal.PtrToStructure and Marshal.StructureToPtr (#3805)

* common: Make BinaryReaderExtensions Read & Write take unamanged types

This allows us to not rely on Marshal.PtrToStructure and Marshal.StructureToPtr for those.

* common: Make MemoryHelper Read & Write takes unamanged types

* Update Marshal.SizeOf => Unsafe.SizeOf when appropriate and start moving software applet to unmanaged types
This commit is contained in:
Mary-nyan 2022-11-24 15:26:29 +01:00 committed by GitHub
parent a1ddaa2736
commit f4e879a1e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 88 additions and 137 deletions

View file

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Common namespace Ryujinx.Common
@ -7,49 +8,15 @@ namespace Ryujinx.Common
public static class BinaryReaderExtensions public static class BinaryReaderExtensions
{ {
public unsafe static T ReadStruct<T>(this BinaryReader reader) public unsafe static T ReadStruct<T>(this BinaryReader reader)
where T : struct where T : unmanaged
{ {
int size = Marshal.SizeOf<T>(); return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
byte[] data = reader.ReadBytes(size);
fixed (byte* ptr = data)
{
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
}
public unsafe static T[] ReadStructArray<T>(this BinaryReader reader, int count)
where T : struct
{
int size = Marshal.SizeOf<T>();
T[] result = new T[count];
for (int i = 0; i < count; i++)
{
byte[] data = reader.ReadBytes(size);
fixed (byte* ptr = data)
{
result[i] = Marshal.PtrToStructure<T>((IntPtr)ptr);
}
}
return result;
} }
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value) public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
where T : struct where T : unmanaged
{ {
long size = Marshal.SizeOf<T>(); ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
byte[] data = new byte[size];
fixed (byte* ptr = data)
{
Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
}
writer.Write(data); writer.Write(data);
} }

View file

@ -1,6 +1,7 @@
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -23,34 +24,18 @@ namespace Ryujinx.Cpu
} }
} }
public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : struct public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : unmanaged
{ {
long size = Marshal.SizeOf<T>(); return MemoryMarshal.Cast<byte, T>(memory.GetSpan(position, Unsafe.SizeOf<T>()))[0];
byte[] data = new byte[size];
memory.Read(position, data);
fixed (byte* ptr = data)
{
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
} }
public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : struct public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : unmanaged
{ {
long size = Marshal.SizeOf<T>(); ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
byte[] data = new byte[size];
fixed (byte* ptr = data)
{
Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
}
memory.Write(position, data); memory.Write(position, data);
return (ulong)size; return (ulong)data.Length;
} }
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1) public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)

View file

@ -1,4 +1,5 @@
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
using Ryujinx.HLE.HOS.Services.Am.AppletAE; using Ryujinx.HLE.HOS.Services.Am.AppletAE;
@ -9,6 +10,7 @@ using Ryujinx.Memory;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -78,13 +80,13 @@ namespace Ryujinx.HLE.HOS.Applets
var launchParams = _normalSession.Pop(); var launchParams = _normalSession.Pop();
var keyboardConfig = _normalSession.Pop(); var keyboardConfig = _normalSession.Pop();
_isBackground = keyboardConfig.Length == Marshal.SizeOf<SoftwareKeyboardInitialize>(); _isBackground = keyboardConfig.Length == Unsafe.SizeOf<SoftwareKeyboardInitialize>();
if (_isBackground) if (_isBackground)
{ {
// Initialize the keyboard applet in background mode. // Initialize the keyboard applet in background mode.
_keyboardBackgroundInitialize = ReadStruct<SoftwareKeyboardInitialize>(keyboardConfig); _keyboardBackgroundInitialize = MemoryMarshal.Read<SoftwareKeyboardInitialize>(keyboardConfig);
_backgroundState = InlineKeyboardState.Uninitialized; _backgroundState = InlineKeyboardState.Uninitialized;
if (_device.UiHandler == null) if (_device.UiHandler == null)
@ -342,7 +344,7 @@ namespace Ryujinx.HLE.HOS.Applets
else else
{ {
int wordsCount = reader.ReadInt32(); int wordsCount = reader.ReadInt32();
int wordSize = Marshal.SizeOf<SoftwareKeyboardUserWord>(); int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>();
remaining = stream.Length - stream.Position; remaining = stream.Length - stream.Position;
if (wordsCount > MaxUserWords) if (wordsCount > MaxUserWords)
@ -359,8 +361,7 @@ namespace Ryujinx.HLE.HOS.Applets
for (int word = 0; word < wordsCount; word++) for (int word = 0; word < wordsCount; word++)
{ {
byte[] wordData = reader.ReadBytes(wordSize); _keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>();
_keyboardBackgroundUserWords[word] = ReadStruct<SoftwareKeyboardUserWord>(wordData);
} }
} }
} }
@ -369,27 +370,25 @@ namespace Ryujinx.HLE.HOS.Applets
case InlineKeyboardRequest.SetCustomizeDic: case InlineKeyboardRequest.SetCustomizeDic:
// Read the custom dic data. // Read the custom dic data.
remaining = stream.Length - stream.Position; remaining = stream.Length - stream.Position;
if (remaining != Marshal.SizeOf<SoftwareKeyboardCustomizeDic>()) if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>())
{ {
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes"); Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes");
} }
else else
{ {
var keyboardDicData = reader.ReadBytes((int)remaining); _keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>();
_keyboardBackgroundDic = ReadStruct<SoftwareKeyboardCustomizeDic>(keyboardDicData);
} }
break; break;
case InlineKeyboardRequest.SetCustomizedDictionaries: case InlineKeyboardRequest.SetCustomizedDictionaries:
// Read the custom dictionaries data. // Read the custom dictionaries data.
remaining = stream.Length - stream.Position; remaining = stream.Length - stream.Position;
if (remaining != Marshal.SizeOf<SoftwareKeyboardDictSet>()) if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>())
{ {
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes"); Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes");
} }
else else
{ {
var keyboardDictData = reader.ReadBytes((int)remaining); _keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>();
_keyboardBackgroundDictSet = ReadStruct<SoftwareKeyboardDictSet>(keyboardDictData);
} }
break; break;
case InlineKeyboardRequest.Calc: case InlineKeyboardRequest.Calc:

View file

@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// <summary> /// <summary>
/// A structure used by SetCustomizeDic request to software keyboard. /// A structure used by SetCustomizeDic request to software keyboard.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Size = 0x70)]
struct SoftwareKeyboardCustomizeDic struct SoftwareKeyboardCustomizeDic
{ {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 112)] // Unknown
public byte[] Unknown;
} }
} }

View file

@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{ {
@ -21,8 +22,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// <summary> /// <summary>
/// Array of word entries in the buffer. /// Array of word entries in the buffer.
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] public Array24<ulong> Entries;
public ulong[] Entries;
/// <summary> /// <summary>
/// Number of used entries in the Entries field. /// Number of used entries in the Entries field.

View file

@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
/// <summary> /// <summary>
/// A structure used by SetUserWordInfo request to the software keyboard. /// A structure used by SetUserWordInfo request to the software keyboard.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Size = 0x64)]
struct SoftwareKeyboardUserWord struct SoftwareKeyboardUserWord
{ {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] // Unknown
public byte[] Unknown;
} }
} }

View file

@ -1,9 +1,12 @@
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
{ {
[StructLayout(LayoutKind.Sequential, Pack = 0x8, CharSet = CharSet.Ansi)] [StructLayout(LayoutKind.Sequential, Pack = 0x8)]
struct UserPresence struct UserPresence
{ {
public UserId UserId; public UserId UserId;
@ -13,15 +16,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool SamePresenceGroupApplication; public bool SamePresenceGroupApplication;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)] public Array3<byte> Unknown;
public char[] Unknown; private AppKeyValueStorageHolder _appKeyValueStorage;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xC0)] public Span<byte> AppKeyValueStorage => MemoryMarshal.Cast<AppKeyValueStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _appKeyValueStorage, AppKeyValueStorageHolder.Size));
public char[] AppKeyValueStorage;
[StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = Size)]
private struct AppKeyValueStorageHolder
{
public const int Size = 0xC0;
}
public override string ToString() public override string ToString()
{ {
return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {AppKeyValueStorage} }}"; return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {Encoding.ASCII.GetString(AppKeyValueStorage)} }}";
} }
} }
} }

View file

@ -236,23 +236,14 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
ulong position = context.Request.PtrBuff[0].Position; ulong position = context.Request.PtrBuff[0].Position;
ulong size = context.Request.PtrBuff[0].Size; ulong size = context.Request.PtrBuff[0].Size;
byte[] bufferContent = new byte[size]; ReadOnlySpan<UserPresence> userPresenceInputArray = MemoryMarshal.Cast<byte, UserPresence>(context.Memory.GetSpan(position, (int)size));
context.Memory.Read(position, bufferContent);
if (uuid.IsNull) if (uuid.IsNull)
{ {
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
int elementCount = bufferContent.Length / Marshal.SizeOf<UserPresence>(); Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray = userPresenceInputArray.ToArray() });
using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(bufferContent)))
{
UserPresence[] userPresenceInputArray = bufferReader.ReadStructArray<UserPresence>(elementCount);
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray });
}
return ResultCode.Success; return ResultCode.Success;
} }

View file

@ -1,15 +1,13 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
{ {
[StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
struct NotificationInfo struct NotificationInfo
{ {
public NotificationEventType Type; public NotificationEventType Type;
private Array4<byte> _padding;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4)]
public char[] Padding;
public long NetworkUserIdPlaceholder; public long NetworkUserIdPlaceholder;
} }
} }

View file

@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
@ -75,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++) for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++)
{ {
MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value); MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Unsafe.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value);
} }
context.ResponseData.Write(filteredApplicationPlayStatistics.Count()); context.ResponseData.Write(filteredApplicationPlayStatistics.Count());

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.Clock namespace Ryujinx.HLE.HOS.Services.Time.Clock
@ -16,14 +17,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public CalendarAdditionalInfo NetworkCalendarAdditionalTime; public CalendarAdditionalInfo NetworkCalendarAdditionalTime;
public SteadyClockTimePoint SteadyClockTimePoint; public SteadyClockTimePoint SteadyClockTimePoint;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24)] private LocationNameStorageHolder _locationName;
public char[] LocationName;
public Span<byte> LocationName => MemoryMarshal.Cast<LocationNameStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _locationName, LocationNameStorageHolder.Size));
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool IsAutomaticCorrectionEnabled; public bool IsAutomaticCorrectionEnabled;
public byte Type; public byte Type;
public ushort Unknown; public ushort Unknown;
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)]
private struct LocationNameStorageHolder
{
public const int Size = 0x24;
}
public static ResultCode GetCurrentTime(out long currentTime, SteadyClockTimePoint steadyClockTimePoint, SystemClockContext context) public static ResultCode GetCurrentTime(out long currentTime, SteadyClockTimePoint steadyClockTimePoint, SystemClockContext context)
{ {
currentTime = 0; currentTime = 0;

View file

@ -8,7 +8,9 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Time namespace Ryujinx.HLE.HOS.Services.Time
{ {
@ -281,7 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ {
byte type = context.RequestData.ReadByte(); byte type = context.RequestData.ReadByte();
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>()); context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<ClockSnapshot>());
context.RequestData.BaseStream.Position += 7; context.RequestData.BaseStream.Position += 7;
@ -372,12 +374,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
return result; return result;
} }
char[] tzName = deviceLocationName.ToCharArray(); ReadOnlySpan<byte> tzName = Encoding.ASCII.GetBytes(deviceLocationName);
char[] locationName = new char[0x24];
Array.Copy(tzName, locationName, tzName.Length); tzName.CopyTo(clockSnapshot.LocationName);
clockSnapshot.LocationName = locationName;
result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext); result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext);
@ -414,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcPtrBuffDesc ipcDesc) private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcPtrBuffDesc ipcDesc)
{ {
Debug.Assert(ipcDesc.Size == (ulong)Marshal.SizeOf<ClockSnapshot>()); Debug.Assert(ipcDesc.Size == (ulong)Unsafe.SizeOf<ClockSnapshot>());
byte[] temp = new byte[ipcDesc.Size]; byte[] temp = new byte[ipcDesc.Size];

View file

@ -5,6 +5,7 @@ using Ryujinx.HLE.Utilities;
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -890,14 +891,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
long streamLength = reader.BaseStream.Length; long streamLength = reader.BaseStream.Length;
if (streamLength < Marshal.SizeOf<TzifHeader>()) if (streamLength < Unsafe.SizeOf<TzifHeader>())
{ {
return false; return false;
} }
TzifHeader header = reader.ReadStruct<TzifHeader>(); TzifHeader header = reader.ReadStruct<TzifHeader>();
streamLength -= Marshal.SizeOf<TzifHeader>(); streamLength -= Unsafe.SizeOf<TzifHeader>();
int ttisGMTCount = Detzcode32(header.TtisGMTCount); int ttisGMTCount = Detzcode32(header.TtisGMTCount);
int ttisSTDCount = Detzcode32(header.TtisSTDCount); int ttisSTDCount = Detzcode32(header.TtisSTDCount);

View file

@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Input.Motion.CemuHook.Protocol namespace Ryujinx.Input.Motion.CemuHook.Protocol
{ {
@ -8,9 +9,7 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol
public MessageType Type; public MessageType Type;
public SubscriberType SubscriberType; public SubscriberType SubscriberType;
public byte Slot; public byte Slot;
public Array6<byte> MacAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] MacAddress;
} }
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
@ -27,11 +26,8 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol
public uint DPadAnalog; public uint DPadAnalog;
public ulong MainButtonsAnalog; public ulong MainButtonsAnalog;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public Array6<byte> Touch1;
public byte[] Touch1; public Array6<byte> Touch2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Touch2;
public ulong MotionTimestamp; public ulong MotionTimestamp;
public float AccelerometerX; public float AccelerometerX;

View file

@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Input.Motion.CemuHook.Protocol namespace Ryujinx.Input.Motion.CemuHook.Protocol
{ {
@ -14,8 +15,6 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol
{ {
public MessageType Type; public MessageType Type;
public int PortsCount; public int PortsCount;
public Array4<byte> PortIndices;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] PortIndices;
} }
} }

View file

@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Input.Motion.CemuHook.Protocol namespace Ryujinx.Input.Motion.CemuHook.Protocol
{ {
@ -11,8 +12,7 @@ namespace Ryujinx.Input.Motion.CemuHook.Protocol
public DeviceModelType ModelType; public DeviceModelType ModelType;
public ConnectionType ConnectionType; public ConnectionType ConnectionType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public Array6<byte> MacAddress;
public byte[] MacAddress;
public BatteryStatus BatteryStatus; public BatteryStatus BatteryStatus;
} }