#region License information // ---------------------------------------------------------------------------- // // Eq2VpkTool - A tool to extract Everquest II VPK files // Blaz (blaz@blazlabs.com) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA // // ( The full text of the license can be found in the License.txt file ) // // ---------------------------------------------------------------------------- #endregion #region Using directives using System; using System.IO; using System.Threading; using System.Collections.Generic; using Eq2FileSystem = Everquest2.IO.FileSystem; using Eq2FileSystemInfo = Everquest2.IO.FileSystemInfo; using Eq2FileInfo = Everquest2.IO.FileInfo; using Eq2DirectoryInfo = Everquest2.IO.DirectoryInfo; using Eq2FileStream = Everquest2.IO.FileStream; #endregion namespace Eq2VpkTool { /// /// Manages a set of extractor threads. /// public class ExtractionManager { #region Methods /// /// Begins an asynchronous extraction. /// /// Files and directories to extract. /// Path to extract the items to. Must end with a directory separator character. /// Callback method to invoke every time a file is extracted. /// User defined object. /// IAsyncResult object that can be used to query the state of the extraction. public IAsyncResult BeginExtract(Eq2FileSystemInfo[] items, string outputPath, AsyncCallback userCallback, object stateObject) { Thread thread = new Thread(new ParameterizedThreadStart(Extract)); AsyncResult result = new AsyncResult(thread, items, outputPath, userCallback, stateObject); lock (currentExtractions) currentExtractions.Add(result); thread.Start(result); return result; } public void Extract(object obj) { AsyncResult info = obj as AsyncResult; try { try { if (!Directory.Exists(info.outputPath)) { Directory.CreateDirectory(info.outputPath); } foreach (Eq2FileSystemInfo item in info.items) { if (item is Eq2DirectoryInfo) { ExtractDirectory(item as Eq2DirectoryInfo, info.outputPath, info); } else if (item is Eq2FileInfo) { FileStream stream = ExtractFile(item as Eq2FileInfo, info.outputPath, info); // Close the stream so the file gets written to disk immediately. stream.Close(); } } } catch (UnauthorizedAccessException) {} catch (IOException) {} } finally { lock (currentExtractions) currentExtractions.Remove(info); } } public FileStream ExtractFile(Eq2FileInfo file, string outputPath) { AsyncResult dummy = new AsyncResult(); return ExtractFile(file, outputPath, dummy); } private FileStream ExtractFile(Eq2FileInfo file, string outputPath, AsyncResult info) { string filename = outputPath + file.Name; FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write); using (Eq2FileStream eq2Stream = file.OpenRead()) { long size = eq2Stream.Length; byte[] data = new byte[size]; eq2Stream.Read(data, 0, (int)size); stream.Write(data, 0, (int)size); } if (info.userCallback != null) info.userCallback(info); return stream; } public void ExtractDirectory(Eq2DirectoryInfo directory, string outputPath) { AsyncResult dummy = new AsyncResult(); ExtractDirectory(directory, outputPath, dummy); } private void ExtractDirectory(Eq2DirectoryInfo directory, string outputPath, AsyncResult info) { string directoryPath = outputPath + directory.Name + Path.DirectorySeparatorChar; Directory.CreateDirectory(directoryPath); Eq2FileSystemInfo[] children = directory.GetFileSystemInfos(); foreach (Eq2FileSystemInfo child in children) { if (child is Eq2DirectoryInfo) { ExtractDirectory(child as Eq2DirectoryInfo, directoryPath, info); } else if (child is Eq2FileInfo) { FileStream stream = ExtractFile(child as Eq2FileInfo, directoryPath, info); // Close the stream so the file gets written to disk immediately. stream.Close(); } } } /// /// Blocks the calling thread until the specified extraction completes. /// /// Object representing the state, as returned from BeginExtract. /// Always returns zero. public int EndExtract(IAsyncResult asyncResult) { AsyncResult result = asyncResult as AsyncResult; result.thread.Join(); result.CompletedSynchronously = true; lock (currentExtractions) currentExtractions.Remove(result); return 0; } /// /// Aborts all ongoing extractions. /// public void Close() { AsyncResult[] extractionInfos; lock (currentExtractions) { extractionInfos = new AsyncResult[currentExtractions.Count]; currentExtractions.CopyTo(extractionInfos, 0); } foreach (AsyncResult extractionInfo in extractionInfos) { extractionInfo.thread.Abort(); extractionInfo.thread.Join(); } } #endregion #region Types private class AsyncResult : IAsyncResult { public AsyncResult() { } public AsyncResult(Thread thread, Eq2FileSystemInfo[] items, string outputPath, AsyncCallback userCallback, object stateObject) { this.thread = thread; this.items = items; this.outputPath = outputPath; this.userCallback = userCallback; this.stateObject = stateObject; completedSynchronously = false; } public object AsyncState { get { return stateObject; } } public bool IsCompleted { get { return !thread.IsAlive; } } public bool CompletedSynchronously { get { return completedSynchronously; } internal set { completedSynchronously = value; } } public WaitHandle AsyncWaitHandle { // Not implemented. Always return null. get { return null; } } public Thread thread; public Eq2FileSystemInfo[] items; public string outputPath; public AsyncCallback userCallback; private object stateObject; private bool completedSynchronously; } #endregion #region Fields private IList currentExtractions = new List(); #endregion } }