public void WriteChunk(IoWriter io,IffChunk c) { var typeString = CHUNK_TYPES.FirstOrDefault(x => x.Value == c.GetType()).Key; io.WriteCString((typeString == null) ? c.ChunkType : typeString,4); byte[] data; using (var cstr = new MemoryStream()) { if (c.Write(this,cstr)) { data = cstr.ToArray(); } else { data = c.OriginalData; } } //todo: exporting PIFF as IFF SHOULD NOT DO THIS c.OriginalData = data; //if we revert, it is to the last save. c.RuntimeInfo = ChunkRuntimeState.Normal; io.WriteUInt32((uint)data.Length + 76); io.WriteUInt16(c.ChunkID); io.WriteUInt16(c.ChunkFlags); io.WriteCString(c.ChunkLabel,64); io.WriteBytes(data); }
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; }