diff --git a/README.md b/README.md
index 5fa24b8c..de93739d 100644
--- a/README.md
+++ b/README.md
@@ -74,11 +74,16 @@ XCI options:
--securedir
Specify secure XCI directory path.
--logodir Specify logo XCI directory path.
--outdir Specify XCI directory path.
+ --nspout Specify file for the created NSP.
+Partition FS and XCI options:
--exefs Specify main ExeFS file path.
--exefsdir Specify main ExeFS directory path.
--romfs Specify main RomFS file path.
--romfsdir Specify main RomFS directory path.
- --nspout Specify file for the created NSP.
+ --listapps List application info.
+ --listtitles List title info for all titles.
+ --listncas List info for all NCAs.
+ --title Specify title ID to use.
Package1 options:
--outdir Specify Package1 directory path.
Package2 options:
diff --git a/src/LibHac/Ncm/ContentEnums.cs b/src/LibHac/Ncm/ContentEnums.cs
index 792ef977..2923d8fd 100644
--- a/src/LibHac/Ncm/ContentEnums.cs
+++ b/src/LibHac/Ncm/ContentEnums.cs
@@ -1,4 +1,6 @@
-namespace LibHac.Ncm;
+using System;
+
+namespace LibHac.Ncm;
public enum ContentType : byte
{
@@ -24,11 +26,13 @@ public enum ContentMetaType : byte
Delta = 0x83
}
+[Flags]
public enum ContentMetaAttribute : byte
{
None = 0,
- IncludesExFatDriver = 1,
- Rebootless = 2
+ IncludesExFatDriver = 1 << 0,
+ Rebootless = 1 << 1,
+ Compacted = 1 << 2,
}
public enum UpdateType : byte
@@ -36,4 +40,4 @@ public enum UpdateType : byte
ApplyAsDelta = 0,
Overwrite = 1,
Create = 2
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/Tools/Ncm/Cnmt.cs b/src/LibHac/Tools/Ncm/Cnmt.cs
index 28922c4e..56d234d8 100644
--- a/src/LibHac/Tools/Ncm/Cnmt.cs
+++ b/src/LibHac/Tools/Ncm/Cnmt.cs
@@ -15,6 +15,7 @@ public class Cnmt
public int TableOffset { get; }
public int ContentEntryCount { get; }
public int MetaEntryCount { get; }
+ public ContentMetaAttribute ContentMetaAttributes { get; }
public CnmtContentEntry[] ContentEntries { get; }
public CnmtContentMetaEntry[] MetaEntries { get; }
@@ -42,11 +43,12 @@ public class Cnmt
TableOffset = reader.ReadUInt16();
ContentEntryCount = reader.ReadUInt16();
MetaEntryCount = reader.ReadUInt16();
+ ContentMetaAttributes = (ContentMetaAttribute)reader.ReadByte();
// Old, pre-release cnmt files don't have the "required system version" field.
// Try to detect this by reading the padding after that field.
// The old format usually contains hashes there.
- file.Position += 8;
+ file.Position += 7;
int padding = reader.ReadInt32();
bool isOldCnmtFormat = padding != 0;
diff --git a/src/hactoolnet/CliParser.cs b/src/hactoolnet/CliParser.cs
index 1c19ac0b..74eedb42 100644
--- a/src/hactoolnet/CliParser.cs
+++ b/src/hactoolnet/CliParser.cs
@@ -9,70 +9,70 @@ internal static class CliParser
{
private static CliOption[] GetCliOptions() => new[]
{
- new CliOption("custom", 0, (o, _) => o.RunCustom = true),
- new CliOption("intype", 't', 1, (o, a) => o.InFileType = ParseFileType(a[0])),
- new CliOption("raw", 'r', 0, (o, _) => o.Raw = true),
- new CliOption("verify", 'y', 0, (o, _) => o.Validate = true),
- new CliOption("dev", 'd', 0, (o, _) => o.UseDevKeys = true),
- new CliOption("enablehash", 'h', 0, (o, _) => o.EnableHash = true),
- new CliOption("disablekeywarns", 0, (o, _) => o.DisableKeyWarns = true),
- new CliOption("keyset", 'k', 1, (o, a) => o.Keyfile = a[0]),
- new CliOption("titlekeys", 1, (o, a) => o.TitleKeyFile = a[0]),
- new CliOption("consolekeys", 1, (o, a) => o.ConsoleKeyFile = a[0]),
- new CliOption("accesslog", 1, (o, a) => o.AccessLog = a[0]),
- new CliOption("resultlog", 1, (o, a) => o.ResultLog = a[0]),
- new CliOption("section0", 1, (o, a) => o.SectionOut[0] = a[0]),
- new CliOption("section1", 1, (o, a) => o.SectionOut[1] = a[0]),
- new CliOption("section2", 1, (o, a) => o.SectionOut[2] = a[0]),
- new CliOption("section3", 1, (o, a) => o.SectionOut[3] = a[0]),
- new CliOption("section0dir", 1, (o, a) => o.SectionOutDir[0] = a[0]),
- new CliOption("section1dir", 1, (o, a) => o.SectionOutDir[1] = a[0]),
- new CliOption("section2dir", 1, (o, a) => o.SectionOutDir[2] = a[0]),
- new CliOption("section3dir", 1, (o, a) => o.SectionOutDir[3] = a[0]),
- new CliOption("header", 1, (o, a) => o.HeaderOut = a[0]),
- new CliOption("exefs", 1, (o, a) => o.ExefsOut = a[0]),
- new CliOption("exefsdir", 1, (o, a) => o.ExefsOutDir = a[0]),
- new CliOption("romfs", 1, (o, a) => o.RomfsOut = a[0]),
- new CliOption("romfsdir", 1, (o, a) => o.RomfsOutDir = a[0]),
- new CliOption("debugoutdir", 1, (o, a) => o.DebugOutDir = a[0]),
- new CliOption("savedir", 1, (o, a) => o.SaveOutDir = a[0]),
- new CliOption("outdir", 1, (o, a) => o.OutDir = a[0]),
- new CliOption("ini1dir", 1, (o, a) => o.Ini1OutDir = a[0]),
- new CliOption("outfile", 1, (o, a) => o.OutFile = a[0]),
- new CliOption("plaintext", 1, (o, a) => o.PlaintextOut = a[0]),
- new CliOption("ciphertext", 1, (o, a) => o.CiphertextOut = a[0]),
- new CliOption("uncompressed", 1, (o, a) => o.UncompressedOut = a[0]),
- new CliOption("nspout", 1, (o, a) => o.NspOut = a[0]),
- new CliOption("sdseed", 1, (o, a) => o.SdSeed = a[0]),
- new CliOption("sdpath", 1, (o, a) => o.SdPath = a[0]),
- new CliOption("basenca", 1, (o, a) => o.BaseNca = a[0]),
- new CliOption("basefile", 1, (o, a) => o.BaseFile = a[0]),
- new CliOption("rootdir", 1, (o, a) => o.RootDir = a[0]),
- new CliOption("updatedir", 1, (o, a) => o.UpdateDir = a[0]),
- new CliOption("normaldir", 1, (o, a) => o.NormalDir = a[0]),
- new CliOption("securedir", 1, (o, a) => o.SecureDir = a[0]),
- new CliOption("logodir", 1, (o, a) => o.LogoDir = a[0]),
- new CliOption("repack", 1, (o, a) => o.RepackSource = a[0]),
- new CliOption("listapps", 0, (o, _) => o.ListApps = true),
- new CliOption("listtitles", 0, (o, _) => o.ListTitles = true),
- new CliOption("listncas", 0, (o, _) => o.ListNcas = true),
- new CliOption("listromfs", 0, (o, _) => o.ListRomFs = true),
- new CliOption("listfiles", 0, (o, _) => o.ListFiles = true),
- new CliOption("sign", 0, (o, _) => o.SignSave = true),
- new CliOption("trim", 0, (o, _) => o.TrimSave = true),
- new CliOption("readbench", 0, (o, _) => o.ReadBench = true),
- new CliOption("hashedfs", 0, (o, _) => o.BuildHfs = true),
- new CliOption("extractini1", 0, (o, _) => o.ExtractIni1 = true),
- new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])),
- new CliOption("bench", 1, (o, a) => o.BenchType = a[0]),
- new CliOption("cpufreq", 1, (o, a) => o.CpuFrequencyGhz = ParseDouble(a[0])),
+ new CliOption("custom", 0, (o, _) => o.RunCustom = true),
+ new CliOption("intype", 't', 1, (o, a) => o.InFileType = ParseFileType(a[0])),
+ new CliOption("raw", 'r', 0, (o, _) => o.Raw = true),
+ new CliOption("verify", 'y', 0, (o, _) => o.Validate = true),
+ new CliOption("dev", 'd', 0, (o, _) => o.UseDevKeys = true),
+ new CliOption("enablehash", 'h', 0, (o, _) => o.EnableHash = true),
+ new CliOption("disablekeywarns", 0, (o, _) => o.DisableKeyWarns = true),
+ new CliOption("keyset", 'k', 1, (o, a) => o.Keyfile = a[0]),
+ new CliOption("titlekeys", 1, (o, a) => o.TitleKeyFile = a[0]),
+ new CliOption("consolekeys", 1, (o, a) => o.ConsoleKeyFile = a[0]),
+ new CliOption("accesslog", 1, (o, a) => o.AccessLog = a[0]),
+ new CliOption("resultlog", 1, (o, a) => o.ResultLog = a[0]),
+ new CliOption("section0", 1, (o, a) => o.SectionOut[0] = a[0]),
+ new CliOption("section1", 1, (o, a) => o.SectionOut[1] = a[0]),
+ new CliOption("section2", 1, (o, a) => o.SectionOut[2] = a[0]),
+ new CliOption("section3", 1, (o, a) => o.SectionOut[3] = a[0]),
+ new CliOption("section0dir", 1, (o, a) => o.SectionOutDir[0] = a[0]),
+ new CliOption("section1dir", 1, (o, a) => o.SectionOutDir[1] = a[0]),
+ new CliOption("section2dir", 1, (o, a) => o.SectionOutDir[2] = a[0]),
+ new CliOption("section3dir", 1, (o, a) => o.SectionOutDir[3] = a[0]),
+ new CliOption("header", 1, (o, a) => o.HeaderOut = a[0]),
+ new CliOption("exefs", 1, (o, a) => o.ExefsOut = a[0]),
+ new CliOption("exefsdir", 1, (o, a) => o.ExefsOutDir = a[0]),
+ new CliOption("romfs", 1, (o, a) => o.RomfsOut = a[0]),
+ new CliOption("romfsdir", 1, (o, a) => o.RomfsOutDir = a[0]),
+ new CliOption("debugoutdir", 1, (o, a) => o.DebugOutDir = a[0]),
+ new CliOption("savedir", 1, (o, a) => o.SaveOutDir = a[0]),
+ new CliOption("outdir", 1, (o, a) => o.OutDir = a[0]),
+ new CliOption("ini1dir", 1, (o, a) => o.Ini1OutDir = a[0]),
+ new CliOption("outfile", 1, (o, a) => o.OutFile = a[0]),
+ new CliOption("plaintext", 1, (o, a) => o.PlaintextOut = a[0]),
+ new CliOption("ciphertext", 1, (o, a) => o.CiphertextOut = a[0]),
+ new CliOption("uncompressed", 1, (o, a) => o.UncompressedOut = a[0]),
+ new CliOption("nspout", 1, (o, a) => o.NspOut = a[0]),
+ new CliOption("sdseed", 1, (o, a) => o.SdSeed = a[0]),
+ new CliOption("sdpath", 1, (o, a) => o.SdPath = a[0]),
+ new CliOption("basenca", 1, (o, a) => o.BaseNca = a[0]),
+ new CliOption("basefile", 1, (o, a) => o.BaseFile = a[0]),
+ new CliOption("rootdir", 1, (o, a) => o.RootDir = a[0]),
+ new CliOption("updatedir", 1, (o, a) => o.UpdateDir = a[0]),
+ new CliOption("normaldir", 1, (o, a) => o.NormalDir = a[0]),
+ new CliOption("securedir", 1, (o, a) => o.SecureDir = a[0]),
+ new CliOption("logodir", 1, (o, a) => o.LogoDir = a[0]),
+ new CliOption("repack", 1, (o, a) => o.RepackSource = a[0]),
+ new CliOption("listapps", 0, (o, _) => o.ListApps = true),
+ new CliOption("listtitles", 0, (o, _) => o.ListTitles = true),
+ new CliOption("listncas", 0, (o, _) => o.ListNcas = true),
+ new CliOption("listromfs", 0, (o, _) => o.ListRomFs = true),
+ new CliOption("listfiles", 0, (o, _) => o.ListFiles = true),
+ new CliOption("sign", 0, (o, _) => o.SignSave = true),
+ new CliOption("trim", 0, (o, _) => o.TrimSave = true),
+ new CliOption("readbench", 0, (o, _) => o.ReadBench = true),
+ new CliOption("hashedfs", 0, (o, _) => o.BuildHfs = true),
+ new CliOption("extractini1", 0, (o, _) => o.ExtractIni1 = true),
+ new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])),
+ new CliOption("bench", 1, (o, a) => o.BenchType = a[0]),
+ new CliOption("cpufreq", 1, (o, a) => o.CpuFrequencyGhz = ParseDouble(a[0])),
- new CliOption("replacefile", 2, (o, a) =>
- {
- o.ReplaceFileDest = a[0];
- o.ReplaceFileSource = a[1];
- })
- };
+ new CliOption("replacefile", 2, (o, a) =>
+ {
+ o.ReplaceFileDest = a[0];
+ o.ReplaceFileSource = a[1];
+ })
+ };
public static Options Parse(string[] args)
{
@@ -249,11 +249,16 @@ internal static class CliParser
sb.AppendLine(" --securedir Specify secure XCI directory path.");
sb.AppendLine(" --logodir Specify logo XCI directory path.");
sb.AppendLine(" --outdir Specify XCI directory path.");
+ sb.AppendLine(" --nspout Specify file for the created NSP.");
+ sb.AppendLine("Partition FS and XCI options:");
sb.AppendLine(" --exefs Specify main ExeFS file path.");
sb.AppendLine(" --exefsdir Specify main ExeFS directory path.");
sb.AppendLine(" --romfs Specify main RomFS file path.");
sb.AppendLine(" --romfsdir Specify main RomFS directory path.");
- sb.AppendLine(" --nspout Specify file for the created NSP.");
+ sb.AppendLine(" --listapps List application info.");
+ sb.AppendLine(" --listtitles List title info for all titles.");
+ sb.AppendLine(" --listncas List info for all NCAs.");
+ sb.AppendLine(" --title Specify title ID to use.");
sb.AppendLine("Package1 options:");
sb.AppendLine(" --outdir Specify Package1 directory path.");
sb.AppendLine("Package2 options:");
@@ -319,4 +324,4 @@ internal static class CliParser
public int ArgsNeeded { get; }
public Action Assigner { get; }
}
-}
+}
\ No newline at end of file
diff --git a/src/hactoolnet/ProcessAppFs.cs b/src/hactoolnet/ProcessAppFs.cs
new file mode 100644
index 00000000..43853bd9
--- /dev/null
+++ b/src/hactoolnet/ProcessAppFs.cs
@@ -0,0 +1,145 @@
+using System.Linq;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using LibHac.Ncm;
+using LibHac.Spl;
+using LibHac.Tools.Es;
+using LibHac.Tools.Fs;
+using LibHac.Tools.FsSystem;
+using LibHac.Tools.FsSystem.NcaUtils;
+
+namespace hactoolnet;
+
+internal static class ProcessAppFs
+{
+ private static void ImportTickets(Context ctx, IFileSystem fileSystem)
+ {
+ foreach (DirectoryEntryEx entry in fileSystem.EnumerateEntries("*.tik", SearchOptions.Default))
+ {
+ using var tikFile = new UniqueRef();
+ fileSystem.OpenFile(ref tikFile.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+ var ticket = new Ticket(tikFile.Get.AsStream());
+
+ if (ticket.TitleKeyType != TitleKeyType.Common)
+ continue;
+
+ if (ticket.RightsId.IsZeros())
+ continue;
+
+ var rightsId = SpanHelpers.AsStruct(ticket.RightsId);
+ var accessKey = SpanHelpers.AsStruct(ticket.TitleKeyBlock);
+
+ ctx.KeySet.ExternalKeySet.Add(rightsId, accessKey).ThrowIfFailure();
+ }
+ }
+
+ public static void Process(Context ctx, IFileSystem fileSystem)
+ {
+ ImportTickets(ctx, fileSystem);
+
+ SwitchFs switchFs = SwitchFs.OpenNcaDirectory(ctx.KeySet, fileSystem);
+
+ if (ctx.Options.ListNcas)
+ {
+ ctx.Logger.LogMessage(ProcessSwitchFs.ListNcas(switchFs));
+ }
+
+ if (ctx.Options.ListTitles)
+ {
+ ctx.Logger.LogMessage(ProcessSwitchFs.ListTitles(switchFs));
+ }
+
+ if (ctx.Options.ListApps)
+ {
+ ctx.Logger.LogMessage(ProcessSwitchFs.ListApplications(switchFs));
+ }
+
+ ulong id = GetTargetProgramId(ctx, switchFs);
+ if (id == ulong.MaxValue)
+ {
+ ctx.Logger.LogMessage("Title ID must be specified to dump ExeFS or RomFS");
+ return;
+ }
+
+ if (!switchFs.Titles.TryGetValue(id, out Title title))
+ {
+ ctx.Logger.LogMessage($"Could not find title {id:X16}");
+ return;
+ }
+
+ if (title.MainNca == null)
+ {
+ ctx.Logger.LogMessage($"Could not find main data for title {id:X16}");
+ return;
+ }
+
+ if (title.Metadata?.ContentMetaAttributes.HasFlag(ContentMetaAttribute.Compacted) == true)
+ {
+ ctx.Logger.LogMessage($"Cannot extract compacted NCAs");
+ return;
+ }
+
+ if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
+ {
+ if (!title.MainNca.Nca.SectionExists(NcaSectionType.Code))
+ {
+ ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section");
+ return;
+ }
+
+ if (ctx.Options.ExefsOutDir != null)
+ {
+ IFileSystem fs = title.MainNca.OpenFileSystem(NcaSectionType.Code, ctx.Options.IntegrityLevel);
+ fs.Extract(ctx.Options.ExefsOutDir, ctx.Logger);
+ }
+
+ if (ctx.Options.ExefsOut != null)
+ {
+ title.MainNca.Nca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
+ }
+ }
+
+ if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ListRomFs)
+ {
+ if (!title.MainNca.Nca.SectionExists(NcaSectionType.Data))
+ {
+ ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section");
+ return;
+ }
+
+ ProcessRomfs.Process(ctx, title.MainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel));
+ }
+
+ if (ctx.Options.NspOut != null)
+ {
+ ProcessPfs.CreateNsp(ctx, switchFs);
+ }
+ }
+
+ private static ulong GetTargetProgramId(Context ctx, SwitchFs switchFs)
+ {
+ ulong id = ctx.Options.TitleId;
+ if (id != 0)
+ {
+ return id;
+ }
+
+ if (switchFs.Applications.Count != 1)
+ {
+ return ulong.MaxValue;
+ }
+
+ ulong applicationId = switchFs.Applications.Values.First().TitleId;
+ ulong updateId = applicationId | 0x800ul;
+
+ if (switchFs.Titles.ContainsKey(updateId))
+ {
+ return updateId;
+ }
+
+ return applicationId;
+ }
+}
\ No newline at end of file
diff --git a/src/hactoolnet/ProcessPfs.cs b/src/hactoolnet/ProcessPfs.cs
index ec6bef80..42fdf987 100644
--- a/src/hactoolnet/ProcessPfs.cs
+++ b/src/hactoolnet/ProcessPfs.cs
@@ -1,4 +1,5 @@
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text;
using LibHac.Fs;
@@ -24,6 +25,11 @@ internal static class ProcessPfs
{
pfs.Extract(ctx.Options.OutDir, ctx.Logger);
}
+
+ if (pfs.EnumerateEntries("*.nca", SearchOptions.Default).Any())
+ {
+ ProcessAppFs.Process(ctx, pfs);
+ }
}
}
diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs
index 2998c695..c554eef0 100644
--- a/src/hactoolnet/ProcessSwitchFs.cs
+++ b/src/hactoolnet/ProcessSwitchFs.cs
@@ -234,7 +234,7 @@ internal static class ProcessSwitchFs
}
}
- static string ListTitles(SwitchFs sdfs)
+ public static string ListTitles(SwitchFs sdfs)
{
var table = new TableBuilder("Title ID", "Version", "", "Type", "Size", "Display Version", "Name");
@@ -252,7 +252,7 @@ internal static class ProcessSwitchFs
return table.Print();
}
- static string ListNcas(SwitchFs sdfs)
+ public static string ListNcas(SwitchFs sdfs)
{
var table = new TableBuilder("NCA ID", "Type", "Title ID");
@@ -264,7 +264,7 @@ internal static class ProcessSwitchFs
return table.Print();
}
- static string ListApplications(SwitchFs sdfs)
+ public static string ListApplications(SwitchFs sdfs)
{
var sb = new StringBuilder();
diff --git a/src/hactoolnet/ProcessXci.cs b/src/hactoolnet/ProcessXci.cs
index 70688f1e..eb13e85f 100644
--- a/src/hactoolnet/ProcessXci.cs
+++ b/src/hactoolnet/ProcessXci.cs
@@ -8,7 +8,6 @@ using LibHac.FsSystem;
using LibHac.Gc.Impl;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
-using LibHac.Tools.FsSystem.NcaUtils;
namespace hactoolnet;
@@ -65,80 +64,13 @@ internal static class ProcessXci
}
}
- if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
+ if (xci.HasPartition(XciPartitionType.Secure))
{
- Nca mainNca = GetXciMainNca(xci, ctx);
-
- if (mainNca == null)
- {
- ctx.Logger.LogMessage("Could not find Program NCA");
- return;
- }
-
- if (!mainNca.SectionExists(NcaSectionType.Code))
- {
- ctx.Logger.LogMessage("NCA has no ExeFS section");
- return;
- }
-
- if (ctx.Options.ExefsOutDir != null)
- {
- mainNca.ExtractSection(NcaSectionType.Code, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
- }
-
- if (ctx.Options.ExefsOut != null)
- {
- mainNca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
- }
- }
-
- if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ListRomFs)
- {
- Nca mainNca = GetXciMainNca(xci, ctx);
-
- if (mainNca == null)
- {
- ctx.Logger.LogMessage("Could not find Program NCA");
- return;
- }
-
- if (!mainNca.SectionExists(NcaSectionType.Data))
- {
- ctx.Logger.LogMessage("NCA has no RomFS section");
- return;
- }
-
- ProcessRomfs.Process(ctx, mainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel, false));
+ ProcessAppFs.Process(ctx, xci.OpenPartition(XciPartitionType.Secure));
}
}
}
- private static Nca GetXciMainNca(Xci xci, Context ctx)
- {
- XciPartition partition = xci.OpenPartition(XciPartitionType.Secure);
-
- if (partition == null)
- {
- ctx.Logger.LogMessage("Could not find secure partition");
- return null;
- }
-
- Nca mainNca = null;
-
- foreach (PartitionFileEntry fileEntry in partition.Files.Where(x => x.Name.EndsWith(".nca")))
- {
- IStorage ncaStorage = partition.OpenFile(fileEntry, OpenMode.Read).AsStorage();
- var nca = new Nca(ctx.KeySet, ncaStorage);
-
- if (nca.Header.ContentType == NcaContentType.Program)
- {
- mainNca = nca;
- }
- }
-
- return mainNca;
- }
-
private static string Print(this Xci xci)
{
const int colLen = 52;