mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-19 08:21:22 +00:00
188 lines
6.2 KiB
C#
188 lines
6.2 KiB
C#
|
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
|
|||
|
}
|
|||
|
}
|