LibHac/libhac/Nca.cs

152 lines
4.7 KiB
C#
Raw Normal View History

2018-06-21 23:03:58 +02:00
using System;
2018-06-22 21:05:29 +02:00
using System.Collections.Generic;
2018-06-21 23:03:58 +02:00
using System.IO;
2018-06-21 16:25:20 +02:00
using libhac.XTSSharp;
namespace libhac
{
2018-06-26 00:26:47 +02:00
public class Nca : IDisposable
2018-06-21 16:25:20 +02:00
{
2018-06-21 23:03:58 +02:00
public NcaHeader Header { get; private set; }
2018-06-27 02:10:21 +02:00
public string NcaId { get; set; }
public string Filename { get; set; }
2018-06-21 23:03:58 +02:00
public bool HasRightsId { get; private set; }
public int CryptoType { get; private set; }
public byte[][] DecryptedKeys { get; } = Util.CreateJaggedArray<byte[][]>(4, 0x10);
2018-06-22 21:05:29 +02:00
public Stream Stream { get; private set; }
2018-06-26 00:26:47 +02:00
private bool KeepOpen { get; }
2018-06-22 21:05:29 +02:00
public List<NcaSection> Sections = new List<NcaSection>();
2018-06-21 16:25:20 +02:00
2018-06-26 00:26:47 +02:00
public Nca(Keyset keyset, Stream stream, bool keepOpen)
2018-06-21 16:25:20 +02:00
{
2018-06-26 00:26:47 +02:00
stream.Position = 0;
KeepOpen = keepOpen;
2018-06-22 21:05:29 +02:00
Stream = stream;
2018-06-26 00:26:47 +02:00
DecryptHeader(keyset, stream);
2018-06-21 23:03:58 +02:00
CryptoType = Math.Max(Header.CryptoType, Header.CryptoType2);
if (CryptoType > 0) CryptoType--;
HasRightsId = !Header.RightsId.IsEmpty();
if (!HasRightsId)
{
DecryptKeyArea(keyset);
}
2018-06-22 21:05:29 +02:00
for (int i = 0; i < 4; i++)
{
2018-06-26 00:26:47 +02:00
var section = ParseSection(i);
2018-06-22 21:05:29 +02:00
if (section != null) Sections.Add(section);
}
}
2018-06-26 00:26:47 +02:00
public Stream OpenSection(int index)
2018-06-22 21:05:29 +02:00
{
if (index >= Sections.Count) throw new ArgumentOutOfRangeException(nameof(index));
var sect = Sections[index];
2018-06-22 23:17:20 +02:00
long offset = sect.Offset;
long size = sect.Size;
switch (sect.Header.FsType)
{
case SectionFsType.Pfs0:
2018-06-26 00:26:47 +02:00
offset = sect.Offset + sect.Pfs0.Pfs0Offset;
size = sect.Pfs0.Pfs0Size;
2018-06-22 23:17:20 +02:00
break;
case SectionFsType.Romfs:
2018-06-28 03:25:25 +02:00
offset = sect.Offset + (long)sect.Header.Romfs.IvfcHeader.LevelHeaders[Romfs.IvfcMaxLevel - 1].LogicalOffset;
size = (long)sect.Header.Romfs.IvfcHeader.LevelHeaders[Romfs.IvfcMaxLevel - 1].HashDataSize;
2018-06-22 23:17:20 +02:00
break;
default:
throw new ArgumentOutOfRangeException();
}
Stream.Position = offset;
2018-06-22 21:05:29 +02:00
switch (sect.Header.CryptType)
{
case SectionCryptType.None:
break;
case SectionCryptType.XTS:
break;
case SectionCryptType.CTR:
2018-06-26 00:26:47 +02:00
return new RandomAccessSectorStream(new AesCtrStream(Stream, DecryptedKeys[2], offset, size, offset), false);
2018-06-22 21:05:29 +02:00
case SectionCryptType.BKTR:
break;
default:
throw new ArgumentOutOfRangeException();
}
2018-06-26 00:26:47 +02:00
return Stream;
2018-06-21 16:25:20 +02:00
}
2018-06-26 00:26:47 +02:00
private void DecryptHeader(Keyset keyset, Stream stream)
2018-06-21 16:25:20 +02:00
{
2018-06-26 00:26:47 +02:00
byte[] headerBytes = new byte[0xC00];
2018-06-21 16:25:20 +02:00
var xts = XtsAes128.Create(keyset.header_key);
2018-06-26 00:26:47 +02:00
using (var headerDec = new RandomAccessSectorStream(new XtsSectorStream(stream, xts, 0x200)))
{
headerDec.Read(headerBytes, 0, headerBytes.Length);
}
2018-06-21 23:03:58 +02:00
2018-06-26 00:26:47 +02:00
var reader = new BinaryReader(new MemoryStream(headerBytes));
2018-06-21 23:03:58 +02:00
2018-06-26 00:26:47 +02:00
Header = NcaHeader.Read(reader);
2018-06-21 23:03:58 +02:00
}
private void DecryptKeyArea(Keyset keyset)
{
for (int i = 0; i < 4; i++)
{
Crypto.DecryptEcb(keyset.key_area_keys[CryptoType][Header.KaekInd], Header.EncryptedKeys[i],
DecryptedKeys[i], 0x10);
}
}
2018-06-26 00:26:47 +02:00
private NcaSection ParseSection(int index)
2018-06-21 23:03:58 +02:00
{
2018-06-22 21:05:29 +02:00
var entry = Header.SectionEntries[index];
var header = Header.FsHeaders[index];
if (entry.MediaStartOffset == 0) return null;
2018-06-21 23:03:58 +02:00
2018-06-22 21:05:29 +02:00
var sect = new NcaSection();
2018-06-21 23:03:58 +02:00
2018-06-22 21:05:29 +02:00
sect.SectionNum = index;
sect.Offset = Util.MediaToReal(entry.MediaStartOffset);
sect.Size = Util.MediaToReal(entry.MediaEndOffset) - sect.Offset;
sect.Header = header;
sect.Type = header.Type;
2018-06-21 23:03:58 +02:00
2018-06-22 21:05:29 +02:00
if (sect.Type == SectionType.Pfs0)
2018-06-21 23:03:58 +02:00
{
2018-06-26 00:26:47 +02:00
sect.Pfs0 = header.Pfs0;
2018-06-21 23:03:58 +02:00
}
2018-06-22 21:05:29 +02:00
return sect;
2018-06-21 23:03:58 +02:00
}
2018-06-26 00:26:47 +02:00
public void Dispose()
{
if (!KeepOpen)
{
Stream?.Dispose();
}
}
2018-06-21 23:03:58 +02:00
}
2018-06-22 21:05:29 +02:00
public class NcaSection
2018-06-21 23:03:58 +02:00
{
2018-06-22 23:17:20 +02:00
public Stream Stream { get; set; }
2018-06-22 21:05:29 +02:00
public NcaFsHeader Header { get; set; }
public SectionType Type { get; set; }
public int SectionNum { get; set; }
public long Offset { get; set; }
public long Size { get; set; }
2018-06-26 00:26:47 +02:00
public Pfs0Superblock Pfs0 { get; set; }
2018-06-21 23:03:58 +02:00
}
2018-06-21 16:25:20 +02:00
}