mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-20 08:41:21 +00:00
345 lines
12 KiB
C#
Executable file
345 lines
12 KiB
C#
Executable file
using FSO.Files.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace FSO.Files.Formats.IFF.Chunks
|
|
{
|
|
public class TREE : IffChunk
|
|
{
|
|
public static TREE GenerateEmpty(BHAV bhav)
|
|
{
|
|
var result = new TREE();
|
|
result.ChunkLabel = "";
|
|
result.ChunkID = bhav.ChunkID;
|
|
result.AddedByPatch = true;
|
|
result.ChunkProcessed = true;
|
|
result.RuntimeInfo = ChunkRuntimeState.Modified;
|
|
result.ChunkType = "TREE";
|
|
|
|
result.CorrectConnections(bhav);
|
|
return result;
|
|
/*
|
|
var additionID = bhav.Instructions.Length;
|
|
|
|
Func<byte, short> resolveTrueFalse = (byte pointer) =>
|
|
{
|
|
switch (pointer)
|
|
{
|
|
case 253:
|
|
return -1;
|
|
case 255:
|
|
//generate false
|
|
case 254:
|
|
//generate true
|
|
}
|
|
if (pointer == 255) return -1;
|
|
else if (pointer == 2)
|
|
};
|
|
|
|
//make an entry for each instruction. positions and sizes don't matter - we have a runtime flag to indicate they are not valid
|
|
for (int i=0; i<bhav.Instructions.Length; i++)
|
|
{
|
|
var inst = bhav.Instructions[i];
|
|
var box = new TREEBox(result);
|
|
box.InternalID = i;
|
|
box.PosisionInvalid = true;
|
|
box.Type = TREEBoxType.Primitive;
|
|
box.TruePointer =
|
|
}
|
|
*/
|
|
}
|
|
|
|
public List<TREEBox> Entries = new List<TREEBox>();
|
|
public int PrimitiveCount => Entries.FindLastIndex(x => x.Type == TREEBoxType.Primitive) + 1;
|
|
|
|
//runtime
|
|
public uint TreeVersion = 0;
|
|
|
|
public override void Read(IffFile iff, Stream stream)
|
|
{
|
|
using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
|
{
|
|
var zero = io.ReadInt32();
|
|
var version = io.ReadInt32();
|
|
if (version > 1) throw new Exception("Unexpected TREE version: " + version);
|
|
string magic = io.ReadCString(4); //HBGN
|
|
if (magic != "EERT") throw new Exception("Magic number should be 'EERT', got " + magic);
|
|
var entryCount = io.ReadInt32();
|
|
Entries.Clear();
|
|
for (int i=0; i<entryCount; i++)
|
|
{
|
|
var box = new TREEBox(this);
|
|
box.Read(io, version);
|
|
box.InternalID = (short)i;
|
|
Entries.Add(box);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override bool Write(IffFile iff, Stream stream)
|
|
{
|
|
using (var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
|
{
|
|
io.WriteInt32(0);
|
|
io.WriteInt32(1);
|
|
io.WriteCString("EERT", 4);
|
|
io.WriteInt32(Entries.Count);
|
|
foreach (var entry in Entries)
|
|
{
|
|
entry.Write(io);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void ApplyPointerDelta(int delta, int after)
|
|
{
|
|
foreach (var box in Entries)
|
|
{
|
|
if (box.InternalID >= after) box.InternalID += (short)delta;
|
|
if (box.TruePointer >= after) box.TruePointer += (short)delta;
|
|
if (box.FalsePointer >= after) box.FalsePointer += (short)delta;
|
|
}
|
|
}
|
|
|
|
public void CorrectConnections(BHAV bhav)
|
|
{
|
|
//make sure there are enough primitives for the bhav
|
|
var realPrimCount = bhav.Instructions.Length;
|
|
var treePrimCount = Entries.FindLastIndex(x => x.Type == TREEBoxType.Primitive) + 1;
|
|
|
|
ApplyPointerDelta(realPrimCount-treePrimCount, treePrimCount);
|
|
if (realPrimCount > treePrimCount)
|
|
{
|
|
//add new treeboxes
|
|
for (int i=treePrimCount; i<realPrimCount; i++)
|
|
{
|
|
var box = new TREEBox(this);
|
|
box.InternalID = (short)i;
|
|
box.PosisionInvalid = true;
|
|
box.Type = TREEBoxType.Primitive;
|
|
Entries.Insert(i, box);
|
|
}
|
|
}
|
|
else if (treePrimCount > realPrimCount)
|
|
{
|
|
//remove treeboxes
|
|
for (int i=treePrimCount; i>realPrimCount; i--)
|
|
{
|
|
Entries.RemoveAt(i-1);
|
|
}
|
|
}
|
|
|
|
//make sure connections for each of the primitives match the BHAV
|
|
//if they don't, reconnect them or generate new boxes (true/false endpoints, maybe gotos in future)
|
|
|
|
for (int i=0; i<realPrimCount; i++)
|
|
{
|
|
var prim = bhav.Instructions[i];
|
|
var box = Entries[i];
|
|
|
|
if (prim.TruePointer != GetTrueID(box.TruePointer))
|
|
{
|
|
box.TruePointer = GetCorrectBox(prim.TruePointer);
|
|
}
|
|
if (prim.FalsePointer != GetTrueID((short)box.FalsePointer))
|
|
{
|
|
box.FalsePointer = GetCorrectBox(prim.FalsePointer);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DeleteBox(TREEBox box)
|
|
{
|
|
//remove box. apply delta
|
|
var id = box.InternalID;
|
|
foreach (var box2 in Entries)
|
|
{
|
|
if (box2.TruePointer == id) box2.TruePointer = -1;
|
|
if (box2.FalsePointer == id) box2.FalsePointer = -1;
|
|
}
|
|
Entries.RemoveAt(id);
|
|
ApplyPointerDelta(-1, id);
|
|
}
|
|
|
|
public void InsertPrimitiveBox(TREEBox box)
|
|
{
|
|
var primEnd = PrimitiveCount;
|
|
ApplyPointerDelta(1, primEnd);
|
|
|
|
box.InternalID = (short)primEnd;
|
|
Entries.Insert(primEnd, box);
|
|
}
|
|
|
|
public void InsertRemovedBox(TREEBox box)
|
|
{
|
|
var oldIndex = box.InternalID;
|
|
ApplyPointerDelta(1, oldIndex);
|
|
Entries.Insert(oldIndex, box);
|
|
}
|
|
|
|
public TREEBox MakeNewPrimitiveBox(TREEBoxType type)
|
|
{
|
|
var primEnd = PrimitiveCount;
|
|
ApplyPointerDelta(1, primEnd);
|
|
//find end of primitives and add box there. apply delta
|
|
var box = new TREEBox(this);
|
|
box.InternalID = (short)primEnd;
|
|
box.PosisionInvalid = true;
|
|
box.Type = type;
|
|
Entries.Insert(primEnd, box);
|
|
return box;
|
|
}
|
|
|
|
public TREEBox MakeNewSpecialBox(TREEBoxType type)
|
|
{
|
|
//add box at end. no delta needs to be applied.
|
|
var box = new TREEBox(this);
|
|
box.InternalID = (short)Entries.Count;
|
|
box.PosisionInvalid = true;
|
|
box.Type = type;
|
|
Entries.Add(box);
|
|
return box;
|
|
}
|
|
|
|
private short GetCorrectBox(byte realID)
|
|
{
|
|
switch (realID)
|
|
{
|
|
case 255:
|
|
//create false box
|
|
var f = MakeNewSpecialBox(TREEBoxType.False);
|
|
return f.InternalID;
|
|
case 254:
|
|
//create true box
|
|
var t = MakeNewSpecialBox(TREEBoxType.True);
|
|
return t.InternalID;
|
|
case 253:
|
|
return -1;
|
|
default:
|
|
return realID;
|
|
}
|
|
}
|
|
|
|
public byte GetTrueID(short boxID)
|
|
{
|
|
return GetBox(boxID)?.TrueID ?? 253;
|
|
}
|
|
|
|
public TREEBox GetBox(short pointer)
|
|
{
|
|
if (pointer < 0 || pointer >= Entries.Count) return null;
|
|
return Entries[pointer];
|
|
}
|
|
}
|
|
|
|
public class TREEBox
|
|
{
|
|
//runtime
|
|
public short InternalID = -1;
|
|
public bool PosisionInvalid; //forces a regeneration of position using the default tree algorithm
|
|
public TREE Parent;
|
|
public byte TrueID
|
|
{
|
|
get
|
|
{
|
|
switch (Type)
|
|
{
|
|
case TREEBoxType.Primitive:
|
|
return (byte)InternalID;
|
|
case TREEBoxType.Goto:
|
|
return LabelTrueID(new HashSet<short>());
|
|
case TREEBoxType.Label:
|
|
return 253; //arrows cannot point to a label
|
|
case TREEBoxType.True:
|
|
return 254;
|
|
case TREEBoxType.False:
|
|
return 255;
|
|
}
|
|
return 253;
|
|
}
|
|
}
|
|
|
|
public byte LabelTrueID(HashSet<short> visited)
|
|
{
|
|
if (Type != TREEBoxType.Goto) return TrueID;
|
|
if (visited.Contains(InternalID)) return 253; //error
|
|
visited.Add(InternalID);
|
|
return Parent?.GetBox(Parent.GetBox(TruePointer)?.TruePointer ?? -1)?.LabelTrueID(visited) ?? 253;
|
|
}
|
|
|
|
//data
|
|
public TREEBoxType Type;
|
|
public ushort Unknown;
|
|
public short Width;
|
|
public short Height;
|
|
public short X;
|
|
public short Y;
|
|
public short CommentSize = 0x10;
|
|
public short TruePointer = -1;
|
|
public short Special; //0 or -1... unknown.
|
|
public int FalsePointer = -1;
|
|
public string Comment = "";
|
|
public int TrailingZero = 0;
|
|
|
|
public TREEBox(TREE parent)
|
|
{
|
|
Parent = parent;
|
|
}
|
|
|
|
public void Read(IoBuffer io, int version)
|
|
{
|
|
Type = (TREEBoxType)io.ReadUInt16();
|
|
Unknown = io.ReadUInt16();
|
|
Width = io.ReadInt16();
|
|
Height = io.ReadInt16();
|
|
X = io.ReadInt16();
|
|
Y = io.ReadInt16();
|
|
CommentSize = io.ReadInt16();
|
|
TruePointer = io.ReadInt16();
|
|
Special = io.ReadInt16();
|
|
FalsePointer = io.ReadInt32();
|
|
Comment = io.ReadNullTerminatedString();
|
|
if (Comment.Length % 2 == 0) io.ReadByte(); //padding to 2 byte align
|
|
if (version > 0) TrailingZero = io.ReadInt32();
|
|
|
|
if (!Enum.IsDefined(typeof(TREEBoxType), Type)) throw new Exception("Unexpected TREE box type: " + Type.ToString());
|
|
if (Special < -1 || Special > 0) throw new Exception("Unexpected TREE special: " + Special);
|
|
if (Unknown != 0) throw new Exception("Unexpected Unknown: " + Unknown);
|
|
if (TrailingZero != 0) Console.WriteLine("Unexpected TrailingZero: " + TrailingZero);
|
|
}
|
|
|
|
public void Write(IoWriter io)
|
|
{
|
|
io.WriteUInt16((ushort)Type);
|
|
io.WriteUInt16(Unknown);
|
|
io.WriteInt16(Width);
|
|
io.WriteInt16(Height);
|
|
io.WriteInt16(X);
|
|
io.WriteInt16(Y);
|
|
io.WriteInt16(CommentSize);
|
|
io.WriteInt16(TruePointer);
|
|
io.WriteInt16(Special);
|
|
io.WriteInt32(FalsePointer);
|
|
io.WriteCString(Comment);
|
|
if (Comment.Length % 2 == 0) io.WriteByte(0xCD); //padding to 2 byte align
|
|
io.WriteInt32(TrailingZero);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Type.ToString() + " (" + TruePointer + ((FalsePointer == -1) ? "" : ("/"+FalsePointer)) + "): " + Comment;
|
|
}
|
|
}
|
|
|
|
public enum TREEBoxType : ushort
|
|
{
|
|
Primitive = 0,
|
|
True = 1,
|
|
False = 2,
|
|
Comment = 3,
|
|
Label = 4,
|
|
Goto = 5 //no comment size, roughly primitive sized (180, 48), pointer goes to Label
|
|
}
|
|
}
|