aboutsummaryrefslogblamecommitdiff
path: root/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs
blob: 3017f40419a383f03358f5386d86b4e093851b98 (plain) (tree)




























































































































































































































                                                                                                                                                           
using Ryujinx.Common;
using Ryujinx.Horizon.Sdk.Sf.Cmif;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Ryujinx.Horizon.Sdk.Sf.Hipc
{
    ref struct HipcMessage
    {
        public const int AutoReceiveStatic = byte.MaxValue;

        public HipcMetadata Meta;
        public HipcMessageData Data;
        public ulong Pid;

        public HipcMessage(Span<byte> data)
        {
            int initialLength = data.Length;

            Header header = MemoryMarshal.Cast<byte, Header>(data)[0];

            data = data.Slice(Unsafe.SizeOf<Header>());

            int receiveStaticsCount = 0;
            ulong pid = 0;

            if (header.ReceiveStaticMode != 0)
            {
                if (header.ReceiveStaticMode == 2)
                {
                    receiveStaticsCount = AutoReceiveStatic;
                }
                else if (header.ReceiveStaticMode > 2)
                {
                    receiveStaticsCount = header.ReceiveStaticMode - 2;
                }
            }

            SpecialHeader specialHeader = default;

            if (header.HasSpecialHeader)
            {
                specialHeader = MemoryMarshal.Cast<byte, SpecialHeader>(data)[0];

                data = data.Slice(Unsafe.SizeOf<SpecialHeader>());

                if (specialHeader.SendPid)
                {
                    pid = MemoryMarshal.Cast<byte, ulong>(data)[0];

                    data = data.Slice(sizeof(ulong));
                }
            }

            Meta = new HipcMetadata()
            {
                Type = (int)header.Type,
                SendStaticsCount = header.SendStaticsCount,
                SendBuffersCount = header.SendBuffersCount,
                ReceiveBuffersCount = header.ReceiveBuffersCount,
                ExchangeBuffersCount = header.ExchangeBuffersCount,
                DataWordsCount = header.DataWordsCount,
                ReceiveStaticsCount = receiveStaticsCount,
                SendPid = specialHeader.SendPid,
                CopyHandlesCount = specialHeader.CopyHandlesCount,
                MoveHandlesCount = specialHeader.MoveHandlesCount
            };

            Data = CreateMessageData(Meta, data, initialLength);
            Pid = pid;
        }

        public static HipcMessageData WriteResponse(
            Span<byte> destination,
            int sendStaticCount,
            int dataWordsCount,
            int copyHandlesCount,
            int moveHandlesCount)
        {
            return WriteMessage(destination, new HipcMetadata()
            {
                SendStaticsCount = sendStaticCount,
                DataWordsCount = dataWordsCount,
                CopyHandlesCount = copyHandlesCount,
                MoveHandlesCount = moveHandlesCount
            });
        }

        public static HipcMessageData WriteMessage(Span<byte> destination, HipcMetadata meta)
        {
            int initialLength = destination.Length;

            bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0;

            MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header()
            {
                Type = (CommandType)meta.Type,
                SendStaticsCount = meta.SendStaticsCount,
                SendBuffersCount = meta.SendBuffersCount,
                ReceiveBuffersCount = meta.ReceiveBuffersCount,
                ExchangeBuffersCount = meta.ExchangeBuffersCount,
                DataWordsCount = meta.DataWordsCount,
                ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0,
                HasSpecialHeader = hasSpecialHeader
            };

            destination = destination.Slice(Unsafe.SizeOf<Header>());

            if (hasSpecialHeader)
            {
                MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader()
                {
                    SendPid = meta.SendPid,
                    CopyHandlesCount = meta.CopyHandlesCount,
                    MoveHandlesCount = meta.MoveHandlesCount
                };

                destination = destination.Slice(Unsafe.SizeOf<SpecialHeader>());

                if (meta.SendPid)
                {
                    destination = destination.Slice(sizeof(ulong));
                }
            }

            return CreateMessageData(meta, destination, initialLength);
        }

        private static HipcMessageData CreateMessageData(HipcMetadata meta, Span<byte> data, int initialLength)
        {
            Span<int> copyHandles = Span<int>.Empty;

            if (meta.CopyHandlesCount != 0)
            {
                copyHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.CopyHandlesCount);

                data = data.Slice(meta.CopyHandlesCount * sizeof(int));
            }

            Span<int> moveHandles = Span<int>.Empty;

            if (meta.MoveHandlesCount != 0)
            {
                moveHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.MoveHandlesCount);

                data = data.Slice(meta.MoveHandlesCount * sizeof(int));
            }

            Span<HipcStaticDescriptor> sendStatics = Span<HipcStaticDescriptor>.Empty;

            if (meta.SendStaticsCount != 0)
            {
                sendStatics = MemoryMarshal.Cast<byte, HipcStaticDescriptor>(data).Slice(0, meta.SendStaticsCount);

                data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf<HipcStaticDescriptor>());
            }

            Span<HipcBufferDescriptor> sendBuffers = Span<HipcBufferDescriptor>.Empty;

            if (meta.SendBuffersCount != 0)
            {
                sendBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.SendBuffersCount);

                data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
            }

            Span<HipcBufferDescriptor> receiveBuffers = Span<HipcBufferDescriptor>.Empty;

            if (meta.ReceiveBuffersCount != 0)
            {
                receiveBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ReceiveBuffersCount);

                data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
            }

            Span<HipcBufferDescriptor> exchangeBuffers = Span<HipcBufferDescriptor>.Empty;

            if (meta.ExchangeBuffersCount != 0)
            {
                exchangeBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ExchangeBuffersCount);

                data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
            }

            Span<uint> dataWords = Span<uint>.Empty;

            if (meta.DataWordsCount != 0)
            {
                int dataOffset = initialLength - data.Length;
                int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10);

                int padding = (dataOffsetAligned - dataOffset) / sizeof(uint);

                dataWords = MemoryMarshal.Cast<byte, uint>(data).Slice(padding, meta.DataWordsCount - padding);

                data = data.Slice(meta.DataWordsCount * sizeof(uint));
            }

            Span<HipcReceiveListEntry> receiveList = Span<HipcReceiveListEntry>.Empty;

            if (meta.ReceiveStaticsCount != 0)
            {
                int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount;

                receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data).Slice(0, receiveListSize);
            }

            return new HipcMessageData()
            {
                SendStatics = sendStatics,
                SendBuffers = sendBuffers,
                ReceiveBuffers = receiveBuffers,
                ExchangeBuffers = exchangeBuffers,
                DataWords = dataWords,
                ReceiveList = receiveList,
                CopyHandles = copyHandles,
                MoveHandles = moveHandles
            };
        }
    }
}