mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Change NCA section open functions
Add an option to open by section type
This commit is contained in:
parent
093d88a58e
commit
a1bdadb89b
13 changed files with 284 additions and 276 deletions
|
@ -1,5 +1,6 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using LibHac.IO.NcaUtils;
|
||||
|
||||
namespace LibHac
|
||||
{
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace LibHac.IO
|
|||
public string Magic;
|
||||
public int Version;
|
||||
public int NumEntries;
|
||||
public int Field1C;
|
||||
public int FieldC;
|
||||
|
||||
public BucketTreeHeader(IStorage storage)
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ namespace LibHac.IO
|
|||
Magic = reader.ReadAscii(4);
|
||||
Version = reader.ReadInt32();
|
||||
NumEntries = reader.ReadInt32();
|
||||
Field1C = reader.ReadInt32();
|
||||
FieldC = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.RomFs;
|
||||
|
||||
namespace LibHac
|
||||
namespace LibHac.IO.NcaUtils
|
||||
{
|
||||
public class Nca : IDisposable
|
||||
{
|
||||
|
@ -89,13 +88,27 @@ namespace LibHac
|
|||
return sect.Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName);
|
||||
}
|
||||
|
||||
private IStorage OpenRawSection(int index, bool leaveOpen)
|
||||
private IStorage OpenEncryptedStorage(int index)
|
||||
{
|
||||
if (index < 0 || index > 3) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
NcaSection sect = Sections[index];
|
||||
if (sect == null) return null;
|
||||
if (sect == null) throw new ArgumentOutOfRangeException(nameof(index), "Section is empty");
|
||||
|
||||
long offset = sect.Offset;
|
||||
long size = sect.Size;
|
||||
|
||||
if (!Util.IsSubRange(offset, size, BaseStorage.Length))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Section offset (0x{offset:x}) and length (0x{size:x}) fall outside the total NCA length (0x{BaseStorage.Length:x}).");
|
||||
}
|
||||
|
||||
return BaseStorage.Slice(offset, size);
|
||||
}
|
||||
|
||||
private IStorage OpenDecryptedStorage(IStorage baseStorage, NcaSection sect)
|
||||
{
|
||||
if (sect.Header.EncryptionType != NcaEncryptionType.None)
|
||||
{
|
||||
if (IsMissingTitleKey)
|
||||
|
@ -109,118 +122,113 @@ namespace LibHac
|
|||
}
|
||||
}
|
||||
|
||||
long offset = sect.Offset;
|
||||
long size = sect.Size;
|
||||
|
||||
// todo
|
||||
//if (!Util.IsSubRange(offset, size, StreamSource.Length))
|
||||
//{
|
||||
// throw new InvalidDataException(
|
||||
// $"Section offset (0x{offset:x}) and length (0x{size:x}) fall outside the total NCA length (0x{StreamSource.Length:x}).");
|
||||
//}
|
||||
|
||||
IStorage rawStorage = BaseStorage.Slice(offset, size, leaveOpen);
|
||||
|
||||
switch (sect.Header.EncryptionType)
|
||||
{
|
||||
case NcaEncryptionType.None:
|
||||
return rawStorage;
|
||||
return baseStorage;
|
||||
case NcaEncryptionType.XTS:
|
||||
throw new NotImplementedException("NCA sections using XTS are not supported");
|
||||
case NcaEncryptionType.AesCtr:
|
||||
return new CachedStorage(new Aes128CtrStorage(rawStorage, DecryptedKeys[2], offset, sect.Header.Ctr, leaveOpen), 0x4000, 4, leaveOpen);
|
||||
return new CachedStorage(new Aes128CtrStorage(baseStorage, DecryptedKeys[2], sect.Offset, sect.Header.Ctr, true), 0x4000, 4, true);
|
||||
case NcaEncryptionType.AesCtrEx:
|
||||
BktrPatchInfo info = sect.Header.BktrInfo;
|
||||
|
||||
long bktrOffset = info.RelocationHeader.Offset;
|
||||
long bktrSize = size - bktrOffset;
|
||||
long bktrSize = sect.Size - bktrOffset;
|
||||
long dataSize = info.RelocationHeader.Offset;
|
||||
|
||||
IStorage bucketTreeHeader = new MemoryStorage(sect.Header.BktrInfo.EncryptionHeader.Header);
|
||||
IStorage bucketTreeData = new CachedStorage(new Aes128CtrStorage(rawStorage.Slice(bktrOffset, bktrSize, leaveOpen), DecryptedKeys[2], bktrOffset + offset, sect.Header.Ctr, leaveOpen), 4, leaveOpen);
|
||||
IStorage bucketTreeData = new CachedStorage(new Aes128CtrStorage(baseStorage.Slice(bktrOffset, bktrSize), DecryptedKeys[2], bktrOffset + sect.Offset, sect.Header.Ctr, true), 4, true);
|
||||
|
||||
IStorage encryptionBucketTreeData = bucketTreeData.Slice(info.EncryptionHeader.Offset - bktrOffset);
|
||||
IStorage decStorage = new Aes128CtrExStorage(rawStorage.Slice(0, dataSize, leaveOpen), bucketTreeHeader, encryptionBucketTreeData, DecryptedKeys[2], offset, sect.Header.Ctr, leaveOpen);
|
||||
decStorage = new CachedStorage(decStorage, 0x4000, 4, leaveOpen);
|
||||
IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), bucketTreeHeader, encryptionBucketTreeData, DecryptedKeys[2], sect.Offset, sect.Header.Ctr, true);
|
||||
decStorage = new CachedStorage(decStorage, 0x4000, 4, true);
|
||||
|
||||
return new ConcatenationStorage(new[] { decStorage, bucketTreeData }, leaveOpen);
|
||||
return new ConcatenationStorage(new[] { decStorage, bucketTreeData }, true);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens one of the sections in the current <see cref="Nca"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the NCA section to open. Valid indexes are 0-3.</param>
|
||||
/// <param name="raw"><see langword="true"/> to open the raw section with hash metadata.</param>
|
||||
/// <param name="integrityCheckLevel">The level of integrity checks to be performed when reading the section.
|
||||
/// Always <see cref="IntegrityCheckLevel.None"/> if <paramref name="raw"/> is <see langword="false"/>.</param>
|
||||
/// <param name="leaveOpen"><see langword="true"/> to leave the storage open after the <see cref="Nca"/> object is disposed; otherwise, <see langword="false"/>.</param>
|
||||
/// <returns>A <see cref="Stream"/> that provides access to the specified section. <see langword="null"/> if the section does not exist.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="index"/> is outside the valid range.</exception>
|
||||
public IStorage OpenSection(int index, bool raw, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
|
||||
public IStorage OpenRawStorage(int index)
|
||||
{
|
||||
IStorage rawStorage = OpenRawSection(index, leaveOpen);
|
||||
IStorage encryptedStorage = OpenEncryptedStorage(index);
|
||||
IStorage decryptedStorage = OpenDecryptedStorage(encryptedStorage, Sections[index]);
|
||||
|
||||
return decryptedStorage;
|
||||
}
|
||||
|
||||
public IStorage OpenRawStorage(NcaSectionType type)
|
||||
{
|
||||
return OpenRawStorage(GetSectionIndexFromType(type));
|
||||
}
|
||||
|
||||
public IStorage OpenStorage(int index, IntegrityCheckLevel integrityCheckLevel)
|
||||
{
|
||||
IStorage rawStorage = OpenRawStorage(index);
|
||||
|
||||
NcaSection sect = Sections[index];
|
||||
NcaFsHeader header = sect.Header;
|
||||
|
||||
// todo don't assume that ctr ex means it's a patch
|
||||
if (header.EncryptionType == NcaEncryptionType.AesCtrEx)
|
||||
{
|
||||
if (raw && BaseNca == null) return rawStorage;
|
||||
|
||||
BktrHeader bktrInfo = header.BktrInfo.RelocationHeader;
|
||||
IStorage patchStorage = rawStorage.Slice(0, bktrInfo.Offset, leaveOpen);
|
||||
|
||||
if (BaseNca == null) return patchStorage;
|
||||
|
||||
IStorage baseStorage = BaseNca.OpenSection(ProgramPartitionType.Data, true, IntegrityCheckLevel.None, leaveOpen);
|
||||
IStorage bktrHeader = new MemoryStorage(bktrInfo.Header);
|
||||
IStorage bktrData = rawStorage.Slice(bktrInfo.Offset, bktrInfo.Size, leaveOpen);
|
||||
|
||||
rawStorage = new IndirectStorage(bktrHeader, bktrData, leaveOpen, baseStorage, patchStorage);
|
||||
return rawStorage.Slice(0, header.BktrInfo.RelocationHeader.Offset);
|
||||
}
|
||||
|
||||
if (raw || rawStorage == null) return rawStorage;
|
||||
|
||||
switch (header.HashType)
|
||||
{
|
||||
case NcaHashType.Sha256:
|
||||
return InitIvfcForPartitionfs(header.Sha256Info, rawStorage, integrityCheckLevel, leaveOpen);
|
||||
return InitIvfcForPartitionfs(header.Sha256Info, rawStorage, integrityCheckLevel, true);
|
||||
case NcaHashType.Ivfc:
|
||||
return new HierarchicalIntegrityVerificationStorage(header.IvfcInfo, new MemoryStorage(header.IvfcInfo.MasterHash), rawStorage,
|
||||
IntegrityStorageType.RomFs, integrityCheckLevel, leaveOpen);
|
||||
IntegrityStorageType.RomFs, integrityCheckLevel, true);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens one of the sections in the current <see cref="Nca"/> as a <see cref="HierarchicalIntegrityVerificationStorage"/>
|
||||
/// Only works with sections that have a <see cref="NcaFsHeader.HashType"/> of <see cref="NcaHashType.Ivfc"/> or <see cref="NcaHashType.Sha256"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the NCA section to open. Valid indexes are 0-3.</param>
|
||||
/// <param name="integrityCheckLevel">The level of integrity checks to be performed when reading the section.</param>
|
||||
/// <param name="leaveOpen"><see langword="true"/> to leave the storage open after the <see cref="Nca"/> object is disposed; otherwise, <see langword="false"/>.</param>
|
||||
/// <returns>A <see cref="Stream"/> that provides access to the specified section. <see langword="null"/> if the section does not exist,
|
||||
/// or is has no hash metadata.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="index"/> is outside the valid range.</exception>
|
||||
public HierarchicalIntegrityVerificationStorage OpenHashedSection(int index, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen) =>
|
||||
OpenSection(index, false, integrityCheckLevel, leaveOpen) as HierarchicalIntegrityVerificationStorage;
|
||||
public IStorage OpenStorage(NcaSectionType type, IntegrityCheckLevel integrityCheckLevel)
|
||||
{
|
||||
return OpenStorage(GetSectionIndexFromType(type), integrityCheckLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens one of the sections in the current <see cref="Nca"/>. For use with <see cref="ContentType.Program"/> type NCAs.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of section to open.</param>
|
||||
/// <param name="raw"><see langword="true"/> to open the raw section with hash metadata.</param>
|
||||
/// <param name="integrityCheckLevel">The level of integrity checks to be performed when reading the section.
|
||||
/// Always <see cref="IntegrityCheckLevel.None"/> if <paramref name="raw"/> is <see langword="false"/>.</param>
|
||||
/// <param name="leaveOpen"><see langword="true"/> to leave the storage open after the <see cref="Nca"/> object is disposed; otherwise, <see langword="false"/>.</param>
|
||||
/// <returns>A <see cref="Stream"/> that provides access to the specified section. <see langword="null"/> if the section does not exist.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="type"/> is outside the valid range.</exception>
|
||||
public IStorage OpenSection(ProgramPartitionType type, bool raw, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen) =>
|
||||
OpenSection((int)type, raw, integrityCheckLevel, leaveOpen);
|
||||
public IFileSystem OpenFileSystem(int index, IntegrityCheckLevel integrityCheckLevel)
|
||||
{
|
||||
IStorage storage = OpenStorage(index, integrityCheckLevel);
|
||||
|
||||
switch (Sections[index].Header.Type)
|
||||
{
|
||||
case SectionType.Pfs0:
|
||||
return new PartitionFileSystem(storage);
|
||||
case SectionType.Romfs:
|
||||
return new RomFsFileSystem(storage);
|
||||
case SectionType.Bktr:
|
||||
// todo Possibly check if a patch completely replaces the original
|
||||
throw new InvalidOperationException("Cannot open a patched section without the original");
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public IFileSystem OpenFileSystem(NcaSectionType type, IntegrityCheckLevel integrityCheckLevel)
|
||||
{
|
||||
return OpenFileSystem(GetSectionIndexFromType(type), integrityCheckLevel);
|
||||
}
|
||||
|
||||
private int GetSectionIndexFromType(NcaSectionType type)
|
||||
{
|
||||
ContentType contentType = Header.ContentType;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case NcaSectionType.Code when contentType == ContentType.Program: return 0;
|
||||
case NcaSectionType.Data when contentType == ContentType.Program: return 1;
|
||||
case NcaSectionType.Logo when contentType == ContentType.Program: return 2;
|
||||
case NcaSectionType.Data: return 0;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(type), "NCA does not contain this section type.");
|
||||
}
|
||||
}
|
||||
|
||||
private static HierarchicalIntegrityVerificationStorage InitIvfcForPartitionfs(Sha256Info sb,
|
||||
IStorage pfsStorage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
|
||||
|
@ -257,24 +265,18 @@ namespace LibHac
|
|||
return new HierarchicalIntegrityVerificationStorage(initInfo, integrityCheckLevel, leaveOpen);
|
||||
}
|
||||
|
||||
public IFileSystem OpenSectionFileSystem(int index, IntegrityCheckLevel integrityCheckLevel)
|
||||
public bool SectionExists(int index)
|
||||
{
|
||||
if (Sections[index] == null) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
NcaFsHeader header = Sections[index].Header;
|
||||
if (index < 0 || index > 3) return false;
|
||||
|
||||
IStorage storage = OpenSection(index, false, integrityCheckLevel, true);
|
||||
return Sections[index] != null;
|
||||
}
|
||||
|
||||
switch (header.Type)
|
||||
{
|
||||
case SectionType.Pfs0:
|
||||
return new PartitionFileSystem(storage);
|
||||
case SectionType.Romfs:
|
||||
return new RomFsFileSystem(storage);
|
||||
case SectionType.Bktr:
|
||||
return new RomFsFileSystem(storage);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
public bool SectionIsDecryptable(int index)
|
||||
{
|
||||
if (!SectionExists(index)) return false;
|
||||
|
||||
return Sections[index].Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -299,11 +301,11 @@ namespace LibHac
|
|||
{
|
||||
if (Header.ContentType != ContentType.Program) return;
|
||||
|
||||
var pfs = new PartitionFileSystem(OpenSection(ProgramPartitionType.Code, false, IntegrityCheckLevel.ErrorOnInvalid, true));
|
||||
IFileSystem pfs = OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
if (!pfs.FileExists("main.npdm")) return;
|
||||
|
||||
IStorage npdmStorage = pfs.OpenFile("main.npdm", OpenMode.Read).AsStorage();
|
||||
IFile npdmStorage = pfs.OpenFile("main.npdm", OpenMode.Read);
|
||||
|
||||
Npdm = new Npdm.NpdmBinary(npdmStorage.AsStream(), Keyset);
|
||||
|
||||
|
@ -316,7 +318,7 @@ namespace LibHac
|
|||
|
||||
foreach (NcaSection section in Sections.Where(x => x != null).OrderBy(x => x.Offset))
|
||||
{
|
||||
list.Add(OpenRawSection(section.SectionNum, true));
|
||||
list.Add(OpenRawStorage(section.SectionNum));
|
||||
}
|
||||
|
||||
return new ConcatenationStorage(list, true);
|
||||
|
@ -425,7 +427,7 @@ namespace LibHac
|
|||
break;
|
||||
}
|
||||
|
||||
IStorage storage = OpenSection(index, true, IntegrityCheckLevel.None, true);
|
||||
IStorage storage = OpenRawStorage(index);
|
||||
|
||||
var hashTable = new byte[size];
|
||||
storage.Read(hashTable, offset);
|
||||
|
@ -496,88 +498,4 @@ namespace LibHac
|
|||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NcaExtensions
|
||||
{
|
||||
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;
|
||||
|
||||
IStorage storage = nca.OpenSection(index, raw, integrityCheckLevel, true);
|
||||
string dir = Path.GetDirectoryName(filename);
|
||||
if (!string.IsNullOrWhiteSpace(dir)) Directory.CreateDirectory(dir);
|
||||
|
||||
using (var outFile = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite))
|
||||
{
|
||||
storage.CopyToStream(outFile, storage.Length, logger);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
IStorage storage = nca.OpenSection(index, false, integrityCheckLevel, true);
|
||||
|
||||
switch (section.Type)
|
||||
{
|
||||
case SectionType.Invalid:
|
||||
break;
|
||||
case SectionType.Pfs0:
|
||||
var pfs0 = new PartitionFileSystem(storage);
|
||||
pfs0.Extract(outputDir, logger);
|
||||
break;
|
||||
case SectionType.Romfs:
|
||||
var romfs = new RomFsFileSystem(storage);
|
||||
romfs.Extract(outputDir, logger);
|
||||
break;
|
||||
case SectionType.Bktr:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
NcaSection sect = nca.Sections[index];
|
||||
NcaHashType hashType = sect.Header.HashType;
|
||||
if (hashType != NcaHashType.Sha256 && hashType != NcaHashType.Ivfc) return Validity.Unchecked;
|
||||
|
||||
HierarchicalIntegrityVerificationStorage stream = nca.OpenHashedSection(index, IntegrityCheckLevel.IgnoreOnInvalid, true);
|
||||
if (stream == null) return Validity.Unchecked;
|
||||
|
||||
if (!quiet) logger?.LogMessage($"Verifying section {index}...");
|
||||
Validity validity = stream.Validate(true, logger);
|
||||
|
||||
if (hashType == NcaHashType.Ivfc)
|
||||
{
|
||||
stream.SetLevelValidities(sect.Header.IvfcInfo);
|
||||
}
|
||||
else if (hashType == NcaHashType.Sha256)
|
||||
{
|
||||
sect.Header.Sha256Info.HashValidity = validity;
|
||||
}
|
||||
|
||||
return validity;
|
||||
}
|
||||
}
|
||||
}
|
76
src/LibHac/IO/NcaUtils/NcaExtensions.cs
Normal file
76
src/LibHac/IO/NcaUtils/NcaExtensions.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
|
||||
namespace LibHac.IO.NcaUtils
|
||||
{
|
||||
public static class NcaExtensions
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (openRaw) return nca.OpenRawStorage(type);
|
||||
return nca.OpenStorage(type, integrityCheckLevel);
|
||||
}
|
||||
|
||||
public static void ExportSection(this Nca nca, int index, string filename, bool raw = false,
|
||||
IntegrityCheckLevel integrityCheckLevel = IntegrityCheckLevel.None, IProgressReport logger = null)
|
||||
{
|
||||
nca.OpenStorage(index, integrityCheckLevel, raw)
|
||||
.WriteAllBytes(filename, logger);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
IFileSystem fs = nca.OpenFileSystem(index, integrityCheckLevel);
|
||||
fs.Extract(outputDir, logger);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
NcaSection sect = nca.Sections[index];
|
||||
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;
|
||||
if (stream == null) return Validity.Unchecked;
|
||||
|
||||
if (!quiet) logger?.LogMessage($"Verifying section {index}...");
|
||||
Validity validity = stream.Validate(true, logger);
|
||||
|
||||
if (hashType == NcaHashType.Ivfc)
|
||||
{
|
||||
stream.SetLevelValidities(sect.Header.IvfcInfo);
|
||||
}
|
||||
else if (hashType == NcaHashType.Sha256)
|
||||
{
|
||||
sect.Header.Sha256Info.HashValidity = validity;
|
||||
}
|
||||
|
||||
return validity;
|
||||
}
|
||||
}
|
||||
}
|
71
src/LibHac/IO/NcaUtils/NcaFsHeader.cs
Normal file
71
src/LibHac/IO/NcaUtils/NcaFsHeader.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibHac.IO.NcaUtils
|
||||
{
|
||||
public class NcaFsHeader
|
||||
{
|
||||
public short Version;
|
||||
public NcaFormatType FormatType;
|
||||
public NcaHashType HashType;
|
||||
public NcaEncryptionType EncryptionType;
|
||||
public SectionType Type;
|
||||
|
||||
public IvfcHeader IvfcInfo;
|
||||
public Sha256Info Sha256Info;
|
||||
public BktrPatchInfo BktrInfo;
|
||||
|
||||
public byte[] Ctr;
|
||||
|
||||
public NcaFsHeader(BinaryReader reader)
|
||||
{
|
||||
long start = reader.BaseStream.Position;
|
||||
Version = reader.ReadInt16();
|
||||
FormatType = (NcaFormatType)reader.ReadByte();
|
||||
HashType = (NcaHashType)reader.ReadByte();
|
||||
EncryptionType = (NcaEncryptionType)reader.ReadByte();
|
||||
reader.BaseStream.Position += 3;
|
||||
|
||||
switch (HashType)
|
||||
{
|
||||
case NcaHashType.Sha256:
|
||||
Sha256Info = new Sha256Info(reader);
|
||||
break;
|
||||
case NcaHashType.Ivfc:
|
||||
IvfcInfo = new IvfcHeader(reader);
|
||||
break;
|
||||
}
|
||||
|
||||
if (EncryptionType == NcaEncryptionType.AesCtrEx)
|
||||
{
|
||||
BktrInfo = new BktrPatchInfo();
|
||||
|
||||
reader.BaseStream.Position = start + 0x100;
|
||||
|
||||
BktrInfo.RelocationHeader = new BktrHeader(reader);
|
||||
BktrInfo.EncryptionHeader = new BktrHeader(reader);
|
||||
}
|
||||
|
||||
if (FormatType == NcaFormatType.Pfs0)
|
||||
{
|
||||
Type = SectionType.Pfs0;
|
||||
}
|
||||
else if (FormatType == NcaFormatType.Romfs)
|
||||
{
|
||||
if (EncryptionType == NcaEncryptionType.AesCtrEx)
|
||||
{
|
||||
Type = SectionType.Bktr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = SectionType.Romfs;
|
||||
}
|
||||
}
|
||||
|
||||
reader.BaseStream.Position = start + 0x140;
|
||||
Ctr = reader.ReadBytes(8).Reverse().ToArray();
|
||||
|
||||
reader.BaseStream.Position = start + 512;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using LibHac.IO;
|
||||
|
||||
namespace LibHac
|
||||
namespace LibHac.IO.NcaUtils
|
||||
{
|
||||
public class NcaHeader
|
||||
{
|
||||
|
@ -98,72 +96,6 @@ namespace LibHac
|
|||
}
|
||||
}
|
||||
|
||||
public class NcaFsHeader
|
||||
{
|
||||
public short Version;
|
||||
public NcaFormatType FormatType;
|
||||
public NcaHashType HashType;
|
||||
public NcaEncryptionType EncryptionType;
|
||||
public SectionType Type;
|
||||
|
||||
public IvfcHeader IvfcInfo;
|
||||
public Sha256Info Sha256Info;
|
||||
public BktrPatchInfo BktrInfo;
|
||||
|
||||
public byte[] Ctr;
|
||||
|
||||
public NcaFsHeader(BinaryReader reader)
|
||||
{
|
||||
long start = reader.BaseStream.Position;
|
||||
Version = reader.ReadInt16();
|
||||
FormatType = (NcaFormatType)reader.ReadByte();
|
||||
HashType = (NcaHashType)reader.ReadByte();
|
||||
EncryptionType = (NcaEncryptionType)reader.ReadByte();
|
||||
reader.BaseStream.Position += 3;
|
||||
|
||||
switch (HashType)
|
||||
{
|
||||
case NcaHashType.Sha256:
|
||||
Sha256Info = new Sha256Info(reader);
|
||||
break;
|
||||
case NcaHashType.Ivfc:
|
||||
IvfcInfo = new IvfcHeader(reader);
|
||||
break;
|
||||
}
|
||||
|
||||
if (EncryptionType == NcaEncryptionType.AesCtrEx)
|
||||
{
|
||||
BktrInfo = new BktrPatchInfo();
|
||||
|
||||
reader.BaseStream.Position = start + 0x100;
|
||||
|
||||
BktrInfo.RelocationHeader = new BktrHeader(reader);
|
||||
BktrInfo.EncryptionHeader = new BktrHeader(reader);
|
||||
}
|
||||
|
||||
if (FormatType == NcaFormatType.Pfs0)
|
||||
{
|
||||
Type = SectionType.Pfs0;
|
||||
}
|
||||
else if (FormatType == NcaFormatType.Romfs)
|
||||
{
|
||||
if (EncryptionType == NcaEncryptionType.AesCtrEx)
|
||||
{
|
||||
Type = SectionType.Bktr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = SectionType.Romfs;
|
||||
}
|
||||
}
|
||||
|
||||
reader.BaseStream.Position = start + 0x140;
|
||||
Ctr = reader.ReadBytes(8).Reverse().ToArray();
|
||||
|
||||
reader.BaseStream.Position = start + 512;
|
||||
}
|
||||
}
|
||||
|
||||
public class BktrPatchInfo
|
||||
{
|
||||
public BktrHeader RelocationHeader;
|
||||
|
@ -174,7 +106,7 @@ namespace LibHac
|
|||
{
|
||||
public byte[] MasterHash;
|
||||
public int BlockSize; // In bytes
|
||||
public uint Always2;
|
||||
public uint LevelCount;
|
||||
public long HashTableOffset;
|
||||
public long HashTableSize;
|
||||
public long DataOffset;
|
||||
|
@ -187,7 +119,7 @@ namespace LibHac
|
|||
{
|
||||
MasterHash = reader.ReadBytes(0x20);
|
||||
BlockSize = reader.ReadInt32();
|
||||
Always2 = reader.ReadUInt32();
|
||||
LevelCount = reader.ReadUInt32();
|
||||
HashTableOffset = reader.ReadInt64();
|
||||
HashTableSize = reader.ReadInt64();
|
||||
DataOffset = reader.ReadInt64();
|
||||
|
@ -261,6 +193,13 @@ namespace LibHac
|
|||
Logo
|
||||
};
|
||||
|
||||
public enum NcaSectionType
|
||||
{
|
||||
Code,
|
||||
Data,
|
||||
Logo
|
||||
};
|
||||
|
||||
public enum ContentType
|
||||
{
|
||||
Program,
|
||||
|
@ -307,12 +246,4 @@ namespace LibHac
|
|||
Romfs,
|
||||
Bktr
|
||||
}
|
||||
|
||||
public enum Validity : byte
|
||||
{
|
||||
Unchecked,
|
||||
Invalid,
|
||||
Valid,
|
||||
MissingKey
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.RomFs;
|
||||
using LibHac.IO.NcaUtils;
|
||||
using LibHac.IO.Save;
|
||||
|
||||
namespace LibHac
|
||||
|
@ -127,10 +127,10 @@ namespace LibHac
|
|||
{
|
||||
var title = new Title();
|
||||
|
||||
// Meta contents always have 1 Partition FS section with 1 file in it
|
||||
IStorage sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true);
|
||||
var pfs0 = new PartitionFileSystem(sect);
|
||||
IFile file = pfs0.OpenFile(pfs0.Files[0], OpenMode.Read);
|
||||
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
string cnmtPath = fs.EnumerateEntries("*.cnmt").Single().FullPath;
|
||||
|
||||
IFile file = fs.OpenFile(cnmtPath, OpenMode.Read);
|
||||
|
||||
var metadata = new Cnmt(file.AsStream());
|
||||
title.Id = metadata.TitleId;
|
||||
|
@ -168,7 +168,7 @@ namespace LibHac
|
|||
{
|
||||
foreach (Title title in Titles.Values.Where(x => x.ControlNca != null))
|
||||
{
|
||||
var romfs = new RomFsFileSystem(title.ControlNca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true));
|
||||
IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
IFile control = romfs.OpenFile("control.nacp", OpenMode.Read);
|
||||
|
||||
title.Control = new Nacp(control.AsStream());
|
||||
|
|
10
src/LibHac/Validity.cs
Normal file
10
src/LibHac/Validity.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace LibHac
|
||||
{
|
||||
public enum Validity : byte
|
||||
{
|
||||
Unchecked,
|
||||
Invalid,
|
||||
Valid,
|
||||
MissingKey
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using LibHac;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.NcaUtils;
|
||||
using static hactoolnet.Print;
|
||||
|
||||
namespace hactoolnet
|
||||
|
@ -26,7 +26,7 @@ namespace hactoolnet
|
|||
try
|
||||
{
|
||||
var nca = new Nca(ctx.Keyset, deltaStorage, true);
|
||||
IFileSystem fs = nca.OpenSectionFileSystem(0, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
IFileSystem fs = nca.OpenFileSystem(0, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
if (!fs.FileExists(FragmentFileName))
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using LibHac;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.RomFs;
|
||||
using LibHac.IO.NcaUtils;
|
||||
using static hactoolnet.Print;
|
||||
|
||||
namespace hactoolnet
|
||||
|
@ -45,7 +45,7 @@ namespace hactoolnet
|
|||
|
||||
if (ctx.Options.ListRomFs && nca.Sections[1] != null)
|
||||
{
|
||||
var romfs = new RomFsFileSystem(nca.OpenSection(1, false, ctx.Options.IntegrityLevel, true));
|
||||
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, ctx.Options.IntegrityLevel);
|
||||
|
||||
foreach (DirectoryEntry entry in romfs.EnumerateEntries())
|
||||
{
|
||||
|
@ -76,14 +76,14 @@ namespace hactoolnet
|
|||
|
||||
if (ctx.Options.RomfsOutDir != null)
|
||||
{
|
||||
IFileSystem romfs = nca.OpenSectionFileSystem(section.SectionNum, ctx.Options.IntegrityLevel);
|
||||
IFileSystem romfs = nca.OpenFileSystem(section.SectionNum, ctx.Options.IntegrityLevel);
|
||||
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
|
||||
}
|
||||
|
||||
if (ctx.Options.ReadBench)
|
||||
{
|
||||
long bytesToRead = 1024L * 1024 * 1024 * 5;
|
||||
IStorage storage = nca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel, true);
|
||||
IStorage storage = nca.OpenStorage(section.SectionNum, ctx.Options.IntegrityLevel);
|
||||
var dest = new NullStorage(storage.Length);
|
||||
|
||||
int iterations = (int)(bytesToRead / storage.Length) + 1;
|
||||
|
@ -125,7 +125,7 @@ namespace hactoolnet
|
|||
|
||||
if (ctx.Options.ExefsOutDir != null)
|
||||
{
|
||||
IFileSystem pfs = nca.OpenSectionFileSystem(section.SectionNum, ctx.Options.IntegrityLevel);
|
||||
IFileSystem pfs = nca.OpenFileSystem(section.SectionNum, ctx.Options.IntegrityLevel);
|
||||
pfs.Extract(ctx.Options.ExefsOutDir, ctx.Logger);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Reflection;
|
|||
using System.Text;
|
||||
using LibHac;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.NcaUtils;
|
||||
using static hactoolnet.Print;
|
||||
|
||||
namespace hactoolnet
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using LibHac;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.RomFs;
|
||||
using LibHac.IO.NcaUtils;
|
||||
using LibHac.IO.Save;
|
||||
|
||||
namespace hactoolnet
|
||||
|
@ -119,7 +119,7 @@ namespace hactoolnet
|
|||
|
||||
if (ctx.Options.RomfsOutDir != null)
|
||||
{
|
||||
var romfs = new RomFsFileSystem(title.MainNca.OpenSection(section.SectionNum, false, ctx.Options.IntegrityLevel, true));
|
||||
IFileSystem romfs = title.MainNca.OpenFileSystem(section.SectionNum, ctx.Options.IntegrityLevel);
|
||||
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using LibHac;
|
||||
using LibHac.IO;
|
||||
using LibHac.IO.RomFs;
|
||||
using LibHac.IO.NcaUtils;
|
||||
using static hactoolnet.Print;
|
||||
|
||||
namespace hactoolnet
|
||||
|
@ -111,7 +111,7 @@ namespace hactoolnet
|
|||
|
||||
if (ctx.Options.RomfsOutDir != null)
|
||||
{
|
||||
var romfs = new RomFsFileSystem(mainNca.OpenSection(romfsSection.SectionNum, false, ctx.Options.IntegrityLevel, true));
|
||||
IFileSystem romfs = mainNca.OpenFileSystem(romfsSection.SectionNum, ctx.Options.IntegrityLevel);
|
||||
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue