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.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using LibHac.IO.NcaUtils;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace LibHac.IO
|
||||||
public string Magic;
|
public string Magic;
|
||||||
public int Version;
|
public int Version;
|
||||||
public int NumEntries;
|
public int NumEntries;
|
||||||
public int Field1C;
|
public int FieldC;
|
||||||
|
|
||||||
public BucketTreeHeader(IStorage storage)
|
public BucketTreeHeader(IStorage storage)
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ namespace LibHac.IO
|
||||||
Magic = reader.ReadAscii(4);
|
Magic = reader.ReadAscii(4);
|
||||||
Version = reader.ReadInt32();
|
Version = reader.ReadInt32();
|
||||||
NumEntries = reader.ReadInt32();
|
NumEntries = reader.ReadInt32();
|
||||||
Field1C = reader.ReadInt32();
|
FieldC = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using LibHac.IO;
|
|
||||||
using LibHac.IO.RomFs;
|
using LibHac.IO.RomFs;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac.IO.NcaUtils
|
||||||
{
|
{
|
||||||
public class Nca : IDisposable
|
public class Nca : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -89,13 +88,27 @@ namespace LibHac
|
||||||
return sect.Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName);
|
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));
|
if (index < 0 || index > 3) throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
NcaSection sect = Sections[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 (sect.Header.EncryptionType != NcaEncryptionType.None)
|
||||||
{
|
{
|
||||||
if (IsMissingTitleKey)
|
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)
|
switch (sect.Header.EncryptionType)
|
||||||
{
|
{
|
||||||
case NcaEncryptionType.None:
|
case NcaEncryptionType.None:
|
||||||
return rawStorage;
|
return baseStorage;
|
||||||
case NcaEncryptionType.XTS:
|
case NcaEncryptionType.XTS:
|
||||||
throw new NotImplementedException("NCA sections using XTS are not supported");
|
throw new NotImplementedException("NCA sections using XTS are not supported");
|
||||||
case NcaEncryptionType.AesCtr:
|
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:
|
case NcaEncryptionType.AesCtrEx:
|
||||||
BktrPatchInfo info = sect.Header.BktrInfo;
|
BktrPatchInfo info = sect.Header.BktrInfo;
|
||||||
|
|
||||||
long bktrOffset = info.RelocationHeader.Offset;
|
long bktrOffset = info.RelocationHeader.Offset;
|
||||||
long bktrSize = size - bktrOffset;
|
long bktrSize = sect.Size - bktrOffset;
|
||||||
long dataSize = info.RelocationHeader.Offset;
|
long dataSize = info.RelocationHeader.Offset;
|
||||||
|
|
||||||
IStorage bucketTreeHeader = new MemoryStorage(sect.Header.BktrInfo.EncryptionHeader.Header);
|
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 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);
|
IStorage decStorage = new Aes128CtrExStorage(baseStorage.Slice(0, dataSize), bucketTreeHeader, encryptionBucketTreeData, DecryptedKeys[2], sect.Offset, sect.Header.Ctr, true);
|
||||||
decStorage = new CachedStorage(decStorage, 0x4000, 4, leaveOpen);
|
decStorage = new CachedStorage(decStorage, 0x4000, 4, true);
|
||||||
|
|
||||||
return new ConcatenationStorage(new[] { decStorage, bucketTreeData }, leaveOpen);
|
return new ConcatenationStorage(new[] { decStorage, bucketTreeData }, true);
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public IStorage OpenRawStorage(int index)
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
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];
|
NcaSection sect = Sections[index];
|
||||||
NcaFsHeader header = sect.Header;
|
NcaFsHeader header = sect.Header;
|
||||||
|
|
||||||
|
// todo don't assume that ctr ex means it's a patch
|
||||||
if (header.EncryptionType == NcaEncryptionType.AesCtrEx)
|
if (header.EncryptionType == NcaEncryptionType.AesCtrEx)
|
||||||
{
|
{
|
||||||
if (raw && BaseNca == null) return rawStorage;
|
return rawStorage.Slice(0, header.BktrInfo.RelocationHeader.Offset);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (raw || rawStorage == null) return rawStorage;
|
|
||||||
|
|
||||||
switch (header.HashType)
|
switch (header.HashType)
|
||||||
{
|
{
|
||||||
case NcaHashType.Sha256:
|
case NcaHashType.Sha256:
|
||||||
return InitIvfcForPartitionfs(header.Sha256Info, rawStorage, integrityCheckLevel, leaveOpen);
|
return InitIvfcForPartitionfs(header.Sha256Info, rawStorage, integrityCheckLevel, true);
|
||||||
case NcaHashType.Ivfc:
|
case NcaHashType.Ivfc:
|
||||||
return new HierarchicalIntegrityVerificationStorage(header.IvfcInfo, new MemoryStorage(header.IvfcInfo.MasterHash), rawStorage,
|
return new HierarchicalIntegrityVerificationStorage(header.IvfcInfo, new MemoryStorage(header.IvfcInfo.MasterHash), rawStorage,
|
||||||
IntegrityStorageType.RomFs, integrityCheckLevel, leaveOpen);
|
IntegrityStorageType.RomFs, integrityCheckLevel, true);
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public IStorage OpenStorage(NcaSectionType type, IntegrityCheckLevel integrityCheckLevel)
|
||||||
/// 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"/>.
|
return OpenStorage(GetSectionIndexFromType(type), integrityCheckLevel);
|
||||||
/// </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;
|
|
||||||
|
|
||||||
/// <summary>
|
public IFileSystem OpenFileSystem(int index, IntegrityCheckLevel integrityCheckLevel)
|
||||||
/// Opens one of the sections in the current <see cref="Nca"/>. For use with <see cref="ContentType.Program"/> type NCAs.
|
{
|
||||||
/// </summary>
|
IStorage storage = OpenStorage(index, integrityCheckLevel);
|
||||||
/// <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>
|
switch (Sections[index].Header.Type)
|
||||||
/// <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>
|
case SectionType.Pfs0:
|
||||||
/// <param name="leaveOpen"><see langword="true"/> to leave the storage open after the <see cref="Nca"/> object is disposed; otherwise, <see langword="false"/>.</param>
|
return new PartitionFileSystem(storage);
|
||||||
/// <returns>A <see cref="Stream"/> that provides access to the specified section. <see langword="null"/> if the section does not exist.</returns>
|
case SectionType.Romfs:
|
||||||
/// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="type"/> is outside the valid range.</exception>
|
return new RomFsFileSystem(storage);
|
||||||
public IStorage OpenSection(ProgramPartitionType type, bool raw, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen) =>
|
case SectionType.Bktr:
|
||||||
OpenSection((int)type, raw, integrityCheckLevel, leaveOpen);
|
// 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,
|
private static HierarchicalIntegrityVerificationStorage InitIvfcForPartitionfs(Sha256Info sb,
|
||||||
IStorage pfsStorage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
|
IStorage pfsStorage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
|
||||||
|
@ -257,24 +265,18 @@ namespace LibHac
|
||||||
return new HierarchicalIntegrityVerificationStorage(initInfo, integrityCheckLevel, leaveOpen);
|
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));
|
if (index < 0 || index > 3) return false;
|
||||||
NcaFsHeader header = Sections[index].Header;
|
|
||||||
|
|
||||||
IStorage storage = OpenSection(index, false, integrityCheckLevel, true);
|
return Sections[index] != null;
|
||||||
|
}
|
||||||
|
|
||||||
switch (header.Type)
|
public bool SectionIsDecryptable(int index)
|
||||||
{
|
{
|
||||||
case SectionType.Pfs0:
|
if (!SectionExists(index)) return false;
|
||||||
return new PartitionFileSystem(storage);
|
|
||||||
case SectionType.Romfs:
|
return Sections[index].Header.EncryptionType == NcaEncryptionType.None || !IsMissingTitleKey && string.IsNullOrWhiteSpace(MissingKeyName);
|
||||||
return new RomFsFileSystem(storage);
|
|
||||||
case SectionType.Bktr:
|
|
||||||
return new RomFsFileSystem(storage);
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -299,11 +301,11 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
if (Header.ContentType != ContentType.Program) return;
|
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;
|
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);
|
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))
|
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);
|
return new ConcatenationStorage(list, true);
|
||||||
|
@ -425,7 +427,7 @@ namespace LibHac
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
IStorage storage = OpenSection(index, true, IntegrityCheckLevel.None, true);
|
IStorage storage = OpenRawStorage(index);
|
||||||
|
|
||||||
var hashTable = new byte[size];
|
var hashTable = new byte[size];
|
||||||
storage.Read(hashTable, offset);
|
storage.Read(hashTable, offset);
|
||||||
|
@ -496,88 +498,4 @@ namespace LibHac
|
||||||
return hash;
|
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.IO;
|
||||||
using System.Linq;
|
|
||||||
using LibHac.IO;
|
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac.IO.NcaUtils
|
||||||
{
|
{
|
||||||
public class NcaHeader
|
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 class BktrPatchInfo
|
||||||
{
|
{
|
||||||
public BktrHeader RelocationHeader;
|
public BktrHeader RelocationHeader;
|
||||||
|
@ -174,7 +106,7 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
public byte[] MasterHash;
|
public byte[] MasterHash;
|
||||||
public int BlockSize; // In bytes
|
public int BlockSize; // In bytes
|
||||||
public uint Always2;
|
public uint LevelCount;
|
||||||
public long HashTableOffset;
|
public long HashTableOffset;
|
||||||
public long HashTableSize;
|
public long HashTableSize;
|
||||||
public long DataOffset;
|
public long DataOffset;
|
||||||
|
@ -187,7 +119,7 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
MasterHash = reader.ReadBytes(0x20);
|
MasterHash = reader.ReadBytes(0x20);
|
||||||
BlockSize = reader.ReadInt32();
|
BlockSize = reader.ReadInt32();
|
||||||
Always2 = reader.ReadUInt32();
|
LevelCount = reader.ReadUInt32();
|
||||||
HashTableOffset = reader.ReadInt64();
|
HashTableOffset = reader.ReadInt64();
|
||||||
HashTableSize = reader.ReadInt64();
|
HashTableSize = reader.ReadInt64();
|
||||||
DataOffset = reader.ReadInt64();
|
DataOffset = reader.ReadInt64();
|
||||||
|
@ -261,6 +193,13 @@ namespace LibHac
|
||||||
Logo
|
Logo
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public enum NcaSectionType
|
||||||
|
{
|
||||||
|
Code,
|
||||||
|
Data,
|
||||||
|
Logo
|
||||||
|
};
|
||||||
|
|
||||||
public enum ContentType
|
public enum ContentType
|
||||||
{
|
{
|
||||||
Program,
|
Program,
|
||||||
|
@ -307,12 +246,4 @@ namespace LibHac
|
||||||
Romfs,
|
Romfs,
|
||||||
Bktr
|
Bktr
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Validity : byte
|
|
||||||
{
|
|
||||||
Unchecked,
|
|
||||||
Invalid,
|
|
||||||
Valid,
|
|
||||||
MissingKey
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using LibHac.IO;
|
using LibHac.IO;
|
||||||
using LibHac.IO.RomFs;
|
using LibHac.IO.NcaUtils;
|
||||||
using LibHac.IO.Save;
|
using LibHac.IO.Save;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac
|
||||||
|
@ -127,10 +127,10 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
var title = new Title();
|
var title = new Title();
|
||||||
|
|
||||||
// Meta contents always have 1 Partition FS section with 1 file in it
|
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||||
IStorage sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true);
|
string cnmtPath = fs.EnumerateEntries("*.cnmt").Single().FullPath;
|
||||||
var pfs0 = new PartitionFileSystem(sect);
|
|
||||||
IFile file = pfs0.OpenFile(pfs0.Files[0], OpenMode.Read);
|
IFile file = fs.OpenFile(cnmtPath, OpenMode.Read);
|
||||||
|
|
||||||
var metadata = new Cnmt(file.AsStream());
|
var metadata = new Cnmt(file.AsStream());
|
||||||
title.Id = metadata.TitleId;
|
title.Id = metadata.TitleId;
|
||||||
|
@ -168,7 +168,7 @@ namespace LibHac
|
||||||
{
|
{
|
||||||
foreach (Title title in Titles.Values.Where(x => x.ControlNca != null))
|
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);
|
IFile control = romfs.OpenFile("control.nacp", OpenMode.Read);
|
||||||
|
|
||||||
title.Control = new Nacp(control.AsStream());
|
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.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
|
||||||
using LibHac.IO;
|
using LibHac.IO;
|
||||||
|
using LibHac.IO.NcaUtils;
|
||||||
using static hactoolnet.Print;
|
using static hactoolnet.Print;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
|
@ -26,7 +26,7 @@ namespace hactoolnet
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var nca = new Nca(ctx.Keyset, deltaStorage, true);
|
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))
|
if (!fs.FileExists(FragmentFileName))
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.IO;
|
using LibHac.IO;
|
||||||
using LibHac.IO.RomFs;
|
using LibHac.IO.NcaUtils;
|
||||||
using static hactoolnet.Print;
|
using static hactoolnet.Print;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
|
@ -45,7 +45,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.ListRomFs && nca.Sections[1] != null)
|
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())
|
foreach (DirectoryEntry entry in romfs.EnumerateEntries())
|
||||||
{
|
{
|
||||||
|
@ -76,14 +76,14 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.RomfsOutDir != null)
|
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);
|
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.Options.ReadBench)
|
if (ctx.Options.ReadBench)
|
||||||
{
|
{
|
||||||
long bytesToRead = 1024L * 1024 * 1024 * 5;
|
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);
|
var dest = new NullStorage(storage.Length);
|
||||||
|
|
||||||
int iterations = (int)(bytesToRead / storage.Length) + 1;
|
int iterations = (int)(bytesToRead / storage.Length) + 1;
|
||||||
|
@ -125,7 +125,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.ExefsOutDir != null)
|
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);
|
pfs.Extract(ctx.Options.ExefsOutDir, ctx.Logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.IO;
|
using LibHac.IO;
|
||||||
|
using LibHac.IO.NcaUtils;
|
||||||
using static hactoolnet.Print;
|
using static hactoolnet.Print;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.IO;
|
using LibHac.IO;
|
||||||
using LibHac.IO.RomFs;
|
using LibHac.IO.NcaUtils;
|
||||||
using LibHac.IO.Save;
|
using LibHac.IO.Save;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
|
@ -119,7 +119,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.RomfsOutDir != null)
|
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);
|
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.IO;
|
using LibHac.IO;
|
||||||
using LibHac.IO.RomFs;
|
using LibHac.IO.NcaUtils;
|
||||||
using static hactoolnet.Print;
|
using static hactoolnet.Print;
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
|
@ -111,7 +111,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.RomfsOutDir != null)
|
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);
|
romfs.Extract(ctx.Options.RomfsOutDir, ctx.Logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue