Use SharedStreams with savefile reading. Fixes #4

This commit is contained in:
Alex Barney 2018-08-24 17:59:45 -05:00
parent 26bf0876cb
commit 80cf0b43fb
5 changed files with 89 additions and 97 deletions

View file

@ -437,31 +437,26 @@ namespace hactoolnet
var dir = ctx.Options.DebugOutDir;
Directory.CreateDirectory(dir);
File.WriteAllBytes(Path.Combine(dir, "L0_0_DuplexL1A"), save.DuplexL1A);
File.WriteAllBytes(Path.Combine(dir, "L0_1_DuplexL1B"), save.DuplexL1B);
File.WriteAllBytes(Path.Combine(dir, "L0_2_DuplexDataA"), save.DuplexDataA);
File.WriteAllBytes(Path.Combine(dir, "L0_3_DuplexDataB"), save.DuplexDataB);
File.WriteAllBytes(Path.Combine(dir, "L1_0_MasterHashA"), save.Header.MasterHashA);
File.WriteAllBytes(Path.Combine(dir, "L1_1_MasterHashB"), save.Header.MasterHashB);
File.WriteAllBytes(Path.Combine(dir, "L1_2_DuplexMasterA"), save.Header.DuplexMasterA);
File.WriteAllBytes(Path.Combine(dir, "L1_3_DuplexMasterB"), save.Header.DuplexMasterB);
save.DuplexL1A.WriteAllBytes(Path.Combine(dir, "L0_0_DuplexL1A"), ctx.Logger);
save.DuplexL1B.WriteAllBytes(Path.Combine(dir, "L0_1_DuplexL1B"), ctx.Logger);
save.DuplexDataA.WriteAllBytes(Path.Combine(dir, "L0_2_DuplexDataA"), ctx.Logger);
save.DuplexDataB.WriteAllBytes(Path.Combine(dir, "L0_3_DuplexDataB"), ctx.Logger);
save.JournalData.WriteAllBytes(Path.Combine(dir, "L0_4_JournalData"), ctx.Logger);
save.FileRemap.Position = layout.JournalDataOffset;
using (var outFile = new FileStream(Path.Combine(dir, "L0_4_JournalData"), FileMode.Create, FileAccess.Write))
{
save.FileRemap.CopyStream(outFile, layout.JournalDataSizeB + layout.SizeReservedArea);
}
save.JournalTable.WriteAllBytes(Path.Combine(dir, "L1_0_JournalTable"), ctx.Logger);
save.JournalBitmapUpdatedPhysical.WriteAllBytes(Path.Combine(dir, "L1_1_JournalBitmapUpdatedPhysical"), ctx.Logger);
save.JournalBitmapUpdatedVirtual.WriteAllBytes(Path.Combine(dir, "L1_2_JournalBitmapUpdatedVirtual"), ctx.Logger);
save.JournalBitmapUnassigned.WriteAllBytes(Path.Combine(dir, "L1_3_JournalBitmapUnassigned"), ctx.Logger);
save.JournalLayer1Hash.WriteAllBytes(Path.Combine(dir, "L1_4_Layer1Hash"), ctx.Logger);
save.JournalLayer2Hash.WriteAllBytes(Path.Combine(dir, "L1_5_Layer2Hash"), ctx.Logger);
save.JournalLayer3Hash.WriteAllBytes(Path.Combine(dir, "L1_6_Layer3Hash"), ctx.Logger);
save.JournalFat.WriteAllBytes(Path.Combine(dir, "L1_7_FAT"), ctx.Logger);
File.WriteAllBytes(Path.Combine(dir, "L1_0_JournalTable"), save.JournalTable);
File.WriteAllBytes(Path.Combine(dir, "L1_1_JournalBitmapUpdatedPhysical"), save.JournalBitmapUpdatedPhysical);
File.WriteAllBytes(Path.Combine(dir, "L1_2_JournalBitmapUpdatedVirtual"), save.JournalBitmapUpdatedVirtual);
File.WriteAllBytes(Path.Combine(dir, "L1_3_JournalBitmapUnassigned"), save.JournalBitmapUnassigned);
File.WriteAllBytes(Path.Combine(dir, "L1_4_Layer1Hash"), save.JournalLayer1Hash);
File.WriteAllBytes(Path.Combine(dir, "L1_5_Layer2Hash"), save.JournalLayer2Hash);
File.WriteAllBytes(Path.Combine(dir, "L1_6_Layer3Hash"), save.JournalLayer3Hash);
File.WriteAllBytes(Path.Combine(dir, "L1_7_FAT"), save.JournalFat);
save.JournalStream.Position = 0;
using (var outFile = new FileStream(Path.Combine(dir, "L2_0_SaveData"), FileMode.Create, FileAccess.Write))
{
save.JournalStream.CopyStream(outFile, save.JournalStream.Length);
}
save.JournalStreamSource.CreateStream().WriteAllBytes(Path.Combine(dir, "L2_0_SaveData"), ctx.Logger);
}
}
}

View file

@ -16,6 +16,11 @@ namespace libhac.Savefile
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)
@ -40,6 +45,16 @@ namespace libhac.Savefile
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++)

View file

@ -81,13 +81,9 @@ namespace libhac.Savefile
}
}
public static MappingEntry[] ReadMappingEntries(byte[] mapTable, byte[] bitmapUpdatedPhysical,
byte[] bitmapUpdatedVirtual, byte[] bitmapUnassigned, int count)
public static MappingEntry[] ReadMappingEntries(Stream mapTable, int count)
{
var physicalBits = new BitReader(bitmapUpdatedPhysical);
var virtualBits = new BitReader(bitmapUpdatedVirtual);
var unassignedBits = new BitReader(bitmapUnassigned);
var tableReader = new BinaryReader(new MemoryStream(mapTable));
var tableReader = new BinaryReader(mapTable);
var map = new MappingEntry[count];
for (int i = 0; i < count; i++)
@ -95,10 +91,7 @@ namespace libhac.Savefile
var entry = new MappingEntry
{
VirtualIndex = i,
PhysicalIndex = tableReader.ReadInt32() & 0x7FFFFFFF,
//UpdatedPhysical = physicalBits.ReadBool(),
//UpdatedVirtual = virtualBits.ReadBool(),
//Unassigned = unassignedBits.ReadBool()
PhysicalIndex = tableReader.ReadInt32() & 0x7FFFFFFF
};
map[i] = entry;

View file

@ -9,31 +9,33 @@ namespace libhac.Savefile
public class Savefile
{
public Header Header { get; }
public RemapStream FileRemap { get; }
public RemapStream MetaRemap { get; }
private Stream FileStream { get; }
public JournalStream JournalStream { get; }
private RemapStream FileRemap { get; }
public SharedStreamSource FileRemapSource { get; }
private RemapStream MetaRemap { get; }
public SharedStreamSource MetaRemapSource { get; }
private JournalStream JournalStream { get; }
public SharedStreamSource JournalStreamSource { get; }
public byte[] DuplexL1A { get; }
public byte[] DuplexL1B { get; }
public byte[] DuplexDataA { get; }
public byte[] DuplexDataB { get; }
public Stream DuplexL1A { get; }
public Stream DuplexL1B { get; }
public Stream DuplexDataA { get; }
public Stream DuplexDataB { get; }
public Stream JournalData { get; }
public byte[] JournalTable { get; }
public byte[] JournalBitmapUpdatedPhysical { get; }
public byte[] JournalBitmapUpdatedVirtual { get; }
public byte[] JournalBitmapUnassigned { get; }
public byte[] JournalLayer1Hash { get; }
public byte[] JournalLayer2Hash { get; }
public byte[] JournalLayer3Hash { get; }
public byte[] JournalFat { get; }
public Stream JournalTable { get; }
public Stream JournalBitmapUpdatedPhysical { get; }
public Stream JournalBitmapUpdatedVirtual { get; }
public Stream JournalBitmapUnassigned { get; }
public Stream JournalLayer1Hash { get; }
public Stream JournalLayer2Hash { get; }
public Stream JournalLayer3Hash { get; }
public Stream JournalFat { get; }
public FileEntry[] Files { get; private set; }
private Dictionary<string, FileEntry> FileDict { get; }
public Savefile(Stream file, IProgressReport logger = null)
{
FileStream = file;
using (var reader = new BinaryReader(file, Encoding.Default, true))
{
Header = new Header(reader, logger);
@ -42,56 +44,33 @@ namespace libhac.Savefile
new SubStream(file, layout.FileMapDataOffset, layout.FileMapDataSize),
Header.FileMapEntries, Header.FileRemap.MapSegmentCount);
DuplexL1A = new byte[layout.DuplexL1Size];
DuplexL1B = new byte[layout.DuplexL1Size];
DuplexDataA = new byte[layout.DuplexDataSize];
DuplexDataB = new byte[layout.DuplexDataSize];
FileRemapSource = new SharedStreamSource(FileRemap);
FileRemap.Position = layout.DuplexL1OffsetA;
FileRemap.Read(DuplexL1A, 0, DuplexL1A.Length);
FileRemap.Position = layout.DuplexL1OffsetB;
FileRemap.Read(DuplexL1B, 0, DuplexL1B.Length);
FileRemap.Position = layout.DuplexDataOffsetA;
FileRemap.Read(DuplexDataA, 0, DuplexDataA.Length);
FileRemap.Position = layout.DuplexDataOffsetB;
FileRemap.Read(DuplexDataB, 0, DuplexDataB.Length);
DuplexL1A = FileRemapSource.CreateStream(layout.DuplexL1OffsetA, layout.DuplexL1Size);
DuplexL1B = FileRemapSource.CreateStream(layout.DuplexL1OffsetB, layout.DuplexL1Size);
DuplexDataA = FileRemapSource.CreateStream(layout.DuplexDataOffsetA, layout.DuplexDataSize);
DuplexDataB = FileRemapSource.CreateStream(layout.DuplexDataOffsetB, layout.DuplexDataSize);
JournalData = FileRemapSource.CreateStream(layout.JournalDataOffset, layout.JournalDataSizeB + layout.SizeReservedArea);
var duplexDataOffset = layout.DuplexIndex == 0 ? layout.DuplexDataOffsetA : layout.DuplexDataOffsetB;
var duplexData = new SubStream(FileRemap, duplexDataOffset, layout.DuplexDataSize);
var duplexData = layout.DuplexIndex == 0 ? DuplexDataA : DuplexDataB;
MetaRemap = new RemapStream(duplexData, Header.MetaMapEntries, Header.MetaRemap.MapSegmentCount);
MetaRemapSource = new SharedStreamSource(MetaRemap);
JournalTable = new byte[layout.JournalTableSize];
JournalBitmapUpdatedPhysical = new byte[layout.JournalBitmapUpdatedPhysicalSize];
JournalBitmapUpdatedVirtual = new byte[layout.JournalBitmapUpdatedVirtualSize];
JournalBitmapUnassigned = new byte[layout.JournalBitmapUnassignedSize];
JournalLayer1Hash = new byte[layout.Layer1HashSize];
JournalLayer2Hash = new byte[layout.Layer2HashSize];
JournalLayer3Hash = new byte[layout.Layer3HashSize];
JournalFat = new byte[layout.Field150];
JournalTable = MetaRemapSource.CreateStream(layout.JournalTableOffset, layout.JournalTableSize);
JournalBitmapUpdatedPhysical = MetaRemapSource.CreateStream(layout.JournalBitmapUpdatedPhysicalOffset, layout.JournalBitmapUpdatedPhysicalSize);
JournalBitmapUpdatedVirtual = MetaRemapSource.CreateStream(layout.JournalBitmapUpdatedVirtualOffset, layout.JournalBitmapUpdatedVirtualSize);
JournalBitmapUnassigned = MetaRemapSource.CreateStream(layout.JournalBitmapUnassignedOffset, layout.JournalBitmapUnassignedSize);
JournalLayer1Hash = MetaRemapSource.CreateStream(layout.Layer1HashOffset, layout.Layer1HashSize);
JournalLayer2Hash = MetaRemapSource.CreateStream(layout.Layer2HashOffset, layout.Layer2HashSize);
JournalLayer3Hash = MetaRemapSource.CreateStream(layout.Layer3HashOffset, layout.Layer3HashSize);
JournalFat = MetaRemapSource.CreateStream(layout.Field148, layout.Field150);
MetaRemap.Position = layout.JournalTableOffset;
MetaRemap.Read(JournalTable, 0, JournalTable.Length);
MetaRemap.Position = layout.JournalBitmapUpdatedPhysicalOffset;
MetaRemap.Read(JournalBitmapUpdatedPhysical, 0, JournalBitmapUpdatedPhysical.Length);
MetaRemap.Position = layout.JournalBitmapUpdatedVirtualOffset;
MetaRemap.Read(JournalBitmapUpdatedVirtual, 0, JournalBitmapUpdatedVirtual.Length);
MetaRemap.Position = layout.JournalBitmapUnassignedOffset;
MetaRemap.Read(JournalBitmapUnassigned, 0, JournalBitmapUnassigned.Length);
MetaRemap.Position = layout.Layer1HashOffset;
MetaRemap.Read(JournalLayer1Hash, 0, JournalLayer1Hash.Length);
MetaRemap.Position = layout.Layer2HashOffset;
MetaRemap.Read(JournalLayer2Hash, 0, JournalLayer2Hash.Length);
MetaRemap.Position = layout.Layer3HashOffset;
MetaRemap.Read(JournalLayer3Hash, 0, JournalLayer3Hash.Length);
MetaRemap.Position = layout.Field148;
MetaRemap.Read(JournalFat, 0, JournalFat.Length);
var journalMap = JournalStream.ReadMappingEntries(JournalTable, Header.Journal.MappingEntryCount);
var journalMap = JournalStream.ReadMappingEntries(JournalTable, JournalBitmapUpdatedPhysical,
JournalBitmapUpdatedVirtual, JournalBitmapUnassigned, Header.Journal.MappingEntryCount);
var journalData = new SubStream(FileRemap, layout.JournalDataOffset,
var journalData = FileRemapSource.CreateStream(layout.JournalDataOffset,
layout.JournalDataSizeB + layout.SizeReservedArea);
JournalStream = new JournalStream(journalData, journalMap, (int)Header.Journal.BlockSize);
JournalStreamSource = new SharedStreamSource(JournalStream);
ReadFileInfo();
Dictionary<string, FileEntry> dictionary = new Dictionary<string, FileEntry>();
foreach (FileEntry entry in Files)
@ -115,7 +94,7 @@ namespace libhac.Savefile
public Stream OpenFile(FileEntry file)
{
return new SubStream(JournalStream, file.Offset, file.Size);
return JournalStreamSource.CreateStream(file.Offset, file.Size);
}
public bool FileExists(string filename) => FileDict.ContainsKey(filename);
@ -128,12 +107,12 @@ namespace libhac.Savefile
FileEntry[] dirEntries;
FileEntry[] fileEntries;
using (var reader = new BinaryReader(JournalStream, Encoding.Default, true))
using (var reader = new BinaryReader(JournalStreamSource.CreateStream(), Encoding.Default, true))
{
JournalStream.Position = dirOffset;
reader.BaseStream.Position = dirOffset;
dirEntries = ReadFileEntries(reader);
JournalStream.Position = fileOffset;
reader.BaseStream.Position = fileOffset;
fileEntries = ReadFileEntries(reader);
}
@ -161,7 +140,7 @@ namespace libhac.Savefile
private FileEntry[] ReadFileEntries(BinaryReader reader)
{
var count = reader.ReadInt32();
JournalStream.Position -= 4;
reader.BaseStream.Position -= 4;
var entries = new FileEntry[count];
for (int i = 0; i < count; i++)

View file

@ -78,6 +78,16 @@ namespace libhac
}
}
public static void WriteAllBytes(this Stream input, string filename, IProgressReport progress = null)
{
input.Position = 0;
using (var outFile = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
input.CopyStream(outFile, input.Length, progress);
}
}
public static string ReadAsciiZ(this BinaryReader reader, int maxLength = int.MaxValue)
{
var start = reader.BaseStream.Position;