libdgf/LibDgf/Dat/DatBuilder.cs
2021-02-16 01:04:29 -07:00

147 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace LibDgf.Dat
{
public class DatBuilder
{
public class ReplacementEntry
{
public int Index { get; set; }
public DatReader SourceDat { get; set; }
public int SourceIndex { get; set; }
public string SourceFile { get; set; }
}
class NewEntry
{
public DatEntry ArchEntry { get; set; } = new DatEntry();
public DatEntry OrigEntry { get; set; }
public int OrigIndex { get; set; }
public ReplacementEntry ReplacementEntry { get; set; }
}
DatReader sourceDat;
public List<ReplacementEntry> ReplacementEntries { get; } = new List<ReplacementEntry>();
public DatBuilder(DatReader sourceDat = null)
{
this.sourceDat = sourceDat;
}
public void Build(Stream destStream)
{
// Check there are no duplicated indexes
HashSet<int> indexSet = new HashSet<int>();
foreach (var entry in ReplacementEntries)
{
if (entry.Index < 0) throw new InvalidOperationException("Entry with negative index present.");
indexSet.Add(entry.Index);
}
if (indexSet.Count != ReplacementEntries.Count)
{
throw new InvalidOperationException("Replacement entries with non-unique IDs present.");
}
ReplacementEntries.Sort((x, y) => x.Index.CompareTo(y.Index));
List<NewEntry> newEntries = new List<NewEntry>();
// Copy over original entries
if (sourceDat != null)
{
for (int i = 0; i < sourceDat.EntriesCount; ++i)
{
var e = sourceDat.GetEntry(i);
newEntries.Add(new NewEntry { OrigEntry = e, OrigIndex = i });
}
}
// Set replacement entries
foreach (var rep in ReplacementEntries)
{
if (rep.Index > newEntries.Count)
throw new InvalidOperationException("Replacement entries results in incontinuity.");
var newEntry = new NewEntry { ReplacementEntry = rep };
if (rep.Index == newEntries.Count)
newEntries.Add(newEntry);
else
newEntries[rep.Index] = newEntry;
}
// Update size and position
uint dataOffset = 0;
for (int i = 0; i < newEntries.Count; ++i)
{
var newEntry = newEntries[i];
newEntry.ArchEntry.Offset = dataOffset;
var repEntry = newEntry.ReplacementEntry;
if (repEntry == null)
{
newEntry.ArchEntry.Length = newEntry.OrigEntry.Length;
}
else
{
if (repEntry.SourceDat != null)
{
if (repEntry.SourceFile != null)
throw new InvalidOperationException("Replacement entries with both DAT and file source specified exist.");
newEntry.ArchEntry.Length = repEntry.SourceDat.GetEntry(repEntry.SourceIndex).Length;
}
else if (repEntry.SourceFile != null)
{
newEntry.ArchEntry.Length = (uint)new FileInfo(repEntry.SourceFile).Length;
}
else
{
newEntry.ArchEntry.Length = 0;
newEntries.RemoveAt(i);
--i;
}
}
dataOffset += newEntry.ArchEntry.Length;
}
// Write file
BinaryWriter bw = new BinaryWriter(destStream);
bw.Write("DAT\0".ToCharArray());
bw.Write(newEntries.Count);
dataOffset = (uint)(((newEntries.Count + 1) * 8 + 15) & ~15);
foreach (var newEntry in newEntries)
{
newEntry.ArchEntry.Offset += dataOffset;
newEntry.ArchEntry.Write(bw);
}
// Do we need to 16-byte align anything after the header?
if (destStream.Position < dataOffset)
bw.Write(new byte[dataOffset - destStream.Position]);
foreach (var newEntry in newEntries)
{
var repEntry = newEntry.ReplacementEntry;
if (repEntry == null)
{
bw.Write(sourceDat.GetData(newEntry.OrigIndex));
}
else
{
if (repEntry.SourceDat != null)
{
bw.Write(repEntry.SourceDat.GetData(repEntry.SourceIndex));
}
else
{
using (FileStream fs = File.OpenRead(repEntry.SourceFile))
{
fs.CopyTo(destStream);
}
}
}
}
}
}
}