aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs')
-rw-r--r--Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs175
1 files changed, 175 insertions, 0 deletions
diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
new file mode 100644
index 000000000..473f374db
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
@@ -0,0 +1,175 @@
1using LibHac;
2using LibHac.Common;
3using LibHac.Fs;
4using LibHac.Fs.Fsa;
5using LibHac.Loader;
6using LibHac.Ncm;
7using LibHac.Ns;
8using LibHac.Tools.FsSystem;
9using LibHac.Tools.FsSystem.NcaUtils;
10using Ryujinx.Common.Logging;
11using System.IO;
12using System.Linq;
13using ApplicationId = LibHac.Ncm.ApplicationId;
14
15namespace Ryujinx.HLE.Loaders.Processes.Extensions
16{
17 static class NcaExtensions
18 {
19 public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
20 {
21 // Extract RomFs and ExeFs from NCA.
22 IStorage romFs = nca.GetRomFs(device, patchNca);
23 IFileSystem exeFs = nca.GetExeFs(device, patchNca);
24
25 if (exeFs == null)
26 {
27 Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
28
29 return ProcessResult.Failed;
30 }
31
32 // Load Npdm file.
33 MetaLoader metaLoader = exeFs.GetNpdm();
34
35 // Collecting mods related to AocTitleIds and ProgramId.
36 device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
37 device.Configuration.ContentManager.GetAocTitleIds().Prepend(metaLoader.GetProgramId()),
38 device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
39 device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
40
41 // Load Nacp file.
42 var nacpData = new BlitStruct<ApplicationControlProperty>(1);
43
44 if (controlNca != null)
45 {
46 nacpData = controlNca.GetNacp(device);
47 }
48
49 /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant update.
50
51 // Load program 0 control NCA as we are going to need it for display version.
52 (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
53
54 // NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application.
55 // As such, to avoid PTC cache confusion, we only trust the program 0 display version when launching a sub program.
56 if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0)
57 {
58 nacpData.Value.DisplayVersion = updateProgram0ControlNca.GetNacp(_device).Value.DisplayVersion;
59 }
60
61 */
62
63 ProcessResult processResult = exeFs.Load(device, nacpData, metaLoader);
64
65 // Load RomFS.
66 if (romFs == null)
67 {
68 Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA");
69 }
70 else
71 {
72 romFs = device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(processResult.ProgramId, romFs);
73
74 device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romFs.AsStream(FileAccess.Read));
75 }
76
77 // Don't create save data for system programs.
78 if (processResult.ProgramId != 0 && (processResult.ProgramId < SystemProgramId.Start.Value || processResult.ProgramId > SystemAppletId.End.Value))
79 {
80 // Multi-program applications can technically use any program ID for the main program, but in practice they always use 0 in the low nibble.
81 // We'll know if this changes in the future because applications will get errors when trying to mount the correct save.
82 ProcessLoaderHelper.EnsureSaveData(device, new ApplicationId(processResult.ProgramId & ~0xFul), nacpData);
83 }
84
85 return processResult;
86 }
87
88 public static int GetProgramIndex(this Nca nca)
89 {
90 return (int)(nca.Header.TitleId & 0xF);
91 }
92
93 public static bool IsProgram(this Nca nca)
94 {
95 return nca.Header.ContentType == NcaContentType.Program;
96 }
97
98 public static bool IsPatch(this Nca nca)
99 {
100 int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
101
102 return nca.IsProgram() && nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection();
103 }
104
105 public static bool IsControl(this Nca nca)
106 {
107 return nca.Header.ContentType == NcaContentType.Control;
108 }
109
110 public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null)
111 {
112 IFileSystem exeFs = null;
113
114 if (patchNca == null)
115 {
116 if (nca.CanOpenSection(NcaSectionType.Code))
117 {
118 exeFs = nca.OpenFileSystem(NcaSectionType.Code, device.System.FsIntegrityCheckLevel);
119 }
120 }
121 else
122 {
123 if (patchNca.CanOpenSection(NcaSectionType.Code))
124 {
125 exeFs = nca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, device.System.FsIntegrityCheckLevel);
126 }
127 }
128
129 return exeFs;
130 }
131
132 public static IStorage GetRomFs(this Nca nca, Switch device, Nca patchNca = null)
133 {
134 IStorage romFs = null;
135
136 if (patchNca == null)
137 {
138 if (nca.CanOpenSection(NcaSectionType.Data))
139 {
140 romFs = nca.OpenStorage(NcaSectionType.Data, device.System.FsIntegrityCheckLevel);
141 }
142 }
143 else
144 {
145 if (patchNca.CanOpenSection(NcaSectionType.Data))
146 {
147 romFs = nca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, device.System.FsIntegrityCheckLevel);
148 }
149 }
150
151 return romFs;
152 }
153
154 public static BlitStruct<ApplicationControlProperty> GetNacp(this Nca controlNca, Switch device)
155 {
156 var nacpData = new BlitStruct<ApplicationControlProperty>(1);
157
158 using var controlFile = new UniqueRef<IFile>();
159
160 Result result = controlNca.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel)
161 .OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read);
162
163 if (result.IsSuccess())
164 {
165 result = controlFile.Get.Read(out long bytesRead, 0, nacpData.ByteSpan, ReadOption.None);
166 }
167 else
168 {
169 nacpData.ByteSpan.Clear();
170 }
171
172 return nacpData;
173 }
174 }
175} \ No newline at end of file