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 void Read(IffFile iff, Stream stream) { using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN)) { Version = io.ReadUInt16(); SourceIff = 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(); e.Delete = io.ReadByte()>0; if (!e.Delete) { 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 static PIFFEntry MakeChunkDiff(IffChunk chk) { var e = new PIFFEntry { Type = chk.ChunkType, ChunkID = chk.OriginalID, NewChunkID = chk.ChunkID }; if (chk == null) { e.Delete = true; return e; } byte[] newData = null; using (var stream = new MemoryStream()) { if (!chk.Write(chk.ChunkParent, stream)) { return null; //use original } newData = stream.ToArray(); } e.ChunkLabel = (chk.OriginalLabel==chk.ChunkLabel)?"":chk.ChunkLabel; e.ChunkFlags = chk.ChunkFlags; e.NewDataSize = (uint)newData.Length; //encode difference as sequence of changes var oldData = chk.OriginalData; var patches = new List<PIFFPatch>(); int i; for (i=0; i<newData.Length; i += 1000) { if (i >= oldData.Length) { //no more comparisons, just add the remainder var remain = new byte[newData.Length-i]; Array.Copy(newData, i, remain, 0, remain.Length); patches.Add(new PIFFPatch { Mode = PIFFPatchMode.Add, Data = remain, Offset = (uint)i, Size = (uint)remain.Length }); break; } //dynamic programming matrix. int m = Math.Min(1000, Math.Max(0, newData.Length - i))+1; int n = Math.Min(1000, Math.Max(0, oldData.Length - i))+1; ushort[,] comp = new ushort[m, n]; for (int x=1; x<m; x++) { for (int y=1; y<n; y++) { if (newData[i+x-1] == oldData[i + y -1]) comp[x, y] = (ushort)(comp[x - 1, y - 1] + 1); else comp[x, y] = Math.Max(comp[x, y - 1], comp[x - 1, y]); } } var changes = new Stack<byte>(); //backtrack through compare { int x = m-1, y = n-1; while (true) { if (x>0 && y>0 && newData[i + x -1] == oldData[i + y -1]) { x--; y--; changes.Push(0); //no change } else if (y>0 && (x==0 || comp[x,y-1] >= comp[x-1,y])) { y--; changes.Push(2); //remove } else if (x>0 && (y==0 || comp[x,y-1] < comp[x-1,y])) { x--; changes.Push(1); //add } else { break; } } } byte lastC = 0; PIFFPatch curr = null; List<byte> addArray = null; int ptr = 0; foreach (var c in changes) { if (c != lastC && curr != null) { if (lastC == 1) curr.Data = addArray.ToArray(); patches.Add(curr); curr = null; } if (c == 0) ptr++; else if (c == 1) { if (lastC != 1) { curr = new PIFFPatch { Mode = PIFFPatchMode.Add, Offset = (uint)(i + ptr), Size = 1 }; addArray = new List<byte>(); addArray.Add(newData[i + ptr]); } else { curr.Size++; addArray.Add(newData[i + ptr]); } ptr++; } else { if (lastC != 2) curr = new PIFFPatch { Mode = PIFFPatchMode.Remove, Offset = (uint)(i + ptr), Size = 1 }; else curr.Size++; } lastC = c; } if (curr != null) { if (lastC == 1) curr.Data = addArray.ToArray(); patches.Add(curr); } if (m < n) { //remainder on src to be removed patches.Add(new PIFFPatch { Mode = PIFFPatchMode.Remove, Offset = (uint)(i+ptr), Size = (uint)(n-m) }); } /*else if (m != n) { //remainder on dest to be added var remain = new byte[m-n]; Array.Copy(newData, i+ptr, remain, 0, remain.Length); patches.Add(new PIFFPatch { Mode = PIFFPatchMode.Add, Data = remain, Offset = (uint)(i+ ptr), Size = (uint)remain.Length }); }*/ } if (oldData.Length > i) { //ran out of new data, but old is still going. Remove the remainder. patches.Add(new PIFFPatch { Mode = PIFFPatchMode.Remove, Offset = (uint)newData.Length, Size = (uint)(oldData.Length - i) }); } e.Patches = patches.ToArray(); return e; }