147 lines
5.2 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|