예제 #1
0
 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;
         }
     }
 }
예제 #2
0
        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);
        }