LibHac/src/hactoolnet/ProcessXci.cs

209 lines
7.8 KiB
C#
Raw Normal View History

2019-01-29 22:58:48 +01:00
using System;
using System.IO;
using System.Linq;
using System.Text;
using LibHac;
2019-05-14 23:39:20 +02:00
using LibHac.Fs;
using LibHac.Fs.NcaUtils;
using static hactoolnet.Print;
namespace hactoolnet
{
internal static class ProcessXci
{
public static void Process(Context ctx)
{
2019-01-22 22:09:46 +01:00
using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
{
2019-01-22 22:09:46 +01:00
var xci = new Xci(ctx.Keyset, file);
ctx.Logger.LogMessage(xci.Print());
if (ctx.Options.RootDir != null)
{
2019-01-29 22:58:48 +01:00
xci.OpenPartition(XciPartitionType.Root).Extract(ctx.Options.RootDir, ctx.Logger);
}
2019-01-29 22:58:48 +01:00
if (ctx.Options.UpdateDir != null && xci.HasPartition(XciPartitionType.Update))
{
2019-01-29 22:58:48 +01:00
xci.OpenPartition(XciPartitionType.Update).Extract(ctx.Options.UpdateDir, ctx.Logger);
}
2019-01-29 22:58:48 +01:00
if (ctx.Options.NormalDir != null && xci.HasPartition(XciPartitionType.Normal))
{
2019-01-29 22:58:48 +01:00
xci.OpenPartition(XciPartitionType.Normal).Extract(ctx.Options.NormalDir, ctx.Logger);
}
2019-01-29 22:58:48 +01:00
if (ctx.Options.SecureDir != null && xci.HasPartition(XciPartitionType.Secure))
{
2019-01-29 22:58:48 +01:00
xci.OpenPartition(XciPartitionType.Secure).Extract(ctx.Options.SecureDir, ctx.Logger);
}
2019-01-29 22:58:48 +01:00
if (ctx.Options.LogoDir != null && xci.HasPartition(XciPartitionType.Logo))
{
2019-01-29 22:58:48 +01:00
xci.OpenPartition(XciPartitionType.Logo).Extract(ctx.Options.LogoDir, ctx.Logger);
}
2019-01-29 22:58:48 +01:00
if (ctx.Options.OutDir != null)
{
2019-01-29 22:58:48 +01:00
XciPartition root = xci.OpenPartition(XciPartitionType.Root);
if (root == null)
{
ctx.Logger.LogMessage("Could not find root partition");
return;
}
2019-01-03 02:16:19 +01:00
foreach (PartitionFileEntry sub in root.Files)
{
var subPfs = new PartitionFileSystem(root.OpenFile(sub, OpenMode.Read).AsStorage());
2018-10-03 00:25:58 +02:00
string subDir = Path.Combine(ctx.Options.OutDir, sub.Name);
subPfs.Extract(subDir, ctx.Logger);
}
}
if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null)
{
2018-10-03 00:25:58 +02:00
Nca mainNca = GetXciMainNca(xci, ctx);
if (mainNca == null)
{
ctx.Logger.LogMessage("Could not find Program NCA");
return;
}
2019-03-16 03:12:43 +01:00
if (!mainNca.SectionExists(NcaSectionType.Code))
{
ctx.Logger.LogMessage("NCA has no ExeFS section");
return;
}
if (ctx.Options.ExefsOutDir != null)
{
2019-03-16 03:12:43 +01:00
mainNca.ExtractSection(NcaSectionType.Code, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.ExefsOut != null)
{
2019-03-16 03:12:43 +01:00
mainNca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
}
if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null)
{
2018-10-03 00:25:58 +02:00
Nca mainNca = GetXciMainNca(xci, ctx);
if (mainNca == null)
{
ctx.Logger.LogMessage("Could not find Program NCA");
return;
}
2019-03-16 03:12:43 +01:00
if (!mainNca.SectionExists(NcaSectionType.Data))
{
ctx.Logger.LogMessage("NCA has no RomFS section");
return;
}
2019-03-16 03:12:43 +01:00
ProcessRomfs.Process(ctx, mainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel, false));
}
}
}
private static Nca GetXciMainNca(Xci xci, Context ctx)
{
2019-01-29 22:58:48 +01:00
XciPartition partition = xci.OpenPartition(XciPartitionType.Secure);
if (partition == null)
{
ctx.Logger.LogMessage("Could not find secure partition");
return null;
}
Nca mainNca = null;
2019-01-29 22:58:48 +01:00
foreach (PartitionFileEntry fileEntry in partition.Files.Where(x => x.Name.EndsWith(".nca")))
{
2019-01-29 22:58:48 +01:00
IStorage ncaStorage = partition.OpenFile(fileEntry, OpenMode.Read).AsStorage();
var nca = new Nca(ctx.Keyset, ncaStorage);
2019-08-20 04:24:30 +02:00
if (nca.Header.ContentType == NcaContentType.Program)
{
mainNca = nca;
}
}
return mainNca;
}
private static string Print(this Xci xci)
{
const int colLen = 36;
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("XCI:");
PrintItem(sb, colLen, "Magic:", xci.Header.Magic);
PrintItem(sb, colLen, $"Header Signature{xci.Header.SignatureValidity.GetValidityString()}:", xci.Header.Signature);
2019-01-29 22:58:48 +01:00
PrintItem(sb, colLen, $"Header Hash{xci.Header.PartitionFsHeaderValidity.GetValidityString()}:", xci.Header.RootPartitionHeaderHash);
PrintItem(sb, colLen, "Cartridge Type:", GetCartridgeType(xci.Header.RomSize));
PrintItem(sb, colLen, "Cartridge Size:", $"0x{Util.MediaToReal(xci.Header.ValidDataEndPage + 1):x12}");
PrintItem(sb, colLen, "Header IV:", xci.Header.AesCbcIv);
2019-01-29 22:58:48 +01:00
PrintPartition(sb, colLen, xci.OpenPartition(XciPartitionType.Root), XciPartitionType.Root);
foreach (XciPartitionType type in Enum.GetValues(typeof(XciPartitionType)))
{
2019-01-29 22:58:48 +01:00
if (type == XciPartitionType.Root || !xci.HasPartition(type)) continue;
XciPartition partition = xci.OpenPartition(type);
PrintPartition(sb, colLen, partition, type);
}
return sb.ToString();
}
2019-01-29 22:58:48 +01:00
private static void PrintPartition(StringBuilder sb, int colLen, XciPartition partition, XciPartitionType type)
{
const int fileNameLen = 57;
2019-01-29 22:58:48 +01:00
sb.AppendLine($"{type.ToString()} Partition:{partition.HashValidity.GetValidityString()}");
PrintItem(sb, colLen, " Magic:", partition.Header.Magic);
PrintItem(sb, colLen, " Offset:", $"{partition.Offset:x12}");
PrintItem(sb, colLen, " Number of files:", partition.Files.Length);
2019-01-29 22:58:48 +01:00
string name = type.GetFileName();
if (partition.Files.Length > 0 && partition.Files.Length < 100)
{
for (int i = 0; i < partition.Files.Length; i++)
{
2019-01-03 02:16:19 +01:00
PartitionFileEntry file = partition.Files[i];
string label = i == 0 ? " Files:" : "";
string offsets = $"{file.Offset:x12}-{file.Offset + file.Size:x12}{file.HashValidity.GetValidityString()}";
2019-01-29 22:58:48 +01:00
string data = $"{name}:/{file.Name}".PadRight(fileNameLen) + offsets;
PrintItem(sb, colLen, label, data);
}
}
}
private static string GetCartridgeType(RomSize size)
{
switch (size)
{
case RomSize.Size1Gb: return "1GB";
case RomSize.Size2Gb: return "2GB";
case RomSize.Size4Gb: return "4GB";
case RomSize.Size8Gb: return "8GB";
case RomSize.Size16Gb: return "16GB";
case RomSize.Size32Gb: return "32GB";
default: return string.Empty;
}
}
}
}