aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs')
-rw-r--r--Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs244
1 files changed, 244 insertions, 0 deletions
diff --git a/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
new file mode 100644
index 000000000..785db0e50
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
@@ -0,0 +1,244 @@
1using LibHac.Common;
2using LibHac.Fs;
3using LibHac.Fs.Fsa;
4using LibHac.FsSystem;
5using LibHac.Ns;
6using LibHac.Tools.Fs;
7using LibHac.Tools.FsSystem;
8using LibHac.Tools.FsSystem.NcaUtils;
9using Ryujinx.Common.Logging;
10using Ryujinx.HLE.Loaders.Executables;
11using Ryujinx.HLE.Loaders.Processes.Extensions;
12using System.Collections.Concurrent;
13using System.IO;
14using System.Linq;
15using Path = System.IO.Path;
16
17namespace Ryujinx.HLE.Loaders.Processes
18{
19 public class ProcessLoader
20 {
21 private readonly Switch _device;
22
23 private readonly ConcurrentDictionary<ulong, ProcessResult> _processesByPid;
24
25 private ulong _latestPid;
26
27 public ProcessResult ActiveApplication => _processesByPid[_latestPid];
28
29 public ProcessLoader(Switch device)
30 {
31 _device = device;
32 _processesByPid = new ConcurrentDictionary<ulong, ProcessResult>();
33 }
34
35 public bool LoadXci(string path)
36 {
37 FileStream stream = new(path, FileMode.Open, FileAccess.Read);
38 Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
39
40 if (!xci.HasPartition(XciPartitionType.Secure))
41 {
42 Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI Secure partition");
43
44 return false;
45 }
46
47 (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage);
48
49 if (!success)
50 {
51 Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad));
52
53 return false;
54 }
55
56 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
57 {
58 if (processResult.Start(_device))
59 {
60 _latestPid = processResult.ProcessId;
61
62 return true;
63 }
64 }
65
66 return false;
67 }
68
69 public bool LoadNsp(string path)
70 {
71 FileStream file = new(path, FileMode.Open, FileAccess.Read);
72 PartitionFileSystem partitionFileSystem = new(file.AsStorage());
73
74 (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage);
75
76 if (processResult.ProcessId == 0)
77 {
78 // This is not a normal NSP, it's actually a ExeFS as a NSP
79 processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), true);
80 }
81
82 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
83 {
84 if (processResult.Start(_device))
85 {
86 _latestPid = processResult.ProcessId;
87
88 return true;
89 }
90 }
91
92 if (!success)
93 {
94 Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad));
95 }
96
97 return false;
98 }
99
100 public bool LoadNca(string path)
101 {
102 FileStream file = new(path, FileMode.Open, FileAccess.Read);
103 Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
104
105 ProcessResult processResult = nca.Load(_device, null, null);
106
107 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
108 {
109 if (processResult.Start(_device))
110 {
111 // NOTE: Check if process is SystemApplicationId or ApplicationId
112 if (processResult.ProgramId > 0x01000000000007FF)
113 {
114 _latestPid = processResult.ProcessId;
115 }
116
117 return true;
118 }
119 }
120
121 return false;
122 }
123
124 public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
125 {
126 ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
127
128 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
129 {
130 if (processResult.Start(_device))
131 {
132 _latestPid = processResult.ProcessId;
133
134 return true;
135 }
136 }
137
138 return false;
139 }
140
141 public bool LoadNxo(string path)
142 {
143 var nacpData = new BlitStruct<ApplicationControlProperty>(1);
144 IFileSystem dummyExeFs = null;
145 Stream romfsStream = null;
146
147 string programName = "";
148 ulong programId = 0000000000000000;
149
150 // Load executable.
151 IExecutable executable;
152
153 if (Path.GetExtension(path).ToLower() == ".nro")
154 {
155 FileStream input = new(path, FileMode.Open);
156 NroExecutable nro = new(input.AsStorage());
157
158 executable = nro;
159
160 // Open RomFS if exists.
161 IStorage romFsStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.RomFs, false);
162 romFsStorage.GetSize(out long romFsSize).ThrowIfFailure();
163 if (romFsSize != 0)
164 {
165 romfsStream = romFsStorage.AsStream();
166 }
167
168 // Load Nacp if exists.
169 IStorage nacpStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.Nacp, false);
170 nacpStorage.GetSize(out long nacpSize).ThrowIfFailure();
171 if (nacpSize != 0)
172 {
173 nacpStorage.Read(0, nacpData.ByteSpan);
174
175 programName = nacpData.Value.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
176
177 if (string.IsNullOrWhiteSpace(programName))
178 {
179 programName = nacpData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
180 }
181
182 if (nacpData.Value.PresenceGroupId != 0)
183 {
184 programId = nacpData.Value.PresenceGroupId;
185 }
186 else if (nacpData.Value.SaveDataOwnerId != 0)
187 {
188 programId = nacpData.Value.SaveDataOwnerId;
189 }
190 else if (nacpData.Value.AddOnContentBaseId != 0)
191 {
192 programId = nacpData.Value.AddOnContentBaseId - 0x1000;
193 }
194 }
195
196 // TODO: Add icon maybe ?
197 }
198 else
199 {
200 programName = System.IO.Path.GetFileNameWithoutExtension(path);
201
202 executable = new NsoExecutable(new LocalStorage(path, FileAccess.Read), programName);
203 }
204
205 // Explicitly null TitleId to disable the shader cache.
206 Graphics.Gpu.GraphicsConfig.TitleId = null;
207 _device.Gpu.HostInitalized.Set();
208
209 ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
210 _device.System.KernelContext,
211 dummyExeFs.GetNpdm(),
212 nacpData.Value,
213 diskCacheEnabled: false,
214 allowCodeMemoryForJit: true,
215 programName,
216 programId,
217 null,
218 executable);
219
220 // Make sure the process id is valid.
221 if (processResult.ProcessId != 0)
222 {
223 // Load RomFS.
224 if (romfsStream != null)
225 {
226 _device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romfsStream);
227 }
228
229 // Start process.
230 if (_processesByPid.TryAdd(processResult.ProcessId, processResult))
231 {
232 if (processResult.Start(_device))
233 {
234 _latestPid = processResult.ProcessId;
235
236 return true;
237 }
238 }
239 }
240
241 return false;
242 }
243 }
244} \ No newline at end of file