コード例 #1
0
            public Dictionary <int, ExportEntry> ExportDeltas = new Dictionary <int, ExportEntry>(); //includes header and data

            /// <summary>
            /// Compares two packages - vanilla vs modified - and returns the result. This MUST be a vanilla file as vanilla constraints are assumed.
            /// </summary>
            /// <param name="ancestorPackage">VANILLA FILE</param>
            /// <param name="modifiedPackage"></param>
            /// <returns></returns>
            public static PackageDelta CalculateDelta(IMEPackage ancestorPackage, IMEPackage modifiedPackage)
            {
                PackageDelta delta = new PackageDelta();

                #region Exports Comparison
                {
                    int numExportsToEnumerate = ancestorPackage.ExportCount;

                    for (int i = 0; i < numExportsToEnumerate; i++)
                    {
                        ExportEntry exp1 = ancestorPackage.Exports[i];
                        ExportEntry exp2 = modifiedPackage.Exports[i];

                        //make data offset and data size the same, as the exports could be the same even if it was appended later.
                        //The datasize being different is a data difference not a true header difference so we won't list it here.
                        byte[] header1 = exp1.Header.TypedClone();
                        byte[] header2 = exp2.Header.TypedClone();
                        Buffer.BlockCopy(BitConverter.GetBytes((long)0), 0, header1, 32, sizeof(long)); //zero out offset
                        Buffer.BlockCopy(BitConverter.GetBytes((long)0), 0, header2, 32, sizeof(long)); //zero out offset

                        //if (!StructuralComparisons.StructuralEqualityComparer.Equals(header1, header2))
                        if (!header1.SequenceEqual(header2))
                        {
                            delta.ExportDeltas[i] = modifiedPackage.Exports[i];
                            continue;
                        }

                        if (!exp1.Data.SequenceEqual(exp2.Data))
                        {
                            delta.ExportDeltas[i] = modifiedPackage.Exports[i];
                        }
                    }

                    if (modifiedPackage.ExportCount > ancestorPackage.ExportCount)
                    {
                        for (int i = ancestorPackage.ExportCount; i < modifiedPackage.ExportCount; i++)
                        {
                            //added exports.
                            delta.ExportDeltas[i] = modifiedPackage.Exports[i];
                        }
                    }
                }
                #endregion

                #region Imports
                {
                    int numImportsToEnumerate = ancestorPackage.ImportCount;

                    for (int i = 0; i < numImportsToEnumerate; i++)
                    {
                        ImportEntry imp1 = ancestorPackage.Imports[i];
                        ImportEntry imp2 = modifiedPackage.Imports[i];
                        if (!imp1.Header.SequenceEqual(imp2.Header))
                        {
                            delta.ImportDeltas[i] = imp2; //0-based index
                        }
                    }

                    if (modifiedPackage.ImportCount > ancestorPackage.ImportCount)
                    {
                        //added imports
                        for (int i = ancestorPackage.ImportCount; i < modifiedPackage.ImportCount; i++)
                        {
                            delta.ImportDeltas[i] = modifiedPackage.Imports[i]; //0-based index
                        }
                    }
                }
                #endregion

                #region Names
                {
                    //you cannot delete names in packages.
                    //as such target will always have at least same amount of names as vanilla.
                    int numNamesToEnumerate = ancestorPackage.NameCount;
                    for (int i = 0; i < numNamesToEnumerate; i++)
                    {
                        var name1 = ancestorPackage.Names[i];
                        var name2 = modifiedPackage.Names[i];

                        if (!name1.Equals(name2, StringComparison.InvariantCultureIgnoreCase))
                        {
                            delta.NameDeltas[i] = name2; // i => name 2
                        }
                    }

                    if (modifiedPackage.NameCount > ancestorPackage.NameCount)
                    {
                        for (int i = ancestorPackage.NameCount; i < modifiedPackage.NameCount; i++)
                        {
                            //added names.
                            delta.NameDeltas[i] = modifiedPackage.Names[i];
                        }
                    }
                }
                #endregion
                return(delta);
            }
コード例 #2
0
        public static bool AttemptMerge(MEPackage vanillaPackage, MEPackage modifiedVanillaPackage, MEPackage targetPackage)
        {
            PackageDelta vanillaToModifiedDelta = PackageDelta.CalculateDelta(vanillaPackage, modifiedVanillaPackage);
            PackageDelta vanillaToTargetDelta   = PackageDelta.CalculateDelta(vanillaPackage, targetPackage);
            string       loggingPrefix          = targetPackage.FilePath == null ? targetPackage.FileSourceForDebugging : Path.GetFileName(targetPackage.FilePath);
            //Check merge conditions
            var nameConflicts   = vanillaToModifiedDelta.NameDeltas.Keys.Intersect(vanillaToTargetDelta.NameDeltas.Keys).ToList();
            var importConflicts = vanillaToModifiedDelta.ImportDeltas.Keys.Intersect(vanillaToTargetDelta.ImportDeltas.Keys).ToList();
            var exportConflicts = vanillaToModifiedDelta.ExportDeltas.Keys.Intersect(vanillaToTargetDelta.ExportDeltas.Keys).ToList();

            Log.Information($@"[{loggingPrefix}] Performing three way merge pre-check");
            //Name deltas

            if (nameConflicts.Count > 0)
            {
                //need to check if the conflicts result in same value, in this case it would not be a conflict.
                foreach (int nameIndex in nameConflicts)
                {
                    var modifiedName = modifiedVanillaPackage.Names[nameIndex];
                    var targetName   = targetPackage.Names[nameIndex];
                    if (modifiedName != targetName)
                    {
                        //Differing names in same spots.
                        Log.Information($@"[{loggingPrefix}] Cannot merge files: Name index {nameIndex} is different between modified and target.");
                        return(false);
                    }
                }
            }

            if (importConflicts.Count > 0)
            {
                //todo
            }

            if (exportConflicts.Count > 0)
            {
                //hmm... this will be a tough one.
                foreach (int exportTableIndex in exportConflicts)
                {
                    //we will have to check sizes if we ever hope to have way to merge this
                    var modifiedData = modifiedVanillaPackage.Exports[exportTableIndex].Data;
                    var targetData   = targetPackage.Exports[exportTableIndex].Data;

                    if (!modifiedData.SequenceEqual(targetData))
                    {
                        Log.Information($@"[{loggingPrefix}] Cannot merge files: Export table index {exportTableIndex} data is different between modified and target.");
                        return(false);
                    }

                    //We will have to ignore size here somehow...
                    //var modifiedHeader = modifiedVanillaPackage.Exports[exportTableIndex].Header;
                    //var targetHeader = targetPackage.Exports[exportTableIndex].Header;
                }
            }

            //Merge is OK to perform
            //Apply vanilla to modified delta to target package
            foreach (var nameDelta in vanillaToModifiedDelta.NameDeltas)
            {
                if (nameDelta.Key >= targetPackage.NameCount)
                {
                    //add it
                    Log.Information($@"[{loggingPrefix}] Adding name {nameDelta.Value}");
                    targetPackage.addName(nameDelta.Value);
                }
                else
                {
                    Log.Information($@"[{loggingPrefix}] Updating name index {nameDelta.Key} to {nameDelta.Value}");
                    targetPackage.replaceName(nameDelta.Key, nameDelta.Value);
                }
            }

            foreach (var exportDelta in vanillaToModifiedDelta.ExportDeltas)
            {
                if (exportDelta.Key >= targetPackage.ExportCount)
                {
                    //add it
                    Log.Information($@"[{loggingPrefix}] Adding export {exportDelta.Value.GetInstancedFullPath}");
                    targetPackage.addExport(exportDelta.Value); //not sure if this is possible
                }
                else
                {
                    //gonna need this reviewed, not entirely sure this is OK to do
                    Log.Information($@"[{loggingPrefix}] Updating export {exportDelta.Value.GetInstancedFullPath}");

                    targetPackage.Exports[exportDelta.Key].Data   = exportDelta.Value.Data;
                    targetPackage.Exports[exportDelta.Key].Header = exportDelta.Value.Header;
                }
            }

            foreach (var importDelta in vanillaToModifiedDelta.ImportDeltas)
            {
                if (importDelta.Key >= targetPackage.ImportCount)
                {
                    //add it
                    Log.Information($@"[{loggingPrefix}] Adding import {importDelta.Value.GetInstancedFullPath}");

                    targetPackage.addImport(importDelta.Value); //not sure if this is possible
                }
                else
                {
                    Log.Information($@"[{loggingPrefix}] Updating import {importDelta.Value.GetInstancedFullPath}");

                    //gonna need this reviewed, not entirely sure this is OK to do
                    //targetPackage.Imports[importDelta.Key].Data = importDelta.Value.Data;
                    targetPackage.Imports[importDelta.Key].Header = importDelta.Value.Header;
                }
            }

            Log.Information($@"[{loggingPrefix}] Finished three way merge");
            return(true);
        }