private void InteractiveExtractNodes(TreeNode[] nodes)
{
extractionSettingsDialog.AllowRecurse = true;
if (extractionSettingsDialog.ShowDialog(this) != DialogResult.OK) return;
// Save the settings now…
Settings.Default.ExtractionDirectory = extractionSettingsDialog.DestinationDirectory;
Settings.Default.ExtractionRecurse = extractionSettingsDialog.Recurse;
Settings.Default.ExtractionOverwriteFiles = extractionSettingsDialog.OverwriteFiles;
Settings.Default.Save();
var directoryInfo = new DirectoryInfo(extractionSettingsDialog.DestinationDirectory);
ulong totalSize = 0;
int totalFileCount = 0;
// Initialize the stack.
extractionStack = extractionStack ?? new Stack<TreeNode>();
extractionStack.Clear();
// Proceed to a tree walk for each requested node, in order to compute total file count and total size
{
foreach (var node in nodes)
{
var currentNode = node;
do
{
if (currentNode.Nodes.Count > 0 && (extractionSettingsDialog.Recurse || extractionStack.Count == 0))
{
extractionStack.Push(currentNode);
currentNode = currentNode.FirstNode;
}
else
{
var file = currentNode.Tag as MpqFile;
if (file != null)
{
totalSize += unchecked((ulong)file.Size);
checked { totalFileCount++; } // Maybe this will fail someday, but I don't think the current Win32 ListView can handle more than 2^31 nodes…
}
while (currentNode == null || (currentNode != node && (currentNode = currentNode.NextNode) == null) && extractionStack.Count > 0)
if ((currentNode = extractionStack.Pop()) != node)
currentNode = currentNode.NextNode;
}
} while (currentNode != node);
}
}
// TODO: Make something a little better…
if (totalFileCount == 0) return;
// Initialize the extraction progress dialog
extractionProgressionDialog.TotalSize = totalSize;
extractionProgressionDialog.ProcessedSize = 0;
extractionProgressionDialog.TotalFileCount = totalFileCount;
extractionProgressionDialog.ProcessedFileCount = 0;
extractionProgressionDialog.CurrentFileName = null;
// Proceed to a tree walk for each requested node, but this time for doing real work
// The extraction will be done on a separate thread, with progress displayed by the modal dialog
extractionProgressionDialog.ShowDialog
(
this,
(dialog, state) =>
{
var buffer = new byte[4096];
int extractedFileCount = 0;
foreach (var node in nodes)
{
string directoryPhysicalPath = directoryInfo.FullName;
var currentNode = node;
do
{
if (currentNode.Nodes.Count > 0 && (extractionSettingsDialog.Recurse || extractionStack.Count == 0))
{
// Store the relative path into the tag, which should be null for directories…
currentNode.Tag = directoryPhysicalPath;
directoryPhysicalPath = Path.Combine(directoryPhysicalPath, currentNode.Text);
if (!Directory.Exists(directoryPhysicalPath))
try { Directory.CreateDirectory(directoryPhysicalPath); }
catch (Exception ex)
{
dialog.ErrorDialog(ex.Message);
return;
}
extractionStack.Push(currentNode);
currentNode = currentNode.FirstNode;
}
else
{
var file = currentNode.Tag as MpqFile;
if (file != null)
{
string filePhysicalPath = Path.Combine(directoryPhysicalPath, currentNode.Text);
bool canExtract = false;
dialog.UpdateFileInformation(extractedFileCount + 1, file.Name);
if (extractionSettingsDialog.OverwriteFiles || !File.Exists(filePhysicalPath)) canExtract = true;
else
{
switch (dialog.AskForOverwrite(currentNode.Text, directoryPhysicalPath, unchecked((ulong)file.Size)))
{
case DialogResult.Yes: canExtract = true; break;
case DialogResult.No: canExtract = false; break;
case DialogResult.Cancel: return;
}
}
if (canExtract)
{
try
{
using (var inputStream = file.Open())
using (var outputStream = File.OpenWrite(filePhysicalPath))
{
int byteCounter = 0;
int length;
do
{
length = inputStream.Read(buffer, 0, 4096);
outputStream.Write(buffer, 0, length);
if ((byteCounter += length) >= 0x100000)
{
dialog.ProcessedSize += 0x100000UL;
byteCounter -= 0x100000;
}
}
while (length == 4096);
if (byteCounter > 0) dialog.ProcessedSize += unchecked((ulong)byteCounter);
extractedFileCount++;
}
}
catch (Exception ex)
{
ErrorDialog(ex.ToString());
return;
}
}
else
{
dialog.TotalSize = totalSize -= unchecked((ulong)file.Size);
dialog.TotalFileCount = totalFileCount--;
}
}
while (currentNode == null || (currentNode != node && (currentNode = currentNode.NextNode) == null) && extractionStack.Count > 0)
{
currentNode = extractionStack.Pop();
directoryPhysicalPath = currentNode.Tag as string;
currentNode.Tag = null; // Clean the tag when no longer needed
if (currentNode != node)
currentNode = currentNode.NextNode;
}
}
} while (currentNode != node);
}
}
);
}