Esempio n. 1
0
        public ModelDelta Revert(IDataModel model)
        {
            var reverse = new ModelDelta {
                HasDataChange = HasDataChange
            };

            foreach (var kvp in oldData)
            {
                var(index, data)       = (kvp.Key, kvp.Value);
                reverse.oldData[index] = model.Count > index ? model[index] : (byte)0xFF;
                if (model.Count > index)
                {
                    model[index] = data;
                }
            }

            foreach (var kvp in addedRuns)
            {
                reverse.removedRuns[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedRuns)
            {
                reverse.addedRuns[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in addedNames)
            {
                reverse.removedNames[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedNames)
            {
                reverse.addedNames[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in addedLists)
            {
                reverse.removedLists[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedLists)
            {
                reverse.addedLists[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in addedMatchedWords)
            {
                reverse.removedMatchedWords[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedMatchedWords)
            {
                reverse.addedMatchedWords[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in addedOffsetPointers)
            {
                reverse.removedOffsetPointers[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedOffsetPointers)
            {
                reverse.addedOffsetPointers[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in addedUnmappedPointers)
            {
                reverse.removedUnmappedPointers[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedUnmappedPointers)
            {
                reverse.addedUnmappedPointers[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in addedUnmappedConstants)
            {
                reverse.removedUnmappedConstants[kvp.Key] = kvp.Value;
            }
            foreach (var kvp in removedUnmappedConstants)
            {
                reverse.addedUnmappedConstants[kvp.Key] = kvp.Value;
            }

            if (oldDataLength != -1 || newDataLength != -1)
            {
                model.ExpandData(reverse, oldDataLength - 1);
                model.ContractData(reverse, oldDataLength - 1);
            }

            model.MassUpdateFromDelta(addedRuns, removedRuns,
                                      addedNames, removedNames,
                                      addedUnmappedPointers, removedUnmappedPointers,
                                      addedMatchedWords, removedMatchedWords,
                                      addedOffsetPointers, removedOffsetPointers,
                                      addedUnmappedConstants, removedUnmappedConstants,
                                      addedLists, removedLists);

            return(reverse);
        }
Esempio n. 2
0
        // return -1 if the header is wrong (UPS1)
        // return -2 if the source file CRC doesn't match
        // return -3 if the patch file CRC doesn't match
        // return -4 if the source file size doesn't match
        // return -5 if the result file CRC doesn't match
        // return -6 if the UPS content didn't finish the last chunk with exactly 12 bytes left
        // return -7 if trying to write past the end of the destination file
        // returns a positive integer, the address of the first change, if everything worked correctly
        public static int ApplyUPSPatch(IDataModel model, byte[] patch, Func <ModelDelta> tokenFactory, bool ignoreChecksums, out UpsPatchDirection direction)
        {
            // 4 byte header: "UPS1"
            // variable width source-size
            // variable width destination-size
            // 12 byte footer: 3 CRC32 checksums. Source file, destination file, patch file (CRC of everything except the last 4 bytes)
            direction = UpsPatchDirection.Fail;

            // check header
            var headerMatches = patch.Take(4).Select(b => (char)b).SequenceEqual("UPS1");

            if (!headerMatches)
            {
                return(-1);
            }

            // check source CRC
            var currentCRC              = CalcCRC32(model.RawData);
            var patchSourceFileCRC      = patch.ReadMultiByteValue(patch.Length - 12, 4);
            var patchDestinationFileCRC = patch.ReadMultiByteValue(patch.Length - 8, 4);

            if (currentCRC == patchSourceFileCRC)
            {
                direction = UpsPatchDirection.SourceToDestination;
            }
            if (currentCRC == patchDestinationFileCRC)
            {
                direction = UpsPatchDirection.DestinationToSource;
            }
            if (direction == UpsPatchDirection.Fail && !ignoreChecksums)
            {
                return(-2);
            }
            if (direction == UpsPatchDirection.Fail)
            {
                direction = UpsPatchDirection.SourceToDestination;
            }

            // check patch CRC
            var patchWithoutCRC = new byte[patch.Length - 4];

            Array.Copy(patch, patchWithoutCRC, patchWithoutCRC.Length);
            var patchCRC = CalcCRC32(patchWithoutCRC);

            if (patchCRC != patch.ReadMultiByteValue(patch.Length - 4, 4))
            {
                return(-3);
            }

            // resize (bigger)
            int readIndex = 4, firstEdit = int.MaxValue;
            int sourceSize      = ReadVariableWidthInteger(patch, ref readIndex);
            int destinationSize = ReadVariableWidthInteger(patch, ref readIndex);
            int writeLength     = destinationSize;

            if (direction == UpsPatchDirection.DestinationToSource)
            {
                (sourceSize, destinationSize) = (destinationSize, sourceSize);
            }
            if (sourceSize != model.Count && !ignoreChecksums)
            {
                return(-4);
            }
            var token = tokenFactory.Invoke();

            model.ExpandData(token, destinationSize - 1);
            token.ChangeData(model, sourceSize, new byte[Math.Max(0, destinationSize - sourceSize)]);
            token.ChangeData(model, destinationSize, new byte[Math.Max(0, sourceSize - destinationSize)]);

            // run algorithm
            firstEdit = RunUPSPatchAlgorithm(model, patch, token, writeLength, destinationSize, ref readIndex);
            if (firstEdit < 0)
            {
                return(firstEdit);
            }

            // resize (smaller)
            model.ContractData(token, destinationSize - 1);

            // check result CRC
            if (!ignoreChecksums)
            {
                var finalCRC = CalcCRC32(model.RawData);
                if (direction == UpsPatchDirection.SourceToDestination && finalCRC != patchDestinationFileCRC)
                {
                    return(-5);
                }
                if (direction == UpsPatchDirection.DestinationToSource && finalCRC != patchSourceFileCRC)
                {
                    return(-5);
                }
            }

            // check that the chunk ended cleanly
            if (direction == UpsPatchDirection.SourceToDestination && readIndex != patch.Length - 12)
            {
                return(-6);
            }

            return(firstEdit);
        }