diff --git a/LibHac/HierarchicalIntegrityVerificationStream.cs b/LibHac/HierarchicalIntegrityVerificationStream.cs
index bdb09c2b..5a5adad8 100644
--- a/LibHac/HierarchicalIntegrityVerificationStream.cs
+++ b/LibHac/HierarchicalIntegrityVerificationStream.cs
@@ -8,18 +8,18 @@ namespace LibHac
{
public Stream[] Levels { get; }
public Stream DataLevel { get; }
- public bool EnableIntegrityChecks { get; }
+ public IntegrityCheckLevel IntegrityCheckLevel { get; }
- public HierarchicalIntegrityVerificationStream(IntegrityVerificationInfo[] levelInfo, bool enableIntegrityChecks)
+ public HierarchicalIntegrityVerificationStream(IntegrityVerificationInfo[] levelInfo, IntegrityCheckLevel integrityCheckLevel)
{
Levels = new Stream[levelInfo.Length];
- EnableIntegrityChecks = enableIntegrityChecks;
+ IntegrityCheckLevel = integrityCheckLevel;
Levels[0] = levelInfo[0].Data;
for (int i = 1; i < Levels.Length; i++)
{
- var levelData = new IntegrityVerificationStream(levelInfo[i], Levels[i - 1], enableIntegrityChecks);
+ var levelData = new IntegrityVerificationStream(levelInfo[i], Levels[i - 1], integrityCheckLevel);
Levels[i] = new RandomAccessSectorStream(levelData);
}
diff --git a/LibHac/IntegrityVerificationStream.cs b/LibHac/IntegrityVerificationStream.cs
index 2c835aab..9057cad5 100644
--- a/LibHac/IntegrityVerificationStream.cs
+++ b/LibHac/IntegrityVerificationStream.cs
@@ -10,7 +10,7 @@ namespace LibHac
private const int DigestSize = 0x20;
private Stream HashStream { get; }
- public bool EnableIntegrityChecks { get; }
+ public IntegrityCheckLevel IntegrityCheckLevel { get; }
private byte[] Salt { get; }
private IntegrityStreamType Type { get; }
@@ -18,11 +18,11 @@ namespace LibHac
private readonly byte[] _hashBuffer = new byte[DigestSize];
private readonly SHA256 _hash = SHA256.Create();
- public IntegrityVerificationStream(IntegrityVerificationInfo info, Stream hashStream, bool enableIntegrityChecks)
+ public IntegrityVerificationStream(IntegrityVerificationInfo info, Stream hashStream, IntegrityCheckLevel integrityCheckLevel)
: base(info.Data, info.BlockSize)
{
HashStream = hashStream;
- EnableIntegrityChecks = enableIntegrityChecks;
+ IntegrityCheckLevel = integrityCheckLevel;
Salt = info.Salt;
Type = info.Type;
}
@@ -84,7 +84,7 @@ namespace LibHac
}
}
- if (!EnableIntegrityChecks) return bytesRead;
+ if (IntegrityCheckLevel == IntegrityCheckLevel.None) return bytesRead;
_hash.Initialize();
@@ -139,4 +139,23 @@ namespace LibHac
RomFs,
PartitionFs
}
+
+ ///
+ /// Represents the level of integrity checks to be performed.
+ ///
+ public enum IntegrityCheckLevel
+ {
+ ///
+ /// No integrity checks will be performed.
+ ///
+ None,
+ ///
+ ///
+ ///
+ WarnOnInvalid,
+ ///
+ /// An will be thrown if an integrity check fails.
+ ///
+ ErrorOnInvalid
+ }
}
diff --git a/LibHac/Nca.cs b/LibHac/Nca.cs
index ff2c00de..eab91d70 100644
--- a/LibHac/Nca.cs
+++ b/LibHac/Nca.cs
@@ -140,7 +140,7 @@ namespace LibHac
false);
if (BaseNca == null) return rawStream;
- Stream baseStream = BaseNca.OpenSection(ProgramPartitionType.Data, true, false);
+ Stream baseStream = BaseNca.OpenSection(ProgramPartitionType.Data, true, IntegrityCheckLevel.None);
if (baseStream == null) throw new InvalidDataException("Base NCA has no RomFS section");
return new Bktr(rawStream, baseStream, sect);
@@ -155,11 +155,11 @@ namespace LibHac
///
/// The index of the NCA section to open. Valid indexes are 0-3.
/// to open the raw section with hash metadata.
- /// to enable data integrity checks when reading the section.
- /// Only applies if is .
+ /// The level of integrity checks to be performed when reading the section.
+ /// Always if is .
/// A that provides access to the specified section. if the section does not exist.
/// The specified is outside the valid range.
- public Stream OpenSection(int index, bool raw, bool enableIntegrityChecks)
+ public Stream OpenSection(int index, bool raw, IntegrityCheckLevel integrityCheckLevel)
{
Stream rawStream = OpenRawSection(index);
NcaSection sect = Sections[index];
@@ -173,9 +173,9 @@ namespace LibHac
switch (header.HashType)
{
case NcaHashType.Sha256:
- return InitIvfcForPartitionfs(header.Sha256Info, new SharedStreamSource(rawStream), enableIntegrityChecks);
+ return InitIvfcForPartitionfs(header.Sha256Info, new SharedStreamSource(rawStream), integrityCheckLevel);
case NcaHashType.Ivfc:
- return InitIvfcForRomfs(header.IvfcInfo, new SharedStreamSource(rawStream), enableIntegrityChecks);
+ return InitIvfcForRomfs(header.IvfcInfo, new SharedStreamSource(rawStream), integrityCheckLevel);
default:
throw new ArgumentOutOfRangeException();
@@ -187,15 +187,15 @@ namespace LibHac
///
/// The type of section to open.
/// to open the raw section with hash metadata.
- /// to enable data integrity checks when reading the section.
- /// Only applies if is .
+ /// The level of integrity checks to be performed when reading the section.
+ /// Always if is .
/// A that provides access to the specified section. if the section does not exist.
/// The specified is outside the valid range.
- public Stream OpenSection(ProgramPartitionType type, bool raw, bool enableIntegrityChecks) =>
- OpenSection((int)type, raw, enableIntegrityChecks);
+ public Stream OpenSection(ProgramPartitionType type, bool raw, IntegrityCheckLevel integrityCheckLevel) =>
+ OpenSection((int)type, raw, integrityCheckLevel);
private static HierarchicalIntegrityVerificationStream InitIvfcForRomfs(IvfcHeader ivfc,
- SharedStreamSource romfsStreamSource, bool enableIntegrityChecks)
+ SharedStreamSource romfsStreamSource, IntegrityCheckLevel integrityCheckLevel)
{
var initInfo = new IntegrityVerificationInfo[ivfc.NumLevels];
@@ -219,11 +219,11 @@ namespace LibHac
};
}
- return new HierarchicalIntegrityVerificationStream(initInfo, enableIntegrityChecks);
+ return new HierarchicalIntegrityVerificationStream(initInfo, integrityCheckLevel);
}
private static Stream InitIvfcForPartitionfs(Sha256Info sb,
- SharedStreamSource pfsStreamSource, bool enableIntegrityChecks)
+ SharedStreamSource pfsStreamSource, IntegrityCheckLevel integrityCheckLevel)
{
SharedStream hashStream = pfsStreamSource.CreateStream(sb.HashTableOffset, sb.HashTableSize);
SharedStream dataStream = pfsStreamSource.CreateStream(sb.DataOffset, sb.DataSize);
@@ -252,7 +252,7 @@ namespace LibHac
Type = IntegrityStreamType.PartitionFs
};
- return new HierarchicalIntegrityVerificationStream(initInfo, enableIntegrityChecks);
+ return new HierarchicalIntegrityVerificationStream(initInfo, integrityCheckLevel);
}
///
@@ -376,7 +376,7 @@ namespace LibHac
return;
}
- Stream stream = OpenSection(index, true, false);
+ Stream stream = OpenSection(index, true, IntegrityCheckLevel.None);
var hashTable = new byte[size];
stream.Position = offset;
@@ -385,25 +385,6 @@ namespace LibHac
sect.MasterHashValidity = Crypto.CheckMemoryHashTable(hashTable, expected, 0, hashTable.Length);
}
- public void VerifySection(int index, IProgressReport logger = null)
- {
- if (Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index));
-
- NcaSection sect = Sections[index];
- Stream stream = OpenSection(index, false, true);
- logger?.LogMessage($"Verifying section {index}...");
-
- switch (sect.Header.HashType)
- {
- case NcaHashType.Sha256:
- break;
- case NcaHashType.Ivfc:
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
-
public void Dispose()
{
if (!KeepOpen)
@@ -444,12 +425,12 @@ namespace LibHac
public static class NcaExtensions
{
- public static void ExportSection(this Nca nca, int index, string filename, bool raw = false, bool verify = false, IProgressReport logger = null)
+ public static void ExportSection(this Nca nca, int index, string filename, bool raw = false, IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
{
if (index < 0 || index > 3) throw new IndexOutOfRangeException();
if (nca.Sections[index] == null) return;
- Stream section = nca.OpenSection(index, raw, verify);
+ Stream section = nca.OpenSection(index, raw, integrityCheckLevel);
string dir = Path.GetDirectoryName(filename);
if (!string.IsNullOrWhiteSpace(dir)) Directory.CreateDirectory(dir);
@@ -459,13 +440,13 @@ namespace LibHac
}
}
- public static void ExtractSection(this Nca nca, int index, string outputDir, bool verify = false, IProgressReport logger = null)
+ public static void ExtractSection(this Nca nca, int index, string outputDir, IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
{
if (index < 0 || index > 3) throw new IndexOutOfRangeException();
if (nca.Sections[index] == null) return;
NcaSection section = nca.Sections[index];
- Stream stream = nca.OpenSection(index, false, verify);
+ Stream stream = nca.OpenSection(index, false, integrityCheckLevel);
switch (section.Type)
{
@@ -483,5 +464,31 @@ namespace LibHac
break;
}
}
+
+ public static void VerifySection(this Nca nca, int index, IProgressReport logger = null)
+ {
+ if (nca.Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index));
+
+ NcaSection sect = nca.Sections[index];
+ Stream stream = nca.OpenSection(index, false, IntegrityCheckLevel.WarnOnInvalid);
+ logger?.LogMessage($"Verifying section {index}...");
+
+ switch (sect.Header.HashType)
+ {
+ case NcaHashType.Sha256:
+ case NcaHashType.Ivfc:
+ if (stream is HierarchicalIntegrityVerificationStream ivfc)
+ {
+ for (int i = 1; i < ivfc.Levels.Length; i++)
+ {
+ logger?.LogMessage($" Verifying IVFC Level {i}...");
+ ivfc.Levels[i].CopyStream(Stream.Null, ivfc.Levels[i].Length, logger);
+ }
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
}
}
diff --git a/LibHac/Savefile/Savefile.cs b/LibHac/Savefile/Savefile.cs
index 015e47ac..cee44595 100644
--- a/LibHac/Savefile/Savefile.cs
+++ b/LibHac/Savefile/Savefile.cs
@@ -41,7 +41,7 @@ namespace LibHac.Savefile
public DirectoryEntry[] Directories { get; private set; }
private Dictionary FileDict { get; }
- public Savefile(Keyset keyset, Stream file, bool enableIntegrityChecks)
+ public Savefile(Keyset keyset, Stream file, IntegrityCheckLevel integrityCheckLevel)
{
SavefileSource = new SharedStreamSource(file);
@@ -106,7 +106,7 @@ namespace LibHac.Savefile
JournalStream = new JournalStream(journalData, journalMap, (int)Header.Journal.BlockSize);
JournalStreamSource = new SharedStreamSource(JournalStream);
- IvfcStream = InitIvfcStream(enableIntegrityChecks);
+ IvfcStream = InitIvfcStream(integrityCheckLevel);
IvfcStreamSource = new SharedStreamSource(IvfcStream);
ReadFileInfo();
@@ -120,7 +120,7 @@ namespace LibHac.Savefile
}
}
- private HierarchicalIntegrityVerificationStream InitIvfcStream(bool enableIntegrityChecks)
+ private HierarchicalIntegrityVerificationStream InitIvfcStream(IntegrityCheckLevel integrityCheckLevel)
{
IvfcHeader ivfc = Header.Ivfc;
@@ -151,7 +151,7 @@ namespace LibHac.Savefile
};
}
- return new HierarchicalIntegrityVerificationStream(initInfo, enableIntegrityChecks);
+ return new HierarchicalIntegrityVerificationStream(initInfo, integrityCheckLevel);
}
public Stream OpenFile(string filename)
diff --git a/LibHac/SwitchFs.cs b/LibHac/SwitchFs.cs
index a009ff83..967aa14e 100644
--- a/LibHac/SwitchFs.cs
+++ b/LibHac/SwitchFs.cs
@@ -126,7 +126,7 @@ namespace LibHac
string sdPath = "/" + Util.GetRelativePath(file, SaveDir).Replace('\\', '/');
var nax0 = new Nax0(Keyset, stream, sdPath, false);
- save = new Savefile.Savefile(Keyset, nax0.Stream, false);
+ save = new Savefile.Savefile(Keyset, nax0.Stream, IntegrityCheckLevel.None);
}
catch (Exception ex)
{
@@ -147,7 +147,7 @@ namespace LibHac
var title = new Title();
// Meta contents always have 1 Partition FS section with 1 file in it
- Stream sect = nca.OpenSection(0, false, true);
+ Stream sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid);
var pfs0 = new Pfs(sect);
Stream file = pfs0.OpenFile(pfs0.Files[0]);
@@ -187,7 +187,7 @@ namespace LibHac
{
foreach (Title title in Titles.Values.Where(x => x.ControlNca != null))
{
- var romfs = new Romfs(title.ControlNca.OpenSection(0, false, true));
+ var romfs = new Romfs(title.ControlNca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid));
byte[] control = romfs.GetFile("/control.nacp");
var reader = new BinaryReader(new MemoryStream(control));
diff --git a/NandReader/Program.cs b/NandReader/Program.cs
index 37c9d3bf..4718535d 100644
--- a/NandReader/Program.cs
+++ b/NandReader/Program.cs
@@ -102,7 +102,7 @@ namespace NandReader
private static List ReadTickets(Keyset keyset, Stream savefile)
{
var tickets = new List();
- var save = new Savefile(keyset, savefile, false);
+ var save = new Savefile(keyset, savefile, IntegrityCheckLevel.None);
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin"));
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin"));
diff --git a/NandReaderGui/ViewModel/NandViewModel.cs b/NandReaderGui/ViewModel/NandViewModel.cs
index 2f4a1504..feddd0ad 100644
--- a/NandReaderGui/ViewModel/NandViewModel.cs
+++ b/NandReaderGui/ViewModel/NandViewModel.cs
@@ -84,7 +84,7 @@ namespace NandReaderGui.ViewModel
private static List ReadTickets(Keyset keyset, Stream savefile)
{
var tickets = new List();
- var save = new Savefile(keyset, savefile, false);
+ var save = new Savefile(keyset, savefile, IntegrityCheckLevel.None);
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin"));
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin"));
diff --git a/hactoolnet/Options.cs b/hactoolnet/Options.cs
index 1437906b..69d4aa30 100644
--- a/hactoolnet/Options.cs
+++ b/hactoolnet/Options.cs
@@ -36,6 +36,9 @@ namespace hactoolnet
public bool ListRomFs;
public bool SignSave;
public ulong TitleId;
+
+ public IntegrityCheckLevel IntegrityLevel =>
+ EnableHash ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
}
internal enum FileType
diff --git a/hactoolnet/ProcessNca.cs b/hactoolnet/ProcessNca.cs
index 3d5948e1..51eae229 100644
--- a/hactoolnet/ProcessNca.cs
+++ b/hactoolnet/ProcessNca.cs
@@ -26,12 +26,12 @@ namespace hactoolnet
{
if (ctx.Options.SectionOut[i] != null)
{
- nca.ExportSection(i, ctx.Options.SectionOut[i], ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger);
+ nca.ExportSection(i, ctx.Options.SectionOut[i], ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.SectionOutDir[i] != null)
{
- nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.EnableHash, ctx.Logger);
+ nca.ExtractSection(i, ctx.Options.SectionOutDir[i], ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.Validate && nca.Sections[i] != null)
@@ -42,7 +42,7 @@ namespace hactoolnet
if (ctx.Options.ListRomFs && nca.Sections[1] != null)
{
- var romfs = new Romfs(nca.OpenSection(1, false, ctx.Options.EnableHash));
+ var romfs = new Romfs(nca.OpenSection(1, false, ctx.Options.IntegrityLevel));
foreach (RomfsFile romfsFile in romfs.Files)
{
@@ -68,12 +68,12 @@ namespace hactoolnet
if (ctx.Options.RomfsOut != null)
{
- nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger);
+ nca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.RomfsOutDir != null)
{
- var romfs = new Romfs(nca.OpenSection(section.SectionNum, false, ctx.Options.EnableHash));
+ var romfs = new Romfs(nca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel));
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
}
}
@@ -90,12 +90,12 @@ namespace hactoolnet
if (ctx.Options.ExefsOut != null)
{
- nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger);
+ nca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.ExefsOutDir != null)
{
- nca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.EnableHash, ctx.Logger);
+ nca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
}
}
diff --git a/hactoolnet/ProcessSave.cs b/hactoolnet/ProcessSave.cs
index 91efb08c..060f3c3c 100644
--- a/hactoolnet/ProcessSave.cs
+++ b/hactoolnet/ProcessSave.cs
@@ -14,7 +14,7 @@ namespace hactoolnet
{
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite))
{
- var save = new Savefile(ctx.Keyset, file, ctx.Options.EnableHash);
+ var save = new Savefile(ctx.Keyset, file, ctx.Options.IntegrityLevel);
if (ctx.Options.OutDir != null)
{
diff --git a/hactoolnet/ProcessSwitchFs.cs b/hactoolnet/ProcessSwitchFs.cs
index 74813824..e1e88ae9 100644
--- a/hactoolnet/ProcessSwitchFs.cs
+++ b/hactoolnet/ProcessSwitchFs.cs
@@ -55,12 +55,12 @@ namespace hactoolnet
if (ctx.Options.ExefsOutDir != null)
{
- title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.EnableHash, ctx.Logger);
+ title.MainNca.ExtractSection(section.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.ExefsOut != null)
{
- title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger);
+ title.MainNca.ExportSection(section.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
}
@@ -95,13 +95,13 @@ namespace hactoolnet
if (ctx.Options.RomfsOutDir != null)
{
- var romfs = new Romfs(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.EnableHash));
+ var romfs = new Romfs(title.MainNca.OpenSection(section.SectionNum, false, 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.EnableHash, ctx.Logger);
+ title.MainNca.ExportSection(section.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
}
diff --git a/hactoolnet/ProcessXci.cs b/hactoolnet/ProcessXci.cs
index 8edb511a..d2e5aabf 100644
--- a/hactoolnet/ProcessXci.cs
+++ b/hactoolnet/ProcessXci.cs
@@ -79,12 +79,12 @@ namespace hactoolnet
if (ctx.Options.ExefsOutDir != null)
{
- mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.EnableHash, ctx.Logger);
+ mainNca.ExtractSection(exefsSection.SectionNum, ctx.Options.ExefsOutDir, ctx.Options.IntegrityLevel, ctx.Logger);
}
if (ctx.Options.ExefsOut != null)
{
- mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.EnableHash, ctx.Logger);
+ mainNca.ExportSection(exefsSection.SectionNum, ctx.Options.ExefsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
}
@@ -108,13 +108,13 @@ namespace hactoolnet
if (ctx.Options.RomfsOutDir != null)
{
- var romfs = new Romfs(mainNca.OpenSection(romfsSection.SectionNum, false, ctx.Options.EnableHash));
+ var romfs = new Romfs(mainNca.OpenSection(romfsSection.SectionNum, false, 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.EnableHash, ctx.Logger);
+ mainNca.ExportSection(romfsSection.SectionNum, ctx.Options.RomfsOut, ctx.Options.Raw, ctx.Options.IntegrityLevel, ctx.Logger);
}
}
}