using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { class Parcel { private readonly byte[] _rawData; private Span Raw => new Span(_rawData); private ref ParcelHeader Header => ref MemoryMarshal.Cast(_rawData)[0]; private Span Payload => Raw.Slice((int)Header.PayloadOffset, (int)Header.PayloadSize); private Span Objects => Raw.Slice((int)Header.ObjectOffset, (int)Header.ObjectsSize); private int _payloadPosition; private int _objectPosition; public Parcel(byte[] rawData) { _rawData = rawData; _payloadPosition = 0; _objectPosition = 0; } public Parcel(uint payloadSize, uint objectsSize) { uint headerSize = (uint)Unsafe.SizeOf(); _rawData = new byte[BitUtils.AlignUp(headerSize + payloadSize + objectsSize, 4)]; Header.PayloadSize = payloadSize; Header.ObjectsSize = objectsSize; Header.PayloadOffset = headerSize; Header.ObjectOffset = Header.PayloadOffset + Header.ObjectsSize; } public string ReadInterfaceToken() { // Ignore the policy flags int strictPolicy = ReadInt32(); return ReadString16(); } public string ReadString16() { int size = ReadInt32(); if (size < 0) { return ""; } ReadOnlySpan data = ReadInPlace((size + 1) * 2); // Return the unicode string without the last character (null terminator) return Encoding.Unicode.GetString(data.Slice(0, size * 2)); } public int ReadInt32() => ReadUnmanagedType(); public uint ReadUInt32() => ReadUnmanagedType(); public bool ReadBoolean() => ReadUnmanagedType() != 0; public long ReadInt64() => ReadUnmanagedType(); public ulong ReadUInt64() => ReadUnmanagedType(); public T ReadFlattenable() where T : unmanaged, IFlattenable { long flattenableSize = ReadInt64(); T result = new T(); Debug.Assert(flattenableSize == result.GetFlattenedSize()); result.Unflatten(this); return result; } public T ReadUnmanagedType() where T: unmanaged { ReadOnlySpan data = ReadInPlace(Unsafe.SizeOf()); return MemoryMarshal.Cast(data)[0]; } public ReadOnlySpan ReadInPlace(int size) { ReadOnlySpan result = Payload.Slice(_payloadPosition, size); _payloadPosition += BitUtils.AlignUp(size, 4); return result; } [StructLayout(LayoutKind.Sequential, Size = 0x28)] private struct FlatBinderObject { public int Type; public int Flags; public long BinderId; public long Cookie; private byte _serviceNameStart; public Span ServiceName => MemoryMarshal.CreateSpan(ref _serviceNameStart, 0x8); } public void WriteObject(T obj, string serviceName) where T: IBinder { FlatBinderObject flatBinderObject = new FlatBinderObject { Type = 2, Flags = 0, BinderId = HOSBinderDriverServer.GetBinderId(obj), }; Encoding.ASCII.GetBytes(serviceName).CopyTo(flatBinderObject.ServiceName); WriteUnmanagedType(ref flatBinderObject); // TODO: figure out what this value is WriteInplaceObject(new byte[4] { 0, 0, 0, 0 }); } public AndroidStrongPointer ReadStrongPointer() where T : unmanaged, IFlattenable { bool hasObject = ReadBoolean(); if (hasObject) { T obj = ReadFlattenable(); return new AndroidStrongPointer(obj); } else { return new AndroidStrongPointer(); } } public void WriteStrongPointer(ref AndroidStrongPointer value) where T: unmanaged, IFlattenable { WriteBoolean(!value.IsNull); if (!value.IsNull) { WriteFlattenable(ref value.Object); } } public void WriteFlattenable(ref T value) where T : unmanaged, IFlattenable { WriteInt64(value.GetFlattenedSize()); value.Flatten(this); } public void WriteStatus(Status status) => WriteUnmanagedType(ref status); public void WriteBoolean(bool value) => WriteUnmanagedType(ref value); public void WriteInt32(int value) => WriteUnmanagedType(ref value); public void WriteUInt32(uint value) => WriteUnmanagedType(ref value); public void WriteInt64(long value) => WriteUnmanagedType(ref value); public void WriteUInt64(ulong value) => WriteUnmanagedType(ref value); public void WriteUnmanagedSpan(ReadOnlySpan value) where T : unmanaged { WriteInplace(MemoryMarshal.Cast(value)); } public void WriteUnmanagedType(ref T value) where T : unmanaged { WriteInplace(SpanHelpers.AsByteSpan(ref value)); } public void WriteInplace(ReadOnlySpan data) { Span result = Payload.Slice(_payloadPosition, data.Length); data.CopyTo(result); _payloadPosition += BitUtils.AlignUp(data.Length, 4); } public void WriteInplaceObject(ReadOnlySpan data) { Span result = Objects.Slice(_objectPosition, data.Length); data.CopyTo(result); _objectPosition += BitUtils.AlignUp(data.Length, 4); } private void UpdateHeader() { uint headerSize = (uint)Unsafe.SizeOf(); Header.PayloadSize = (uint)_payloadPosition; Header.ObjectsSize = (uint)_objectPosition; Header.PayloadOffset = headerSize; Header.ObjectOffset = Header.PayloadOffset + Header.PayloadSize; } public ReadOnlySpan Finish() { UpdateHeader(); return Raw.Slice(0, (int)(Header.PayloadSize + Header.ObjectsSize + Unsafe.SizeOf())); } } }