LibHac/libhac/BktrCryptoStream.cs

111 lines
3.8 KiB
C#
Raw Normal View History

2018-08-25 23:46:08 +02:00
using System;
using System.Collections.Generic;
2018-07-07 22:45:06 +02:00
using System.IO;
using System.Linq;
2018-08-30 23:18:27 +02:00
using libhac.Streams;
2018-07-07 22:45:06 +02:00
namespace libhac
{
public class BktrCryptoStream : Aes128CtrStream
2018-07-07 22:45:06 +02:00
{
public SubsectionBlock SubsectionBlock { get; }
private List<SubsectionEntry> SubsectionEntries { get; } = new List<SubsectionEntry>();
private List<long> SubsectionOffsets { get; }
private SubsectionEntry CurrentEntry { get; set; }
public BktrCryptoStream(Stream baseStream, byte[] key, long offset, long length, long counterOffset, byte[] ctrHi, BktrSuperblock bktr)
2018-07-07 22:45:06 +02:00
: base(baseStream, key, offset, length, counterOffset, ctrHi)
{
BktrHeader header = bktr.SubsectionHeader;
2018-07-07 22:45:06 +02:00
byte[] subsectionBytes;
using (var streamDec = new RandomAccessSectorStream(new Aes128CtrStream(baseStream, key, offset, length, counterOffset, ctrHi)))
2018-07-07 22:45:06 +02:00
{
streamDec.Position = header.Offset;
subsectionBytes = new byte[header.Size];
streamDec.Read(subsectionBytes, 0, subsectionBytes.Length);
}
using (var reader = new BinaryReader(new MemoryStream(subsectionBytes)))
{
SubsectionBlock = new SubsectionBlock(reader);
}
foreach (var bucket in SubsectionBlock.Buckets)
{
SubsectionEntries.AddRange(bucket.Entries);
}
// Add a subsection for the BKTR headers to make things easier
var headerSubsection = new SubsectionEntry
{
Offset = bktr.RelocationHeader.Offset,
Counter = (uint)(ctrHi[4] << 24 | ctrHi[5] << 16 | ctrHi[6] << 8 | ctrHi[7]),
OffsetEnd = long.MaxValue
};
SubsectionEntries.Add(headerSubsection);
for (int i = 0; i < SubsectionEntries.Count - 1; i++)
{
SubsectionEntries[i].Next = SubsectionEntries[i + 1];
SubsectionEntries[i].OffsetEnd = SubsectionEntries[i + 1].Offset;
}
SubsectionOffsets = SubsectionEntries.Select(x => x.Offset).ToList();
CurrentEntry = GetSubsectionEntry(0);
UpdateCounterSubsection(CurrentEntry.Counter);
2018-07-07 22:45:06 +02:00
baseStream.Position = offset;
}
public override long Position
{
get => base.Position;
set
{
base.Position = value;
CurrentEntry = GetSubsectionEntry(value);
UpdateCounterSubsection(CurrentEntry.Counter);
2018-07-07 22:45:06 +02:00
}
}
public override int Read(byte[] buffer, int offset, int count)
{
2018-08-25 23:46:08 +02:00
int totalBytesRead = 0;
var outPos = offset;
while (count > 0)
2018-07-07 22:45:06 +02:00
{
2018-08-25 23:46:08 +02:00
int bytesToRead = (int)Math.Min(CurrentEntry.OffsetEnd - Position, count);
int bytesRead = base.Read(buffer, outPos, bytesToRead);
outPos += bytesRead;
totalBytesRead += bytesRead;
count -= bytesRead;
if (Position >= CurrentEntry.OffsetEnd)
{
CurrentEntry = CurrentEntry.Next;
UpdateCounterSubsection(CurrentEntry.Counter);
}
2018-07-07 22:45:06 +02:00
}
2018-08-25 23:46:08 +02:00
return totalBytesRead;
2018-07-07 22:45:06 +02:00
}
private SubsectionEntry GetSubsectionEntry(long offset)
{
var index = SubsectionOffsets.BinarySearch(offset);
if (index < 0) index = ~index - 1;
return SubsectionEntries[index];
}
private void UpdateCounterSubsection(uint value)
{
Counter[7] = (byte)value;
Counter[6] = (byte)(value >> 8);
Counter[5] = (byte)(value >> 16);
Counter[4] = (byte)(value >> 24);
}
2018-07-07 22:45:06 +02:00
}
}