void MergeBundleFileNode(TreeNode fileNode, TreeNode[] checkedModNodes, Merge merge, bool isNew) { _outputPath = Path.Combine(Paths.MergedBundleContent, fileNode.Text); if (File.Exists(_outputPath) && !ConfirmOutputOverwrite(_outputPath)) { return; } _vanillaFile = null; var metadata1 = checkedModNodes[0].GetMetadata(); var source1 = MergeSource.FromBundle(new FileInfo(metadata1.FilePath), metadata1.FileHash); for (int i = 1; i < checkedModNodes.Length; ++i) { ++ProgressInfo.CurrentMergeNum; var metadata2 = checkedModNodes[i].GetMetadata(); var source2 = MergeSource.FromBundle(new FileInfo(metadata2.FilePath), metadata2.FileHash); if (!GetUnpackedFiles(fileNode.Text, ref source1, ref source2)) { if (DialogResult.Abort != HandleCanceledMerge(checkedModNodes.Length - i - 1, merge)) { continue; } break; } var mergedFile = MergeText(merge, source1, source2, true); if (mergedFile != null) { source1 = MergeSource.FromFlatFile(mergedFile, null); } else if (DialogResult.Abort == HandleCanceledMerge(checkedModNodes.Length - i - 1, merge)) { break; } } if (merge.BundleName != null && isNew && merge.Mods.Count > 1) { _bundleChanged = true; _pendingBundleMerges.Add(merge); } }
FileInfo MergeText(Merge merge, MergeSource source1, MergeSource source2, bool isBundled) { ProgressInfo.CurrentAction = $"Merging {source1.Name} && {source2.Name} — waiting for KDiff3 to close"; var exitCode = KDiff3.Run(source1, source2, _vanillaFile, _outputPath, !isBundled); if (exitCode == 0) { if (!source1.TextFile.FullName.EqualsIgnoreCase(_outputPath) && !source1.TextFile.FullName.StartsWithIgnoreCase(Paths.MergedBundleContentAbsolute)) { _inventory.AddModToMerge(source1, merge); } if (!source2.TextFile.FullName.EqualsIgnoreCase(_outputPath) && !source2.TextFile.FullName.StartsWithIgnoreCase(Paths.MergedBundleContentAbsolute)) { _inventory.AddModToMerge(source2, merge); } if (Program.Settings.Get <bool>("PlayCompletionSounds")) { System.Media.SystemSounds.Asterisk.Play(); } if (Program.Settings.Get <bool>("ReportAfterMerge")) { using (var reportForm = new MergeReportForm( ProgressInfo.CurrentMergeNum, ProgressInfo.TotalMergeCount, source1.TextFile.FullName, source2.TextFile.FullName, _outputPath, source1.Name, source2.Name)) { ProgressInfo.CurrentAction = "Showing merge report"; Program.MainForm.ShowModal(reportForm); } } return(new FileInfo(_outputPath)); } else { return(null); } }
void MergeFlatFileNode(TreeNode fileNode, TreeNode[] checkedModNodes, Merge merge, bool isNew) { var metadata1 = checkedModNodes[0].GetMetadata(); var source1 = MergeSource.FromFlatFile(new FileInfo(metadata1.FilePath), metadata1.FileHash); var relPath = Paths.GetRelativePath( source1.TextFile.FullName, Path.Combine(Paths.ModsDirectory, source1.Name)); _outputPath = Path.Combine(Paths.ModsDirectory, _mergedModName, relPath); if (File.Exists(_outputPath) && !ConfirmOutputOverwrite(_outputPath)) { return; } _vanillaFile = new FileInfo(fileNode.GetMetadata().FilePath); for (int i = 1; i < checkedModNodes.Length; ++i) { ++ProgressInfo.CurrentMergeNum; var metadata2 = checkedModNodes[i].GetMetadata(); var source2 = MergeSource.FromFlatFile(new FileInfo(metadata2.FilePath), metadata2.FileHash); var mergedFile = MergeText(merge, source1, source2, false); if (mergedFile != null) { source1 = MergeSource.FromFlatFile(mergedFile, null); } else if (DialogResult.Abort == HandleCanceledMerge(checkedModNodes.Length - i - 1, merge)) { break; } } if (isNew && merge.Mods.Count > 1) { ProgressInfo.CurrentAction = "Adding script merge to inventory"; _inventory.Merges.Add(merge); } }
static void MergeToMain() { try { WorkspaceInfo mWkInfo = FindWorkspace.InfoForApplicationPath(Application.dataPath, Plastic.API); if (mWkInfo == null) { return; } RepositorySpec repSpec = Plastic.API.GetRepositorySpec(mWkInfo); RepositoryInfo repInfo = Plastic.API.GetRepositoryInfo(repSpec); BranchInfo workingBranchInfo = Plastic.API.GetWorkingBranch(mWkInfo); BranchInfo mainBranchInfo = Plastic.API.GetMainBranch(repSpec); if (workingBranchInfo.BranchId == mainBranchInfo.BranchId) { return; } SpecGenerator specGenerator = new SpecGenerator(repInfo); BranchSpec sourceSpec = specGenerator.GenBranchSpec(false, workingBranchInfo.BranchName); BranchSpec destinationSpec = specGenerator.GenBranchSpec(false, mainBranchInfo.BranchName); MergeSource mergeSource = MergeSourceBuilder.BuildMergeSource(repInfo, null, sourceSpec, destinationSpec, new MergeParameters()); BuildMerge.ApplyMergeForMerge(mWkInfo, null).MergeTo(mergeSource, mMergeComments); } finally { string plasticIniFilePah = Path.Combine(Application.dataPath, "../plastic.ini"); File.Delete(plasticIniFilePah); } }
void IPlasticAPI.PerformUpdateMerge(WorkspaceInfo wkInfo, MergeSource mergeSource, MergeResult mergeResult, ICmdNotifier notifier) { throw new NotImplementedException(); }
bool GetUnpackedFiles(string contentRelativePath, ref MergeSource source1, ref MergeSource source2) { if (_vanillaFile == null) { ProgressInfo.CurrentAction = "Searching for corresponding vanilla bundle"; var bundleDirsList = Directory.GetDirectories(Paths.BundlesDirectory) .Select(path => Path.Combine(path, "bundles")) .Concat( Directory.GetDirectories(Paths.DlcDirectory) .Where(path => new Regex("DLC[0-9]*$", RegexOptions.IgnoreCase).IsMatch(path) || new Regex("ep[0-9]$", RegexOptions.IgnoreCase).IsMatch(path) || new Regex("bob$", RegexOptions.IgnoreCase).IsMatch(path)) .Select(path => Path.Combine(path, Paths.BundleBase, "bundles")) ) .Where(path => Directory.Exists(path)) .OrderBy(path => path, new LoadOrderComparer()) .ToList(); // Ensure patch bundle directories are always last var patchDirs = bundleDirsList.FindAll(x => x.Contains("patch")); var bundleDirs = bundleDirsList .Except(patchDirs) .Union(patchDirs) .ToArray(); for (int i = bundleDirs.Length - 1; i >= 0; --i) // Search vanilla directories in reverse { // order, as patches & DLC override content. var bundleFiles = Directory.GetFiles(bundleDirs[i], "*.bundle"); foreach (var bundle in bundleFiles) { var contentPaths = QuickBms.GetBundleContentPaths(bundle); if (contentPaths.Any(path => path.EqualsIgnoreCase(contentRelativePath))) { _vanillaFile = new FileInfo(bundle); break; } } if (_vanillaFile != null) { break; } } if (_vanillaFile != null) { ProgressInfo.CurrentAction = "Unpacking vanilla bundle content file"; var vanillaContentPath = UnpackFile(_vanillaFile.FullName, contentRelativePath, "Vanilla"); _vanillaFile = new FileInfo(vanillaContentPath); } } if (source1.TextFile == null) { ProgressInfo.CurrentAction = $"Unpacking bundle content file for {source1.Name}"; var modContentFile1 = UnpackFile(source1.Bundle.FullName, contentRelativePath, "Mod 1"); if (modContentFile1 == null) { return(false); } source1.TextFile = new FileInfo(modContentFile1); } ProgressInfo.CurrentAction = $"Unpacking bundle content file for {source2.Name}"; var modContentFile2 = UnpackFile(source2.Bundle.FullName, contentRelativePath, "Mod 2"); if (modContentFile2 == null) { return(false); } source2.TextFile = new FileInfo(modContentFile2); return(true); }
bool GetUnpackedFiles(string contentRelativePath, ref MergeSource source1, ref MergeSource source2) { if (_vanillaFile == null) { ProgressInfo.CurrentAction = "Searching for corresponding vanilla bundle"; var bundleDirs = Directory.GetDirectories(Paths.BundlesDirectory) .Select(path => Path.Combine(path, "bundles")) .Concat( Directory.GetDirectories(Paths.DlcDirectory) .Where(path => new Regex("DLC[0-9]*$").IsMatch(path) || new Regex("ep[0-9]$").IsMatch(path)) .Select(path => Path.Combine(path, Paths.BundleBase, "bundles")) ) .Where(path => Directory.Exists(path)) .OrderBy(path => path, new LoadOrderComparer()) .ToArray(); for (int i = bundleDirs.Length - 1; i >= 0; --i) // Search vanilla directories in reverse { // order, as patches & DLC override content. var bundleFiles = Directory.GetFiles(bundleDirs[i], "*.bundle"); foreach (var bundle in bundleFiles) { var contentPaths = QuickBms.GetBundleContentPaths(bundle); if (contentPaths.Any(path => path.EqualsIgnoreCase(contentRelativePath))) { _vanillaFile = new FileInfo(bundle); break; } } if (_vanillaFile != null) break; } if (_vanillaFile != null) { ProgressInfo.CurrentAction = "Unpacking vanilla bundle content file"; var vanillaContentPath = UnpackFile(_vanillaFile.FullName, contentRelativePath, "Vanilla"); _vanillaFile = new FileInfo(vanillaContentPath); } } if (source1.TextFile == null) { ProgressInfo.CurrentAction = $"Unpacking bundle content file for {source1.Name}"; var modContentFile1 = UnpackFile(source1.Bundle.FullName, contentRelativePath, "Mod 1"); if (modContentFile1 == null) return false; source1.TextFile = new FileInfo(modContentFile1); } ProgressInfo.CurrentAction = $"Unpacking bundle content file for {source2.Name}"; var modContentFile2 = UnpackFile(source2.Bundle.FullName, contentRelativePath, "Mod 2"); if (modContentFile2 == null) return false; source2.TextFile = new FileInfo(modContentFile2); return true; }
FileInfo MergeText(Merge merge, MergeSource source1, MergeSource source2) { ProgressInfo.CurrentAction = $"Merging {source1.Name} && {source2.Name} — waiting for KDiff3 to close"; var exitCode = KDiff3.Run(source1, source2, _vanillaFile, _outputPath); if (exitCode == 0) { if (!source1.TextFile.FullName.EqualsIgnoreCase(_outputPath) && !source1.TextFile.FullName.StartsWithIgnoreCase(Paths.MergedBundleContentAbsolute)) { _inventory.AddModToMerge(source1, merge); } if (!source2.TextFile.FullName.EqualsIgnoreCase(_outputPath) && !source2.TextFile.FullName.StartsWithIgnoreCase(Paths.MergedBundleContentAbsolute)) { _inventory.AddModToMerge(source2, merge); } if (Program.Settings.Get<bool>("PlayCompletionSounds")) { System.Media.SystemSounds.Asterisk.Play(); } if (Program.Settings.Get<bool>("ReportAfterMerge")) { using (var reportForm = new MergeReportForm( ProgressInfo.CurrentMergeNum, ProgressInfo.TotalMergeCount, source1.TextFile.FullName, source2.TextFile.FullName, _outputPath, source1.Name, source2.Name)) { ProgressInfo.CurrentAction = "Showing merge report"; Program.MainForm.ShowModal(reportForm); } } return new FileInfo(_outputPath); } else return null; }
} // public static CompareResult CompareTwoOfAKind<T> /// <summary> /// Merge two sorted lists, returning a new sorted list containg the new /// or updated items from a second list. Please see Remarks. /// </summary> /// <typeparam name="T"> /// All three lists (both inputs, paMasterList and paNewItems, and the /// returned list) must contain objects of the same type, and that type /// must implement the IComparable interface and have a parameterless /// default constructor. /// </typeparam> /// <param name="paMasterList"> /// This array is the master list. Items without matching items in list /// paNewItems are preserved. Please see Remarks. /// </param> /// <param name="paNewItems"> /// An item that matches an item in list paMasterList replaces it. An /// item that doesn't match any existing item in list paMasterList is /// merged into it. Please see Remarks. /// </param> /// <returns> /// The returned list contains everything in list paNewItems, and /// everything in list paMasterList that has no matching item in list /// paMasterList. Please see Remarks. Since both input lists are sorted, /// the new list is also sorted. /// </returns> /// <remarks> /// The goal of this routine is to merge two lists, the first of which /// is treated as a master list, into which new and updated items from /// from the second list are merged. /// /// Merging is based on comparing items from both lists based on the /// values returned by their respective CompareTo methods. Values that /// return zero (equality) are merged by replacing the value from the /// first list, represented by the first argument (paMasterList) with /// that from the second list, represented by the second argument /// (paNewItems). /// /// This algorithm imposes four requirements on its inputs. /// /// 1) Both input arrays must be composed of objects of the same type. /// /// 2) That type must implement the IComparable interface. /// /// 3) That type must have a default constructor. /// /// 4) Both input arrays must be sorted. /// /// In return, it makes the following four guarantees. /// /// 1) Every item in array paNewItems will become part of the new list. /// /// 2) Every item in array paMasterList that has no matching value in /// array paNewItems will become part of the new list. /// /// 3) Every item in array paNewItems that matches an item in array /// paMasterList replaces that matching item. /// /// 4) Every item in array paNewItems that doesn't match any item in /// paMasterList is added to the list. /// /// On input, both lists must be sorted, which is the first reason that /// the objects in the arrays must implement IComparable. The second /// reason is that this routine must compare the two lists in order to /// merge them correctly. The comparison happens in CompareTwoOfAKind, a /// companion routine that also takes generics meeting the first of the /// two specified constraints. /// </remarks> public static T [ ] MergeNewItemsIntoArray<T> ( T [ ] paMasterList , T [ ] paNewItems ) where T : IComparable , new ( ) { const string ARG_NAME_OLD = @"paMasterList"; const string ARG_NAME_NEW = @"paNewItems"; const long EMPTY = MagicNumbers.ZERO; // ---------------------------------------------------------------- // First things first: Sanity check both inputs. // ---------------------------------------------------------------- if ( paMasterList == null ) { throw new ArgumentNullException ( ARG_NAME_OLD , WizardWrx.DLLServices2.Properties.Resources.ERRMSG_ARG_IS_NULL ); } // if ( paMasterList == null ) if ( paNewItems == null ) { throw new ArgumentNullException ( ARG_NAME_NEW , WizardWrx.DLLServices2.Properties.Resources.ERRMSG_ARG_IS_NULL ); } // if ( paNewItems == null ) long lngMasterItemsCount = paMasterList.LongLength; long lngNewItemsCount = paNewItems.LongLength; ArrayList alMerged = new ArrayList ( ); // ---------------------------------------------------------------- // Next, address the three degenerate cases of one or both arrays // being empty. // ---------------------------------------------------------------- if ( lngMasterItemsCount == EMPTY || lngNewItemsCount == EMPTY ) { // One or both arrays are empty. if ( lngMasterItemsCount == EMPTY ) { // Old array is empty. Use new array, unless it is also empty. if ( lngNewItemsCount > EMPTY ) { // New array is populated. Fill the output array from it. alMerged.AddRange ( paNewItems ); } // if ( panew.Length > EMPTY ) } // TRUE block, if ( paold.Length == EMPTY ) else { // Fill the output array from the old array. The new one is, a priori, empty. alMerged.AddRange ( paMasterList ); } // FALSE block, if ( paold.Length == EMPTY ) } // if ( paold.Length == EMPTY || panew.Length == EMPTY ) // ---------------------------------------------------------------- // The general case prevails; both arrays contain data. // ---------------------------------------------------------------- bool fMoreData = true; long lngIndexMaster = ArrayInfo.ARRAY_FIRST_ELEMENT; long lngIndexNewItems = ArrayInfo.ARRAY_FIRST_ELEMENT; MergeSource enmMergeSource = MergeSource.Undetermined; while ( fMoreData ) { switch ( enmMergeSource ) { case MergeSource.FinishFromNewItemsList: while ( lngIndexNewItems < lngNewItemsCount ) { alMerged.Add ( paNewItems [ lngIndexNewItems ] ); lngIndexNewItems++; } // while ( lngIndexNewItems < lngNewItemsCount ) fMoreData = false; break; // case MergeSource.FinishFronNew case MergeSource.FinishFromMasterList: while ( lngIndexMaster < lngMasterItemsCount ) { alMerged.Add ( paMasterList [ lngIndexMaster ] ); lngIndexMaster++; } // while ( lngIndexMaster < lngMasterItemsCount ) fMoreData = false; break; // case MergeSource.FinishFromOld default: // ---------------------------------------------------- // The designs of this routine and CompareTwoOfAKind // ensure that CompareTwoOfAKind succeeds, and that its // return value is always one of the three cases // written into this switch block. // ---------------------------------------------------- switch ( CompareTwoOfAKind ( paMasterList [ lngIndexMaster ] , paNewItems [ lngIndexNewItems ] ) ) { case CompareResult.EqualTo: enmMergeSource = MergeSource.AddItemFromNewList; alMerged.Add ( paNewItems [ lngIndexNewItems ] ); lngIndexNewItems++; lngIndexMaster++; break; // case CompareResult.EqualTo case CompareResult.LessThan: enmMergeSource = MergeSource.AddItemFromMasterList; alMerged.Add ( paMasterList [ lngIndexMaster ] ); lngIndexMaster++; break; // case CompareResult.LessThan case CompareResult.GreaterThan: enmMergeSource = MergeSource.AddItemFromNewList; alMerged.Add ( paNewItems [ lngIndexNewItems ] ); lngIndexNewItems++; break; // case CompareResult.GreaterThan } // switch ( CompareTwoOfAKind ( panew [ lngIndexNewItems ] , paold [ lngIndexMaster ] ) ) // ---------------------------------------------------- // After each iteration, see whether either or both // lists are exhausted. // ---------------------------------------------------- if ( lngIndexNewItems >= lngNewItemsCount && lngIndexMaster >= lngMasterItemsCount ) { // The merge is finished when both lists are exhausted. fMoreData = false; } else { // Check for the special case of one list being exhausted. if ( lngIndexNewItems >= lngNewItemsCount ) { enmMergeSource = MergeSource.FinishFromMasterList; } // if ( lngIndexNewItems > panew.Length ) else if ( lngIndexMaster >= lngMasterItemsCount ) { enmMergeSource = MergeSource.FinishFromNewItemsList; } // else if ( lngIndexMaster >= lngMasterItemsCount ) } // if ( lngIndexNewItems > panew.Length && lngIndexMaster > paold.Length ) break; // Default case } // switch ( enmMergeSource ) } // while ( fMoreData ) T [ ] raMerged = ( T [ ] ) alMerged.ToArray ( typeof ( T ) ); return raMerged; } // public static T [] MergeNewItemsIntoArray<T>