/// <summary> /// Checks the modules to see if any of the specified hifs are installed already, /// if they are it prompts the user to see if we should continue. /// </summary> /// <param name="hifs">The list of hifs</param> /// <param name="modules">The list of modules</param> /// <returns>True if the user cancels the operation, false if they do not</returns> protected bool CheckForHifConflicts(HakInfo[] hifs, string[] modules) { // Get the list of conflicts if there aren't any then just return false. HifConflictCollection conflicts = HakInstaller.CheckInstalledHifs(hifs, modules); if (null == conflicts) return false; // There are conflicts, prompt the user for what to do. HifConflictsForm form = new HifConflictsForm(conflicts); return DialogResult.Cancel == form.ShowDialog(this); }
/// <summary> /// Attempts to resolve tlk file conflicts between the module tlk and any /// tlk's defined in the hifs. It does this by attempting to build a /// new tlk file containing all of the tlk entries from all tlks. If there /// are no overlapping entries in the tlks's then this will succeed and /// the name of the new tlk will be returned, if there are overlaps then /// this will fail and string.Empty will be returned. /// </summary> /// <param name="hakInfos">The HIFs being added to the module</param> /// <param name="module">The module for which we are resolving conflicts</param> /// <param name="moduleInfo">The module info for the module</param> /// <param name="conflicts">The list of files in conflict</param> /// <returns>The name of the merge hak file, or string.Empty if a merge tlk /// could not be generated.</returns> public string ResolveConflicts(HakInfo[] hakInfos, Erf module, ModuleInfo moduleInfo, OverwriteWarningCollection conflicts) { try { // Reset the message shown flag so we show the message once. conflictHakMessageShown = false; // Generate the name of the conflict resolution hak and the directory // in which to place the files that will be added to the hak. conflictHak = GetFileName(module, "hak"); conflictHakDir = NWN.NWNInfo.GetFullFilePath(conflictHak) + ".temp"; OverwriteWarningCollection copy = conflicts.Clone(); foreach (OverwriteWarning conflict in copy) { // Check to see if we can attempt to resolve the conflict, if // we can then attempt to resolve it, and if the resolution is // successful then remove the conflict from the collection. switch (Path.GetExtension(conflict.File).ToLower()) { case ".2da": DisplayConflictHakMessage(module); if (Resolve2daConflict(hakInfos, module, moduleInfo, conflict)) conflicts.Remove(conflict); break; } } // Get all of the files in the conflict hak directory, if there are none // then there is no conflict hak. if (!Directory.Exists(conflictHakDir)) return string.Empty; string[] files = Directory.GetFiles(conflictHakDir); if (0 == files.Length) return string.Empty; // We have some resolved conflicts make the merge hak. Erf hak = Erf.New(Erf.ErfType.HAK, "Auto-generated merge hak"); foreach (string file in files) hak.AddFile(file, true); hak.SaveAs(NWN.NWNInfo.GetFullFilePath(conflictHak)); return conflictHak; } finally { if (Directory.Exists(conflictHakDir)) Directory.Delete(conflictHakDir, true); } }
/// <summary> /// Handler for the install button click event. It installs the selected /// haks in the selected modules. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void PerformInstall(HakInfo[] hifs, string[] modules) { try { // Before starting check for hif conflicts and ask the user if they really want to // continue. if (CheckForHifConflicts(hifs, modules)) return; // Create a progress control, InstallProgressForm progress = new InstallProgressForm(); ThreadPool.QueueUserWorkItem(new WaitCallback(DoHakInstall), new InstallInfo(hifs, modules, progress)); progress.ShowDialog(this); } finally { } }
/// <summary> /// Runs the application in console mode, silently adding the haks to /// the modules given on the command line. /// </summary> /// <returns>0 if successful, otherwise -1</returns> private static int ConsoleMode() { // Validate the files, if we fail validation then exit now. if (!ValidateFiles(hifStrings) || !ValidateFiles(moduleStrings)) return -1; // Convert the string collections to arrays and install the haks // in the modules. HakInfo[] hifs = new HakInfo[hifStrings.Count]; for (int i = 0; i < hifStrings.Count; i++) hifs[i] = new HakInfo(hifStrings[i]); string[] modules = new string[moduleStrings.Count]; moduleStrings.CopyTo(modules, 0); try { HakInstaller.InstallHaks(hifs, modules, null); } catch (Exception e) { Console.WriteLine(e.Message); } return 0; }
/// <summary> /// Class constructor /// </summary> /// <param name="hifs">The list of haks to add</param> /// <param name="modules">The list of modules to add haks to</param> /// <param name="progress">The object used to show progress</param> public InstallInfo(HakInfo[] hifs, string[]modules, InstallProgressForm progress) { this.hifs = hifs; this.modules = modules; this.progress = progress; }
/// <summary> /// Loads all of the hack info files into the hak check list box. /// </summary> protected void LoadHakInfoList(CheckedListBox checkedHaks) { // Get all of the modules in the module directory and add them to // the list box. string[] haks = Directory.GetFiles(NWNInfo.GetPathForFile("foo.hif"), "*.hif"); foreach (string hak in haks) { // Load the HIF now and perform validation before adding it to the // list of HIFs. HakInfo hif = new HakInfo(hak); string error; if (hif.Validate(out error)) checkedHaks.Items.Add(hif); else MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (0 == checkedHaks.Items.Count) { MessageBox.Show(StringResources.GetString("NoHIFS"), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Close(); Hide(); Application.Exit(); } }
/// <summary> /// Installs the listed haks (defined by hif files) on the module. /// </summary> /// <param name="hakInfos">The array of loaded hif files to install.</param> /// <param name="decompressedErfs">The string collection of decompressed /// erf files from the hifs. The values in here should be the /// temp directories in which the erfs have been decompressed to. If this /// collection is empty then the erfs will be decompressed to temp directories /// and the directories returned in this collection. It will be the caller's /// responsibility to delete them.</param> /// <param name="moduleFile">The module to add the haks to</param> /// <param name="progress">An interface used to an object used to display /// progress information, or null if no progress information is desired</param> private void DoInstall(HakInfo[] hakInfos, StringCollection decompressedErfs, string moduleFile, IHakInstallProgress progress) { mergeHak = string.Empty; mergeTlk = string.Empty; StringCollection tempDirs = new StringCollection(); try { // Load the module file. Progress(progress, true, "Loading {0}", moduleFile); if (progress.IsCancelled) return; Erf module = Erf.Load(NWNInfo.GetFullFilePath(moduleFile)); // Decompress the module to a temp directory. Progress(progress, true, "Decompressing {0}", moduleFile); currentTempDir = Decompress(module, tempDirs); // Load the moduleInfo for the module. ModuleInfo moduleInfo = new ModuleInfo(currentTempDir); // Check for any tlk file conflicts and abort the install if they // cannot be resolved. CheckForTlkConflicts(hakInfos, module, moduleInfo, progress); // Check for any file conflicts and make sure it is OK with the user. Progress(progress, false, "Checking for overwrites"); CheckForHakConflicts(hakInfos, decompressedErfs, module, moduleInfo, progress); // Add any erf files to the module. foreach (string erfDir in decompressedErfs) { // Add each of the erf files to the module. string[] files = Directory.GetFiles(erfDir); foreach (string file in files) { // Add the file to the module. Progress(progress, true, "Adding {1} to\n{0}", moduleFile, Path.GetFileName(file)); module.AddFile(Path.Combine(erfDir, file), true); // If the file is an area file then add it to the module's area list. if (0 == string.Compare(".are", Path.GetExtension(file), true, CultureInfo.InvariantCulture)) moduleInfo.AddAreas(new string[] { Path.GetFileNameWithoutExtension(file) }); } } // If we are overwriting files in the module then warn the user that // they are doing so and give them a chance to abort. if (module.ReplacedFiles.Count > 0) { // Create a conflict collection and ask the user what to do. If // they cancel then trow a cancel exception to abort adding the // hak(s). FileConflictCollection conflicts = CreateConflictCollection(module); if (!progress.ShouldReplaceFiles(conflicts)) throw new InstallCancelledException(); // The user may have chosen to keep some of the original module // files, un-add any file that they chose not to replace. foreach (FileConflict conflict in conflicts) if (!conflict.ReplaceFile) module.RemoveFileFromAddedList(conflict.HakFile); } // Now loop through all of the haks and make any module changes // required. foreach (HakInfo hakInfo in hakInfos) { // Set all of the module properties. Progress(progress, true, "Setting module properties for {0}", moduleFile); PropertyHandlerDictionary handlers = objects["Module"]; foreach (DictionaryEntry entry in hakInfo.ModuleProperties) { if (progress.IsCancelled) throw new InstallCancelledException(); // Resolve the DictionaryEntry to native data. string property = (string) entry.Key; StringCollection values = (StringCollection) entry.Value; if (0 == values.Count) continue; // Look up the handler for the property, throwing an exception // if we don't find one, then invoke it. PropertyHandler handler = handlers[property]; if (null == handler) throw new InvalidOperationException("Unknown module property " + property); handler(module, moduleInfo, property, values); } } // If we have a merge hak then add it now so it goes to the top of // the hak list. if (string.Empty != mergeHak) { StringCollection mergeHakCollection = new StringCollection(); mergeHakCollection.Add(mergeHak); Module_Hak(module, moduleInfo, "hak", mergeHakCollection); } // Build string arrays of the hif names and versions of all of the HakInfo // objects we added to the module, then update the module's installed // HakInfo property. This is a custom property used by this tool to // keep track of what is installed on a module. string[] hifs = new string[hakInfos.Length]; float[] versions = new float[hakInfos.Length]; for (int i = 0; i < hakInfos.Length; i++) { hifs[i] = hakInfos[i].Name; versions[i] = hakInfos[i].Version; } moduleInfo.AddInstalledHakInfos(hifs, versions); // Save the changes to the module info file. moduleInfo.Save(); // Backup the old module file before saving. string backupName = Path.Combine(NWNInfo.GetPathForFile(moduleFile), Path.GetFileNameWithoutExtension(moduleFile) + ".BackupMod"); File.Copy(NWNInfo.GetFullFilePath(moduleFile), backupName, true); // Recreate the module file with our changed files. Progress(progress, true, "Saving {0}", moduleFile); module.RecreateFile(); // If we created merge files then display a message to the user // telling them what we did. if (string.Empty != mergeHak || string.Empty != mergeTlk) { string files = "\r\n\r\n\t"; if (string.Empty != mergeHak) files += NWN.NWNInfo.GetPartialFilePath(mergeHak); if (string.Empty != mergeTlk) { if (string.Empty != files) files += "\r\n\t"; files += NWN.NWNInfo.GetPartialFilePath(mergeTlk); } progress.DisplayMessage(string.Format( "There were conflicts between the custom content you are trying to add and " + "the files already used by the module '{0}'. Merge files were created to resolve " + "these conflicts, you should delete these files when you are finished " + "with your module." + files, Path.GetFileNameWithoutExtension(module.FileName))); } } catch (Exception e) { // Delete any merge files we created, the install is failing.s if (string.Empty != mergeTlk) File.Delete(NWN.NWNInfo.GetFullFilePath(mergeTlk)); if (string.Empty != mergeHak) File.Delete(NWN.NWNInfo.GetFullFilePath(mergeHak)); // If the exception isn't an InstallCancelledException then throw it. // InstallCancelledExceptions are thrown to abort the install, we want to eat those. if (!(e is InstallCancelledException)) throw; } finally { // Always clean up temp dirs no matter what. foreach (string dir in tempDirs) try { if (Directory.Exists(dir)) Directory.Delete(dir, true); } catch{} } }
/// <summary> /// Validates the single HIF and throws an EntryPointNotFoundException /// to exit the application if it does not validate. /// </summary> private void ValidateHIF() { hif = new HakInfo(NWNInfo.GetFullFilePath(MainForm.Hif)); // Validate the PRC pack HIF, if validation fails then display // the error message and exit the app. string error; if (!hif.Validate(out error)) { MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); throw new EntryPointNotFoundException(); } }
/// <summary> /// Attempts to resolve conflicts for a 2da file. It does this be attempting to /// merge all duplicate copies of the 2da file into one merge 2da file. /// </summary> /// <param name="hakInfos">The HIFs being added to the module</param> /// <param name="module">The module</param> /// <param name="moduleInfo">The module info for the module</param> /// <param name="conflict">The 2da file in conflict</param> private bool Resolve2daConflict(HakInfo[] hakInfos, Erf module, ModuleInfo moduleInfo, OverwriteWarning conflict) { try { // Create an array list and get the 2da from the module, // adding it to the list if we get it. ArrayList list = new ArrayList(); _2DA twoDA = Get2da(moduleInfo, conflict.File); if (null != twoDA) list.Add(twoDA); // Now get all of the copies of the 2da from the various HIFs and // add them as well. foreach (HakInfo hakInfo in hakInfos) { twoDA = Get2da(hakInfo, conflict.File); if (null != twoDA) list.Add(twoDA); } // Load the BioWare version of the the 2da to use as a baseline, if the // file isn't in the bioware directory then we will have to make due w/o // it just make a blank 2da with the correct schema. _2DA bioware = LoadBioWare2da(conflict.File); // At this point we have all relevent copies of the conflicting 2da loaded into // memory, we now need to generate a merge 2da if possible. _2DA merge = Merge2das(bioware, list); if (null == merge) return false; // We have successfully merged all of the 2das, save the merge 2da and // return true. if (!Directory.Exists(conflictHakDir)) Directory.CreateDirectory(conflictHakDir); merge.SaveAs(Path.Combine(conflictHakDir, conflict.File)); return true; } catch (Exception) { return false; } }
/// <summary> /// This function checks for tlk conflicts, checking to see if the module /// and hifs have tlk files. If there are multiple tlk files it will attempt /// to generate a merge tlk file, if this cannot be done it will display an /// error message and throw an InstallCancelledException to cancel the install. /// </summary> /// <param name="hakInfos">The hak infos being added to the module</param> /// <param name="module">The module</param> /// <param name="moduleInfo">The module info</param> /// <param name="progress">The object implemening the progress interface</param> private void CheckForTlkConflicts(HakInfo[] hakInfos, Erf module, ModuleInfo moduleInfo, IHakInstallProgress progress) { // Create a tlk string collection and add the module's tlk if it has one. StringCollection tlks = new StringCollection(); if (string.Empty != moduleInfo.CustomTlk) tlks.Add(moduleInfo.CustomTlk.ToLower() + ".tlk"); // Add all of the tlk's from all of the HIFs. foreach (HakInfo hif in hakInfos) { StringCollection hifTlks = hif.ModuleProperties["customtlk"]; if (null != hifTlks && hifTlks.Count > 0) { // Loop through the tlk's individually to exclude duplicates. foreach (string hifTlk in hifTlks) { string lower = hifTlk.ToLower(); if (!tlks.Contains(lower)) tlks.Add(lower); } } } // If we have less than 2 tlks there is no conflict to resolve. if (tlks.Count < 2) return; // We have 2 or more tlk files, create a conflict resolver to // build a merge tlk file. ConflictResolver resolver = new ConflictResolver(progress); string[] tlkStrings = new string[tlks.Count]; tlks.CopyTo(tlkStrings, 0); mergeTlk = resolver.ResolveTlkConflict(module, tlkStrings); // If we don't get a merge tlk back from the conflict resolver then we couldn't // resolve the conflict. This is a fatal error so display an error message and // cancel the install. if (string.Empty == mergeTlk) { progress.DisplayErrorMessage("The module and custom content contain tlk files " + "that cannot be merged. The module update will be aborted."); throw new InstallCancelledException(); } // Save the merge tlk as the module's custom tlk. moduleInfo.CustomTlk = Path.GetFileNameWithoutExtension(mergeTlk.ToLower()); }
/// <summary> /// This function checks for hak conflicts, checking to see if any files /// in the hifs will overwrite files in the module or vica versa. If /// overwrites will happen, it prompts the user to see if we should continue, /// throwing an InstallCancelledException() if the user chooses to cancel. /// </summary> /// <param name="hakInfos">The hak infos being added to the module</param> /// <param name="decompressedErfs">The decompressed erfs</param> /// <param name="module">The module</param> /// <param name="moduleInfo">The module info</param> /// <param name="progress">The object implemening the progress interface</param> private void CheckForHakConflicts(HakInfo[] hakInfos, StringCollection decompressedErfs, Erf module, ModuleInfo moduleInfo, IHakInstallProgress progress) { // Create a hashtable for fast lookup and add all of the files in all // of the decompressed erf's to it. Hashtable hifErfHash = new Hashtable(10000); foreach(string directory in decompressedErfs) { // Strip the ".temp" off the end of the name. string erf = Path.GetFileNameWithoutExtension(directory); string[] files = Directory.GetFiles(directory); foreach (string file in files) { // Only add the ERF file if it's not already there. We assume that // the ERF's in the HIF play well together so we ignore duplicates. string key = Path.GetFileName(file).ToLower(); if ("exportinfo.gff" != key && !hifErfHash.Contains(key)) hifErfHash.Add(key, erf.ToLower()); } } // Build a list of all of the added haks. StringCollection hakInfoHaks = new StringCollection(); foreach (HakInfo hakInfo in hakInfos) { StringCollection temp = hakInfo.ModuleProperties["hak"] as StringCollection; if (null != temp) { foreach (string tempString in temp) hakInfoHaks.Add(tempString.ToLower()); } } // Add all of the files in all of the haks to the hash table. Hashtable hifHakHash = new Hashtable(10000); foreach (string hakName in hakInfoHaks) { Erf hak = Erf.Load(NWNInfo.GetFullFilePath(hakName)); StringCollection files = hak.Files; foreach (string file in files) try { string key = file.ToLower(); string hakNameLower = hakName.ToLower(); hifHakHash.Add(key, hakNameLower); } catch (ArgumentException) {} } // At this point we have built a lookup hash table that contains every // file going into the module (either directly in an erf or indirectly // in a hak). Now we need to loop through all of the files in the // module (and all of it's haks) and check to see if any of them are // going to get overwritten. At this point we have several cases. // 1. Module content is going to get replaced by erf content. We // do not handle that case now, we wait until the end and allow // the user to selectivly overwrite whatever they wish. // 2. Module content is going to get replaced by hak content. We must // warn the user that module files will not be used and the module // may not work. // 3. Module hak content is going to get replaced by hak content. Same // as above. // 4. Module hak content is going to overwrite erf content from the hif. // In this case the hif's content is the content that is going to be // ignored, again the user has to be warned. OverwriteWarningCollection hakWarnings = new OverwriteWarningCollection(); OverwriteWarningCollection erfWarnings = new OverwriteWarningCollection(); string moduleFileName = Path.GetFileName(module.FileName); // Loop through all of the files in the module checking to see if files in // added haks will overwrite them. StringCollection moduleFiles = module.Files; foreach (string file in moduleFiles) { string source = hifHakHash[file.ToLower()] as string; if (null != source) hakWarnings.Add(new OverwriteWarning(file.ToLower(), moduleFileName, source)); } // Loop through all of the files in the module's haks checking to see if // files in the added haks will overwrite them or if they will overwrite // files in added erf's. StringCollection moduleHaks = moduleInfo.Haks; foreach (string moduleHak in moduleHaks) { // Check to see if the hak in the module is one of the haks being added (this is // a no-op condition which will result in 100% duplicates, no need to check it). string hak = moduleHak + ".hak"; if (hakInfoHaks.Contains(hak.ToLower())) continue; Erf erf = Erf.Load(NWNInfo.GetFullFilePath(hak)); StringCollection hakFiles = erf.Files; foreach (string file in hakFiles) { // If the file is in the hak hash then it is going to be // overwritten by the hif's haks. string key = file.ToLower(); string source = hifHakHash[key] as string; if (null != source) hakWarnings.Add(new OverwriteWarning(key, Path.GetFileName(erf.FileName.ToLower()), source)); // If the file is in the erf hash then it will overwrite the // hif's erf. source = hifErfHash[key] as string; if (null != source) erfWarnings.Add(new OverwriteWarning(key, source, Path.GetFileName(erf.FileName.ToLower()))); } } // We have built the list of conflicts, before asking the user try to resolve the // conflicts as we may be able to generate a merge hak to resolve some of them. if (hakWarnings.Count > 0) { ConflictResolver resolver = new ConflictResolver(progress); mergeHak = resolver.ResolveConflicts(hakInfos, module, moduleInfo, hakWarnings); } // We have finished checking for files that are going to get overwritten. // If we have any warnings to issue to the user then do so now. if (hakWarnings.Count > 0 && !progress.ShouldOverwrite(hakWarnings, false, OverwriteWarningType.HifsOverwritesModule)) throw new InstallCancelledException(); if (erfWarnings.Count > 0 && !progress.ShouldOverwrite(erfWarnings, false, OverwriteWarningType.ModuleOverwritesHifs)) throw new InstallCancelledException(); }
/// <summary> /// Installs the listed haks (defined by hif files) on the module. /// </summary> /// <param name="hifs">The list of haks to add</param> /// <param name="moduleFile">The module to add the haks to</param> /// <param name="progress">An interface used to an object used to display /// progress information, or null if no progress information is desired</param> public static void InstallHaks(HakInfo[] hifs, string moduleFile, IHakInstallProgress progress) { // Force the thread to use the invariant culture to make the install // code work on foreign language versions of windows. CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; try { // If no progress was given then use a dummy one which does nothing. if (null == progress) progress = new DummyProgress(); // Invoke the private method on the singleton to do all the real work. Singleton.DoInstall(hifs, new string[] { moduleFile }, progress); } finally { Thread.CurrentThread.CurrentCulture = currentCulture; } }
/// <summary> /// This method checks to see if any of the proposed hifs are installed on any of the /// given modules, returning a collection of conflicts that can be displayed to the user. /// </summary> /// <param name="proposedHifs">The list of hifs that the user wants to add to the modules</param> /// <param name="modules">The list of modules</param> /// <returns>A collection containing the list of conflicts, or null if there are no /// conflicts</returns> public static HifConflictCollection CheckInstalledHifs(HakInfo[] proposedHifs, string[] modules) { // Force the thread to use the invariant culture to make the install // code work on foreign language versions of windows. CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; try { HifConflictCollection conflicts = null; foreach (string module in modules) { // Load the module info data and create an object for it. MemoryStream stream = Erf.GetFile(NWNInfo.GetFullFilePath(module), ModuleInfo.FileName); if (null == stream) NWN.FileTypes.Tools.NWNLogger.Log(10, "HakInstaller.CheckInstalledHifs, Erf.GetFile() returned null!!!"); ModuleInfo info = new ModuleInfo(stream); // Load the installed hifs in module if any. string[] installedHifs; float[] installedVersions; info.GetInstalledHakInfos(out installedHifs, out installedVersions); // Create a StringCollection of the proposed hifs so we can use IndexOf(), // then check to see if there are any hif conflicts, if there are then // add them to the conflict list. StringCollection proposedHifsColl = new StringCollection(); foreach (HakInfo hif in proposedHifs) proposedHifsColl.Add(Path.GetFileNameWithoutExtension(hif.Name).ToLower()); for (int i = 0; i < installedHifs.Length; i++) { if (proposedHifsColl.Contains(installedHifs[i].ToLower())) { HifConflict conflict = new HifConflict(module, installedHifs[i], installedVersions[i], 0); if (null == conflicts) conflicts = new HifConflictCollection(); conflicts.Add(conflict); } } } return conflicts; } finally { Thread.CurrentThread.CurrentCulture = currentCulture; } }
/// <summary> /// Loads a collection of hifs into memory. /// </summary> /// <param name="hifs">The hifs to load</param> /// <param name="progress">An interface used to an object used to display /// progress information, or null if no progress information is desired</param> /// <returns>An array of HakInfo objects representing the hifs</returns> private HakInfo[] LoadHifs(string[] hifs, IHakInstallProgress progress) { // Loop through the array of hif files loading them into HakInfo objects. HakInfo[] hakInfos = new HakInfo[hifs.Length]; for (int i = 0; i < hifs.Length; i++) { // Load the hif file, stepping the progress bar. Progress(progress, true, "Reading {0}", hifs[i]); if (progress.IsCancelled) throw new InstallCancelledException(); hakInfos[i] = new HakInfo(Path.Combine(NWNInfo.HakInfoPath, hifs[i])); } return hakInfos; }
/// <summary> /// Gets the number of progress steps required given a source list of /// hifs and modules. /// </summary> /// <param name="hifs">The list of hifs</param> /// <param name="modules">The list of modules</param> /// <returns>The number of progress steps required.</returns> private int GetProgressCount(HakInfo[] hifs, string[] modules) { // Count the number of erfs and files in the erfs. int fileCount, erfCount; CountAddedFiles(hifs, out fileCount, out erfCount); // Start with 3 steps per module, to load, decompress, and save. int count = modules.Length * 3; // Add one step per hif to load them. count += hifs.Length; // Add 2 steps per erf, one to load one to decompress. count += erfCount * 2; // Add a number of steps to add each file to each module. count += modules.Length * fileCount; // Add 1 steps for each (module, hif) pair to wire up events. count += modules.Length * hifs.Length; return count; }
/// <summary> /// Installs the listed haks (defined by hif files) on the listed /// modules. /// </summary> /// <param name="hifs">The list of haks to add</param> /// <param name="modules">The list of modules to add the haks to</param> /// <param name="progress">An interface used to an object used to display /// progress information, or null if no progress information is desired</param> private void DoInstall(HakInfo[] hifs, string[] modules, IHakInstallProgress progress) { StringCollection tempDirs = null; try { // Calcualte the number of steps needed for the progress bar. The // hard coded numbers are based on the number of step calls in // DoInstall(). progress.ProgressSteps = GetProgressCount(hifs, modules); // Load the hif files and decompress them to temp directories. tempDirs = DecompressHifErfs(hifs, progress); // Now apply the hifs to each module in turn. foreach (string module in modules) DoInstall(hifs, tempDirs, module, progress); } finally { // Always clean up temp dirs no matter what. if (null != tempDirs) { foreach (string dir in tempDirs) try { if (Directory.Exists(dir)) Directory.Delete(dir, true); } catch{} } } }
/// <summary> /// Gets the 2da file from the HIF by looking at all of the haks in the /// HIF and checking each hak for the file. /// </summary> /// <param name="hakInfo">The HIF</param> /// <param name="fileName">The name of the 2da to get</param> /// <returns>A 2da object for the 2da or null if the 2da is not in any /// of the HIF's haks</returns> private _2DA Get2da(HakInfo hakInfo, string fileName) { // Get the list of haks in the HIF and loop through all of them // trying to load the 2da, as soon as we get it return it. StringCollection haks = hakInfo.ModuleProperties["hak"]; if (null == haks) return null; foreach (string hak in haks) { _2DA twoDA = Extract2da(hak, fileName); if (null != twoDA) return twoDA; } return null; }
/// <summary> /// Counts the number of files in the hifs that will be added to a /// module, to allow for progress bar movement. /// </summary> /// <param name="hifs">The list of hifs</param> /// <param name="fileCount">Returns the number of files in the erfs.</param> /// <param name="erfCount">Returns the number of erf files.</param> private void CountAddedFiles(HakInfo[] hifs, out int fileCount, out int erfCount) { fileCount = 0; erfCount = 0; // Now loop through all of the haks and add them to the module. foreach (HakInfo hif in hifs) { erfCount += hif.Erfs.Count; foreach (string erf in hif.Erfs) fileCount += Erf.GetFileCount(Path.Combine(NWNInfo.GetPathForFile(erf), erf)); } }
/// <summary> /// Decompresses all of the ERF files in the hak info objects, returning /// a collection of the temp directories they are in. /// </summary> /// <param name="hakInfos">The array of hak infos for which to decompress /// the erfs</param> /// <param name="progress">An interface used to an object used to display /// progress information, or null if no progress information is desired</param> /// <returns>A string collection of all of the directories in which the /// ERF's have been decompressed.</returns> private StringCollection DecompressHifErfs(HakInfo[] hakInfos, IHakInstallProgress progress) { StringCollection tempDirs = new StringCollection(); foreach (HakInfo hakInfo in hakInfos) { // Add any erf files to the module. foreach (string erf in hakInfo.Erfs) { // Load the erf. Progress(progress, true, "Loading {0}", erf); if (progress.IsCancelled) throw new InstallCancelledException(); Erf hakErf = Erf.Load(Path.Combine(NWNInfo.GetPathForFile(erf), erf)); // Decompress the erf into it's own temporary directory, saving the // directory for later cleanup. Progress(progress, true, "Decompressing {0}", erf); Decompress(hakErf, tempDirs); } } return tempDirs; }
/// <summary> /// Handler for the install button click event. It installs the selected /// haks in the selected modules. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonInstall_Click(object sender, System.EventArgs e) { // Build an array of the checked hif files. HakInfo[] hifs = new HakInfo[checkedHaks.CheckedItems.Count]; for (int i = 0; i < checkedHaks.CheckedIndices.Count; i++) hifs[i] = (HakInfo) checkedHaks.CheckedItems[i]; // Count the total number of .mod/.nwm files selected. int numModules = 0; foreach (Module module in checkedModules.CheckedItems) numModules += module.Modules.Length; // Create the modules list and fill it in. string[] modules = new string[numModules]; numModules = 0; foreach (Module module in checkedModules.CheckedItems) { module.Modules.CopyTo(modules, numModules); numModules += module.Modules.Length; } PerformInstall(hifs, modules); // Clear the checked state of all modules. CheckedListBox.CheckedIndexCollection checkedIndeces = checkedModules.CheckedIndices; foreach (int index in checkedIndeces) checkedModules.SetItemChecked(index, false); }