mysimulation/server/tso.files/Formats/IFF/Chunks/PIFF.cs

188 lines
6.2 KiB
C#
Raw Permalink Normal View History

using FSO.Files.Utils;
using System;
using System.IO;
using System.Linq;
namespace FSO.Files.Formats.IFF.Chunks
{
public class PIFF : IffChunk
{
public static ushort CURRENT_VERSION = 2;
public ushort Version = CURRENT_VERSION;
public string SourceIff;
public string Comment = "";
public PIFFEntry[] Entries;
public PIFF()
{
ChunkType = "PIFF";
}
public void AppendAddedChunks(IffFile file)
{
foreach (var chunk in file.SilentListAll())
{
if (chunk == this) continue;
var entries = Entries.ToList();
entries.Add(new PIFFEntry()
{
ChunkID = chunk.ChunkID,
ChunkLabel = chunk.ChunkLabel,
ChunkFlags = chunk.ChunkFlags,
EntryType = PIFFEntryType.Add,
NewDataSize = (uint)(chunk.ChunkData?.Length ?? 0),
Type = chunk.ChunkType
});
Entries = entries.ToArray();
}
}
public override void Read(IffFile iff, Stream stream)
{
using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
{
Version = io.ReadUInt16();
SourceIff = io.ReadVariableLengthPascalString();
if (Version > 1) Comment = io.ReadVariableLengthPascalString();
Entries = new PIFFEntry[io.ReadUInt16()];
for (int i=0; i<Entries.Length; i++)
{
var e = new PIFFEntry();
e.Type = io.ReadCString(4);
e.ChunkID = io.ReadUInt16();
if (Version > 1) e.Comment = io.ReadVariableLengthPascalString();
e.EntryType = (PIFFEntryType)io.ReadByte();
if (e.EntryType == PIFFEntryType.Patch)
{
e.ChunkLabel = io.ReadVariableLengthPascalString();
e.ChunkFlags = io.ReadUInt16();
if (Version > 0) e.NewChunkID = io.ReadUInt16();
else e.NewChunkID = e.ChunkID;
e.NewDataSize = io.ReadUInt32();
var size = io.ReadUInt32();
e.Patches = new PIFFPatch[size];
uint lastOff = 0;
for (int j=0; j<e.Patches.Length; j++)
{
var p = new PIFFPatch();
p.Offset = lastOff + io.ReadVarLen();
lastOff = p.Offset;
p.Size = io.ReadVarLen();
p.Mode = (PIFFPatchMode)io.ReadByte();
if (p.Mode == PIFFPatchMode.Add) p.Data = io.ReadBytes(p.Size);
e.Patches[j] = p;
}
}
Entries[i] = e;
}
}
}
public override bool Write(IffFile iff, Stream stream)
{
using (var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
{
io.WriteUInt16(CURRENT_VERSION);
io.WriteVariableLengthPascalString(SourceIff);
io.WriteVariableLengthPascalString(Comment);
io.WriteUInt16((ushort)Entries.Length);
foreach (var ent in Entries)
{
io.WriteCString(ent.Type, 4);
io.WriteUInt16(ent.ChunkID);
io.WriteVariableLengthPascalString(ent.Comment);
io.WriteByte((byte)(ent.EntryType));
if (ent.EntryType == PIFFEntryType.Patch)
{
io.WriteVariableLengthPascalString(ent.ChunkLabel); //0 length means no replacement
io.WriteUInt16(ent.ChunkFlags);
io.WriteUInt16(ent.NewChunkID);
io.WriteUInt32(ent.NewDataSize);
io.WriteUInt32((uint)ent.Patches.Length);
uint lastOff = 0;
foreach (var p in ent.Patches)
{
io.WriteVarLen(p.Offset-lastOff);
lastOff = p.Offset;
io.WriteVarLen(p.Size);
io.WriteByte((byte)p.Mode);
if (p.Mode == PIFFPatchMode.Add) io.WriteBytes(p.Data);
}
}
}
}
return true;
}
}
public class PIFFEntry
{
public string Type;
public ushort ChunkID;
public ushort NewChunkID;
public PIFFEntryType EntryType;
public string Comment = "";
public string ChunkLabel;
public ushort ChunkFlags;
public uint NewDataSize;
public PIFFPatch[] Patches;
public byte[] Apply(byte[] src)
{
var result = new byte[NewDataSize];
uint srcPtr = 0;
uint destPtr = 0;
int i = 0;
foreach (var p in Patches)
{
var copyCount = p.Offset - destPtr;
Array.Copy(src, srcPtr, result, destPtr, copyCount);
srcPtr += copyCount; destPtr += copyCount;
if (p.Mode == PIFFPatchMode.Add)
{
Array.Copy(p.Data, 0, result, destPtr, p.Size);
destPtr += p.Size;
} else
{
srcPtr += p.Size;
}
i++;
}
var remainder = NewDataSize - destPtr;
if (remainder != 0) Array.Copy(src, srcPtr, result, destPtr, remainder);
return result;
}
}
public enum PIFFEntryType : byte
{
Patch,
Remove,
Add
}
public struct PIFFPatch
{
public uint Offset;
public uint Size;
public PIFFPatchMode Mode;
public byte[] Data;
}
public enum PIFFPatchMode : byte
{
Remove = 0,
Add = 1
}
}