LibHac/libhac/Savefile/Header.cs
2018-07-21 22:05:57 -05:00

194 lines
7.6 KiB
C#

using System.IO;
using System.Security.Cryptography;
namespace libhac.Savefile
{
public class Header
{
public byte[] Cmac { get; set; }
public FsLayout Layout { get; set; }
public RemapHeader FileRemap { get; set; }
public RemapHeader MetaRemap { get; set; }
public MapEntry[] FileMapEntries { get; set; }
public MapEntry[] MetaMapEntries { get; set; }
public byte[] Data { get; }
public Header(BinaryReader reader, IProgressReport logger = null)
{
reader.BaseStream.Position = 0;
Data = reader.ReadBytes(0x4000);
reader.BaseStream.Position = 0;
Cmac = reader.ReadBytes(0x10);
reader.BaseStream.Position = 0x100;
Layout = new FsLayout(reader);
reader.BaseStream.Position = 0x650;
FileRemap = new RemapHeader(reader);
reader.BaseStream.Position = 0x690;
MetaRemap = new RemapHeader(reader);
reader.BaseStream.Position = Layout.FileMapEntryOffset;
FileMapEntries = new MapEntry[FileRemap.MapEntryCount];
for (int i = 0; i < FileRemap.MapEntryCount; i++)
{
FileMapEntries[i] = new MapEntry(reader);
}
reader.BaseStream.Position = Layout.MetaMapEntryOffset;
MetaMapEntries = new MapEntry[MetaRemap.MapEntryCount];
for (int i = 0; i < MetaRemap.MapEntryCount; i++)
{
MetaMapEntries[i] = new MapEntry(reader);
}
var hashStatus = ValidateHeaderHash() ? "valid" : "invalid";
logger?.LogMessage($"Header hash is {hashStatus}");
}
private bool ValidateHeaderHash()
{
using (SHA256 sha256 = SHA256.Create())
{
var hash = sha256.ComputeHash(Data, 0x300, 0x3d00);
return Util.ArraysEqual(hash, Layout.Hash);
}
}
}
public class FsLayout
{
public string Magic { get; set; }
public uint MagicNum { get; set; }
public byte[] Hash { get; set; }
public long FileMapEntryOffset { get; set; }
public long FileMapEntrySize { get; set; }
public long MetaMapEntryOffset { get; set; }
public long MetaMapEntrySize { get; set; }
public long FileMapDataOffset { get; set; }
public long FileMapDataSize { get; set; }
public long DuplexL1OffsetA { get; set; }
public long DuplexL1OffsetB { get; set; }
public long DuplexL1Size { get; set; }
public long DuplexDataOffsetA { get; set; }
public long DuplexDataOffsetB { get; set; }
public long DuplexDataSize { get; set; }
public long JournalDataOffset { get; set; }
public long JournalDataSizeA { get; set; }
public long JournalDataSizeB { get; set; }
public long SizeReservedArea { get; set; }
public long L1BitmapOffset0 { get; set; }
public long L1BitmapOffset1 { get; set; }
public long L1BitmapSize { get; set; }
public long MasterHashOffset0 { get; set; }
public long MasterHashOffset1 { get; set; }
public long MasterHashSize { get; set; }
public long OffsetJournalTable { get; set; }
public long SizeJournalTable { get; set; }
public long JournalBitmapUpdatedPhysicalOffset { get; set; }
public long JournalBitmapUpdatedPhysicalSize { get; set; }
public long JournalBitmapUpdatedVirtualOffset { get; set; }
public long JournalBitmapUpdatedVirtualSize { get; set; }
public long JournalBitmapUnassignedOffset { get; set; }
public long JournalBitmapUnassignedSize { get; set; }
public long Layer1HashOffset { get; set; }
public long Layer1HashSize { get; set; }
public long Layer2HashOffset { get; set; }
public long Layer2HashSize { get; set; }
public long Layer3HashOffset { get; set; }
public long Layer3HashSize { get; set; }
public long Field148 { get; set; }
public long Field150 { get; set; }
public long Field158 { get; set; }
public FsLayout(BinaryReader reader)
{
Magic = reader.ReadAscii(4);
MagicNum = reader.ReadUInt32();
Hash = reader.ReadBytes(0x20);
FileMapEntryOffset = reader.ReadInt64();
FileMapEntrySize = reader.ReadInt64();
MetaMapEntryOffset = reader.ReadInt64();
MetaMapEntrySize = reader.ReadInt64();
FileMapDataOffset = reader.ReadInt64();
FileMapDataSize = reader.ReadInt64();
DuplexL1OffsetA = reader.ReadInt64();
DuplexL1OffsetB = reader.ReadInt64();
DuplexL1Size = reader.ReadInt64();
DuplexDataOffsetA = reader.ReadInt64();
DuplexDataOffsetB = reader.ReadInt64();
DuplexDataSize = reader.ReadInt64();
JournalDataOffset = reader.ReadInt64();
JournalDataSizeA = reader.ReadInt64();
JournalDataSizeB = reader.ReadInt64();
SizeReservedArea = reader.ReadInt64();
L1BitmapOffset0 = reader.ReadInt64();
L1BitmapOffset1 = reader.ReadInt64();
L1BitmapSize = reader.ReadInt64();
MasterHashOffset0 = reader.ReadInt64();
MasterHashOffset1 = reader.ReadInt64();
MasterHashSize = reader.ReadInt64();
OffsetJournalTable = reader.ReadInt64();
SizeJournalTable = reader.ReadInt64();
JournalBitmapUpdatedPhysicalOffset = reader.ReadInt64();
JournalBitmapUpdatedPhysicalSize = reader.ReadInt64();
JournalBitmapUpdatedVirtualOffset = reader.ReadInt64();
JournalBitmapUpdatedVirtualSize = reader.ReadInt64();
JournalBitmapUnassignedOffset = reader.ReadInt64();
JournalBitmapUnassignedSize = reader.ReadInt64();
Layer1HashOffset = reader.ReadInt64();
Layer1HashSize = reader.ReadInt64();
Layer2HashOffset = reader.ReadInt64();
Layer2HashSize = reader.ReadInt64();
Layer3HashOffset = reader.ReadInt64();
Layer3HashSize = reader.ReadInt64();
Field148 = reader.ReadInt64();
Field150 = reader.ReadInt64();
Field158 = reader.ReadInt64();
}
}
public class RemapHeader
{
public string Magic { get; set; }
public uint MagicNum { get; set; }
public int MapEntryCount { get; set; }
public int MapSegmentCount { get; set; }
public int Field10 { get; set; }
public RemapHeader(BinaryReader reader)
{
Magic = reader.ReadAscii(4);
MagicNum = reader.ReadUInt32();
MapEntryCount = reader.ReadInt32();
MapSegmentCount = reader.ReadInt32();
Field10 = reader.ReadInt32();
}
}
public class MapEntry
{
public long VirtualOffset { get; }
public long PhysicalOffset { get; }
public long Size { get; }
public int Alignment { get; }
public int StorageType { get; }
public long VirtualOffsetEnd => VirtualOffset + Size;
public long PhysicalOffsetEnd => PhysicalOffset + Size;
internal RemapSegment Segment { get; set; }
internal MapEntry Next { get; set; }
public MapEntry(BinaryReader reader)
{
VirtualOffset = reader.ReadInt64();
PhysicalOffset = reader.ReadInt64();
Size = reader.ReadInt64();
Alignment = reader.ReadInt32();
StorageType = reader.ReadInt32();
}
}
}