ThiefLightmapper/KeepersCompound.LGS/Database/File.cs

185 lines
5.8 KiB
C#

using System.Text;
using KeepersCompound.LGS.Database.Chunks;
using Serilog;
namespace KeepersCompound.LGS.Database;
public class DbFile
{
public struct FHeader
{
public uint TocOffset { get; set; }
public Version Version { get; }
public string Deadbeef { get; }
public FHeader(BinaryReader reader)
{
TocOffset = reader.ReadUInt32();
Version = new Version(reader);
reader.ReadBytes(256);
Deadbeef = BitConverter.ToString(reader.ReadBytes(4));
}
public readonly void Write(BinaryWriter writer)
{
writer.Write(TocOffset);
Version.Write(writer);
writer.Write(new byte[256]);
writer.Write(Array.ConvertAll(Deadbeef.Split('-'), s => byte.Parse(s, System.Globalization.NumberStyles.HexNumber)));
}
}
public struct TableOfContents
{
public struct Entry
{
public string Name;
public uint Offset;
public uint Size;
public Entry(BinaryReader reader)
{
Name = reader.ReadNullString(12);
Offset = reader.ReadUInt32();
Size = reader.ReadUInt32();
}
public override readonly string ToString()
{
return $"Name: {Name}, Offset: {Offset}, Size: {Size}";
}
public readonly void Write(BinaryWriter writer)
{
writer.WriteNullString(Name, 12);
writer.Write(Offset);
writer.Write(Size);
}
}
public uint ItemCount { get; }
public List<Entry> Items { get; }
public TableOfContents(BinaryReader reader)
{
ItemCount = reader.ReadUInt32();
Items = new List<Entry>();
for (var i = 0; i < ItemCount; i++)
Items.Add(new Entry(reader));
Items.Sort((a, b) => a.Offset.CompareTo(b.Offset));
}
public readonly void Write(BinaryWriter writer)
{
writer.Write(ItemCount);
foreach (var entry in Items)
{
entry.Write(writer);
}
}
}
public FHeader Header { get; private set; }
public TableOfContents Toc { get; }
public Dictionary<string, IChunk> Chunks { get; set; }
public DbFile(string filename)
{
Log.Information("Loading DbFile: {Path}", filename);
if (!File.Exists(filename))
{
Log.Error("Failed to load DbFile. File does not exist.");
throw new FileNotFoundException();
}
using MemoryStream stream = new(File.ReadAllBytes(filename));
using BinaryReader reader = new(stream, Encoding.UTF8, false);
Header = new(reader);
stream.Seek(Header.TocOffset, SeekOrigin.Begin);
Toc = new(reader);
Chunks = new Dictionary<string, IChunk>();
foreach (var entry in Toc.Items)
{
var chunk = NewChunk(entry.Name);
chunk.Read(reader, entry);
Chunks.Add(entry.Name, chunk);
}
}
public void Save(string filename)
{
Log.Information("Saving DbFile: {Path}", filename);
using var stream = File.Open(filename, FileMode.Create);
using var writer = new BinaryWriter(stream, Encoding.UTF8, false);
Header.Write(writer);
for (var i = 0; i < Toc.ItemCount; i++)
{
var item = Toc.Items[i];
var pos = stream.Position;
var chunk = Chunks[item.Name];
chunk.Write(writer);
var size = stream.Position - pos - 24;
item.Offset = (uint)pos;
item.Size = (uint)size;
Toc.Items[i] = item;
}
var tocOffset = (uint)stream.Position;
Toc.Write(writer);
stream.Seek(0, SeekOrigin.Begin);
writer.Write(tocOffset);
}
public bool TryGetChunk<T>(string name, out T chunk)
{
if (Chunks.TryGetValue(name, out var rawChunk))
{
chunk = (T)rawChunk;
return true;
}
chunk = default;
return false;
}
private static IChunk NewChunk(string entryName)
{
return entryName switch
{
// "AI_ROOM_DB" => new AiRoomDb(),
// "AICONVERSE" => new AiConverseChunk(),
"GAM_FILE" => new GamFile(),
"TXLIST" => new TxList(),
"WREXT" => new WorldRep(),
"BRLIST" => new BrList(),
"LM_PARAM" => new LmParams(),
"RENDPARAMS" => new RendParams(),
"P$ModelName" => new PropertyChunk<PropLabel>(),
"P$Scale" => new PropertyChunk<PropVector>(),
"P$RenderTyp" => new PropertyChunk<PropRenderType>(),
"P$JointPos" => new PropertyChunk<PropJointPos>(),
"P$Immobile" => new PropertyChunk<PropBool>(),
"P$StatShad" => new PropertyChunk<PropBool>(),
"P$OTxtRepr0" => new PropertyChunk<PropString>(),
"P$OTxtRepr1" => new PropertyChunk<PropString>(),
"P$OTxtRepr2" => new PropertyChunk<PropString>(),
"P$OTxtRepr3" => new PropertyChunk<PropString>(),
"P$Light" => new PropertyChunk<PropLight>(),
"P$AnimLight" => new PropertyChunk<PropAnimLight>(),
"P$LightColo" => new PropertyChunk<PropLightColor>(),
"P$Spotlight" => new PropertyChunk<PropSpotlight>(),
"P$SpotAmb" => new PropertyChunk<PropSpotlightAndAmbient>(),
"P$RenderAlp" => new PropertyChunk<PropFloat>(),
"LD$MetaProp" => new LinkDataMetaProp(),
_ when entryName.StartsWith("L$") => new LinkChunk(),
_ when entryName.StartsWith("P$") => new PropertyChunk<PropGeneric>(),
_ => new GenericChunk(),
};
}
}