diff options
Diffstat (limited to 'Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs')
-rw-r--r-- | Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs | 244 |
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 @@ | |||
1 | using LibHac.Common; | ||
2 | using LibHac.Fs; | ||
3 | using LibHac.Fs.Fsa; | ||
4 | using LibHac.FsSystem; | ||
5 | using LibHac.Ns; | ||
6 | using LibHac.Tools.Fs; | ||
7 | using LibHac.Tools.FsSystem; | ||
8 | using LibHac.Tools.FsSystem.NcaUtils; | ||
9 | using Ryujinx.Common.Logging; | ||
10 | using Ryujinx.HLE.Loaders.Executables; | ||
11 | using Ryujinx.HLE.Loaders.Processes.Extensions; | ||
12 | using System.Collections.Concurrent; | ||
13 | using System.IO; | ||
14 | using System.Linq; | ||
15 | using Path = System.IO.Path; | ||
16 | |||
17 | namespace 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 | ||