AA2Install.formMain.inject C# (CSharp) Method

inject() public method

Injects mods as per selected in lsvMods.
public inject ( bool createBackup = false, bool checkConflicts = true, bool suppressPopups = false ) : bool
createBackup bool Creates a backup of modified .pp files if true. Default is false.
checkConflicts bool Checks for conflicts in pending mods. Default is true.
suppressPopups bool If true, does not generate message boxes. Default is false.
return bool
        public bool inject(bool createBackup = false, bool checkConflicts = true, bool suppressPopups = false)
        {
            initializeBench();
            cancelPending = false;
            updateStatus("Compiling changes...", LogIcon.Processing);

            //Compile changes
            _7z.ProgressUpdatedEventArgs updateProgress;
            List<string> rsub = new List<string>();
            List<Mod> mods = new List<Mod>();
            List<Mod> unmods = new List<Mod>();
            foreach (ListViewItem l in lsvMods.Items)
            {
                Mod m = (Mod)l.Tag;
                if (l.Checked != m.Installed)
                {
                    if (l.Checked)
                    {
                        mods.Add(m);
                    }
                    else
                    {
                        unmods.Add(m);
                        rsub.AddRange(m.SubFilenames);
                    }
                }
            }

            if (mods.Count + unmods.Count == 0)
            {
                updateStatus("No changes in selected mods found.", LogIcon.Error, false);
                updateStatus("FAILED: No changes in selected mods", LogIcon.Error, false, true);
                return false;
            }

            //Reset controls
            setEnabled(false);
            btnCancel.Enabled = true;
            prgMinor.Value = 0;
            prgMajor.Value = 0;
            int index = 0;

            Action Fail = () =>
            {
                setEnabled(true);
                prgMinor.Value = 0;
                prgMajor.Value = 0;
            };

            //Check if directory exists
            if (!(Directory.Exists(Paths.AA2Play) && Directory.Exists(Paths.AA2Edit)))
            {
                updateStatus("AA2Play/AA2Edit is not installed/cannot be found", LogIcon.Error, false);
                updateStatus("FAILED: AA2Play/AA2Edit is not installed/cannot be found", LogIcon.Error, false, true);
                Fail();
                return false;
            }
            
            refreshModList(true, txtSearch.Text);

            foreach (ListViewItem l in lsvMods.Items)
            {
                Mod m = (Mod)l.Tag;
                foreach (Mod n in mods)
                    if (n.Filename == m.Filename)
                        l.Checked = true;
                
                foreach (Mod n in unmods)
                    if (n.Filename == m.Filename)
                        l.Checked = false;
            }

            //Clear and create temp
            updateStatus("Clearing TEMP folder...");
            updateStatus("Clearing temporary folders...");

            if (Directory.Exists(Paths.TEMP))
                TryDeleteDirectory(Paths.TEMP);

            if (Directory.Exists(Paths.WORKING))
                TryDeleteDirectory(Paths.WORKING); 

            if (!Directory.Exists(Paths.BACKUP))
                Directory.CreateDirectory(Paths.BACKUP + @"\"); 

            
            Directory.CreateDirectory(Paths.TEMP + @"\");
            Directory.CreateDirectory(Paths.WORKING + @"\");
            Directory.CreateDirectory(Paths.TEMP + @"\AA2_PLAY\");
            Directory.CreateDirectory(Paths.TEMP + @"\AA2_MAKE\");
            Directory.CreateDirectory(Paths.TEMP + @"\BACKUP\");
            Directory.CreateDirectory(Paths.TEMP + @"\BACKUP\AA2_PLAY\");
            Directory.CreateDirectory(Paths.TEMP + @"\BACKUP\AA2_MAKE\");

            //Check conflicts
            if (checkConflicts) { 
                updateStatus("Checking conflicts...");

                Dictionary<string, List<Mod>> files = new Dictionary<string, List<Mod>>();

                foreach (Mod m in modDict.Values)
                    if (lsvMods.Items[m.Name].Checked)
                        m.SubFilenames.ForEach(x =>
                            {
                                if (files.ContainsKey(x))
                                    files[x].Add(m);
                                else
                                    files[x] = new List<Mod> { m };
                            }); //Set each subfile to their respective owner(s)

                bool conflict = false;

                foreach (ListViewItem item in lsvMods.Items) //Loop through list items
                {
                    if (!item.Checked) //We only care about ones that are / will be installed
                        continue;

                    Mod m = item.Tag as Mod; //The current mod we are checking

                    List<string> conflicts = files.Where(x => x.Value.Any(y => y.Name == m.Name)) //If the subfile is contained by the mod
                                                  .Where(x => x.Value.Count > 1) //If there is more than one owner
                                                  .Select(x => x.Key)
                                                  .ToList(); //Convert it to a list
                    

                    if (conflicts.Count > 0)
                    {
                        conflict = true;

                        foreach (string s in conflicts)
                            updateStatus(item.Text + ": " + s, LogIcon.Error, false);

                        item.BackColor = Color.FromArgb(255, 255, 160);
                    }
                }
                if (conflict)
                {
                    updateStatus("Collision detected.", LogIcon.Error, false);
                    updateStatus("FAILED: The highlighted mods have conflicting files", LogIcon.Error, false, true);
                    currentOwner.InvokeMessageBox("Some mods have been detected to have conflicting files.\nYou can use the log to manually fix the conflicting files in the mods (if they can be fixed) or you can proceed anyway by changing the relevant setting in the preferences.\nNote: if you proceed anyway, to uninstall you must uninstall mods in the reverse order you installed them to ensure that wrong files are not left behind.", "Collision detected", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    Fail();
                    return false;
                }
            }

            //Check for free space

            long total7zSize = mods.Select(x => new SevenZipExtractor(x.Filename).UnpackedSize)
                                   .Sum(x => (long)x);

            long totalAA2PlaySize = mods.Select(x => new SevenZipExtractor(x.Filename).Files
                                            .Where(y => y.Filename.StartsWith("AA2_PLAY"))
                                            .Select(y => y.UnpackedSize))
                                        .Sum(x => x.Sum(y => (long)y));

            long totalAA2EditSize = mods.Select(x => new SevenZipExtractor(x.Filename).Files
                                            .Where(y => y.Filename.StartsWith("AA2_MAKE"))
                                            .Select(y => y.UnpackedSize))
                                        .Sum(x => x.Sum(y => (long)y));

            AdditionDictionary requiredSizes = new AdditionDictionary();

            requiredSizes[Paths.TEMP.Remove(1)] = total7zSize;

            if (Paths.TEMP.Remove(1) != Paths.AA2Edit.Remove(1))
                requiredSizes[Paths.AA2Edit.Remove(1)] = totalAA2EditSize + 0x40000000; //an approx. extra 1gb for temp files

            if (Paths.TEMP.Remove(1) != Paths.AA2Play.Remove(1))
                requiredSizes[Paths.AA2Play.Remove(1)] = totalAA2PlaySize + 0x40000000; //an approx. extra 1gb for temp files

            foreach (var kv in requiredSizes)
            {
                bool tryAgain = false;
                do
                {
                    tryAgain = false;
                    if (!IsEnoughFreeSpace(kv.Key, kv.Value))
                    {
                        updateStatus("FAILED: There is not enough free space", LogIcon.Error, false);

                        string spaces = requiredSizes.Select(x => "Drive " + x.Key + ":\\ : Required " + BytesToString(x.Value) + "; Available: " + BytesToString(new DriveInfo(kv.Key).AvailableFreeSpace))
                                        .Aggregate((a, b) => a + "\n" + b);
                        
                        var result = currentOwner.InvokeMessageBox("There is not enough free space to allow an approximate safe installation.\n" + spaces, "Not enough free space", MessageBoxButtons.RetryCancel, MessageBoxIcon.Exclamation);

                        switch(result)
                        {
                            case DialogResult.Retry:
                                tryAgain = true;
                                break;
                            case DialogResult.Cancel:
                            default:
                                Fail();
                                return false;
                        }
                    }
                } while (tryAgain);
            }

            //Verify mods
            List<Mod> combined = mods.Concat(unmods).ToList();
            List<string> estPP = new List<string>();

            foreach (var m in combined)
            {
                SevenZipExtractor s;
                if (m.Installed)
                    s = new SevenZipExtractor(m.BackupFilename);
                else
                    s = new SevenZipExtractor(m.Filename);

                updateStatus("(" + index + "/" + combined.Count + ") Verifying " + m.Name + " (0%)...", LogIcon.Processing, false, true);

                foreach (var d in s.Files)
                {
                    if (d.Attributes.HasFlag(Attributes.Directory) && d.Filename.Length > 9)
                    {
                        string n = d.Filename.Remove(0, 9);

                        if (d.Filename.StartsWith("AA2_MAKE"))
                            estPP.Add(Paths.AA2Edit + @"\" + n + ".pp");
                        else
                            estPP.Add(Paths.AA2Play + @"\" + n + ".pp");
                    }
                }

                SevenZipBase.ProgressUpdatedEventArgs progress = (i) => {
                    this.prgMinor.GetCurrentParent().HybridInvoke(() => {
                        prgMinor.Value = i;
                    });
                    updateStatus("(" + index + "/" + combined.Count + ") Verifying " + m.Name + " (" + i + "%)...", LogIcon.Processing, false, true);
                };

                s.ProgressUpdated += progress;

                bool result = new Func<bool>(() => s.TestArchive()).SemiAsyncWait();

                s.ProgressUpdated -= progress;

                if (!result)
                {
                    updateStatus("FAILED: " + m.Name + " failed to verify.", LogIcon.Error, false);
                    Fail();
                    return false;
                }
            }

            //Verify PPs
            foreach (string p in estPP)
            {
                updateStatus("(" + index + "/" + combined.Count + ") Verifying " + p.Remove(0, p.LastIndexOf('\\') + 1) + "...", LogIcon.Processing);
                if (File.Exists(p))
                    if (!ppHeader.VerifyHeader(p))
                    {
                        updateStatus("FAILED: " + p.Remove(0, p.LastIndexOf('\\') + 1) + " failed to verify.", LogIcon.Error, false);
                        Fail();
                        return false;
                    }
            }

            //Extract all mods

            index = 0;

            string name = "";

            updateProgress = (i) => {
                prgMinor.GetCurrentParent().HybridInvoke(() => {
                    prgMinor.Value = i;
                });
                updateStatus("(" + index + "/" + combined.Count + ") Extracting " + name + " (" + i + "%)...", LogIcon.Processing, false, true);
            };

            _7z.ProgressUpdated += updateProgress;

            foreach (Mod item in combined)
            {
                index++;
                name = item.Name;
                updateStatus("(" + index + "/" + combined.Count + ") Extracting " + name + " (0%)...", LogIcon.Processing);

                if (mods.Contains(item))
                    _7z.Extract(item.Filename);
                else
                    _7z.Extract(item.BackupFilename, Paths.TEMP + @"\BACKUP\");

                prgMajor.Value = (100 * index / combined.Count);
                if (tryCancel())
                {
                    Fail();
                    return false;
                }
            }

            _7z.ProgressUpdated -= updateProgress;

            //Reached point of no return.
            btnCancel.Enabled = false;

            //Build ppQueue
            index = 0;
           
            Queue<basePP> ppQueue = new Queue<basePP>();
            List<basePP> ppList = new List<basePP>();
            updateStatus("Creating .pp file queue...");


            List<string> tempPLAY = new List<string>(Directory.GetDirectories(Paths.TEMP + @"\BACKUP\AA2_PLAY", "jg2*", SearchOption.TopDirectoryOnly));
            List<string> tempEDIT = new List<string>(Directory.GetDirectories(Paths.TEMP + @"\BACKUP\AA2_MAKE", "jg2*", SearchOption.TopDirectoryOnly));

            foreach (string path in tempPLAY)
            {
                ppQueue.Enqueue(new basePP(path, Paths.AA2Play));
            }
            foreach (string path in tempEDIT)
            {
                ppQueue.Enqueue(new basePP(path, Paths.AA2Edit));
            }

            //Prioritise removing subfiles from mods that are being uninstalled
            while (ppQueue.Count > 0)
            {
                basePP bp = ppQueue.Dequeue();

                var r = rsub.ToArray();
                foreach (string s in r)
                {
                    foreach (IWriteFile iw in bp.pp.Subfiles)
                        if (bp.ppDir + "\\" + iw.Name == s.Remove(0, 9))
                        {
                            rsub.Remove(s);
                            bp.pp.Subfiles.Remove(iw);
                            break;
                        }
                }

                prgMinor.Style = ProgressBarStyle.Continuous;
                int i = 1;

                foreach (string s in Directory.GetFiles(bp.ppRAW))
                {
                    string fname = s.Remove(0, s.LastIndexOf('\\') + 1);
                    foreach (IWriteFile sub in bp.pp.Subfiles)
                    {
                        if (fname == sub.Name)
                        {
                            bp.pp.Subfiles.Remove(sub);
                            break;
                        }
                    }
                    prgMinor.Value = (i * 100 / Directory.GetFiles(bp.ppRAW).Length);
                    bp.pp.Subfiles.Add(new Subfile(s));
                    i++;
                }

                ppList.Add(bp);
            }

            tempPLAY = new List<string>(Directory.GetDirectories(Paths.TEMP + @"\AA2_PLAY", "jg2*", SearchOption.TopDirectoryOnly));
            tempEDIT = new List<string>(Directory.GetDirectories(Paths.TEMP + @"\AA2_MAKE", "jg2*", SearchOption.TopDirectoryOnly));

            //Sort the uninstalled .pp files back into the main queue
            foreach (string path in tempPLAY)
            {
                var p = new basePP(path, Paths.AA2Play);
                var o = ppList.Find(x => x.ppDir == p.ppDir);
                if (o != null)
                {
                    p.pp = o.pp;
                    ppList.Remove(o);
                }
                ppQueue.Enqueue(p);
            }
            foreach (string path in tempEDIT)
            {
                var p = new basePP(path, Paths.AA2Edit);
                var o = ppList.Find(x => x.ppDir == p.ppDir);
                if (o != null)
                {
                    p.pp = o.pp;
                    ppList.Remove(o);
                }
                ppQueue.Enqueue(p);
            }

            int ii = 0;
            prgMajor.Value = 0;
            prgMinor.Value = 0;
            foreach (basePP b in ppList)
            {
                ii++;
                updateStatus("(" + ii + "/" + ppList.Count + ") Reverting " + b.ppFile + " (0%)...", LogIcon.Processing);
                if (b.pp.Subfiles.Count > 0)
                {
                    BackgroundWorker bb = b.pp.WriteArchive(b.pp.FilePath, createBackup);

                    bb.ProgressChanged += ((s, e) =>
                    {
                        prgMinor.GetCurrentParent().HybridInvoke(() => {
                            prgMinor.Value = e.ProgressPercentage;
                        });
                        updateStatus("(" + ii + "/" + ppList.Count + ") Reverting " + b.ppFile + " (" + e.ProgressPercentage + "%)...", LogIcon.Processing, false, true);
                    });

                    bb.SemiAsyncWait();
                }
                else
                {
                    File.Delete(b.pp.FilePath);
                }
                prgMajor.Value = (100 * ii / ppList.Count);
            }

            prgMinor.Value = 0;
            prgMajor.Value = 0;

            //Process .pp files
            int initial = ppQueue.Count;
            updateTaskbarProgress();
            index = 0;
            while (ppQueue.Count > 0)
            {
                basePP b = ppQueue.Dequeue();

                updateStatus("(" + (index + 1) + "/" + initial + ") Injecting " + b.ppFile + " (0%)...", LogIcon.Processing);

                prgMinor.Style = ProgressBarStyle.Continuous;
                int i = 1;

                foreach (Mod m in mods)
                {
                    foreach (string s in m.SubFilenames)
                    {
                        if (s.Contains(b.ppDir))
                        {
                            string r = s.Remove(0, 9);
                            string rs = r.Remove(0, r.LastIndexOf('\\') + 1);
                            string workingdir = Paths.WORKING + "\\BACKUP\\" + m.Name.Replace(".7z", "").Replace(".zip", "") + "\\";
                            string directory;
                            if (tempPLAY.Contains(b.ppRAW))
                            {
                                directory = workingdir + "AA2_PLAY\\" + r.Remove(r.LastIndexOf('\\') + 1);
                            }
                            else
                            {
                                directory = workingdir + "AA2_MAKE\\" + r.Remove(r.LastIndexOf('\\') + 1);
                            }

                            Directory.CreateDirectory(directory);
                            foreach (IWriteFile iw in b.pp.Subfiles)
                            {
                                if (iw.Name == rs)
                                {
                                    using (FileStream fs = new FileStream(directory + rs, FileMode.Create))
                                    {
                                        iw.WriteTo(fs);
                                    }
                                }
                            }
                        }
                    }
                }

                foreach (string s in rsub)
                {
                    foreach (IWriteFile iw in b.pp.Subfiles)
                        if (b.ppDir + "\\" + iw.Name == s.Remove(0, 9))
                        {
                            b.pp.Subfiles.Remove(iw);
                            break;
                        }
                }

                foreach (string s in Directory.GetFiles(b.ppRAW))
                {
                    string fname = s.Remove(0, s.LastIndexOf('\\')+1);
                    foreach (IWriteFile sub in b.pp.Subfiles)
                    {
                        if (fname == sub.Name)
                        {
                            b.pp.Subfiles.Remove(sub);
                            break;
                        }
                    }
                    prgMinor.Value = (100 * i / Directory.GetFiles(b.ppRAW).Length);
                    b.pp.Subfiles.Add(new Subfile(s));
                    i++;
                }
                if (b.pp.Subfiles.Count > 0)
                {
                    prgMinor.Value = 0;
                    BackgroundWorker bb = b.pp.WriteArchive(b.pp.FilePath, createBackup);

                    bb.ProgressChanged += ((s, e) =>
                    {
                        prgMinor.GetCurrentParent().HybridInvoke(() =>
                            { prgMinor.Value = e.ProgressPercentage; });
                        
                        updateStatus("(" + (index + 1) + "/" + initial + ") Injecting " + b.ppFile + " (" + e.ProgressPercentage + "%)...", LogIcon.Processing, false, true);
                    });

                    bb.SemiAsyncWait();
                }
                else
                {
                    File.Delete(b.pp.FilePath);
                }
                //Loop complete

                TryDeleteDirectory(b.ppRAW + "\\");

                index++;
                prgMajor.Value = (100 * index / initial);
                updateTaskbarProgress();
            }

            int ind = 0;
            //Archive backups
            prgMinor.Value = 0;
            prgMajor.Value = 0;
            if (!Directory.Exists(Paths.WORKING + "\\BACKUP\\")) { Directory.CreateDirectory(Paths.WORKING + "\\BACKUP\\"); }
            List<string> tempBackup = new List<string>(Directory.GetDirectories(Paths.WORKING + "\\BACKUP\\"));
            foreach (string s in tempBackup)
            {
                ind++;
                prgMajor.Value = (100 * ind / tempBackup.Count);
                updateStatus("(" + ind + "/" + tempBackup.Count + ") Archiving backup of " + s + " (0%)...", LogIcon.Processing);

                updateProgress = (i) => {
                    prgMinor.GetCurrentParent().HybridInvoke(() => {
                        prgMinor.Value = i;
                    });
                    updateStatus("(" + ind + "/" + tempBackup.Count + ") Archiving backup of " + s + " (" + i + "%)...", LogIcon.Processing, false, true);
                };

                _7z.ProgressUpdated += updateProgress;

                string item = s.Remove(0, s.LastIndexOf('\\') + 1);
                string archive = Paths.BACKUP + "\\" + item + ".7z";
                if (Directory.Exists(s + "\\AA2_PLAY\\"))
                {
                    foreach (string sub in Directory.GetDirectories(s + "\\AA2_PLAY\\"))
                    {
                        string g = sub.Remove(0, s.Length + 1) + "\\";
                        _7z.Compress(archive, s, g);
                    }
                }
                if (Directory.Exists(s + "\\AA2_MAKE\\"))
                {
                    foreach (string sub in Directory.GetDirectories(s + "\\AA2_MAKE\\"))
                    {
                        string g = sub.Remove(0, s.Length + 1) + "\\";
                        _7z.Compress(archive, s, g);
                    }
                }

                _7z.ProgressUpdated -= updateProgress;
            }

            //Finish up
            prgMinor.Style = ProgressBarStyle.Continuous;
            updateStatus("Finishing up...");
            mods.AddRange(unmods);

            foreach (Mod m in unmods)
            {
                string s = Paths.BACKUP + "\\" + m.Name.Replace(".zip", ".7z");
                if (File.Exists(s))
                {
                    tryDelete(s);
                }
            }

            Configuration.saveMods(modDict);

            TryDeleteDirectory(Paths.TEMP);
            TryDeleteDirectory(Paths.WORKING);

            updateStatus("Success!", LogIcon.OK, false);
            updateTaskbarProgress(TaskbarProgress.TaskbarStates.NoProgress);
            if (!suppressPopups)
                currentOwner.InvokeMessageBox("Mods successfully synced.");
            refreshModList();
            return true;
        }