ProcessLoader.cs
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; 13 using System.Collections.Concurrent; 14 using System.IO; 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, ulong applicationId) 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, applicationId, 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, ulong applicationId) 70 { 71 FileStream file = new(path, FileMode.Open, FileAccess.Read); 72 PartitionFileSystem partitionFileSystem = new(); 73 partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure(); 74 75 (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage); 76 77 if (processResult.ProcessId == 0) 78 { 79 // This is not a normal NSP, it's actually a ExeFS as a NSP 80 processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), 0, true); 81 } 82 83 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) 84 { 85 if (processResult.Start(_device)) 86 { 87 _latestPid = processResult.ProcessId; 88 89 return true; 90 } 91 } 92 93 if (!success) 94 { 95 Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad)); 96 } 97 98 return false; 99 } 100 101 public bool LoadNca(string path) 102 { 103 FileStream file = new(path, FileMode.Open, FileAccess.Read); 104 Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); 105 106 ProcessResult processResult = nca.Load(_device, null, null); 107 108 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) 109 { 110 if (processResult.Start(_device)) 111 { 112 // NOTE: Check if process is SystemApplicationId or ApplicationId 113 if (processResult.ProgramId > 0x01000000000007FF) 114 { 115 _latestPid = processResult.ProcessId; 116 } 117 118 return true; 119 } 120 } 121 122 return false; 123 } 124 125 public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null) 126 { 127 ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath); 128 129 if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) 130 { 131 if (processResult.Start(_device)) 132 { 133 _latestPid = processResult.ProcessId; 134 135 return true; 136 } 137 } 138 139 return false; 140 } 141 142 public bool LoadNxo(string path) 143 { 144 var nacpData = new BlitStruct<ApplicationControlProperty>(1); 145 IFileSystem dummyExeFs = null; 146 Stream romfsStream = null; 147 148 string programName = ""; 149 ulong programId = 0000000000000000; 150 151 // Load executable. 152 IExecutable executable; 153 154 if (Path.GetExtension(path).ToLower() == ".nro") 155 { 156 FileStream input = new(path, FileMode.Open); 157 NroExecutable nro = new(input.AsStorage()); 158 159 executable = nro; 160 161 // Open RomFS if exists. 162 IStorage romFsStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.RomFs, false); 163 romFsStorage.GetSize(out long romFsSize).ThrowIfFailure(); 164 if (romFsSize != 0) 165 { 166 romfsStream = romFsStorage.AsStream(); 167 } 168 169 // Load Nacp if exists. 170 IStorage nacpStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.Nacp, false); 171 nacpStorage.GetSize(out long nacpSize).ThrowIfFailure(); 172 if (nacpSize != 0) 173 { 174 nacpStorage.Read(0, nacpData.ByteSpan); 175 176 programName = nacpData.Value.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString(); 177 178 if (string.IsNullOrWhiteSpace(programName)) 179 { 180 programName = Array.Find(nacpData.Value.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString(); 181 } 182 183 if (nacpData.Value.PresenceGroupId != 0) 184 { 185 programId = nacpData.Value.PresenceGroupId; 186 } 187 else if (nacpData.Value.SaveDataOwnerId != 0) 188 { 189 programId = nacpData.Value.SaveDataOwnerId; 190 } 191 else if (nacpData.Value.AddOnContentBaseId != 0) 192 { 193 programId = nacpData.Value.AddOnContentBaseId - 0x1000; 194 } 195 } 196 197 // TODO: Add icon maybe ? 198 } 199 else 200 { 201 programName = Path.GetFileNameWithoutExtension(path); 202 203 executable = new NsoExecutable(new LocalStorage(path, FileAccess.Read), programName); 204 } 205 206 // Explicitly null TitleId to disable the shader cache. 207 Graphics.Gpu.GraphicsConfig.TitleId = null; 208 _device.Gpu.HostInitalized.Set(); 209 210 ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, 211 _device.System.KernelContext, 212 dummyExeFs.GetNpdm(), 213 nacpData, 214 diskCacheEnabled: false, 215 allowCodeMemoryForJit: true, 216 programName, 217 programId, 218 0, 219 null, 220 executable); 221 222 // Make sure the process id is valid. 223 if (processResult.ProcessId != 0) 224 { 225 // Load RomFS. 226 if (romfsStream != null) 227 { 228 _device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romfsStream); 229 } 230 231 // Start process. 232 if (_processesByPid.TryAdd(processResult.ProcessId, processResult)) 233 { 234 if (processResult.Start(_device)) 235 { 236 _latestPid = processResult.ProcessId; 237 238 return true; 239 } 240 } 241 } 242 243 return false; 244 } 245 } 246 }