aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Horizon/Sdk/Arp/ArpApi.cs
blob: b0acc0062f57710d2d86feed5d99c65d49401cdb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using Ryujinx.Common.Memory;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Ns;
using Ryujinx.Horizon.Sdk.Sf.Cmif;
using Ryujinx.Horizon.Sdk.Sf.Hipc;
using Ryujinx.Horizon.Sdk.Sm;
using System;
using System.Runtime.CompilerServices;

namespace Ryujinx.Horizon.Sdk.Arp
{
    class ArpApi : IDisposable
    {
        private const string ArpRName = "arp:r";

        private readonly HeapAllocator _allocator;
        private int _sessionHandle;

        public ArpApi(HeapAllocator allocator)
        {
            _allocator = allocator;
        }

        private void InitializeArpRService()
        {
            if (_sessionHandle == 0)
            {
                using var smApi = new SmApi();

                smApi.Initialize();
                smApi.GetServiceHandle(out _sessionHandle, ServiceName.Encode(ArpRName)).AbortOnFailure();
            }
        }

        public Result GetApplicationInstanceId(out ulong applicationInstanceId, ulong applicationPid)
        {
            Span<byte> data = stackalloc byte[8];
            SpanWriter writer = new(data);

            writer.Write(applicationPid);

            InitializeArpRService();

            Result result = ServiceUtil.SendRequest(out CmifResponse response, _sessionHandle, 3, sendPid: false, data);
            if (result.IsFailure)
            {
                applicationInstanceId = 0;

                return result;
            }

            SpanReader reader = new(response.Data);

            applicationInstanceId = reader.Read<ulong>();

            return Result.Success;
        }

        public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty applicationLaunchProperty, ulong applicationInstanceId)
        {
            applicationLaunchProperty = default;

            Span<byte> data = stackalloc byte[8];
            SpanWriter writer = new(data);

            writer.Write(applicationInstanceId);

            InitializeArpRService();

            Result result = ServiceUtil.SendRequest(out CmifResponse response, _sessionHandle, 0, sendPid: false, data);
            if (result.IsFailure)
            {
                return result;
            }

            SpanReader reader = new(response.Data);

            applicationLaunchProperty = reader.Read<ApplicationLaunchProperty>();

            return Result.Success;
        }

        public Result GetApplicationControlProperty(out ApplicationControlProperty applicationControlProperty, ulong applicationInstanceId)
        {
            applicationControlProperty = default;

            Span<byte> data = stackalloc byte[8];
            SpanWriter writer = new(data);

            writer.Write(applicationInstanceId);

            ulong bufferSize = (ulong)Unsafe.SizeOf<ApplicationControlProperty>();
            ulong bufferAddress = _allocator.Allocate(bufferSize);

            InitializeArpRService();

            Result result = ServiceUtil.SendRequest(
                out CmifResponse response,
                _sessionHandle,
                1,
                sendPid: false,
                data,
                stackalloc[] { HipcBufferFlags.Out | HipcBufferFlags.MapAlias | HipcBufferFlags.FixedSize },
                stackalloc[] { new PointerAndSize(bufferAddress, bufferSize) });

            if (result.IsFailure)
            {
                return result;
            }

            applicationControlProperty = HorizonStatic.AddressSpace.Read<ApplicationControlProperty>(bufferAddress);

            _allocator.Free(bufferAddress, bufferSize);

            return Result.Success;
        }

        public void Dispose()
        {
            if (_sessionHandle != 0)
            {
                HorizonStatic.Syscall.CloseHandle(_sessionHandle);

                _sessionHandle = 0;
            }

            GC.SuppressFinalize(this);
        }
    }
}