hactoolnet: Use new NCA APIs

This commit is contained in:
Alex Barney 2019-03-15 21:12:43 -05:00
parent c267826dd1
commit c73493b505
7 changed files with 47 additions and 79 deletions

View file

@ -10,7 +10,7 @@ namespace LibHac.IO.NcaUtils
{ {
private const int HeaderSize = 0xc00; private const int HeaderSize = 0xc00;
private const int HeaderSectorSize = 0x200; private const int HeaderSectorSize = 0x200;
public NcaHeader Header { get; } public NcaHeader Header { get; }
public string NcaId { get; set; } public string NcaId { get; set; }
public string Filename { get; set; } public string Filename { get; set; }
@ -91,6 +91,11 @@ namespace LibHac.IO.NcaUtils
return sect.Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName); return sect.Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName);
} }
public bool CanOpenSection(NcaSectionType type)
{
return CanOpenSection(GetSectionIndexFromType(type));
}
private IStorage OpenEncryptedStorage(int index) private IStorage OpenEncryptedStorage(int index)
{ {
if (index < 0 || index > 3) throw new ArgumentOutOfRangeException(nameof(index)); if (index < 0 || index > 3) throw new ArgumentOutOfRangeException(nameof(index));
@ -275,11 +280,9 @@ namespace LibHac.IO.NcaUtils
return Sections[index] != null; return Sections[index] != null;
} }
public bool SectionIsDecryptable(int index) public bool SectionExists(NcaSectionType type)
{ {
if (!SectionExists(index)) return false; return SectionExists(GetSectionIndexFromType(type));
return Sections[index].Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName);
} }
/// <summary> /// <summary>
@ -363,7 +366,7 @@ namespace LibHac.IO.NcaUtils
{ {
if (Header != null) if (Header != null)
{ {
return Header.Version; return Header.Version;
} }
else else
{ {

View file

@ -25,16 +25,27 @@ namespace LibHac.IO.NcaUtils
.WriteAllBytes(filename, logger); .WriteAllBytes(filename, logger);
} }
public static void ExportSection(this Nca nca, NcaSectionType type, string filename, bool raw = false,
IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
{
nca.OpenStorage(type, integrityCheckLevel, raw)
.WriteAllBytes(filename, logger);
}
public static void ExtractSection(this Nca nca, int index, string outputDir, public static void ExtractSection(this Nca nca, int index, string outputDir,
IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null) IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
{ {
if (index < 0 || index > 3) throw new IndexOutOfRangeException();
if (!nca.SectionIsDecryptable(index)) return;
IFileSystem fs = nca.OpenFileSystem(index, integrityCheckLevel); IFileSystem fs = nca.OpenFileSystem(index, integrityCheckLevel);
fs.Extract(outputDir, logger); fs.Extract(outputDir, logger);
} }
public static void ExtractSection(this Nca nca, NcaSectionType type, string outputDir,
IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
{
IFileSystem fs = nca.OpenFileSystem(type, integrityCheckLevel);
fs.Extract(outputDir, logger);
}
public static Validity VerifyNca(this Nca nca, IProgressReport logger = null, bool quiet = false) public static Validity VerifyNca(this Nca nca, IProgressReport logger = null, bool quiet = false)
{ {
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)

View file

@ -193,13 +193,6 @@ namespace LibHac.IO.NcaUtils
} }
} }
public enum ProgramPartitionType
{
Code,
Data,
Logo
};
public enum NcaSectionType public enum NcaSectionType
{ {
Code, Code,

View file

@ -1,5 +1,4 @@
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using LibHac; using LibHac;
using LibHac.IO; using LibHac.IO;
@ -46,13 +45,13 @@ namespace hactoolnet
nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.IntegrityLevel, ctx.Logger); nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.IntegrityLevel, ctx.Logger);
} }
if (ctx.Options.Validate && nca.Sections[i] != null) if (ctx.Options.Validate && nca.SectionExists(i))
{ {
nca.VerifySection(i, ctx.Logger); nca.VerifySection(i, ctx.Logger);
} }
} }
if (ctx.Options.ListRomFs && nca.Sections[1] != null) if (ctx.Options.ListRomFs && nca.CanOpenSection(NcaSectionType.Data))
{ {
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, ctx.Options.IntegrityLevel); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, ctx.Options.IntegrityLevel);
@ -64,35 +63,26 @@ namespace hactoolnet
if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ReadBench) if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ReadBench)
{ {
NcaSection section = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (!nca.SectionExists(NcaSectionType.Data))
if (section == null)
{ {
ctx.Logger.LogMessage("NCA has no RomFS section"); ctx.Logger.LogMessage("NCA has no RomFS section");
return; return;
} }
if (section.Type == SectionType.Bktr && ctx.Options.BaseNca == null)
{
ctx.Logger.LogMessage("Cannot save BKTR section without base RomFS");
return;
}
if (ctx.Options.RomfsOut != null) if (ctx.Options.RomfsOut != null)
{ {
nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); nca.ExportSection(NcaSectionType.Data, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
} }
if (ctx.Options.RomfsOutDir != null) if (ctx.Options.RomfsOutDir != null)
{ {
IFileSystem romfs = nca.OpenFileSystem(section.SectionNum, ctx.Options.IntegrityLevel); nca.ExtractSection(NcaSectionType.Data, ctx.Options.RomfsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
} }
if (ctx.Options.ReadBench) if (ctx.Options.ReadBench)
{ {
long bytesToRead = 1024L * 1024 * 1024 * 5; long bytesToRead = 1024L * 1024 * 1024 * 5;
IStorage storage = nca.OpenStorage(section.SectionNum, ctx.Options.IntegrityLevel); IStorage storage = nca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel);
var dest = new NullStorage(storage.Length); var dest = new NullStorage(storage.Length);
int iterations = (int)(bytesToRead / storage.Length) + 1; int iterations = (int)(bytesToRead / storage.Length) + 1;
@ -119,9 +109,7 @@ namespace hactoolnet
return; return;
} }
NcaSection section = nca.Sections[(int)ProgramPartitionType.Code]; if (!nca.SectionExists(NcaSectionType.Code))
if (section == null)
{ {
ctx.Logger.LogMessage("Could not find an ExeFS section"); ctx.Logger.LogMessage("Could not find an ExeFS section");
return; return;
@ -129,13 +117,12 @@ namespace hactoolnet
if (ctx.Options.ExefsOut != null) if (ctx.Options.ExefsOut != null)
{ {
nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); nca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
} }
if (ctx.Options.ExefsOutDir != null) if (ctx.Options.ExefsOutDir != null)
{ {
IFileSystem pfs = nca.OpenFileSystem(section.SectionNum, ctx.Options.IntegrityLevel); nca.ExtractSection(NcaSectionType.Code, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
pfs.Extract(ctx.Options.ExefsOutDir, ctx.Logger);
} }
} }
@ -199,7 +186,7 @@ namespace hactoolnet
NcaSection sect = nca.Sections[i]; NcaSection sect = nca.Sections[i];
if (sect == null) continue; if (sect == null) continue;
bool isExefs = nca.Header.ContentType == ContentType.Program && i == (int)ProgramPartitionType.Code; bool isExefs = nca.Header.ContentType == ContentType.Program && i == 0;
sb.AppendLine($" Section {i}:"); sb.AppendLine($" Section {i}:");
PrintItem(sb, colLen, " Offset:", $"0x{sect.Offset:x12}"); PrintItem(sb, colLen, " Offset:", $"0x{sect.Offset:x12}");

View file

@ -10,13 +10,14 @@ namespace hactoolnet
{ {
using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.Read))
{ {
var romfs = new RomFsFileSystem(file); Process(ctx, file);
Process(ctx, romfs);
} }
} }
public static void Process(Context ctx, RomFsFileSystem romfs) public static void Process(Context ctx, IStorage romfsStorage)
{ {
var romfs = new RomFsFileSystem(romfsStorage);
if (ctx.Options.ListRomFs) if (ctx.Options.ListRomFs)
{ {
foreach (DirectoryEntry entry in romfs.EnumerateEntries()) foreach (DirectoryEntry entry in romfs.EnumerateEntries())
@ -29,7 +30,6 @@ namespace hactoolnet
{ {
using (var outFile = new FileStream(ctx.Options.RomfsOut, FileMode.Create, FileAccess.ReadWrite)) using (var outFile = new FileStream(ctx.Options.RomfsOut, FileMode.Create, FileAccess.ReadWrite))
{ {
IStorage romfsStorage = romfs.GetBaseStorage();
romfsStorage.CopyToStream(outFile, romfsStorage.Length, ctx.Logger); romfsStorage.CopyToStream(outFile, romfsStorage.Length, ctx.Logger);
} }
} }

View file

@ -69,9 +69,7 @@ namespace hactoolnet
return; return;
} }
NcaSection section = title.MainNca.Sections[(int)ProgramPartitionType.Code]; if (!title.MainNca.SectionExists(NcaSectionType.Code))
if (section == null)
{ {
ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section"); ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no ExeFS section");
return; return;
@ -79,12 +77,12 @@ namespace hactoolnet
if (ctx.Options.ExefsOutDir != null) if (ctx.Options.ExefsOutDir != null)
{ {
title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); title.MainNca.ExtractSection(NcaSectionType.Code, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
} }
if (ctx.Options.ExefsOut != null) if (ctx.Options.ExefsOut != null)
{ {
title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); title.MainNca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
} }
} }
@ -109,24 +107,13 @@ namespace hactoolnet
return; return;
} }
NcaSection section = title.MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); if (!title.MainNca.SectionExists(NcaSectionType.Data))
if (section == null)
{ {
ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section"); ctx.Logger.LogMessage($"Main NCA for title {id:X16} has no RomFS section");
return; return;
} }
if (ctx.Options.RomfsOutDir != null) ProcessRomfs.Process(ctx, title.MainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel, false));
{
IFileSystem romfs = title.MainNca.OpenFileSystem(section.SectionNum, ctx.Options.IntegrityLevel);
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
}
if (ctx.Options.RomfsOut != null)
{
title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
} }
if (ctx.Options.OutDir != null) if (ctx.Options.OutDir != null)

View file

@ -72,9 +72,7 @@ namespace hactoolnet
return; return;
} }
NcaSection exefsSection = mainNca.Sections[(int)ProgramPartitionType.Code]; if (!mainNca.SectionExists(NcaSectionType.Code))
if (exefsSection == null)
{ {
ctx.Logger.LogMessage("NCA has no ExeFS section"); ctx.Logger.LogMessage("NCA has no ExeFS section");
return; return;
@ -82,12 +80,12 @@ namespace hactoolnet
if (ctx.Options.ExefsOutDir != null) if (ctx.Options.ExefsOutDir != null)
{ {
mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger); mainNca.ExtractSection(NcaSectionType.Code, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
} }
if (ctx.Options.ExefsOut != null) if (ctx.Options.ExefsOut != null)
{ {
mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger); mainNca.ExportSection(NcaSectionType.Code, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
} }
} }
@ -101,24 +99,13 @@ namespace hactoolnet
return; return;
} }
NcaSection romfsSection = mainNca.Sections.FirstOrDefault(x => x.Type == SectionType.Romfs); if (!mainNca.SectionExists(NcaSectionType.Data))
if (romfsSection == null)
{ {
ctx.Logger.LogMessage("NCA has no RomFS section"); ctx.Logger.LogMessage("NCA has no RomFS section");
return; return;
} }
if (ctx.Options.RomfsOutDir != null) ProcessRomfs.Process(ctx, mainNca.OpenStorage(NcaSectionType.Data, ctx.Options.IntegrityLevel, false));
{
IFileSystem romfs = mainNca.OpenFileSystem(romfsSection.SectionNum, ctx.Options.IntegrityLevel);
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
}
if (ctx.Options.RomfsOut != null)
{
mainNca.ExportSection(romfsSection.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
} }
} }
} }