public PIFFListItem(PIFFEntry entry, IffFile target) { Entry = entry; if (entry.ChunkLabel != "") { Name = entry.ChunkLabel; } else { //try find the original to get name var chunks = target.SilentListAll(); Replaced = chunks.FirstOrDefault(chunk => chunk.ChunkType == entry.Type && chunk.OriginalID == entry.ChunkID); if (Replaced == null) { Name = "UNKNOWN"; } else { Name = Replaced.OriginalLabel; } } }
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); }