/// <summary> /// Executes an action (a set of .Net statements that get a <see cref="CancellationToken"/> /// (to reacte on a cancel request and cancel a process) and a <seealso cref="IProgress"/> /// interface to display the progress of the executed actions. /// </summary> /// <param name="settings"></param> internal void StartProcess(ProgressSettings[] settings) { _CancelTokenSource = new CancellationTokenSource(); _CancelToken = _CancelTokenSource.Token; Progress.Aborted(false, false); IsEnabledClose = false; SetProgressing(true); Task taskToProcess = Task.Factory.StartNew(stateObj => { try { foreach (var item in settings) { this.ResetSettings(item); _CancelToken.ThrowIfCancellationRequested(); item.ExecAction(_CancelToken, Progress); } } catch (OperationCanceledException) { Progress.Aborted(true, true); } catch (Exception) { } finally { SetProgressing(false); IsEnabledClose = true; // Close this dialog if we are done progressing here... // Or leave it open if progressing was cancelled or we ran into an error // This approach always requires a close button to be available at the end // of the progress because we otherwise leave a dialog open that cannot be // closed by the user :-( if (CloseDialogOnProgressFinished == true && Progress.AbortedWithCancel == false && Progress.AbortedWithError == false) { OnExecuteCloseDialog(); } } }, _CancelToken).ContinueWith(ant => { }); }
/// <summary> /// Downloads and installs a mod from NexusMods. (by using NXM links) /// </summary> /// <param name="useSourceBA2Archive">When false, creates a new "frozen" mod.</param> public static bool InstallRemote(ManagedMods mods, string nxmLinkStr, bool useSourceBA2Archive = false, Action <Progress> ProgressChanged = null) { NXMLink nxmLink = NXMHandler.ParseLink(nxmLinkStr); // Get the download link from NexusMods: ProgressChanged(Progress.Indetermined("Requesting mod download link...")); string dlLinkStr = NMMod.RequestDownloadLink(nxmLink); if (dlLinkStr == null) { ProgressChanged?.Invoke(Progress.Aborted("Couldn't retrieve download link...")); return(false); } Uri dlLink = new Uri(dlLinkStr); string dlFileName = dlLink.Segments.Last(); string dlPath = Path.Combine(Shared.DownloadsFolder, dlFileName); // Download mod, unless we already have it: if (!File.Exists(dlPath)) { DownloadFile(dlLink.OriginalString, dlPath, ProgressChanged); } if (!File.Exists(dlPath)) { ProgressChanged?.Invoke(Progress.Aborted("Download failed.")); return(false); } // Get remote mod info: ProgressChanged(Progress.Indetermined("Requesting mod information and thumbnail...")); NMMod nmMod = NexusMods.RequestModInformation(nxmLink.modId); // Install mod: ProgressChanged(Progress.Indetermined($"Installing '{nmMod.Title}'...")); ManagedMod newMod = ModInstallations.FromArchive(mods.GamePath, dlPath, useSourceBA2Archive, ProgressChanged); newMod.Title = nmMod.Title; newMod.Version = nmMod.LatestVersion; newMod.URL = nmMod.URL; mods.Add(newMod); mods.Save(); ProgressChanged?.Invoke(Progress.Done($"'{nmMod.Title}' installed.")); return(true); }
/// <summary> /// Loads and converts legacy managed mods to the new format. /// It adds them to an already existing ManagedMods object. /// </summary> public static void ConvertLegacy(ManagedMods mods, GameEdition edition, Action <Progress> ProgressChanged = null) { Directory.CreateDirectory(Path.Combine(mods.GamePath, "FrozenData")); XDocument xmlDoc = XDocument.Load(Path.Combine(mods.ModsPath, "manifest.xml")); // I added a doNotImport="true" attribute, so I can check, whether the manifest.xml has only been generated for backwards-compatibility. // If it exists, we can just skip the import: if (xmlDoc.Root.Attribute("doNotImport") != null) { ProgressChanged?.Invoke(Progress.Aborted("Import skipped.")); return; } // Make backups: File.Copy(Path.Combine(mods.ModsPath, "manifest.xml"), Path.Combine(mods.ModsPath, "manifest.old.xml"), true); if (File.Exists(Path.Combine(mods.ModsPath, "managed.xml"))) { File.Copy(Path.Combine(mods.ModsPath, "managed.xml"), Path.Combine(mods.ModsPath, "managed.old.xml"), true); } // Converting the legacy list will completely erase the current mod list: mods.Mods.Clear(); /* * Converting: */ int modCount = xmlDoc.Descendants("Mod").Count(); int modIndex = 0; foreach (XElement xmlMod in xmlDoc.Descendants("Mod")) { modIndex++; if (xmlMod.Attribute("modFolder") == null) { continue; } ManagedMod mod = new ManagedMod(mods.GamePath); string managedFolderName = xmlMod.Attribute("modFolder").Value; string managedFolderPath = Path.Combine(mods.ModsPath, managedFolderName); string frozenArchivePath = Path.Combine(mods.ModsPath, managedFolderName, "frozen.ba2"); bool isFrozen = File.Exists(frozenArchivePath); mod.ManagedFolderName = managedFolderName; if (xmlMod.Attribute("title") != null) { mod.Title = xmlMod.Attribute("title").Value; } string progressTitle = $"Converting \"{mod.Title}\" ({modIndex} of {modCount})"; float progressPercentage = (float)modIndex / (float)modCount; // In case the mod was "frozen" before, // we'll need to move the *.ba2 archive into the FrozenData folder and then extract it. if (isFrozen) { ProgressChanged?.Invoke(Progress.Ongoing($"{progressTitle}: Extracting *.ba2 archive...", progressPercentage)); File.Move(frozenArchivePath, mod.FrozenArchivePath); Archive2.Extract(mod.FrozenArchivePath, managedFolderPath); mod.Frozen = true; mod.Freeze = true; } // OBSOLETE: We need to rename the old folder to fit with the new format. /*if (Directory.Exists(managedFolderPath)) * { * ProgressChanged?.Invoke(Progress.Ongoing($"{progressTitle}: Moving managed folder...", progressPercentage)); * if (Directory.Exists(mod.ManagedFolderPath)) * Directory.Delete(mod.ManagedFolderPath, true); * Directory.Move(managedFolderPath, mod.ManagedFolderPath); * }*/ ProgressChanged?.Invoke(Progress.Ongoing($"{progressTitle}: Parsing XML...", progressPercentage)); if (xmlMod.Attribute("url") != null) { mod.URL = xmlMod.Attribute("url").Value; } if (xmlMod.Attribute("version") != null) { mod.Version = xmlMod.Attribute("version").Value; } if (xmlMod.Attribute("enabled") != null) { try { mod.Deployed = XmlConvert.ToBoolean(xmlMod.Attribute("enabled").Value); } catch { mod.Deployed = false; } mod.Enabled = mod.Deployed; } if (xmlMod.Attribute("installType") != null) { switch (xmlMod.Attribute("installType").Value) { case "Loose": mod.PreviousMethod = ManagedMod.DeploymentMethod.LooseFiles; break; case "SeparateBA2": mod.PreviousMethod = ManagedMod.DeploymentMethod.SeparateBA2; break; case "BA2Archive": // Backward compatibility case "BundledBA2": case "BundledBA2Textures": // Backward compatibility default: mod.PreviousMethod = ManagedMod.DeploymentMethod.BundledBA2; break; } mod.Method = mod.PreviousMethod; } if (xmlMod.Attribute("format") != null) { switch (xmlMod.Attribute("format").Value) { case "General": mod.CurrentFormat = ManagedMod.ArchiveFormat.General; break; case "DDS": // Backward compatibility case "Textures": mod.CurrentFormat = ManagedMod.ArchiveFormat.Textures; break; case "Auto": default: mod.CurrentFormat = ManagedMod.ArchiveFormat.Auto; break; } mod.Format = mod.CurrentFormat; } if (xmlMod.Attribute("compression") != null) { switch (xmlMod.Attribute("compression").Value) { case "Default": // Backward compatibility case "Compressed": mod.CurrentCompression = ManagedMod.ArchiveCompression.Compressed; break; case "None": // Backward compatibility case "Uncompressed": mod.CurrentCompression = ManagedMod.ArchiveCompression.Uncompressed; break; case "Auto": default: mod.CurrentCompression = ManagedMod.ArchiveCompression.Auto; break; } mod.Compression = mod.CurrentCompression; } if (xmlMod.Attribute("archiveName") != null) { mod.CurrentArchiveName = xmlMod.Attribute("archiveName").Value; mod.ArchiveName = mod.CurrentArchiveName; } if (xmlMod.Attribute("root") != null) { mod.CurrentRootFolder = xmlMod.Attribute("root").Value; mod.RootFolder = mod.CurrentRootFolder; foreach (XElement xmlFile in xmlMod.Descendants("File")) { if (xmlFile.Attribute("path") != null) { mod.LooseFiles.Add(xmlFile.Attribute("path").Value); } } } /*if (xmlMod.Attribute("frozen") != null) * { * frozen = XmlConvert.ToBoolean(xmlMod.Attribute("frozen").Value); * }*/ mods.Add(mod); } // Legacy resource list: if (IniFiles.Config.GetBool("Preferences", "bMultipleGameEditionsUsed", false)) { string backedUpList = IniFiles.Config.GetString("Mods", $"sResourceIndexFileList{edition}", ""); string actualList = IniFiles.F76Custom.GetString("Archive", "sResourceIndexFileList", ""); if (backedUpList != "") { mods.Resources.ReplaceRange(ResourceList.FromString(backedUpList)); } else if (actualList != "") { mods.Resources.ReplaceRange(ResourceList.FromString(actualList)); } } ProgressChanged?.Invoke(Progress.Ongoing("Saving XML...", 1f)); mods.Save(); ProgressChanged?.Invoke(Progress.Done("Legacy mods imported.")); }