Fix opening decrypted NCAs

Fixes an issue where the decrypted NCA could be incorrect if the first section didn't start at offset 0x4000
This commit is contained in:
Alex Barney 2019-03-15 19:29:08 -05:00
parent a1bdadb89b
commit 56c4554d81
3 changed files with 82 additions and 13 deletions

View file

@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace LibHac.IO
{
public class ConcatenationStorageBuilder
{
private List<ConcatenationStorageSegment> Segments { get; }
public ConcatenationStorageBuilder()
{
Segments = new List<ConcatenationStorageSegment>();
}
public ConcatenationStorageBuilder(IEnumerable<ConcatenationStorageSegment> segments)
{
Segments = segments.ToList();
}
public void Add(IStorage storage, long offset)
{
Segments.Add(new ConcatenationStorageSegment(storage, offset));
}
public ConcatenationStorage Build()
{
List<ConcatenationStorageSegment> segments = Segments.OrderBy(x => x.Offset).ToList();
var sources = new List<IStorage>();
long offset = 0;
foreach (ConcatenationStorageSegment segment in segments)
{
long paddingNeeded = segment.Offset - offset;
if (paddingNeeded < 0) throw new InvalidDataException("Builder has segments that overlap.");
if (paddingNeeded > 0)
{
sources.Add(new NullStorage(paddingNeeded));
}
sources.Add(segment.Storage);
offset = segment.Offset + segment.Storage.Length;
}
return new ConcatenationStorage(sources, true);
}
}
public class ConcatenationStorageSegment
{
public IStorage Storage { get; }
public long Offset { get; }
public ConcatenationStorageSegment(IStorage storage, long offset)
{
Storage = storage;
Offset = offset;
}
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LibHac.IO.RomFs;
@ -314,14 +313,15 @@ namespace LibHac.IO.NcaUtils
public IStorage OpenDecryptedNca()
{
var list = new List<IStorage> { OpenHeaderStorage() };
var builder = new ConcatenationStorageBuilder();
builder.Add(OpenHeaderStorage(), 0);
foreach (NcaSection section in Sections.Where(x => x != null).OrderBy(x => x.Offset))
foreach (NcaSection section in Sections.Where(x => x != null))
{
list.Add(OpenRawStorage(section.SectionNum));
builder.Add(OpenRawStorage(section.SectionNum), section.Offset);
}
return new ConcatenationStorage(list, true);
return builder.Build();
}
private NcaHeader DecryptHeader()
@ -336,11 +336,13 @@ namespace LibHac.IO.NcaUtils
private CachedStorage OpenHeaderStorage()
{
int size = 0x4000;
long size = 0xc00;
// Support reading headers that are only 0xC00 bytes long, but still return
// the entire header if available.
if (BaseStorage.Length >= 0xC00 && BaseStorage.Length < 0x4000) size = 0xC00;
// Encrypted portion continues until the first section
if (Sections.Any(x => x != null))
{
size = Sections.Where(x => x != null).Min(x => x.Offset);
}
return new CachedStorage(new Aes128XtsStorage(BaseStorage.Slice(0, size), Keyset.HeaderKey, 0x200, true), 1, true);
}

View file

@ -4,13 +4,15 @@ namespace LibHac.IO.NcaUtils
{
public static class NcaExtensions
{
public static IStorage OpenStorage(this Nca nca, int index, IntegrityCheckLevel integrityCheckLevel, bool openRaw)
public static IStorage OpenStorage(this Nca nca, int index, IntegrityCheckLevel integrityCheckLevel,
bool openRaw)
{
if (openRaw) return nca.OpenRawStorage(index);
return nca.OpenStorage(index, integrityCheckLevel);
}
public static IStorage OpenStorage(this Nca nca, NcaSectionType type, IntegrityCheckLevel integrityCheckLevel, bool openRaw)
public static IStorage OpenStorage(this Nca nca, NcaSectionType type, IntegrityCheckLevel integrityCheckLevel,
bool openRaw)
{
if (openRaw) return nca.OpenRawStorage(type);
return nca.OpenStorage(type, integrityCheckLevel);
@ -23,7 +25,8 @@ namespace LibHac.IO.NcaUtils
.WriteAllBytes(filename, logger);
}
public static void ExtractSection(this Nca nca, int index, string outputDir, IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, 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.SectionIsDecryptable(index)) return;
@ -55,7 +58,8 @@ namespace LibHac.IO.NcaUtils
NcaHashType hashType = sect.Header.HashType;
if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return Validity.Unchecked;
var stream = nca.OpenStorage(index, IntegrityCheckLevel.IgnoreOnInvalid, false) as HierarchicalIntegrityVerificationStorage;
var stream = nca.OpenStorage(index, IntegrityCheckLevel.IgnoreOnInvalid, false)
as HierarchicalIntegrityVerificationStorage;
if (stream == null) return Validity.Unchecked;
if (!quiet) logger?.LogMessage($"Verifying section {index}...");