diff --git a/src/LibHac/Cnmt.cs b/src/LibHac/Cnmt.cs
index d5500bf0..97e553ac 100644
--- a/src/LibHac/Cnmt.cs
+++ b/src/LibHac/Cnmt.cs
@@ -1,5 +1,6 @@
using System.IO;
using System.Linq;
+using LibHac.IO.NcaUtils;
namespace LibHac
{
diff --git a/src/LibHac/IO/BucketTree.cs b/src/LibHac/IO/BucketTree.cs
index 914c1ee3..9e533284 100644
--- a/src/LibHac/IO/BucketTree.cs
+++ b/src/LibHac/IO/BucketTree.cs
@@ -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();
}
}
diff --git a/src/LibHac/Nca.cs b/src/LibHac/IO/NcaUtils/Nca.cs
similarity index 60%
rename from src/LibHac/Nca.cs
rename to src/LibHac/IO/NcaUtils/Nca.cs
index 341ee4c1..d2cf3084 100644
--- a/src/LibHac/Nca.cs
+++ b/src/LibHac/IO/NcaUtils/Nca.cs
@@ -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();
}
}
- ///
- /// Opens one of the sections in the current .
- ///
- /// The index of the NCA section to open. Valid indexes are 0-3.
- /// to open the raw section with hash metadata.
- /// The level of integrity checks to be performed when reading the section.
- /// Always if is .
- /// to leave the storage open after the object is disposed; otherwise, .
- /// A that provides access to the specified section. if the section does not exist.
- /// The specified is outside the valid range.
- 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();
}
}
- ///
- /// Opens one of the sections in the current as a
- /// Only works with sections that have a of or .
- ///
- /// The index of the NCA section to open. Valid indexes are 0-3.
- /// The level of integrity checks to be performed when reading the section.
- /// to leave the storage open after the object is disposed; otherwise, .
- /// A that provides access to the specified section. if the section does not exist,
- /// or is has no hash metadata.
- /// The specified is outside the valid range.
- 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);
+ }
- ///
- /// Opens one of the sections in the current . For use with type NCAs.
- ///
- /// The type of section to open.
- /// to open the raw section with hash metadata.
- /// The level of integrity checks to be performed when reading the section.
- /// Always if is .
- /// to leave the storage open after the object is disposed; otherwise, .
- /// A that provides access to the specified section. if the section does not exist.
- /// The specified is outside the valid range.
- 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);
}
///
@@ -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;
- }
- }
}
diff --git a/src/LibHac/IO/NcaUtils/NcaExtensions.cs b/src/LibHac/IO/NcaUtils/NcaExtensions.cs
new file mode 100644
index 00000000..858ae1bc
--- /dev/null
+++ b/src/LibHac/IO/NcaUtils/NcaExtensions.cs
@@ -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;
+ }
+ }
+}
diff --git a/src/LibHac/IO/NcaUtils/NcaFsHeader.cs b/src/LibHac/IO/NcaUtils/NcaFsHeader.cs
new file mode 100644
index 00000000..23e7fddf
--- /dev/null
+++ b/src/LibHac/IO/NcaUtils/NcaFsHeader.cs
@@ -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;
+ }
+ }
+}
diff --git a/src/LibHac/NcaStructs.cs b/src/LibHac/IO/NcaUtils/NcaStructs.cs
similarity index 74%
rename from src/LibHac/NcaStructs.cs
rename to src/LibHac/IO/NcaUtils/NcaStructs.cs
index 07563588..c1c34095 100644
--- a/src/LibHac/NcaStructs.cs
+++ b/src/LibHac/IO/NcaUtils/NcaStructs.cs
@@ -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
- }
}
diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs
index 89151044..b949fe3f 100644
--- a/src/LibHac/SwitchFs.cs
+++ b/src/LibHac/SwitchFs.cs
@@ -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());
diff --git a/src/LibHac/Validity.cs b/src/LibHac/Validity.cs
new file mode 100644
index 00000000..424dae7c
--- /dev/null
+++ b/src/LibHac/Validity.cs
@@ -0,0 +1,10 @@
+namespace LibHac
+{
+ public enum Validity : byte
+ {
+ Unchecked,
+ Invalid,
+ Valid,
+ MissingKey
+ }
+}
diff --git a/src/hactoolnet/ProcessDelta.cs b/src/hactoolnet/ProcessDelta.cs
index 85934724..e0be4aa6 100644
--- a/src/hactoolnet/ProcessDelta.cs
+++ b/src/hactoolnet/ProcessDelta.cs
@@ -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))
{
diff --git a/src/hactoolnet/ProcessNca.cs b/src/hactoolnet/ProcessNca.cs
index 18990437..3f5e1d30 100644
--- a/src/hactoolnet/ProcessNca.cs
+++ b/src/hactoolnet/ProcessNca.cs
@@ -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);
}
}
diff --git a/src/hactoolnet/ProcessPfs.cs b/src/hactoolnet/ProcessPfs.cs
index 4dedebdd..a2ffb86b 100644
--- a/src/hactoolnet/ProcessPfs.cs
+++ b/src/hactoolnet/ProcessPfs.cs
@@ -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
diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs
index b82ad93a..85528af4 100644
--- a/src/hactoolnet/ProcessSwitchFs.cs
+++ b/src/hactoolnet/ProcessSwitchFs.cs
@@ -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);
}
diff --git a/src/hactoolnet/ProcessXci.cs b/src/hactoolnet/ProcessXci.cs
index 843381ae..cca5bbce 100644
--- a/src/hactoolnet/ProcessXci.cs
+++ b/src/hactoolnet/ProcessXci.cs
@@ -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);
}