/ src / Ryujinx.HLE / Loaders / Processes / Extensions / FileSystemExtensions.cs
FileSystemExtensions.cs
  1  using LibHac.Common;
  2  using LibHac.Fs;
  3  using LibHac.Fs.Fsa;
  4  using LibHac.Loader;
  5  using LibHac.Ns;
  6  using LibHac.Tools.FsSystem;
  7  using Ryujinx.Common.Configuration;
  8  using Ryujinx.Common.Logging;
  9  using Ryujinx.HLE.Loaders.Executables;
 10  using Ryujinx.Memory;
 11  using System;
 12  using System.Linq;
 13  using static Ryujinx.HLE.HOS.ModLoader;
 14  
 15  namespace Ryujinx.HLE.Loaders.Processes.Extensions
 16  {
 17      static class FileSystemExtensions
 18      {
 19          public static MetaLoader GetNpdm(this IFileSystem fileSystem)
 20          {
 21              MetaLoader metaLoader = new();
 22  
 23              if (fileSystem == null || !fileSystem.FileExists(ProcessConst.MainNpdmPath))
 24              {
 25                  Logger.Warning?.Print(LogClass.Loader, "NPDM file not found, using default values!");
 26  
 27                  metaLoader.LoadDefault();
 28              }
 29              else
 30              {
 31                  metaLoader.LoadFromFile(fileSystem);
 32              }
 33  
 34              return metaLoader;
 35          }
 36  
 37          public static ProcessResult Load(this IFileSystem exeFs, Switch device, BlitStruct<ApplicationControlProperty> nacpData, MetaLoader metaLoader, byte programIndex, bool isHomebrew = false)
 38          {
 39              ulong programId = metaLoader.GetProgramId();
 40  
 41              // Replace the whole ExeFs partition by the modded one.
 42              if (device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(programId, ref exeFs))
 43              {
 44                  metaLoader = null;
 45              }
 46  
 47              // Reload the MetaLoader in case of ExeFs partition replacement.
 48              metaLoader ??= exeFs.GetNpdm();
 49  
 50              NsoExecutable[] nsoExecutables = new NsoExecutable[ProcessConst.ExeFsPrefixes.Length];
 51  
 52              for (int i = 0; i < nsoExecutables.Length; i++)
 53              {
 54                  string name = ProcessConst.ExeFsPrefixes[i];
 55  
 56                  if (!exeFs.FileExists($"/{name}"))
 57                  {
 58                      continue; // File doesn't exist, skip.
 59                  }
 60  
 61                  Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
 62  
 63                  using var nsoFile = new UniqueRef<IFile>();
 64  
 65                  exeFs.OpenFile(ref nsoFile.Ref, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
 66  
 67                  nsoExecutables[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
 68              }
 69  
 70              // ExeFs file replacements.
 71              ModLoadResult modLoadResult = device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(programId, nsoExecutables);
 72  
 73              // Take the Npdm from mods if present.
 74              if (modLoadResult.Npdm != null)
 75              {
 76                  metaLoader = modLoadResult.Npdm;
 77              }
 78  
 79              // Collect the Nsos, ignoring ones that aren't used.
 80              nsoExecutables = nsoExecutables.Where(x => x != null).ToArray();
 81  
 82              // Apply Nsos patches.
 83              device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables);
 84  
 85              // Don't use PTC if ExeFS files have been replaced.
 86              bool enablePtc = device.System.EnablePtc && !modLoadResult.Modified;
 87              if (!enablePtc)
 88              {
 89                  Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
 90              }
 91  
 92              string programName = "";
 93  
 94              if (!isHomebrew && programId > 0x010000000000FFFF)
 95              {
 96                  programName = nacpData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString();
 97  
 98                  if (string.IsNullOrWhiteSpace(programName))
 99                  {
100                      programName = Array.Find(nacpData.Value.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString();
101                  }
102              }
103  
104              // Initialize GPU.
105              Graphics.Gpu.GraphicsConfig.TitleId = $"{programId:x16}";
106              device.Gpu.HostInitalized.Set();
107  
108              if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
109              {
110                  device.Configuration.MemoryManagerMode = MemoryManagerMode.SoftwarePageTable;
111              }
112  
113              ProcessResult processResult = ProcessLoaderHelper.LoadNsos(
114                  device,
115                  device.System.KernelContext,
116                  metaLoader,
117                  nacpData,
118                  enablePtc,
119                  true,
120                  programName,
121                  metaLoader.GetProgramId(),
122                  programIndex,
123                  null,
124                  nsoExecutables);
125  
126              // TODO: This should be stored using ProcessId instead.
127              device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(metaLoader.GetProgramId());
128  
129              return processResult;
130          }
131      }
132  }