ExtractionManager.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. #region License information
  2. // ----------------------------------------------------------------------------
  3. //
  4. // Eq2VpkTool - A tool to extract Everquest II VPK files
  5. // Blaz (blaz@blazlabs.com)
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. //
  12. // This program is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public License
  18. // along with this program; if not, write to the Free Software
  19. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  20. //
  21. // ( The full text of the license can be found in the License.txt file )
  22. //
  23. // ----------------------------------------------------------------------------
  24. #endregion
  25. #region Using directives
  26. using System;
  27. using System.IO;
  28. using System.Threading;
  29. using System.Collections.Generic;
  30. using Eq2FileSystem = Everquest2.IO.FileSystem;
  31. using Eq2FileSystemInfo = Everquest2.IO.FileSystemInfo;
  32. using Eq2FileInfo = Everquest2.IO.FileInfo;
  33. using Eq2DirectoryInfo = Everquest2.IO.DirectoryInfo;
  34. using Eq2FileStream = Everquest2.IO.FileStream;
  35. #endregion
  36. namespace Eq2VpkTool
  37. {
  38. /// <summary>
  39. /// Manages a set of extractor threads.
  40. /// </summary>
  41. public class ExtractionManager
  42. {
  43. #region Methods
  44. /// <summary>
  45. /// Begins an asynchronous extraction.
  46. /// </summary>
  47. /// <param name="items">Files and directories to extract.</param>
  48. /// <param name="outputPath">Path to extract the items to. Must end with a directory separator character.</param>
  49. /// <param name="userCallback">Callback method to invoke every time a file is extracted.</param>
  50. /// <param name="stateObject">User defined object.</param>
  51. /// <returns>IAsyncResult object that can be used to query the state of the extraction.</returns>
  52. public IAsyncResult BeginExtract(Eq2FileSystemInfo[] items, string outputPath, AsyncCallback userCallback, object stateObject)
  53. {
  54. Thread thread = new Thread(new ParameterizedThreadStart(Extract));
  55. AsyncResult result = new AsyncResult(thread, items, outputPath, userCallback, stateObject);
  56. lock (currentExtractions) currentExtractions.Add(result);
  57. thread.Start(result);
  58. return result;
  59. }
  60. public void Extract(object obj)
  61. {
  62. AsyncResult info = obj as AsyncResult;
  63. try
  64. {
  65. try
  66. {
  67. if (!Directory.Exists(info.outputPath))
  68. {
  69. Directory.CreateDirectory(info.outputPath);
  70. }
  71. foreach (Eq2FileSystemInfo item in info.items)
  72. {
  73. if (item is Eq2DirectoryInfo)
  74. {
  75. ExtractDirectory(item as Eq2DirectoryInfo, info.outputPath, info);
  76. }
  77. else if (item is Eq2FileInfo)
  78. {
  79. FileStream stream = ExtractFile(item as Eq2FileInfo, info.outputPath, info);
  80. // Close the stream so the file gets written to disk immediately.
  81. stream.Close();
  82. }
  83. }
  84. }
  85. catch (UnauthorizedAccessException) {}
  86. catch (IOException) {}
  87. }
  88. finally
  89. {
  90. lock (currentExtractions) currentExtractions.Remove(info);
  91. }
  92. }
  93. public FileStream ExtractFile(Eq2FileInfo file, string outputPath)
  94. {
  95. AsyncResult dummy = new AsyncResult();
  96. return ExtractFile(file, outputPath, dummy);
  97. }
  98. private FileStream ExtractFile(Eq2FileInfo file, string outputPath, AsyncResult info)
  99. {
  100. string filename = outputPath + file.Name;
  101. FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write);
  102. using (Eq2FileStream eq2Stream = file.OpenRead())
  103. {
  104. long size = eq2Stream.Length;
  105. byte[] data = new byte[size];
  106. eq2Stream.Read(data, 0, (int)size);
  107. stream.Write(data, 0, (int)size);
  108. }
  109. if (info.userCallback != null) info.userCallback(info);
  110. return stream;
  111. }
  112. public void ExtractDirectory(Eq2DirectoryInfo directory, string outputPath)
  113. {
  114. AsyncResult dummy = new AsyncResult();
  115. ExtractDirectory(directory, outputPath, dummy);
  116. }
  117. private void ExtractDirectory(Eq2DirectoryInfo directory, string outputPath, AsyncResult info)
  118. {
  119. string directoryPath = outputPath + directory.Name + Path.DirectorySeparatorChar;
  120. Directory.CreateDirectory(directoryPath);
  121. Eq2FileSystemInfo[] children = directory.GetFileSystemInfos();
  122. foreach (Eq2FileSystemInfo child in children)
  123. {
  124. if (child is Eq2DirectoryInfo)
  125. {
  126. ExtractDirectory(child as Eq2DirectoryInfo, directoryPath, info);
  127. }
  128. else if (child is Eq2FileInfo)
  129. {
  130. FileStream stream = ExtractFile(child as Eq2FileInfo, directoryPath, info);
  131. // Close the stream so the file gets written to disk immediately.
  132. stream.Close();
  133. }
  134. }
  135. }
  136. /// <summary>
  137. /// Blocks the calling thread until the specified extraction completes.
  138. /// </summary>
  139. /// <param name="asyncResult">Object representing the state, as returned from BeginExtract.</param>
  140. /// <returns>Always returns zero.</returns>
  141. public int EndExtract(IAsyncResult asyncResult)
  142. {
  143. AsyncResult result = asyncResult as AsyncResult;
  144. result.thread.Join();
  145. result.CompletedSynchronously = true;
  146. lock (currentExtractions) currentExtractions.Remove(result);
  147. return 0;
  148. }
  149. /// <summary>
  150. /// Aborts all ongoing extractions.
  151. /// </summary>
  152. public void Close()
  153. {
  154. AsyncResult[] extractionInfos;
  155. lock (currentExtractions)
  156. {
  157. extractionInfos = new AsyncResult[currentExtractions.Count];
  158. currentExtractions.CopyTo(extractionInfos, 0);
  159. }
  160. foreach (AsyncResult extractionInfo in extractionInfos)
  161. {
  162. extractionInfo.thread.Abort();
  163. extractionInfo.thread.Join();
  164. }
  165. }
  166. #endregion
  167. #region Types
  168. private class AsyncResult : IAsyncResult
  169. {
  170. public AsyncResult()
  171. {
  172. }
  173. public AsyncResult(Thread thread, Eq2FileSystemInfo[] items, string outputPath, AsyncCallback userCallback, object stateObject)
  174. {
  175. this.thread = thread;
  176. this.items = items;
  177. this.outputPath = outputPath;
  178. this.userCallback = userCallback;
  179. this.stateObject = stateObject;
  180. completedSynchronously = false;
  181. }
  182. public object AsyncState
  183. {
  184. get { return stateObject; }
  185. }
  186. public bool IsCompleted
  187. {
  188. get { return !thread.IsAlive; }
  189. }
  190. public bool CompletedSynchronously
  191. {
  192. get { return completedSynchronously; }
  193. internal set { completedSynchronously = value; }
  194. }
  195. public WaitHandle AsyncWaitHandle
  196. {
  197. // Not implemented. Always return null.
  198. get { return null; }
  199. }
  200. public Thread thread;
  201. public Eq2FileSystemInfo[] items;
  202. public string outputPath;
  203. public AsyncCallback userCallback;
  204. private object stateObject;
  205. private bool completedSynchronously;
  206. }
  207. #endregion
  208. #region Fields
  209. private IList<AsyncResult> currentExtractions = new List<AsyncResult>();
  210. #endregion
  211. }
  212. }