mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
287 lines
11 KiB
C#
287 lines
11 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 JournalHeader Journal { get; set; }
|
|
public SaveHeader Save { get; set; }
|
|
|
|
public RemapHeader FileRemap { get; set; }
|
|
public RemapHeader MetaRemap { get; set; }
|
|
|
|
public MapEntry[] FileMapEntries { get; set; }
|
|
public MapEntry[] MetaMapEntries { get; set; }
|
|
|
|
public byte[] MasterHashA { get; }
|
|
public byte[] MasterHashB { get; }
|
|
public byte[] DuplexMasterA { get; }
|
|
public byte[] DuplexMasterB { get; }
|
|
|
|
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 = 0x408;
|
|
Journal = new JournalHeader(reader);
|
|
|
|
reader.BaseStream.Position = 0x608;
|
|
Save = new SaveHeader(reader);
|
|
|
|
reader.BaseStream.Position = 0x650;
|
|
FileRemap = new RemapHeader(reader);
|
|
reader.BaseStream.Position = 0x690;
|
|
MetaRemap = new RemapHeader(reader);
|
|
|
|
reader.BaseStream.Position = Layout.MasterHashOffset0;
|
|
MasterHashA = reader.ReadBytes((int) Layout.MasterHashSize);
|
|
reader.BaseStream.Position = Layout.MasterHashOffset1;
|
|
MasterHashB = reader.ReadBytes((int) Layout.MasterHashSize);
|
|
|
|
reader.BaseStream.Position = Layout.L1BitmapOffset0;
|
|
DuplexMasterA = reader.ReadBytes((int) Layout.L1BitmapSize);
|
|
reader.BaseStream.Position = Layout.L1BitmapOffset1;
|
|
DuplexMasterB = reader.ReadBytes((int) Layout.L1BitmapSize);
|
|
|
|
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 JournalTableOffset { get; set; }
|
|
public long JournalTableSize { 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 DuplexIndex { 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();
|
|
JournalTableOffset = reader.ReadInt64();
|
|
JournalTableSize = 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();
|
|
DuplexIndex = 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 JournalHeader
|
|
{
|
|
public string Magic { get; }
|
|
public uint MagicNum { get; }
|
|
public long Field8 { get; }
|
|
public long Field10 { get; }
|
|
public long BlockSize { get; }
|
|
public int Field20 { get; }
|
|
public int MappingEntryCount { get; }
|
|
public int Field28 { get; }
|
|
public int Field2C { get; }
|
|
|
|
public JournalHeader(BinaryReader reader)
|
|
{
|
|
Magic = reader.ReadAscii(4);
|
|
MagicNum = reader.ReadUInt32();
|
|
Field8 = reader.ReadInt64();
|
|
Field10 = reader.ReadInt64();
|
|
BlockSize = reader.ReadInt64();
|
|
Field20 = reader.ReadInt32();
|
|
MappingEntryCount = reader.ReadInt32();
|
|
Field28 = reader.ReadInt32();
|
|
Field2C = reader.ReadInt32();
|
|
}
|
|
}
|
|
|
|
public class SaveHeader
|
|
{
|
|
public string Magic { get; }
|
|
public uint MagicNum { get; }
|
|
public int Field8 { get; }
|
|
public int FieldC { get; }
|
|
public int Field10 { get; }
|
|
public int Field14 { get; }
|
|
public long BlockSize { get; }
|
|
public StorageInfo AllocationTableInfo { get; }
|
|
public StorageInfo DataInfo { get; }
|
|
public int DirectoryTableBlock { get; }
|
|
public int FileTableBlock { get; }
|
|
|
|
public SaveHeader(BinaryReader reader)
|
|
{
|
|
Magic = reader.ReadAscii(4);
|
|
MagicNum = reader.ReadUInt32();
|
|
Field8 = reader.ReadInt32();
|
|
FieldC = reader.ReadInt32();
|
|
Field10 = reader.ReadInt32();
|
|
Field14 = reader.ReadInt32();
|
|
BlockSize = reader.ReadInt64();
|
|
AllocationTableInfo = new StorageInfo(reader);
|
|
DataInfo = new StorageInfo(reader);
|
|
DirectoryTableBlock = reader.ReadInt32();
|
|
FileTableBlock = reader.ReadInt32();
|
|
}
|
|
}
|
|
|
|
public class StorageInfo
|
|
{
|
|
public long Offset { get; }
|
|
public int Size { get; }
|
|
public int FieldC { get; }
|
|
|
|
public StorageInfo(BinaryReader reader)
|
|
{
|
|
Offset = reader.ReadInt64();
|
|
Size = reader.ReadInt32();
|
|
FieldC = 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();
|
|
}
|
|
}
|
|
}
|