aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs
blob: c5525a5d607766d136e105b0bcaa8a9ee82d84c7 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;

namespace Ryujinx.HLE.HOS.Services.Ns
{
    [Service("aoc:u")]
    class IAddOnContentManager : IpcService
    {
        private readonly KEvent _addOnContentListChangedEvent;

        private int _addOnContentListChangedEventHandle;

        public IAddOnContentManager(ServiceCtx context)
        {
            _addOnContentListChangedEvent = new KEvent(context.Device.System.KernelContext);
        }

        [CommandHipc(2)]
        // CountAddOnContent(pid) -> u32
        public ResultCode CountAddOnContent(ServiceCtx context)
        {
            long pid = context.Process.Pid;

            // Official code checks ApplicationControlProperty.RuntimeAddOnContentInstall
            // if true calls ns:am ListAvailableAddOnContent again to get updated count

            byte runtimeAddOnContentInstall = context.Device.Application.ControlData.Value.RuntimeAddOnContentInstall;
            if (runtimeAddOnContentInstall != 0)
            {
                Logger.Warning?.Print(LogClass.ServiceNs, $"RuntimeAddOnContentInstall is true. Some DLC may be missing");
            }

            uint aocCount = CountAddOnContentImpl(context);

            context.ResponseData.Write(aocCount);

            Logger.Stub?.PrintStub(LogClass.ServiceNs, new { aocCount, runtimeAddOnContentInstall });

            return ResultCode.Success;
        }

        private static uint CountAddOnContentImpl(ServiceCtx context)
        {
            return (uint)context.Device.System.ContentManager.GetAocCount();
        }

        [CommandHipc(3)]
        // ListAddOnContent(u32, u32, pid) -> (u32, buffer<u32>)
        public ResultCode ListAddOnContent(ServiceCtx context)
        {
            uint startIndex = context.RequestData.ReadUInt32();
            uint bufferSize = context.RequestData.ReadUInt32();
            long pid = context.Process.Pid;

            var aocTitleIds = context.Device.System.ContentManager.GetAocTitleIds();

            uint aocCount = CountAddOnContentImpl(context);

            if (aocCount <= startIndex)
            {
                context.ResponseData.Write((uint)0);

                return ResultCode.Success;
            }

            aocCount = Math.Min(aocCount - startIndex, bufferSize);

            context.ResponseData.Write(aocCount);

            ulong bufAddr = (ulong)context.Request.ReceiveBuff[0].Position;

            ulong aocBaseId = GetAddOnContentBaseIdImpl(context);

            for (int i = 0; i < aocCount; ++i)
            {
                context.Memory.Write(bufAddr + (ulong)i * 4, (int)(aocTitleIds[i + (int)startIndex] - aocBaseId));
            }

            Logger.Stub?.PrintStub(LogClass.ServiceNs, new { bufferSize, startIndex, aocCount });

            return ResultCode.Success;
        }

        [CommandHipc(5)]
        // GetAddOnContentBaseId(pid) -> u64
        public ResultCode GetAddonContentBaseId(ServiceCtx context)
        {
            long pid = context.Process.Pid;

            // Official code calls arp:r GetApplicationControlProperty to get AddOnContentBaseId
            // If the call fails, calls arp:r GetApplicationLaunchProperty to get App TitleId
            ulong aocBaseId = GetAddOnContentBaseIdImpl(context);

            context.ResponseData.Write(aocBaseId);

            Logger.Stub?.PrintStub(LogClass.ServiceNs, $"aocBaseId={aocBaseId:X16}");

            // ResultCode will be error code of GetApplicationLaunchProperty if it fails
            return ResultCode.Success;
        }

        private static ulong GetAddOnContentBaseIdImpl(ServiceCtx context)
        {
            ulong aocBaseId = context.Device.Application.ControlData.Value.AddOnContentBaseId;

            if (aocBaseId == 0)
            {
                aocBaseId = context.Device.Application.TitleId + 0x1000;
            }

            return aocBaseId;
        }

        [CommandHipc(7)]
        // PrepareAddOnContent(u32, pid)
        public ResultCode PrepareAddOnContent(ServiceCtx context)
        {
            uint aocIndex = context.RequestData.ReadUInt32();
            long pid = context.Process.Pid;

            // Official Code calls a bunch of functions from arp:r for aocBaseId
            // and ns:am RegisterContentsExternalKey?, GetOwnedApplicationContentMetaStatus? etc...

            // Ideally, this should probably initialize the AocData values for the specified index

            Logger.Stub?.PrintStub(LogClass.ServiceNs, new { aocIndex });

            return ResultCode.Success;
        }

        [CommandHipc(8)]
        // GetAddOnContentListChangedEvent() -> handle<copy>
        public ResultCode GetAddOnContentListChangedEvent(ServiceCtx context)
        {
            // Official code seems to make an internal call to ns:am Cmd 84 GetDynamicCommitEvent()

            if (_addOnContentListChangedEventHandle == 0)
            {
                if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != KernelResult.Success)
                {
                    throw new InvalidOperationException("Out of handles!");
                }
            }

            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_addOnContentListChangedEventHandle);

            Logger.Stub?.PrintStub(LogClass.ServiceNs);

            return ResultCode.Success;
        }


        [CommandHipc(9)] // [10.0.0+]
        // GetAddOnContentLostErrorCode() -> u64
        public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context)
        {
            // Seems to calculate ((value & 0x1ff)) + 2000 on 0x7d0a4
            // which gives 0x874 (2000+164). 164 being Module ID of `EC (Shop)`
            context.ResponseData.Write(2164L);

            Logger.Stub?.PrintStub(LogClass.ServiceNs);

            return ResultCode.Success;
        }

        [CommandHipc(100)]
        // CreateEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
        public ResultCode CreateEcPurchasedEventManager(ServiceCtx context)
        {
            MakeObject(context, new IPurchaseEventManager(context.Device.System));

            Logger.Stub?.PrintStub(LogClass.ServiceNs);

            return ResultCode.Success;
        }

        [CommandHipc(101)]
        // CreatePermanentEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
        public ResultCode CreatePermanentEcPurchasedEventManager(ServiceCtx context)
        {
            // Very similar to CreateEcPurchasedEventManager but with some extra code

            MakeObject(context, new IPurchaseEventManager(context.Device.System));

            Logger.Stub?.PrintStub(LogClass.ServiceNs);

            return ResultCode.Success;
        }
    }
}