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