mirror of
https://github.com/ncblakely/GiantsTools
synced 2024-11-21 13:45:37 +01:00
158 lines
6.0 KiB
C#
158 lines
6.0 KiB
C#
namespace Giants.EffectCompiler
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Giants.BinTools.Macro;
|
|
using NLog;
|
|
|
|
/// <summary>
|
|
/// Effect binary compiler.
|
|
/// </summary>
|
|
public class FxCompiler
|
|
{
|
|
private static readonly Logger logger = LogManager.GetLogger(nameof(FxCompiler));
|
|
|
|
private FxMacroDefinitionTable macroDefinitionTable;
|
|
private int doneOpcode;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="FxCompiler"/> class.
|
|
/// </summary>
|
|
/// <param name="macroDefinitionTable">The table of macro and symbol definitions.</param>
|
|
public FxCompiler(FxMacroDefinitionTable macroDefinitionTable)
|
|
{
|
|
this.macroDefinitionTable = macroDefinitionTable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compiles a textual effect file to the specified output path.
|
|
/// </summary>
|
|
/// <param name="inputPath">The path to the effect file.</param>
|
|
/// <param name="outputPath">The path to write to.</param>
|
|
public void Compile(
|
|
string inputPath,
|
|
string outputPath)
|
|
{
|
|
if (!File.Exists(inputPath))
|
|
{
|
|
throw new InvalidOperationException($"The input file {inputPath} does not exist.");
|
|
}
|
|
|
|
// Get constants for known symbols
|
|
this.doneOpcode = Utilities.GetFxSymbolValue(this.macroDefinitionTable.SymbolTable, "FxDone");
|
|
|
|
using var streamReader = new StreamReader(inputPath);
|
|
SerializedEffectData serializedEffectData = this.SerializeEffectData(streamReader);
|
|
|
|
using var fileStream = new FileStream(outputPath, FileMode.Create);
|
|
using var binaryWriter = new BinaryWriter(fileStream);
|
|
|
|
this.WriteHeader(binaryWriter, serializedEffectData);
|
|
this.WriteTableOfContents(binaryWriter, serializedEffectData);
|
|
this.WriteEffectData(binaryWriter, serializedEffectData);
|
|
}
|
|
|
|
private void WriteEffectData(BinaryWriter binaryWriter, SerializedEffectData serializedEffectData)
|
|
{
|
|
binaryWriter.Write(serializedEffectData.Data);
|
|
}
|
|
|
|
private void WriteTableOfContents(BinaryWriter binaryWriter, SerializedEffectData serializedEffectData)
|
|
{
|
|
foreach (var entry in serializedEffectData.TableOfContents)
|
|
{
|
|
binaryWriter.Write(Encoding.UTF8.GetBytes(entry.Name));
|
|
binaryWriter.Write('\0');
|
|
binaryWriter.Write(checked(entry.Offset + serializedEffectData.TableOfContentsSize));
|
|
}
|
|
}
|
|
|
|
private void WriteHeader(BinaryWriter binaryWriter, SerializedEffectData serializedEffectData)
|
|
{
|
|
binaryWriter.Write(FxBinaryData.CurrentVersion);
|
|
binaryWriter.Write(checked(serializedEffectData.Data.Length + serializedEffectData.TableOfContentsSize));
|
|
binaryWriter.Write(serializedEffectData.TableOfContents.Count);
|
|
}
|
|
|
|
private void SerializeEffect(string[] tokens, StreamReader reader, BinaryWriter binaryWriter)
|
|
{
|
|
while (!reader.EndOfStream)
|
|
{
|
|
tokens = reader.ReadLine().Split(Utilities.SplitCharacters, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
string macroName = tokens[0];
|
|
if (macroName == "fxdone")
|
|
{
|
|
binaryWriter.Write((byte)this.doneOpcode);
|
|
break;
|
|
}
|
|
|
|
FxMacroDefinition macroDefinition = this.macroDefinitionTable.MacroDefinitions
|
|
.Values
|
|
.FirstOrDefault(x => x.Name.Equals(macroName, StringComparison.OrdinalIgnoreCase) && x.FxDefGroup.Count() == tokens[1..].Length);
|
|
|
|
binaryWriter.Write((byte)macroDefinition.Opcode);
|
|
|
|
if (macroDefinition == null)
|
|
{
|
|
throw new InvalidOperationException("Unknown macro '{macroName}'");
|
|
}
|
|
|
|
var parameters = new List<object>();
|
|
int parameterIndex = 1;
|
|
foreach (var line in macroDefinition.FxDefGroup)
|
|
{
|
|
if (line is DataDefinitionMacroLine dataDefinitionMacroLine)
|
|
{
|
|
dataDefinitionMacroLine.Serialize(tokens[parameterIndex++], this.macroDefinitionTable.SymbolTable, binaryWriter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private SerializedEffectData SerializeEffectData(StreamReader streamReader)
|
|
{
|
|
using var memoryStream = new MemoryStream();
|
|
using var binaryWriter = new BinaryWriter(memoryStream);
|
|
|
|
var tableOfContents = new List<ContentEntry>();
|
|
int tableOfContentsSize = 0; // Keep running total of the final (serialized) size of the TOC
|
|
|
|
while (!streamReader.EndOfStream)
|
|
{
|
|
string[] tokens = streamReader.ReadLine().Split(Utilities.SplitCharacters, StringSplitOptions.RemoveEmptyEntries);
|
|
if (!tokens.Any())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (tokens[0] == "fxdef")
|
|
{
|
|
var contentEntry = new ContentEntry()
|
|
{
|
|
Name = tokens[1],
|
|
Offset = checked((int)binaryWriter.BaseStream.Position)
|
|
};
|
|
|
|
logger.Info($"Compiling {tokens[1]}");
|
|
|
|
tableOfContentsSize += contentEntry.Name.Length + 1 + sizeof(int);
|
|
tableOfContents.Add(contentEntry);
|
|
|
|
this.SerializeEffect(tokens, streamReader, binaryWriter);
|
|
}
|
|
}
|
|
|
|
return new SerializedEffectData
|
|
{
|
|
Data = memoryStream.ToArray(),
|
|
TableOfContents = tableOfContents,
|
|
TableOfContentsSize = tableOfContentsSize,
|
|
};
|
|
}
|
|
}
|
|
}
|