/// <summary>
        /// Creates a new mod from a folder.
        /// </summary>
        /// <param name="gamePath">Path to the game installation</param>
        /// <param name="folderPath">Path to folder</param>
        /// <returns></returns>
        private static ManagedMod FromFolder(string gamePath, string folderPath, Action <Progress> ProgressChanged = null)
        {
            // Get path information:
            folderPath = EnsureLongPathSupport(folderPath);
            string folderName = Path.GetFileName(folderPath);

            // Install mod:
            ManagedMod newMod = new ManagedMod(gamePath);

            newMod.Title             = folderName;
            newMod.ArchiveName       = folderName + ".ba2";
            newMod.ManagedFolderName = folderName;
            if (!Utils.IsFileNameValid(newMod.ManagedFolderName) || Directory.Exists(newMod.ManagedFolderPath))
            {
                newMod.ManagedFolderName = newMod.DefaultManagedFolderName;
            }

            // Copy folder:
            CopyDirectory(folderPath, newMod.ManagedFolderPath, ProgressChanged);

            ModActions.CleanUpFolder(newMod.ManagedFolderPath, ProgressChanged);
            ModActions.DetectOptimalModInstallationOptions(newMod);

            return(newMod);
        }
        public static void Freeze(ManagedMod mod)
        {
            // TODO: Remove connection to ManagedMods
            // Only freeze if not already frozen:
            if (mod.Frozen && File.Exists(mod.FrozenArchivePath))
            {
                // TODO: ModActions.Freeze: Should the mod get "refrozen"?
                //ManagedMods.Instance.logFile.WriteLine($"Cannot freeze a mod ('{mod.Title}') that is already frozen.\n");
                return;
            }

            Directory.CreateDirectory(mod.FrozenDataPath);

            // Getting preset:
            Archive2.Preset preset = ModHelpers.GetArchive2Preset(mod);

            ModDeployment.LogFile.WriteLine($"      Freezing mod '{mod.Title}'...");
            ModDeployment.LogFile.WriteLine($"         Format:      {preset.format}");
            ModDeployment.LogFile.WriteLine($"         Compression: {preset.compression}");
            ModDeployment.LogFile.WriteLine($"         Destination: FrozenData\\{mod.FrozenArchiveName}");

            // Create archive:
            Archive2.Create(mod.FrozenArchivePath, mod.ManagedFolderPath, preset);

            // Change DiskState and save:
            mod.Frozen            = true;
            mod.FrozenCompression = mod.Compression;
            mod.FrozenFormat      = mod.Format;
        }
        /// <summary>
        /// Copies the folder and adds the mod to the list.
        /// Saves the xml file afterwards.
        /// </summary>
        public static void InstallFolder(ManagedMods mods, string folderPath, Action <Progress> ProgressChanged = null)
        {
            ManagedMod newMod = ModInstallations.FromFolder(mods.GamePath, folderPath, ProgressChanged);

            mods.Add(newMod);
            mods.Save();
            ProgressChanged?.Invoke(Progress.Done("Mod folder installed."));
        }
        /// <summary>
        /// Extracts the archive and adds the mod to the list.
        /// Saves the xml file afterwards.
        /// </summary>
        /// <param name="useSourceBA2Archive">When false, creates a new "frozen" mod.</param>
        public static void InstallArchive(ManagedMods mods, string filePath, bool useSourceBA2Archive = false, Action <Progress> ProgressChanged = null)
        {
            ManagedMod newMod = ModInstallations.FromArchive(mods.GamePath, filePath, useSourceBA2Archive, ProgressChanged);

            mods.Add(newMod);
            mods.Save();
            ProgressChanged?.Invoke(Progress.Done("Mod archive installed."));
        }
        /// <summary>
        /// Adds a new blank mod.
        /// </summary>
        public static void InstallBlank(ManagedMods mods)
        {
            ManagedMod newMod = new ManagedMod(mods.GamePath);

            newMod.Title       = "Untitled";
            newMod.ArchiveName = "untitled.ba2";
            Directory.CreateDirectory(newMod.ManagedFolderPath);
            mods.Add(newMod);
            mods.Save();
        }
        public static void Unfreeze(ManagedMod mod)
        {
            // Delete *.ba2:
            if (File.Exists(mod.FrozenArchivePath))
            {
                File.Delete(mod.FrozenArchivePath);
            }

            // Change DiskState and save:
            mod.Frozen = false;
        }
        /// <summary>
        /// Copies and replaces from the external folder into the managed mod folder.
        /// </summary>
        /// <param name="copyFolder">If true, will copy the folder instead of it's contents.</param>
        public static void AddFolder(ManagedMod mod, string folderPath, bool copyFolder, Action <Progress> ProgressChanged = null)
        {
            string longFolderPath = EnsureLongPathSupport(folderPath);
            string folderName     = Path.GetFileName(folderPath);

            CopyDirectory(longFolderPath,
                          copyFolder ? Path.Combine(mod.ManagedFolderPath, folderName) : mod.ManagedFolderPath,
                          ProgressChanged);
            // TODO: ModActions.CleanUpFolder(newMod.ManagedFolderPath, ProgressChanged);
            ProgressChanged?.Invoke(Progress.Done("Folder added to mod."));
        }
        /// <summary>
        /// Creates a deep copy of 'mod'.
        /// </summary>
        /// <param name="mod">The object it makes a copy of.</param>
        public ManagedMod(ManagedMod mod)
        {
            /*
             * Info
             */

            this.title             = mod.title;
            this.ID                = mod.ID;
            this.URL               = mod.URL;
            this.Version           = mod.Version;
            this.guid              = mod.guid;
            this.GamePath          = mod.GamePath;
            this.ManagedFolderName = mod.ManagedFolderName;


            /*
             * General
             */
            this.Enabled        = mod.Enabled;
            this.Deployed       = mod.Deployed;
            this.Method         = mod.Method;
            this.PreviousMethod = mod.PreviousMethod;


            /*
             * SeparateBA2
             */

            this.archiveName        = mod.archiveName;
            this.CurrentArchiveName = mod.CurrentArchiveName;
            this.Compression        = mod.Compression;
            this.CurrentCompression = mod.CurrentCompression;
            this.Format             = mod.Format;
            this.CurrentFormat      = mod.CurrentFormat;


            /*
             * SeparateBA2 Frozen
             */
            this.Freeze            = mod.Freeze;
            this.Frozen            = mod.Frozen;
            this.FrozenCompression = mod.FrozenCompression;
            this.FrozenFormat      = mod.FrozenFormat;


            /*
             * Loose
             */
            this.LooseFiles        = new List <string>(mod.LooseFiles);
            this.RootFolder        = mod.RootFolder;
            this.CurrentRootFolder = mod.CurrentRootFolder;
        }
Esempio n. 9
0
        /// <summary>
        /// Returns the legacy string of ArchiveFormat.
        ///
        /// public enum ArchiveFormat
        /// {
        ///     Auto,
        ///     General,
        ///     Textures
        /// }
        /// </summary>
        /// <returns>"Auto", "General", or "Textures"</returns>
        private static string GetLegacyArchiveFormatName(ManagedMod mod)
        {
            switch (mod.CurrentFormat)
            {
            case ManagedMod.ArchiveFormat.General:
                return("General");

            case ManagedMod.ArchiveFormat.Textures:
                return("Textures");

            case ManagedMod.ArchiveFormat.Auto:
            default:
                return("Auto");
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Returns the legacy string of ArchiveCompression.
        ///
        /// public enum ArchiveCompression
        /// {
        ///     Auto,
        ///     Compressed,
        ///     Uncompressed
        /// }
        /// </summary>
        /// <returns>"Auto", "Compressed", or "Uncompressed"</returns>
        private static string GetLegacyArchiveCompressionName(ManagedMod mod)
        {
            switch (mod.CurrentCompression)
            {
            case ManagedMod.ArchiveCompression.Compressed:
                return("Compressed");

            case ManagedMod.ArchiveCompression.Uncompressed:
                return("Uncompressed");

            case ManagedMod.ArchiveCompression.Auto:
            default:
                return("Auto");
            }
        }
Esempio n. 11
0
        /// <summary>
        /// Returns the legacy string of the install method:
        ///
        /// public enum FileType
        /// {
        ///     Loose,
        ///     BundledBA2,
        ///     SeparateBA2
        /// }
        /// </summary>
        /// <returns>"Loose", "SeparateBA2", or "BundledBA2"</returns>
        private static string GetLegacyInstallMethodName(ManagedMod mod)
        {
            switch (mod.PreviousMethod)
            {
            case ManagedMod.DeploymentMethod.LooseFiles:
                return("Loose");

            case ManagedMod.DeploymentMethod.SeparateBA2:
                return("SeparateBA2");

            case ManagedMod.DeploymentMethod.BundledBA2:
            default:
                return("BundledBA2");
            }
        }
        /// <summary>
        /// Deletes all files of 'mod'.
        /// This includes the managed folder and frozen archive.
        /// </summary>
        private static void DeleteFiles(ManagedMod mod)
        {
            // Delete managed folder:
            if (Directory.Exists(mod.ManagedFolderPath))
            {
                Directory.Delete(mod.ManagedFolderPath, true);
            }

            // Delete frozen archive:
            if (/*mod.Frozen &&
                 * mod.PreviousMethod == ManagedMod.DeploymentMethod.SeparateBA2 &&*/
                File.Exists(mod.FrozenArchivePath))
            {
                File.Delete(mod.FrozenArchivePath);
            }
        }
Esempio n. 13
0
        public static void Remove(ManagedMod mod, ResourceList resources, String GamePath)
        {
            if (mod.Deployed)
            {
                switch (mod.PreviousMethod)
                {
                case ManagedMod.DeploymentMethod.BundledBA2:
                    LogFile.WriteLine($"      Skipped (mod is bundled)");
                    break;

                case ManagedMod.DeploymentMethod.SeparateBA2:
                    LogFile.WriteLine($"      Deleting {mod.CurrentArchiveName}");
                    File.Delete(mod.CurrentArchivePath);
                    resources.Remove(mod.CurrentArchiveName);
                    break;

                case ManagedMod.DeploymentMethod.LooseFiles:
                    LogFile.WriteLine($"      Deleting loose files");
                    foreach (string relFilePath in mod.LooseFiles)
                    {
                        string installedFilePath = Path.GetFullPath(Path.Combine(GamePath, mod.CurrentRootFolder, relFilePath));     // .Replace("\\.\\", "\\")

                        // Delete file, if existing:
                        if (File.Exists(installedFilePath))
                        {
                            File.Delete(installedFilePath);
                        }

                        // Rename backup, if there is one:
                        if (File.Exists(installedFilePath + ".old"))
                        {
                            File.Move(installedFilePath + ".old", installedFilePath);
                        }

                        // Remove empty folders one by one, if existing:
                        else
                        {
                            RemoveEmptyFolders(Path.GetDirectoryName(installedFilePath));
                        }
                    }
                    mod.LooseFiles.Clear();
                    break;
                }

                mod.Deployed = false;
            }
        }
        /// <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);
        }
Esempio n. 15
0
        /// <summary>
        /// Used in the deployment chain to copy individual files to a temporary folder.
        /// It sorts files into different temporary folders.
        /// Each temporary folder gets packed to a bundled *.ba2 archive.
        /// </summary>
        private static void CopyFilesToTempSorted(ManagedMod mod, DeployArchiveList archives)
        {
            // Iterate over each file in the managed folder:
            IEnumerable <string> files = Directory.EnumerateFiles(mod.ManagedFolderPath, "*.*", SearchOption.AllDirectories);

            foreach (string filePath in files)
            {
                FileInfo info          = new FileInfo(filePath);
                string   fileExtension = info.Extension.ToLower();

                // Make a relative path:
                string relativePath = Utils.MakeRelativePath(mod.ManagedFolderPath, filePath);

                // Determine the type of archive:
                string destinationPath;
                if (relativePath.Trim().ToLower().StartsWith("sound") || relativePath.Trim().ToLower().StartsWith("music") ||
                    (new string[] { ".wav", ".xwm", ".fuz", ".lip" }).Contains(fileExtension))
                {
                    archives.SoundsArchive.Count++;
                    destinationPath = Path.Combine(archives.SoundsArchive.TempPath, relativePath);
                }
                else if (fileExtension == ".dds")
                {
                    archives.TexturesArchive.Count++;
                    destinationPath = Path.Combine(archives.TexturesArchive.TempPath, relativePath);
                }
                else
                {
                    archives.GeneralArchive.Count++;
                    destinationPath = Path.Combine(archives.GeneralArchive.TempPath, relativePath);
                }

                // Copy the file to the correct temp folder:
                Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));

                if (Configuration.bUseHardlinks)
                {
                    Utils.CreateHardLink(filePath, destinationPath, true);
                }
                else
                {
                    File.Copy(filePath, destinationPath, true);
                }
            }
        }
        /// <summary>
        /// Creates a new mod from any supported archive. (zip, tar, rar, 7z, ba2)
        /// BA2 files can be installed frozen if needed.
        /// </summary>
        /// <param name="gamePath">Path to the game installation</param>
        /// <param name="filePath">Path to archive</param>
        /// <param name="useSourceBA2Archive">When false, creates a new "frozen" mod.</param>
        /// <returns></returns>
        private static ManagedMod FromArchive(string gamePath, string filePath, bool useSourceBA2Archive = false, Action <Progress> ProgressChanged = null)
        {
            // Get path information:
            string longFilePath  = EnsureLongPathSupport(filePath);
            string fileNameWOEx  = Path.GetFileNameWithoutExtension(longFilePath);
            string fileExtension = Path.GetExtension(longFilePath);

            // Install mod:
            ManagedMod newMod = new ManagedMod(gamePath);

            newMod.Title             = fileNameWOEx;
            newMod.ArchiveName       = fileNameWOEx + ".ba2";
            newMod.ManagedFolderName = fileNameWOEx;
            if (!Utils.IsFileNameValid(newMod.ManagedFolderName) || Directory.Exists(newMod.ManagedFolderPath))
            {
                newMod.ManagedFolderName = newMod.DefaultManagedFolderName;
            }

            // Extract mod:
            ProgressChanged?.Invoke(Progress.Indetermined($"Extracting {Path.GetFileName(filePath)}"));
            ModInstallations.ExtractArchive(longFilePath, newMod.ManagedFolderPath);

            // Freeze mod conditionally:
            if (useSourceBA2Archive && fileExtension == ".ba2")
            {
                // Copy *.ba2 into FrozenData:
                FileInfo frozenPath = new FileInfo(newMod.FrozenArchivePath);
                ProgressChanged?.Invoke(Progress.Indetermined($"Copying {Path.GetFileName(filePath)} to {frozenPath.DirectoryName}"));
                Directory.CreateDirectory(frozenPath.DirectoryName);
                File.Copy(longFilePath, frozenPath.FullName, true);

                newMod.Frozen         = true;
                newMod.Freeze         = true;
                newMod.PreviousMethod = ManagedMod.DeploymentMethod.SeparateBA2;
                newMod.Method         = ManagedMod.DeploymentMethod.SeparateBA2;
            }
            else
            {
                ModActions.CleanUpFolder(newMod.ManagedFolderPath, ProgressChanged);
                ModActions.DetectOptimalModInstallationOptions(newMod);
            }

            return(newMod);
        }
        /// <summary>
        /// Extracts the archive and then copy and replaces from the temp folder into the managed mod folder.
        /// </summary>
        public static void AddArchive(ManagedMod mod, string filePath, Action <Progress> ProgressChanged = null)
        {
            string longFilePath   = EnsureLongPathSupport(filePath);
            string tempFolderPath = Path.Combine(Path.GetTempPath(), $"tmp_{mod.guid}");

            if (Directory.Exists(tempFolderPath))
            {
                Directory.Delete(tempFolderPath, true);
            }
            Directory.CreateDirectory(tempFolderPath);

            ProgressChanged?.Invoke(Progress.Indetermined($"Extracting {Path.GetFileName(filePath)}"));
            ModInstallations.ExtractArchive(longFilePath, tempFolderPath);
            ModActions.CleanUpFolder(tempFolderPath, ProgressChanged);
            CopyDirectory(tempFolderPath, mod.ManagedFolderPath, ProgressChanged);

            Directory.Delete(tempFolderPath, true);

            ProgressChanged?.Invoke(Progress.Done("Archive added to mod."));
        }
Esempio n. 18
0
        /// <summary>
        /// Used in the deployment chain to deploy a single mod with the Loose method.
        /// </summary>
        private static void DeployLooseFiles(ManagedMods mods, ManagedMod mod, String GamePath)
        {
            LogFile.WriteLine($"   Installing mod '{mod.Title}' as LooseFiles");

            mod.LooseFiles.Clear();

            // Iterate over each file in the managed folder ...
            foreach (string filePath in Directory.EnumerateFiles(mod.ManagedFolderPath, "*.*", SearchOption.AllDirectories))
            {
                // ... extract the relative path ...
                string relPath = Utils.MakeRelativePath(mod.ManagedFolderPath, filePath);
                mod.LooseFiles.Add(relPath);

                // ... determine the full destination path ...
                string   destinationPath = Path.Combine(GamePath, mod.RootFolder, relPath);
                FileInfo destInfo        = new FileInfo(destinationPath);
                Directory.CreateDirectory(destInfo.DirectoryName);

                // ... make a backup if the file already exists ...
                if (File.Exists(destinationPath) && !DoesLooseFileBelongToMod(mods, destinationPath) && !File.Exists(destinationPath + ".old"))
                {
                    File.Move(destinationPath, destinationPath + ".old");
                }

                // ... and copy the file (and replace it if necessary).
                LogFile.WriteLine($"      Copying: \"{relPath}\"");
                if (Configuration.bUseHardlinks)
                {
                    Utils.CreateHardLink(filePath, destinationPath, true);
                }
                else
                {
                    File.Copy(filePath, destinationPath, true);
                }
            }

            mod.CurrentRootFolder = mod.RootFolder;
            mod.Deployed          = true;
            mod.PreviousMethod    = ManagedMod.DeploymentMethod.LooseFiles;
        }
        /// <summary>
        /// Renames the managed folder.
        /// </summary>
        /// <param name="mod"></param>
        /// <param name="newFolderName"></param>
        /// <returns>True if successful, False otherwise</returns>
        public static bool RenameFolder(ManagedMod mod, string newFolderName)
        {
            // Don't rename if folder name is equal:
            if (mod.ManagedFolderName == newFolderName)
            {
                return(false);
            }

            // Don't rename if folder name is invalid:
            if (!Utils.IsFileNameValid(newFolderName))
            {
                return(false);
            }

            // Don't rename if folder already exists:
            string newFolderPath = Path.Combine(mod.GamePath, "Mods", newFolderName);

            if (Directory.Exists(newFolderPath))
            {
                return(false);
            }

            // Try to rename folder:
            try
            {
                Directory.Move(mod.ManagedFolderPath, newFolderPath);
            }
            catch
            {
                return(false);
            }

            // Successful?
            mod.ManagedFolderName = newFolderName;
            return(true);
        }
        public static void DetectOptimalModInstallationOptions(ManagedMod mod, Action <Progress> ProgressChanged = null)
        {
            ProgressChanged?.Invoke(Progress.Indetermined("Detecting installation options."));

            /*
             * Searching through folder:
             */

            bool resourceFoldersFound = false;
            bool generalFoldersFound  = false;
            bool texturesFolderFound  = false;
            bool soundFoldersFound    = false;
            bool stringsFolderFound   = false;
            bool dataFolderFound      = false;
            bool videoFolderFound     = false;
            bool dllFound             = false;

            foreach (string folderPath in Directory.EnumerateDirectories(mod.ManagedFolderPath))
            {
                string folderName = Path.GetFileName(folderPath).ToLower();

                if (ModHelpers.ResourceFolders.Contains(folderName))
                {
                    resourceFoldersFound = true;
                }
                if (ModHelpers.GeneralFolders.Contains(folderName))
                {
                    generalFoldersFound = true;
                }
                if (ModHelpers.TextureFolders.Contains(folderName))
                {
                    texturesFolderFound = true;
                }
                if (ModHelpers.SoundFolders.Contains(folderName))
                {
                    soundFoldersFound = true;
                }
                if (folderName == "strings")
                {
                    stringsFolderFound = true;
                }
                if (folderName == "data")
                {
                    dataFolderFound = true;
                }
                if (folderName == "video")
                {
                    videoFolderFound = true;
                }
            }

            foreach (string filePath in Directory.EnumerateFiles(mod.ManagedFolderPath))
            {
                string fileExtension = Path.GetExtension(filePath).ToLower();

                if (fileExtension == ".dll")
                {
                    dllFound = true;
                }
            }


            /*
             * Detecting optimal installation options:
             */

            if (resourceFoldersFound)
            {
                mod.Method      = ManagedMod.DeploymentMethod.SeparateBA2;
                mod.Format      = ManagedMod.ArchiveFormat.Auto;
                mod.Compression = ManagedMod.ArchiveCompression.Auto;
                mod.RootFolder  = "Data";
            }

            if (stringsFolderFound || videoFolderFound)
            {
                mod.Method     = ManagedMod.DeploymentMethod.LooseFiles;
                mod.RootFolder = "Data";
            }

            if (dllFound || dataFolderFound)
            {
                mod.Method     = ManagedMod.DeploymentMethod.LooseFiles;
                mod.RootFolder = ".";
            }

            if (generalFoldersFound)
            {
                mod.Method      = ManagedMod.DeploymentMethod.SeparateBA2;
                mod.Format      = ManagedMod.ArchiveFormat.General;
                mod.Compression = ManagedMod.ArchiveCompression.Compressed;
                mod.RootFolder  = "Data";
            }

            if (texturesFolderFound)
            {
                mod.Method      = ManagedMod.DeploymentMethod.SeparateBA2;
                mod.Format      = ManagedMod.ArchiveFormat.Textures;
                mod.Compression = ManagedMod.ArchiveCompression.Compressed;
                mod.RootFolder  = "Data";
            }

            if (soundFoldersFound)
            {
                mod.Method      = ManagedMod.DeploymentMethod.SeparateBA2;
                mod.Format      = ManagedMod.ArchiveFormat.General;
                mod.Compression = ManagedMod.ArchiveCompression.Uncompressed;
                mod.RootFolder  = "Data";
            }

            if (generalFoldersFound && texturesFolderFound ||
                generalFoldersFound && soundFoldersFound ||
                texturesFolderFound && soundFoldersFound)
            {
                mod.Method      = ManagedMod.DeploymentMethod.BundledBA2;
                mod.Format      = ManagedMod.ArchiveFormat.Auto;
                mod.Compression = ManagedMod.ArchiveCompression.Auto;
                mod.RootFolder  = "Data";
            }
        }
Esempio n. 21
0
        /// <summary>
        /// Used in the deployment chain to deploy a single mod with the SeparateBA2 method.
        /// Freezes a mod if necessary.
        /// </summary>
        private static void DeploySeparateArchive(ManagedMod mod, ResourceList resources)
        {
            LogFile.WriteLine($"   Installing mod '{mod.Title}' as SeparateBA2");

            // If mod is supposed to be deployed frozen...
            if (mod.Freeze)
            {
                // ... freeze if necessary ...
                if (!mod.Frozen)
                {
                    //LogFile.WriteLine($"      Freezing mod...");
                    ModActions.Freeze(mod);
                }

                LogFile.WriteLine($"      Copying frozen archive...");

                // ... and copy it to the Data folder.
                if (Configuration.bUseHardlinks)
                {
                    Utils.CreateHardLink(
                        mod.FrozenArchivePath,
                        mod.ArchivePath,
                        true);
                }
                else
                {
                    File.Copy(
                        mod.FrozenArchivePath,
                        mod.ArchivePath,
                        true);
                }
            }

            // If mod isn't supposed to be deployed frozen...
            else
            {
                // ... unfreeze mod if needed ...
                if (mod.Frozen)
                {
                    LogFile.WriteLine($"      Unfreezing mod...");
                    ModActions.Unfreeze(mod);
                }

                // Getting preset:
                Archive2.Preset preset = ModHelpers.GetArchive2Preset(mod);

                LogFile.WriteLine($"      Creating new archive...");
                LogFile.WriteLine($"         Format:      {preset.format}");
                LogFile.WriteLine($"         Compression: {preset.compression}");

                // ... and create a new archive.
                Archive2.Create(
                    mod.ArchivePath,
                    mod.ManagedFolderPath,
                    preset);
            }

            // Finally, update the disk state ...
            mod.CurrentArchiveName = mod.ArchiveName;
            mod.CurrentCompression = mod.Frozen ? mod.FrozenCompression : mod.Compression;
            mod.CurrentFormat      = mod.Frozen ? mod.FrozenFormat : mod.Format;
            mod.Deployed           = true;
            mod.PreviousMethod     = ManagedMod.DeploymentMethod.SeparateBA2;

            // ... and add the archive to the resource list.
            resources.Add(mod.ArchiveName);

            LogFile.WriteLine($"      Installed.");
        }
Esempio n. 22
0
        /// <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."));
        }
        /// <summary>
        /// Checks if enabled mods lower on the list overwrite files of enabled mods higher on the list.
        /// </summary>
        /// <returns>A list of conflicting mods.</returns>
        public static List <Conflict> GetConflictingFiles(List <ManagedMod> mods)
        {
            List <Conflict> conflictingMods = new List <Conflict>();

            // Mods higher in the list (upperMod) can get overwritten by mods lower in the list (lowerMod).

            // Iterate over all mods:
            for (int i = 1; i < mods.Count; i++)
            {
                ManagedMod lowerMod  = mods[i];
                string     lowerPath = lowerMod.ManagedFolderPath;

                // If not enabled or non-existant, we don't need to check.
                if (!lowerMod.Enabled || !Directory.Exists(lowerPath))
                {
                    continue;
                }

                // Get a list of files with relative paths:
                List <string> lowerRelPaths = new List <string>();
                foreach (string filePath in Directory.EnumerateFiles(lowerPath, "*.*", SearchOption.AllDirectories))
                {
                    lowerRelPaths.Add(Utils.MakeRelativePath(lowerPath, filePath));
                }

                // Iterate over all mods whose files could get overwritten:
                for (int l = 0; l < i; l++)
                {
                    ManagedMod upperMod  = mods[l];
                    string     upperPath = upperMod.ManagedFolderPath;

                    // If not enabled or non-existant, we don't need to check.
                    if (!upperMod.Enabled || !Directory.Exists(upperPath))
                    {
                        continue;
                    }

                    Conflict conflict = new Conflict();
                    conflict.conflictingFiles = new List <string>();
                    conflict.conflictText     = lowerMod.Title + " overrides " + upperMod.Title;

                    // For each file...
                    foreach (string filePath in Directory.EnumerateFiles(upperPath, "*.*", SearchOption.AllDirectories))
                    {
                        string relUpperPath = Utils.MakeRelativePath(upperPath, filePath);

                        // ... check if it gets overwritten by the lower mod:
                        if (lowerRelPaths.Contains(relUpperPath))
                        {
                            // If it does, add it to the list of conflicting files:
                            if (!conflict.conflictingFiles.Contains(relUpperPath))
                            {
                                conflict.conflictingFiles.Add(relUpperPath);
                            }
                        }
                    }

                    // Add the conflict to the conflictingMods list, if files get overwritten:
                    if (conflict.conflictingFiles.Count > 0)
                    {
                        conflictingMods.Add(conflict);
                    }
                }
            }
            return(conflictingMods);
        }
 /// <summary>
 /// Converts ManagedMod.ArchiveCompression and ManagedMod.ArchiveFormat to an Archive2.Preset.
 /// Automatically determines appropriate compression and format if needed.
 /// </summary>
 public static Archive2.Preset GetArchive2Preset(ManagedMod mod)
 {
     return(GetArchive2Preset(mod.ManagedFolderPath, mod.Format, mod.Compression));
 }
        public static ManagedMod Deserialize(XElement xmlMod, string GamePath)
        {
            if (xmlMod.Attribute("guid") == null)
            {
                throw new Exception("Invalid *.xml entry for mod.");
            }

            ManagedMod mod = new ManagedMod(GamePath, new Guid(xmlMod.Attribute("guid").Value));

            if (xmlMod.Element("Title") == null)
            {
                throw new Exception("Invalid *.xml entry for mod.");
            }
            else
            {
                mod.Title = xmlMod.Element("Title").Value;
            }

            if (xmlMod.Element("Folder") != null)
            {
                mod.ManagedFolderName = xmlMod.Element("Folder").Value;
            }

            if (xmlMod.Element("Version") != null)
            {
                mod.Version = xmlMod.Element("Version").Value;
            }

            XElement xmlNexusMods = xmlMod.Element("NexusMods");
            int      modId;

            if (xmlNexusMods != null && xmlNexusMods.Attribute("id") != null && xmlNexusMods.Element("URL") != null)
            {
                if (xmlNexusMods.Attribute("id").TryParseInt(out modId))
                {
                    mod.ID = modId;
                }
                mod.URL = xmlNexusMods.Element("URL").Value;
            }

            XElement xmlDiskState = xmlMod.Element("DiskState");

            if (xmlDiskState == null)
            {
                throw new Exception("Invalid *.xml entry for mod.");
            }

            XElement xmlCurrentDiskState = xmlDiskState.Element("Current");

            if (xmlCurrentDiskState == null)
            {
                throw new Exception("Invalid *.xml entry for mod.");
            }
            if (xmlCurrentDiskState.Attribute("isDeployed") != null &&
                xmlCurrentDiskState.Attribute("isDeployed").TryParseBool(out bool deployed))
            {
                mod.Deployed = deployed;
            }
            if (xmlCurrentDiskState.Element("InstallationMethod") != null)
            {
                mod.PreviousMethod = GetMethod(xmlCurrentDiskState.Element("InstallationMethod").Value);
            }
            if (xmlCurrentDiskState.Element("ArchiveFormat") != null)
            {
                mod.CurrentFormat = GetFormat(xmlCurrentDiskState.Element("ArchiveFormat").Value);
            }
            if (xmlCurrentDiskState.Element("ArchiveCompression") != null)
            {
                mod.CurrentCompression = GetCompression(xmlCurrentDiskState.Element("ArchiveCompression").Value);
            }
            if (xmlCurrentDiskState.Element("ArchiveName") != null)
            {
                mod.CurrentArchiveName = xmlCurrentDiskState.Element("ArchiveName").Value;
            }
            if (xmlCurrentDiskState.Element("RootFolder") != null)
            {
                mod.CurrentRootFolder = xmlCurrentDiskState.Element("RootFolder").Value;
            }

            XElement xmlInstalledLooseFiles = xmlCurrentDiskState.Element("InstalledLooseFiles");

            if (xmlInstalledLooseFiles != null)
            {
                foreach (XElement xmlFile in xmlInstalledLooseFiles.Descendants("File"))
                {
                    if (xmlFile.Attribute("path") != null)
                    {
                        mod.LooseFiles.Add(xmlFile.Attribute("path").Value);
                    }
                }
            }

            XElement xmlPendingDiskState = xmlDiskState.Element("Pending");

            if (xmlPendingDiskState == null)
            {
                throw new Exception("Invalid *.xml entry for mod.");
            }
            if (xmlPendingDiskState.Attribute("isEnabled") != null &&
                xmlPendingDiskState.Attribute("isEnabled").TryParseBool(out bool enabled))
            {
                mod.Enabled = enabled;
            }
            if (xmlPendingDiskState.Element("InstallationMethod") != null)
            {
                mod.Method = GetMethod(xmlPendingDiskState.Element("InstallationMethod").Value);
            }
            if (xmlPendingDiskState.Element("ArchiveName") != null)
            {
                mod.ArchiveName = xmlPendingDiskState.Element("ArchiveName").Value;
            }
            if (xmlPendingDiskState.Element("ArchiveFormat") != null)
            {
                mod.Format = GetFormat(xmlPendingDiskState.Element("ArchiveFormat").Value);
            }
            if (xmlPendingDiskState.Element("ArchiveCompression") != null)
            {
                mod.Compression = GetCompression(xmlPendingDiskState.Element("ArchiveCompression").Value);
            }
            if (xmlPendingDiskState.Element("RootFolder") != null)
            {
                mod.RootFolder = xmlPendingDiskState.Element("RootFolder").Value;
            }

            XElement xmlFrozenDiskState = xmlDiskState.Element("FrozenData");

            if (xmlFrozenDiskState == null)
            {
                throw new Exception("Invalid *.xml entry for mod.");
            }
            if (xmlFrozenDiskState.Attribute("isFrozen") != null &&
                xmlFrozenDiskState.Attribute("isFrozen").TryParseBool(out bool frozen))
            {
                mod.Frozen = frozen;
            }
            if (xmlFrozenDiskState.Attribute("freeze") != null &&
                xmlFrozenDiskState.Attribute("freeze").TryParseBool(out bool freeze))
            {
                mod.Freeze = freeze;
            }
            if (xmlFrozenDiskState.Element("ArchiveFormat") != null)
            {
                mod.FrozenFormat = GetFormat(xmlFrozenDiskState.Element("ArchiveFormat").Value);
            }
            if (xmlFrozenDiskState.Element("ArchiveCompression") != null)
            {
                mod.FrozenCompression = GetCompression(xmlFrozenDiskState.Element("ArchiveCompression").Value);
            }

            XElement xmlNotes = xmlMod.Element("Notes");

            if (xmlNotes != null)
            {
                mod.Notes = xmlNotes.Value;
            }

            return(mod);
        }