Пример #1
0
 /// <summary>
 /// Class constructor
 /// </summary>
 /// <param name="progress">The interface used to provide progress information
 /// to the user</param>
 public ConflictResolver(IHakInstallProgress progress)
 {
     this.progress = progress;
     conflictHak = string.Empty;
     conflictHakDir = string.Empty;
     conflictHakMessageShown = false;
 }
Пример #2
0
        /// <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{}
            }
        }
Пример #3
0
        /// <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());
        }
Пример #4
0
        /// <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;
        }
Пример #5
0
        /// <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();
        }
Пример #6
0
        /// <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;
            }
        }
Пример #7
0
        /// <summary>
        /// Method to display a progress message.  It displays the message,
        /// steps the progress bar, and checks to see if the operation has
        /// been cancelled throwing a InstallCancelledException if it has.
        /// </summary>
        /// <param name="progress">The progress object</param>
        /// <param name="step">If true steps the progress bar</param>
        /// <param name="format">Message format string</param>
        /// <param name="args">Message arguments</param>
        private void Progress(IHakInstallProgress progress, bool step,
			string format, params object[] args)
        {
            // If the operation has been cancelled then abort.
            if (progress.IsCancelled) throw new InstallCancelledException();

            // Display the message and step the progress bar.
            progress.SetMessage(format, args);
            if (step) progress.Step();
        }
Пример #8
0
        /// <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;
        }
Пример #9
0
        /// <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{}
                }
            }
        }