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;
}