Add option to verify SwitchFS and SD cards

This commit is contained in:
Alex Barney 2018-10-12 17:52:15 -05:00
parent e21c384a9c
commit 6213111af9
4 changed files with 93 additions and 11 deletions

View file

@ -78,8 +78,6 @@ namespace LibHac
int bytesToRead = (int)Math.Min(CurrentEntry.OffsetEnd - Position, count); int bytesToRead = (int)Math.Min(CurrentEntry.OffsetEnd - Position, count);
int bytesRead = base.Read(buffer, outPos, bytesToRead); int bytesRead = base.Read(buffer, outPos, bytesToRead);
if (bytesRead == 0) break;
outPos += bytesRead; outPos += bytesRead;
totalBytesRead += bytesRead; totalBytesRead += bytesRead;
count -= bytesRead; count -= bytesRead;
@ -89,6 +87,10 @@ namespace LibHac
CurrentEntry = CurrentEntry.Next; CurrentEntry = CurrentEntry.Next;
UpdateCounterSubsection(CurrentEntry.Counter); UpdateCounterSubsection(CurrentEntry.Counter);
} }
else if (bytesRead == 0)
{
break;
}
} }
return totalBytesRead; return totalBytesRead;

View file

@ -62,7 +62,7 @@ namespace LibHac
{ {
if (validities[i] == Validity.Unchecked) if (validities[i] == Validity.Unchecked)
{ {
levelStream.Position = levelStream.SectorSize * i; levelStream.Position = (long)levelStream.SectorSize * i;
levelStream.Read(buffer, 0, buffer.Length, IntegrityCheckLevel.IgnoreOnInvalid); levelStream.Read(buffer, 0, buffer.Length, IntegrityCheckLevel.IgnoreOnInvalid);
} }

View file

@ -478,33 +478,52 @@ namespace LibHac
} }
} }
public static void VerifySection(this Nca nca, int index, IProgressReport logger = null) public static Validity VerifyNca(this Nca nca, IProgressReport logger = null, bool quiet = false)
{
for (int i = 0; i < 3; i++)
{
if (nca.Sections[i] != null)
{
Validity sectionValidity = VerifySection(nca, i, logger, quiet);
if (sectionValidity == Validity.Invalid) return Validity.Invalid;
}
}
return Validity.Valid;
}
public static Validity VerifySection(this Nca nca, int index, IProgressReport logger = null, bool quiet = false)
{ {
if (nca.Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index)); if (nca.Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index));
NcaSection sect = nca.Sections[index]; NcaSection sect = nca.Sections[index];
NcaHashType hashType = sect.Header.HashType; NcaHashType hashType = sect.Header.HashType;
if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return; if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return Validity.Unchecked;
HierarchicalIntegrityVerificationStream stream = nca.OpenHashedSection(index, IntegrityCheckLevel.IgnoreOnInvalid); HierarchicalIntegrityVerificationStream stream = nca.OpenHashedSection(index, IntegrityCheckLevel.IgnoreOnInvalid);
if (stream == null) return; if (stream == null) return Validity.Unchecked;
logger?.LogMessage($"Verifying section {index}..."); if (!quiet) logger?.LogMessage($"Verifying section {index}...");
for (int i = 0; i < stream.Levels.Length - 1; i++) for (int i = 0; i < stream.Levels.Length - 1; i++)
{ {
logger?.LogMessage($" Verifying Hash Level {i}..."); if (!quiet) logger?.LogMessage($" Verifying Hash Level {i}...");
Validity result = stream.ValidateLevel(i, true, logger); Validity levelValidity = stream.ValidateLevel(i, true, logger);
if (hashType == NcaHashType.Ivfc) if (hashType == NcaHashType.Ivfc)
{ {
sect.Header.IvfcInfo.LevelHeaders[i].HashValidity = result; sect.Header.IvfcInfo.LevelHeaders[i].HashValidity = levelValidity;
} }
else if (hashType == NcaHashType.Sha256 && i == stream.Levels.Length - 2) else if (hashType == NcaHashType.Sha256 && i == stream.Levels.Length - 2)
{ {
sect.Header.Sha256Info.HashValidity = result; sect.Header.Sha256Info.HashValidity = levelValidity;
} }
if (levelValidity == Validity.Invalid) return Validity.Invalid;
} }
return Validity.Valid;
} }
} }
} }

View file

@ -119,6 +119,67 @@ namespace hactoolnet
{ {
ExportSdSaves(ctx, switchFs); ExportSdSaves(ctx, switchFs);
} }
if (ctx.Options.Validate)
{
ValidateSwitchFs(ctx, switchFs);
}
}
private static void ValidateSwitchFs(Context ctx, SwitchFs switchFs)
{
if (ctx.Options.TitleId != 0)
{
ulong id = ctx.Options.TitleId;
if (!switchFs.Titles.TryGetValue(id, out Title title))
{
ctx.Logger.LogMessage($"Could not find title {id:X16}");
return;
}
ValidateTitle(ctx, title, "");
return;
}
foreach (Application app in switchFs.Applications.Values)
{
ctx.Logger.LogMessage($"Checking {app.Name}...");
Title mainTitle = app.Patch ?? app.Main;
if (mainTitle != null)
{
ValidateTitle(ctx, mainTitle, "Main title");
}
foreach (Title title in app.AddOnContent)
{
ValidateTitle(ctx, title, "Add-on content");
}
}
}
private static void ValidateTitle(Context ctx, Title title, string caption)
{
try
{
ctx.Logger.LogMessage($" {caption} {title.Id:x16}");
foreach (Nca nca in title.Ncas)
{
ctx.Logger.LogMessage($" {nca.Header.ContentType.ToString()}");
Validity validity = nca.VerifyNca(ctx.Logger, true);
ctx.Logger.LogMessage($" {validity.ToString()}");
}
}
catch (Exception ex)
{
ctx.Logger.LogMessage($"Error processing title {title.Id:x16}:\n{ex.Message}");
}
} }
private static void SaveTitle(Context ctx, SwitchFs switchFs) private static void SaveTitle(Context ctx, SwitchFs switchFs)