SevenZipExtractor.cs 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495
  1. namespace SevenZip
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Collections.ObjectModel;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.IO;
  9. using System.Linq;
  10. using SevenZip.Sdk.Compression.Lzma;
  11. /// <summary>
  12. /// Class to unpack data from archives supported by 7-Zip.
  13. /// </summary>
  14. /// <example>
  15. /// using (var extr = new SevenZipExtractor(@"C:\Test.7z"))
  16. /// {
  17. /// extr.ExtractArchive(@"C:\TestDirectory");
  18. /// }
  19. /// </example>
  20. public sealed partial class SevenZipExtractor
  21. #if UNMANAGED
  22. : SevenZipBase, IDisposable
  23. #endif
  24. {
  25. #if UNMANAGED
  26. private List<ArchiveFileInfo> _archiveFileData;
  27. private IInArchive _archive;
  28. private IInStream _archiveStream;
  29. private int _offset;
  30. private ArchiveOpenCallback _openCallback;
  31. private string _fileName;
  32. private Stream _inStream;
  33. private long? _packedSize;
  34. private long? _unpackedSize;
  35. private uint? _filesCount;
  36. private bool? _isSolid;
  37. private bool _opened;
  38. private bool _disposed;
  39. private InArchiveFormat _format = (InArchiveFormat)(-1);
  40. private ReadOnlyCollection<ArchiveFileInfo> _archiveFileInfoCollection;
  41. private ReadOnlyCollection<ArchiveProperty> _archiveProperties;
  42. private ReadOnlyCollection<string> _volumeFileNames;
  43. private bool _leaveOpen;
  44. /// <summary>
  45. /// This is used to lock possible Dispose() calls.
  46. /// </summary>
  47. private bool _asynchronousDisposeLock;
  48. #region Constructors
  49. /// <summary>
  50. /// General initialization function.
  51. /// </summary>
  52. /// <param name="archiveFullName">The archive file name.</param>
  53. private void Init(string archiveFullName)
  54. {
  55. _fileName = archiveFullName;
  56. var isExecutable = false;
  57. if ((int)_format == -1)
  58. {
  59. _format = FileChecker.CheckSignature(archiveFullName, out _offset, out isExecutable);
  60. }
  61. PreserveDirectoryStructure = true;
  62. SevenZipLibraryManager.LoadLibrary(this, _format);
  63. try
  64. {
  65. _archive = SevenZipLibraryManager.InArchive(_format, this);
  66. }
  67. catch (SevenZipLibraryException)
  68. {
  69. SevenZipLibraryManager.FreeLibrary(this, _format);
  70. throw;
  71. }
  72. if (isExecutable && _format != InArchiveFormat.PE)
  73. {
  74. if (!Check())
  75. {
  76. CommonDispose();
  77. _format = InArchiveFormat.PE;
  78. SevenZipLibraryManager.LoadLibrary(this, _format);
  79. try
  80. {
  81. _archive = SevenZipLibraryManager.InArchive(_format, this);
  82. }
  83. catch (SevenZipLibraryException)
  84. {
  85. SevenZipLibraryManager.FreeLibrary(this, _format);
  86. throw;
  87. }
  88. }
  89. }
  90. }
  91. /// <summary>
  92. /// General initialization function.
  93. /// </summary>
  94. /// <param name="stream">The stream to read the archive from.</param>
  95. private void Init(Stream stream)
  96. {
  97. ValidateStream(stream);
  98. var isExecutable = false;
  99. if ((int)_format == -1)
  100. {
  101. _format = FileChecker.CheckSignature(stream, out _offset, out isExecutable);
  102. }
  103. PreserveDirectoryStructure = true;
  104. SevenZipLibraryManager.LoadLibrary(this, _format);
  105. try
  106. {
  107. _inStream = new ArchiveEmulationStreamProxy(stream, _offset, _leaveOpen);
  108. _packedSize = stream.Length;
  109. _archive = SevenZipLibraryManager.InArchive(_format, this);
  110. }
  111. catch (SevenZipLibraryException)
  112. {
  113. SevenZipLibraryManager.FreeLibrary(this, _format);
  114. throw;
  115. }
  116. if (isExecutable && _format != InArchiveFormat.PE)
  117. {
  118. if (!Check())
  119. {
  120. CommonDispose();
  121. _format = InArchiveFormat.PE;
  122. try
  123. {
  124. _inStream = new ArchiveEmulationStreamProxy(stream, _offset, _leaveOpen);
  125. _packedSize = stream.Length;
  126. _archive = SevenZipLibraryManager.InArchive(_format, this);
  127. }
  128. catch (SevenZipLibraryException)
  129. {
  130. SevenZipLibraryManager.FreeLibrary(this, _format);
  131. throw;
  132. }
  133. }
  134. }
  135. }
  136. /// <summary>
  137. /// Initializes a new instance of SevenZipExtractor class.
  138. /// </summary>
  139. /// <param name="archiveStream">The stream to read the archive from.
  140. /// Use SevenZipExtractor(string) to extract from disk, though it is not necessary.</param>
  141. /// <remarks>The archive format is guessed by the signature.</remarks>
  142. public SevenZipExtractor(Stream archiveStream) : this(archiveStream, false)
  143. {
  144. }
  145. /// <summary>
  146. /// Initializes a new instance of SevenZipExtractor class.
  147. /// </summary>
  148. /// <param name="archiveStream">The stream to read the archive from.
  149. /// Use SevenZipExtractor(string) to extract from disk, though it is not necessary.</param>
  150. /// <param name="leaveOpen">Leaves the base stream open.</param>
  151. /// <remarks>The archive format is guessed by the signature.</remarks>
  152. public SevenZipExtractor(Stream archiveStream, bool leaveOpen) : this(archiveStream, leaveOpen, (InArchiveFormat)(-1))
  153. {
  154. }
  155. /// <summary>
  156. /// Initializes a new instance of SevenZipExtractor class.
  157. /// </summary>
  158. /// <param name="archiveStream">The stream to read the archive from.
  159. /// Use SevenZipExtractor(string) to extract from disk, though it is not necessary.</param>
  160. /// <param name="leaveOpen">Leaves the base stream open.</param>
  161. /// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
  162. /// Instead, use SevenZipExtractor(Stream archiveStream), that constructor
  163. /// automatically detects the archive format.</param>
  164. public SevenZipExtractor(Stream archiveStream, bool leaveOpen, InArchiveFormat format)
  165. {
  166. _leaveOpen = leaveOpen;
  167. _format = format;
  168. Init(archiveStream);
  169. }
  170. /// <summary>
  171. /// Initializes a new instance of SevenZipExtractor class.
  172. /// </summary>
  173. /// <param name="archiveFullName">The archive full file name.</param>
  174. public SevenZipExtractor(string archiveFullName)
  175. {
  176. Init(archiveFullName);
  177. }
  178. /// <summary>
  179. /// Initializes a new instance of SevenZipExtractor class.
  180. /// </summary>
  181. /// <param name="archiveFullName">The archive full file name.</param>
  182. /// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
  183. /// Instead, use SevenZipExtractor(string archiveFullName), that constructor
  184. /// automatically detects the archive format.</param>
  185. public SevenZipExtractor(string archiveFullName, InArchiveFormat format)
  186. {
  187. _format = format;
  188. Init(archiveFullName);
  189. }
  190. /// <summary>
  191. /// Initializes a new instance of SevenZipExtractor class.
  192. /// </summary>
  193. /// <param name="archiveFullName">The archive full file name.</param>
  194. /// <param name="password">Password for an encrypted archive.</param>
  195. public SevenZipExtractor(string archiveFullName, string password)
  196. : base(password)
  197. {
  198. Init(archiveFullName);
  199. }
  200. /// <summary>
  201. /// Initializes a new instance of SevenZipExtractor class.
  202. /// </summary>
  203. /// <param name="archiveFullName">The archive full file name.</param>
  204. /// <param name="password">Password for an encrypted archive.</param>
  205. /// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
  206. /// Instead, use SevenZipExtractor(string archiveFullName, string password), that constructor
  207. /// automatically detects the archive format.</param>
  208. public SevenZipExtractor(string archiveFullName, string password, InArchiveFormat format)
  209. : base(password)
  210. {
  211. _format = format;
  212. Init(archiveFullName);
  213. }
  214. /// <summary>
  215. /// Initializes a new instance of SevenZipExtractor class.
  216. /// </summary>
  217. /// <param name="archiveStream">The stream to read the archive from.</param>
  218. /// <param name="password">Password for an encrypted archive.</param>
  219. /// <remarks>The archive format is guessed by the signature.</remarks>
  220. public SevenZipExtractor(Stream archiveStream, string password) : this(archiveStream, password, false)
  221. {
  222. }
  223. /// <summary>
  224. /// Initializes a new instance of SevenZipExtractor class.
  225. /// </summary>
  226. /// <param name="archiveStream">The stream to read the archive from.</param>
  227. /// <param name="password">Password for an encrypted archive.</param>
  228. /// <param name="leaveOpen">Leaves the base stream open.</param>
  229. /// <remarks>The archive format is guessed by the signature.</remarks>
  230. public SevenZipExtractor(Stream archiveStream, string password, bool leaveOpen) : this(archiveStream, password, leaveOpen, (InArchiveFormat)(-1))
  231. {
  232. }
  233. /// <summary>
  234. /// Initializes a new instance of SevenZipExtractor class.
  235. /// </summary>
  236. /// <param name="archiveStream">The stream to read the archive from.</param>
  237. /// <param name="password">Password for an encrypted archive.</param>
  238. /// <param name="leaveOpen">Leaves the base stream open.</param>
  239. /// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
  240. /// Instead, use SevenZipExtractor(Stream archiveStream, string password), that constructor
  241. /// automatically detects the archive format.</param>
  242. public SevenZipExtractor(Stream archiveStream, string password, bool leaveOpen, InArchiveFormat format)
  243. : base(password)
  244. {
  245. _format = format;
  246. _leaveOpen = leaveOpen;
  247. Init(archiveStream);
  248. }
  249. #endregion
  250. #region Properties
  251. /// <summary>
  252. /// Gets or sets archive full file name
  253. /// </summary>
  254. public string FileName
  255. {
  256. get
  257. {
  258. DisposedCheck();
  259. return _fileName;
  260. }
  261. }
  262. /// <summary>
  263. /// Gets the size of the archive file
  264. /// </summary>
  265. public long PackedSize
  266. {
  267. get
  268. {
  269. DisposedCheck();
  270. return _packedSize ?? (_fileName != null ?
  271. new FileInfo(_fileName).Length :
  272. -1);
  273. }
  274. }
  275. /// <summary>
  276. /// Gets the size of unpacked archive data
  277. /// </summary>
  278. public long UnpackedSize
  279. {
  280. get
  281. {
  282. DisposedCheck();
  283. if (!_unpackedSize.HasValue)
  284. {
  285. return -1;
  286. }
  287. return _unpackedSize.Value;
  288. }
  289. }
  290. /// <summary>
  291. /// Gets a value indicating whether the archive is solid
  292. /// </summary>
  293. public bool IsSolid
  294. {
  295. get
  296. {
  297. DisposedCheck();
  298. if (!_isSolid.HasValue)
  299. {
  300. GetArchiveInfo(true);
  301. }
  302. Debug.Assert(_isSolid != null);
  303. return _isSolid.Value;
  304. }
  305. }
  306. /// <summary>
  307. /// Gets the number of files in the archive
  308. /// </summary>
  309. [CLSCompliant(false)]
  310. public uint FilesCount
  311. {
  312. get
  313. {
  314. DisposedCheck();
  315. if (!_filesCount.HasValue)
  316. {
  317. GetArchiveInfo(true);
  318. }
  319. Debug.Assert(_filesCount != null);
  320. return _filesCount.Value;
  321. }
  322. }
  323. /// <summary>
  324. /// Gets archive format
  325. /// </summary>
  326. public InArchiveFormat Format
  327. {
  328. get
  329. {
  330. DisposedCheck();
  331. return _format;
  332. }
  333. }
  334. /// <summary>
  335. /// Gets or sets the value indicating whether to preserve the directory structure of extracted files.
  336. /// </summary>
  337. public bool PreserveDirectoryStructure { get; set; }
  338. #endregion
  339. /// <summary>
  340. /// Checked whether the class was disposed.
  341. /// </summary>
  342. /// <exception cref="System.ObjectDisposedException" />
  343. private void DisposedCheck()
  344. {
  345. if (_disposed)
  346. {
  347. throw new ObjectDisposedException("SevenZipExtractor");
  348. }
  349. RecreateInstanceIfNeeded();
  350. }
  351. #region Core private functions
  352. private ArchiveOpenCallback GetArchiveOpenCallback()
  353. {
  354. return _openCallback ?? (_openCallback = string.IsNullOrEmpty(Password)
  355. ? new ArchiveOpenCallback(_fileName)
  356. : new ArchiveOpenCallback(_fileName, Password));
  357. }
  358. /// <summary>
  359. /// Gets the archive input stream.
  360. /// </summary>
  361. /// <returns>The archive input wrapper stream.</returns>
  362. private IInStream GetArchiveStream(bool dispose)
  363. {
  364. if (_archiveStream != null)
  365. {
  366. if (_archiveStream is DisposeVariableWrapper wrapper)
  367. {
  368. wrapper.DisposeStream = dispose;
  369. }
  370. return _archiveStream;
  371. }
  372. if (_inStream != null)
  373. {
  374. _inStream.Seek(0, SeekOrigin.Begin);
  375. _archiveStream = new InStreamWrapper(_inStream, false);
  376. }
  377. else
  378. {
  379. if (!_fileName.EndsWith(".001", StringComparison.OrdinalIgnoreCase))
  380. {
  381. _archiveStream = new InStreamWrapper(
  382. new ArchiveEmulationStreamProxy(new FileStream(
  383. _fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
  384. _offset, _leaveOpen),
  385. dispose);
  386. }
  387. else
  388. {
  389. _archiveStream = new InMultiStreamWrapper(_fileName, dispose);
  390. _packedSize = (_archiveStream as InMultiStreamWrapper)?.Length;
  391. }
  392. }
  393. return _archiveStream;
  394. }
  395. /// <summary>
  396. /// Opens the archive and throws exceptions or returns OperationResult.DataError if any error occurs.
  397. /// </summary>
  398. /// <param name="archiveStream">The IInStream compliant class instance, that is, the input stream.</param>
  399. /// <param name="openCallback">The ArchiveOpenCallback instance.</param>
  400. /// <returns>OperationResult.Ok if Open() succeeds.</returns>
  401. private OperationResult OpenArchiveInner(IInStream archiveStream, IArchiveOpenCallback openCallback)
  402. {
  403. ulong checkPos = 1 << 23;
  404. var res = _archive.Open(archiveStream, ref checkPos, openCallback);
  405. return (OperationResult)res;
  406. }
  407. /// <summary>
  408. /// Opens the archive and throws exceptions or returns OperationResult.DataError if any error occurs.
  409. /// </summary>
  410. /// <param name="archiveStream">The IInStream compliant class instance, that is, the input stream.</param>
  411. /// <param name="openCallback">The ArchiveOpenCallback instance.</param>
  412. /// <returns>True if Open() succeeds; otherwise, false.</returns>
  413. private bool OpenArchive(IInStream archiveStream, ArchiveOpenCallback openCallback)
  414. {
  415. if (!_opened)
  416. {
  417. if (OpenArchiveInner(archiveStream, openCallback) != OperationResult.Ok)
  418. {
  419. if (!ThrowException(null, new SevenZipArchiveException()))
  420. {
  421. return false;
  422. }
  423. }
  424. _volumeFileNames = new ReadOnlyCollection<string>(openCallback.VolumeFileNames);
  425. _opened = true;
  426. }
  427. return true;
  428. }
  429. /// <summary>
  430. /// Retrieves all information about the archive.
  431. /// </summary>
  432. /// <exception cref="SevenZip.SevenZipArchiveException"/>
  433. private void GetArchiveInfo(bool disposeStream)
  434. {
  435. if (_archive == null)
  436. {
  437. if (!ThrowException(null, new SevenZipArchiveException()))
  438. {
  439. return;
  440. }
  441. }
  442. else
  443. {
  444. IInStream archiveStream;
  445. using ((archiveStream = GetArchiveStream(disposeStream)) as IDisposable)
  446. {
  447. var openCallback = GetArchiveOpenCallback();
  448. if (!_opened)
  449. {
  450. if (!OpenArchive(archiveStream, openCallback))
  451. {
  452. return;
  453. }
  454. _opened = !disposeStream;
  455. }
  456. _filesCount = _archive.GetNumberOfItems();
  457. _archiveFileData = new List<ArchiveFileInfo>((int)_filesCount);
  458. if (_filesCount != 0)
  459. {
  460. var data = new PropVariant();
  461. try
  462. {
  463. #region Getting archive items data
  464. for (uint i = 0; i < _filesCount; i++)
  465. {
  466. try
  467. {
  468. var fileInfo = new ArchiveFileInfo { Index = (int)i };
  469. _archive.GetProperty(i, ItemPropId.Path, ref data);
  470. fileInfo.FileName = NativeMethods.SafeCast(data, "[no name]");
  471. _archive.GetProperty(i, ItemPropId.LastWriteTime, ref data);
  472. fileInfo.LastWriteTime = NativeMethods.SafeCast(data, DateTime.Now);
  473. _archive.GetProperty(i, ItemPropId.CreationTime, ref data);
  474. fileInfo.CreationTime = NativeMethods.SafeCast(data, DateTime.Now);
  475. _archive.GetProperty(i, ItemPropId.LastAccessTime, ref data);
  476. fileInfo.LastAccessTime = NativeMethods.SafeCast(data, DateTime.Now);
  477. _archive.GetProperty(i, ItemPropId.Size, ref data);
  478. fileInfo.Size = NativeMethods.SafeCast<ulong>(data, 0);
  479. if (fileInfo.Size == 0)
  480. {
  481. fileInfo.Size = NativeMethods.SafeCast<uint>(data, 0);
  482. }
  483. _archive.GetProperty(i, ItemPropId.Attributes, ref data);
  484. fileInfo.Attributes = NativeMethods.SafeCast<uint>(data, 0);
  485. _archive.GetProperty(i, ItemPropId.IsDirectory, ref data);
  486. fileInfo.IsDirectory = NativeMethods.SafeCast(data, false);
  487. _archive.GetProperty(i, ItemPropId.Encrypted, ref data);
  488. fileInfo.Encrypted = NativeMethods.SafeCast(data, false);
  489. _archive.GetProperty(i, ItemPropId.Crc, ref data);
  490. fileInfo.Crc = NativeMethods.SafeCast<uint>(data, 0);
  491. _archive.GetProperty(i, ItemPropId.Comment, ref data);
  492. fileInfo.Comment = NativeMethods.SafeCast(data, "");
  493. _archive.GetProperty(i, ItemPropId.Method, ref data);
  494. fileInfo.Method = NativeMethods.SafeCast(data, "");
  495. _archiveFileData.Add(fileInfo);
  496. }
  497. catch (InvalidCastException)
  498. {
  499. ThrowException(null, new SevenZipArchiveException("probably archive is corrupted."));
  500. }
  501. }
  502. #endregion
  503. #region Getting archive properties
  504. var numProps = _archive.GetNumberOfArchiveProperties();
  505. var archProps = new List<ArchiveProperty>((int)numProps);
  506. for (uint i = 0; i < numProps; i++)
  507. {
  508. _archive.GetArchivePropertyInfo(i, out _, out var propId, out _);
  509. _archive.GetArchiveProperty(propId, ref data);
  510. if (propId == ItemPropId.Solid)
  511. {
  512. _isSolid = NativeMethods.SafeCast(data, true);
  513. }
  514. // TODO Add more archive properties
  515. if (PropIdToName.PropIdNames.ContainsKey(propId))
  516. {
  517. archProps.Add(new ArchiveProperty
  518. {
  519. Name = PropIdToName.PropIdNames[propId],
  520. Value = data.Object
  521. });
  522. }
  523. else
  524. {
  525. Debug.WriteLine($"An unknown archive property encountered (code {((int)propId).ToString(CultureInfo.InvariantCulture)})");
  526. }
  527. }
  528. _archiveProperties = new ReadOnlyCollection<ArchiveProperty>(archProps);
  529. if (!_isSolid.HasValue && _format == InArchiveFormat.Zip)
  530. {
  531. _isSolid = false;
  532. }
  533. if (!_isSolid.HasValue)
  534. {
  535. _isSolid = true;
  536. }
  537. #endregion
  538. }
  539. catch (Exception)
  540. {
  541. if (openCallback.ThrowException())
  542. {
  543. throw;
  544. }
  545. }
  546. }
  547. }
  548. if (disposeStream)
  549. {
  550. _archive.Close();
  551. _archiveStream = null;
  552. }
  553. _archiveFileInfoCollection = new ReadOnlyCollection<ArchiveFileInfo>(_archiveFileData);
  554. }
  555. }
  556. /// <summary>
  557. /// Ensure that _archiveFileData is loaded.
  558. /// </summary>
  559. /// <param name="disposeStream">Dispose the archive stream after this operation.</param>
  560. private void InitArchiveFileData(bool disposeStream)
  561. {
  562. if (_archiveFileData == null)
  563. {
  564. GetArchiveInfo(disposeStream);
  565. }
  566. }
  567. /// <summary>
  568. /// Produces an array of indexes from 0 to the maximum value in the specified array
  569. /// </summary>
  570. /// <param name="indexes">The source array</param>
  571. /// <returns>The array of indexes from 0 to the maximum value in the specified array</returns>
  572. private static uint[] SolidIndexes(uint[] indexes)
  573. {
  574. var max = indexes.Aggregate(0, (current, i) => Math.Max(current, (int)i));
  575. if (max > 0)
  576. {
  577. max++;
  578. var res = new uint[max];
  579. for (var i = 0; i < max; i++)
  580. {
  581. res[i] = (uint)i;
  582. }
  583. return res;
  584. }
  585. return indexes;
  586. }
  587. /// <summary>
  588. /// Checks whether all the indexes are valid.
  589. /// </summary>
  590. /// <param name="indexes">The indexes to check.</param>
  591. /// <returns>True is valid; otherwise, false.</returns>
  592. private static bool CheckIndexes(params int[] indexes)
  593. {
  594. return indexes.All(i => i >= 0);
  595. }
  596. private void ArchiveExtractCallbackCommonInit(ArchiveExtractCallback aec)
  597. {
  598. aec.Open += ((s, e) => { _unpackedSize = (long)e.TotalSize; });
  599. aec.FileExtractionStarted += FileExtractionStartedEventProxy;
  600. aec.FileExtractionFinished += FileExtractionFinishedEventProxy;
  601. aec.Extracting += ExtractingEventProxy;
  602. aec.FileExists += FileExistsEventProxy;
  603. }
  604. /// <summary>
  605. /// Gets the IArchiveExtractCallback callback
  606. /// </summary>
  607. /// <param name="directory">The directory where extract the files</param>
  608. /// <param name="filesCount">The number of files to be extracted</param>
  609. /// <param name="actualIndexes">The list of actual indexes (solid archives support)</param>
  610. /// <returns>The ArchiveExtractCallback callback</returns>
  611. private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int filesCount, List<uint> actualIndexes)
  612. {
  613. var aec = string.IsNullOrEmpty(Password) ?
  614. new ArchiveExtractCallback(_archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, this) :
  615. new ArchiveExtractCallback(_archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, Password, this);
  616. ArchiveExtractCallbackCommonInit(aec);
  617. return aec;
  618. }
  619. /// <summary>
  620. /// Gets the IArchiveExtractCallback callback
  621. /// </summary>
  622. /// <param name="stream">The stream where extract the file</param>
  623. /// <param name="index">The file index</param>
  624. /// <param name="filesCount">The number of files to be extracted</param>
  625. /// <returns>The ArchiveExtractCallback callback</returns>
  626. private ArchiveExtractCallback GetArchiveExtractCallback(Stream stream, uint index, int filesCount)
  627. {
  628. var aec = string.IsNullOrEmpty(Password)
  629. ? new ArchiveExtractCallback(_archive, stream, filesCount, index, this)
  630. : new ArchiveExtractCallback(_archive, stream, filesCount, index, Password, this);
  631. ArchiveExtractCallbackCommonInit(aec);
  632. return aec;
  633. }
  634. private void FreeArchiveExtractCallback(ArchiveExtractCallback callback)
  635. {
  636. callback.Open -= ((s, e) => { _unpackedSize = (long)e.TotalSize; });
  637. callback.FileExtractionStarted -= FileExtractionStartedEventProxy;
  638. callback.FileExtractionFinished -= FileExtractionFinishedEventProxy;
  639. callback.Extracting -= ExtractingEventProxy;
  640. callback.FileExists -= FileExistsEventProxy;
  641. }
  642. #endregion
  643. #endif
  644. /// <summary>
  645. /// Checks if the specified stream supports extraction.
  646. /// </summary>
  647. /// <param name="stream">The stream to check.</param>
  648. private static void ValidateStream(Stream stream)
  649. {
  650. if (stream == null)
  651. {
  652. throw new ArgumentNullException(nameof(stream));
  653. }
  654. if (!stream.CanSeek || !stream.CanRead)
  655. {
  656. throw new ArgumentException("The specified stream can not seek or read.", nameof(stream));
  657. }
  658. if (stream.Length == 0)
  659. {
  660. throw new ArgumentException("The specified stream has zero length.", nameof(stream));
  661. }
  662. }
  663. #if UNMANAGED
  664. #region IDisposable Members
  665. private void CommonDispose()
  666. {
  667. if (_opened)
  668. {
  669. try
  670. {
  671. _archive?.Close();
  672. }
  673. catch (Exception) { }
  674. }
  675. _archive = null;
  676. _archiveFileData = null;
  677. _archiveProperties = null;
  678. _archiveFileInfoCollection = null;
  679. if (_inStream != null && !_leaveOpen)
  680. {
  681. _inStream.Dispose();
  682. _inStream = null;
  683. }
  684. if (_openCallback != null)
  685. {
  686. try
  687. {
  688. _openCallback.Dispose();
  689. }
  690. catch (ObjectDisposedException) { }
  691. _openCallback = null;
  692. }
  693. if (_archiveStream != null && !_leaveOpen)
  694. {
  695. if (_archiveStream is IDisposable disposable)
  696. {
  697. try
  698. {
  699. if (disposable is DisposeVariableWrapper wrapper)
  700. {
  701. wrapper.DisposeStream = true;
  702. }
  703. disposable.Dispose();
  704. }
  705. catch (ObjectDisposedException) { }
  706. _archiveStream = null;
  707. }
  708. }
  709. SevenZipLibraryManager.FreeLibrary(this, _format);
  710. }
  711. /// <summary>
  712. /// Releases the unmanaged resources used by SevenZipExtractor.
  713. /// </summary>
  714. public void Dispose()
  715. {
  716. if (_asynchronousDisposeLock)
  717. {
  718. throw new InvalidOperationException("SevenZipExtractor instance must not be disposed while making an asynchronous method call.");
  719. }
  720. if (!_disposed)
  721. {
  722. CommonDispose();
  723. }
  724. _disposed = true;
  725. }
  726. #endregion
  727. #region Core public Members
  728. #region Events
  729. /// <summary>
  730. /// Occurs when a new file is going to be unpacked.
  731. /// </summary>
  732. /// <remarks>Occurs when 7-zip engine requests for an output stream for a new file to unpack in.</remarks>
  733. public event EventHandler<FileInfoEventArgs> FileExtractionStarted;
  734. /// <summary>
  735. /// Occurs when a file has been successfully unpacked.
  736. /// </summary>
  737. public event EventHandler<FileInfoEventArgs> FileExtractionFinished;
  738. /// <summary>
  739. /// Occurs when the archive has been unpacked.
  740. /// </summary>
  741. public event EventHandler<EventArgs> ExtractionFinished;
  742. /// <summary>
  743. /// Occurs when data are being extracted.
  744. /// </summary>
  745. /// <remarks>Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines.</remarks>
  746. public event EventHandler<ProgressEventArgs> Extracting;
  747. /// <summary>
  748. /// Occurs during the extraction when a file already exists.
  749. /// </summary>
  750. public event EventHandler<FileOverwriteEventArgs> FileExists;
  751. #region Event proxies
  752. /// <summary>
  753. /// Event proxy for FileExtractionStarted.
  754. /// </summary>
  755. /// <param name="sender">The sender of the event.</param>
  756. /// <param name="e">The event arguments.</param>
  757. private void FileExtractionStartedEventProxy(object sender, FileInfoEventArgs e)
  758. {
  759. OnEvent(FileExtractionStarted, e, true);
  760. }
  761. /// <summary>
  762. /// Event proxy for FileExtractionFinished.
  763. /// </summary>
  764. /// <param name="sender">The sender of the event.</param>
  765. /// <param name="e">The event arguments.</param>
  766. private void FileExtractionFinishedEventProxy(object sender, FileInfoEventArgs e)
  767. {
  768. OnEvent(FileExtractionFinished, e, true);
  769. }
  770. /// <summary>
  771. /// Event proxy for Extracting.
  772. /// </summary>
  773. /// <param name="sender">The sender of the event.</param>
  774. /// <param name="e">The event arguments.</param>
  775. private void ExtractingEventProxy(object sender, ProgressEventArgs e)
  776. {
  777. OnEvent(Extracting, e, false);
  778. }
  779. /// <summary>
  780. /// Event proxy for FileExists.
  781. /// </summary>
  782. /// <param name="sender">The sender of the event.</param>
  783. /// <param name="e">The event arguments.</param>
  784. private void FileExistsEventProxy(object sender, FileOverwriteEventArgs e)
  785. {
  786. OnEvent(FileExists, e, true);
  787. }
  788. #endregion
  789. #endregion
  790. #region Properties
  791. /// <summary>
  792. /// Gets the collection of ArchiveFileInfo with all information about files in the archive
  793. /// </summary>
  794. public ReadOnlyCollection<ArchiveFileInfo> ArchiveFileData
  795. {
  796. get
  797. {
  798. DisposedCheck();
  799. InitArchiveFileData(true);
  800. return _archiveFileInfoCollection;
  801. }
  802. }
  803. /// <summary>
  804. /// Gets the properties for the current archive
  805. /// </summary>
  806. public ReadOnlyCollection<ArchiveProperty> ArchiveProperties
  807. {
  808. get
  809. {
  810. DisposedCheck();
  811. InitArchiveFileData(true);
  812. return _archiveProperties;
  813. }
  814. }
  815. /// <summary>
  816. /// Gets the collection of all file names contained in the archive.
  817. /// </summary>
  818. /// <remarks>
  819. /// Each get recreates the collection
  820. /// </remarks>
  821. public ReadOnlyCollection<string> ArchiveFileNames
  822. {
  823. get
  824. {
  825. DisposedCheck();
  826. InitArchiveFileData(true);
  827. var fileNames = new List<string>(_archiveFileData.Count);
  828. fileNames.AddRange(_archiveFileData.Select(afi => afi.FileName));
  829. return new ReadOnlyCollection<string>(fileNames);
  830. }
  831. }
  832. /// <summary>
  833. /// Gets the list of archive volume file names.
  834. /// </summary>
  835. public ReadOnlyCollection<string> VolumeFileNames
  836. {
  837. get
  838. {
  839. DisposedCheck();
  840. InitArchiveFileData(true);
  841. return _volumeFileNames;
  842. }
  843. }
  844. #endregion
  845. /// <summary>
  846. /// Performs the archive integrity test.
  847. /// </summary>
  848. /// <returns>True is the archive is ok; otherwise, false.</returns>
  849. public bool Check()
  850. {
  851. DisposedCheck();
  852. try
  853. {
  854. InitArchiveFileData(false);
  855. var archiveStream = GetArchiveStream(true);
  856. var openCallback = GetArchiveOpenCallback();
  857. if (!OpenArchive(archiveStream, openCallback))
  858. {
  859. return false;
  860. }
  861. using (var aec = GetArchiveExtractCallback("", (int)_filesCount, null))
  862. {
  863. try
  864. {
  865. CheckedExecute(
  866. _archive.Extract(null, uint.MaxValue, 1, aec),
  867. SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
  868. }
  869. finally
  870. {
  871. FreeArchiveExtractCallback(aec);
  872. }
  873. }
  874. }
  875. catch (Exception)
  876. {
  877. return false;
  878. }
  879. finally
  880. {
  881. _archive?.Close();
  882. if (_archiveStream is IDisposable disposable)
  883. {
  884. disposable.Dispose();
  885. }
  886. _archiveStream = null;
  887. _opened = false;
  888. }
  889. return true;
  890. }
  891. #region ExtractFile overloads
  892. /// <summary>
  893. /// Unpacks the file by its name to the specified stream.
  894. /// </summary>
  895. /// <param name="fileName">The file full name in the archive file table.</param>
  896. /// <param name="stream">The stream where the file is to be unpacked.</param>
  897. public void ExtractFile(string fileName, Stream stream)
  898. {
  899. DisposedCheck();
  900. InitArchiveFileData(false);
  901. var index = -1;
  902. foreach (var afi in _archiveFileData)
  903. {
  904. if (afi.FileName == fileName && !afi.IsDirectory)
  905. {
  906. index = afi.Index;
  907. break;
  908. }
  909. }
  910. if (index == -1)
  911. {
  912. if (!ThrowException(null, new ArgumentOutOfRangeException(
  913. nameof(fileName),
  914. "The specified file name was not found in the archive file table.")))
  915. {
  916. return;
  917. }
  918. }
  919. else
  920. {
  921. ExtractFile(index, stream);
  922. }
  923. }
  924. /// <summary>
  925. /// Unpacks the file by its index to the specified stream.
  926. /// </summary>
  927. /// <param name="index">Index in the archive file table.</param>
  928. /// <param name="stream">The stream where the file is to be unpacked.</param>
  929. public void ExtractFile(int index, Stream stream)
  930. {
  931. DisposedCheck();
  932. ClearExceptions();
  933. if (!CheckIndexes(index))
  934. {
  935. if (!ThrowException(null, new ArgumentException("The index must be more or equal to zero.", nameof(index))))
  936. {
  937. return;
  938. }
  939. }
  940. if (!stream.CanWrite)
  941. {
  942. if (!ThrowException(null, new ArgumentException("The specified stream can not be written.", nameof(stream))))
  943. {
  944. return;
  945. }
  946. }
  947. InitArchiveFileData(false);
  948. if (index > _filesCount - 1)
  949. {
  950. if (!ThrowException(null, new ArgumentOutOfRangeException(
  951. nameof(index), "The specified index is greater than the archive files count.")))
  952. {
  953. return;
  954. }
  955. }
  956. var archiveStream = GetArchiveStream(false);
  957. var openCallback = GetArchiveOpenCallback();
  958. if (!OpenArchive(archiveStream, openCallback))
  959. {
  960. return;
  961. }
  962. try
  963. {
  964. var indexes = new[] { (uint)index };
  965. var entry = _archiveFileData[index];
  966. if (_isSolid.Value && !entry.Method.Equals("Copy", StringComparison.InvariantCultureIgnoreCase))
  967. {
  968. indexes = SolidIndexes(indexes);
  969. }
  970. using (var aec = GetArchiveExtractCallback(stream, (uint)index, indexes.Length))
  971. {
  972. try
  973. {
  974. CheckedExecute(
  975. _archive.Extract(indexes, (uint)indexes.Length, 0, aec),
  976. SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
  977. }
  978. finally
  979. {
  980. FreeArchiveExtractCallback(aec);
  981. }
  982. }
  983. }
  984. catch (Exception)
  985. {
  986. if (openCallback.ThrowException())
  987. {
  988. throw;
  989. }
  990. }
  991. OnEvent(ExtractionFinished, EventArgs.Empty, false);
  992. ThrowUserException();
  993. }
  994. #endregion
  995. #region ExtractFiles overloads
  996. /// <summary>
  997. /// Unpacks files by their indices to the specified directory.
  998. /// </summary>
  999. /// <param name="indexes">indexes of the files in the archive file table.</param>
  1000. /// <param name="directory">Directory where the files are to be unpacked.</param>
  1001. public void ExtractFiles(string directory, params int[] indexes)
  1002. {
  1003. DisposedCheck();
  1004. ClearExceptions();
  1005. if (!CheckIndexes(indexes))
  1006. {
  1007. if (!ThrowException(null, new ArgumentException("The indexes must be more or equal to zero.", nameof(indexes))))
  1008. {
  1009. return;
  1010. }
  1011. }
  1012. InitArchiveFileData(false);
  1013. #region Indexes stuff
  1014. var uIndexes = new uint[indexes.Length];
  1015. for (var i = 0; i < indexes.Length; i++)
  1016. {
  1017. uIndexes[i] = (uint)indexes[i];
  1018. }
  1019. if (uIndexes.Where(i => i >= _filesCount).Any(
  1020. i => !ThrowException(null,
  1021. new ArgumentOutOfRangeException(nameof(indexes),
  1022. $"Index must be less than {_filesCount.Value.ToString(CultureInfo.InvariantCulture)}!"))))
  1023. {
  1024. return;
  1025. }
  1026. var origIndexes = new List<uint>(uIndexes);
  1027. origIndexes.Sort();
  1028. uIndexes = origIndexes.ToArray();
  1029. if (_isSolid.Value)
  1030. {
  1031. uIndexes = SolidIndexes(uIndexes);
  1032. }
  1033. #endregion
  1034. try
  1035. {
  1036. IInStream archiveStream;
  1037. using ((archiveStream = GetArchiveStream(origIndexes.Count != 1)) as IDisposable)
  1038. {
  1039. var openCallback = GetArchiveOpenCallback();
  1040. if (!OpenArchive(archiveStream, openCallback))
  1041. {
  1042. return;
  1043. }
  1044. try
  1045. {
  1046. using (var aec = GetArchiveExtractCallback(directory, (int)_filesCount, origIndexes))
  1047. {
  1048. try
  1049. {
  1050. CheckedExecute(
  1051. _archive.Extract(uIndexes, (uint)uIndexes.Length, 0, aec),
  1052. SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
  1053. }
  1054. finally
  1055. {
  1056. FreeArchiveExtractCallback(aec);
  1057. }
  1058. }
  1059. }
  1060. catch (Exception)
  1061. {
  1062. if (openCallback.ThrowException())
  1063. {
  1064. throw;
  1065. }
  1066. }
  1067. }
  1068. OnEvent(ExtractionFinished, EventArgs.Empty, false);
  1069. }
  1070. finally
  1071. {
  1072. if (origIndexes.Count > 1)
  1073. {
  1074. _archive?.Close();
  1075. _archiveStream = null;
  1076. _opened = false;
  1077. }
  1078. }
  1079. ThrowUserException();
  1080. }
  1081. /// <summary>
  1082. /// Unpacks files by their full names to the specified directory.
  1083. /// </summary>
  1084. /// <param name="fileNames">Full file names in the archive file table.</param>
  1085. /// <param name="directory">Directory where the files are to be unpacked.</param>
  1086. public void ExtractFiles(string directory, params string[] fileNames)
  1087. {
  1088. DisposedCheck();
  1089. InitArchiveFileData(false);
  1090. var indexes = new List<int>(fileNames.Length);
  1091. var archiveFileNames = new List<string>(ArchiveFileNames);
  1092. foreach (var fn in fileNames)
  1093. {
  1094. if (!archiveFileNames.Contains(fn))
  1095. {
  1096. if (!ThrowException(null, new ArgumentOutOfRangeException(nameof(fileNames), $"File \"{fn}\" was not found in the archive file table.")))
  1097. {
  1098. return;
  1099. }
  1100. }
  1101. else
  1102. {
  1103. foreach (var afi in _archiveFileData)
  1104. {
  1105. if (afi.FileName == fn && !afi.IsDirectory)
  1106. {
  1107. indexes.Add(afi.Index);
  1108. break;
  1109. }
  1110. }
  1111. }
  1112. }
  1113. ExtractFiles(directory, indexes.ToArray());
  1114. }
  1115. /// <summary>
  1116. /// Extracts files from the archive, giving a callback the choice what
  1117. /// to do with each file. The order of the files is given by the archive.
  1118. /// 7-Zip (and any other solid) archives are NOT supported.
  1119. /// </summary>
  1120. /// <param name="extractFileCallback">The callback to call for each file in the archive.</param>
  1121. /// <exception cref="SevenZipExtractionFailedException">Thrown when trying to extract from solid archives.</exception>
  1122. public void ExtractFiles(ExtractFileCallback extractFileCallback)
  1123. {
  1124. DisposedCheck();
  1125. InitArchiveFileData(false);
  1126. if (IsSolid)
  1127. {
  1128. throw new SevenZipExtractionFailedException("Solid archives are not supported.");
  1129. }
  1130. foreach (var archiveFileInfo in ArchiveFileData)
  1131. {
  1132. var extractFileCallbackArgs = new ExtractFileCallbackArgs(archiveFileInfo);
  1133. extractFileCallback(extractFileCallbackArgs);
  1134. if (extractFileCallbackArgs.CancelExtraction)
  1135. {
  1136. break;
  1137. }
  1138. if (extractFileCallbackArgs.ExtractToStream != null || extractFileCallbackArgs.ExtractToFile != null)
  1139. {
  1140. var callDone = false;
  1141. try
  1142. {
  1143. if (extractFileCallbackArgs.ExtractToStream != null)
  1144. {
  1145. ExtractFile(archiveFileInfo.Index, extractFileCallbackArgs.ExtractToStream);
  1146. }
  1147. else
  1148. {
  1149. using (var file = new FileStream(extractFileCallbackArgs.ExtractToFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192))
  1150. {
  1151. ExtractFile(archiveFileInfo.Index, file);
  1152. }
  1153. }
  1154. callDone = true;
  1155. }
  1156. catch (Exception ex)
  1157. {
  1158. extractFileCallbackArgs.Exception = ex;
  1159. extractFileCallbackArgs.Reason = ExtractFileCallbackReason.Failure;
  1160. extractFileCallback(extractFileCallbackArgs);
  1161. if (!ThrowException(null, ex))
  1162. {
  1163. return;
  1164. }
  1165. }
  1166. if (callDone)
  1167. {
  1168. extractFileCallbackArgs.Reason = ExtractFileCallbackReason.Done;
  1169. extractFileCallback(extractFileCallbackArgs);
  1170. }
  1171. }
  1172. }
  1173. }
  1174. #endregion
  1175. /// <summary>
  1176. /// Unpacks the whole archive to the specified directory.
  1177. /// </summary>
  1178. /// <param name="directory">The directory where the files are to be unpacked.</param>
  1179. public void ExtractArchive(string directory)
  1180. {
  1181. DisposedCheck();
  1182. ClearExceptions();
  1183. InitArchiveFileData(false);
  1184. try
  1185. {
  1186. IInStream archiveStream;
  1187. using ((archiveStream = GetArchiveStream(true)) as IDisposable)
  1188. {
  1189. var openCallback = GetArchiveOpenCallback();
  1190. if (!OpenArchive(archiveStream, openCallback))
  1191. {
  1192. return;
  1193. }
  1194. try
  1195. {
  1196. using (var aec = GetArchiveExtractCallback(directory, (int)_filesCount, null))
  1197. {
  1198. try
  1199. {
  1200. CheckedExecute(
  1201. _archive.Extract(null, uint.MaxValue, 0, aec),
  1202. SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
  1203. OnEvent(ExtractionFinished, EventArgs.Empty, false);
  1204. }
  1205. finally
  1206. {
  1207. FreeArchiveExtractCallback(aec);
  1208. }
  1209. }
  1210. }
  1211. catch (Exception)
  1212. {
  1213. if (openCallback.ThrowException())
  1214. {
  1215. throw;
  1216. }
  1217. }
  1218. }
  1219. }
  1220. finally
  1221. {
  1222. _archive?.Close();
  1223. _archiveStream = null;
  1224. _opened = false;
  1225. }
  1226. ThrowUserException();
  1227. }
  1228. #endregion
  1229. #endif
  1230. #region LZMA SDK functions
  1231. internal static byte[] GetLzmaProperties(Stream inStream, out long outSize)
  1232. {
  1233. var lzmAproperties = new byte[5];
  1234. if (inStream.Read(lzmAproperties, 0, 5) != 5)
  1235. {
  1236. throw new LzmaException();
  1237. }
  1238. outSize = 0;
  1239. for (var i = 0; i < 8; i++)
  1240. {
  1241. var b = inStream.ReadByte();
  1242. if (b < 0)
  1243. {
  1244. throw new LzmaException();
  1245. }
  1246. outSize |= ((long)(byte)b) << (i << 3);
  1247. }
  1248. return lzmAproperties;
  1249. }
  1250. /// <summary>
  1251. /// Decompress the specified stream (C# inside)
  1252. /// </summary>
  1253. /// <param name="inStream">The source compressed stream</param>
  1254. /// <param name="outStream">The destination uncompressed stream</param>
  1255. /// <param name="inLength">The length of compressed data (null for inStream.Length)</param>
  1256. /// <param name="codeProgressEvent">The event for handling the code progress</param>
  1257. public static void DecompressStream(Stream inStream, Stream outStream, int? inLength, EventHandler<ProgressEventArgs> codeProgressEvent)
  1258. {
  1259. if (!inStream.CanRead || !outStream.CanWrite)
  1260. {
  1261. throw new ArgumentException("The specified streams are invalid.");
  1262. }
  1263. var decoder = new Decoder();
  1264. var inSize = (inLength ?? inStream.Length) - inStream.Position;
  1265. decoder.SetDecoderProperties(GetLzmaProperties(inStream, out var outSize));
  1266. decoder.Code(inStream, outStream, inSize, outSize, new LzmaProgressCallback(inSize, codeProgressEvent));
  1267. }
  1268. /// <summary>
  1269. /// Decompress byte array compressed with LZMA algorithm (C# inside)
  1270. /// </summary>
  1271. /// <param name="data">Byte array to decompress</param>
  1272. /// <returns>Decompressed byte array</returns>
  1273. public static byte[] ExtractBytes(byte[] data)
  1274. {
  1275. using (var inStream = new MemoryStream(data))
  1276. {
  1277. var decoder = new Decoder();
  1278. inStream.Seek(0, 0);
  1279. using (var outStream = new MemoryStream())
  1280. {
  1281. decoder.SetDecoderProperties(GetLzmaProperties(inStream, out var outSize));
  1282. decoder.Code(inStream, outStream, inStream.Length - inStream.Position, outSize, null);
  1283. return outStream.ToArray();
  1284. }
  1285. }
  1286. }
  1287. #endregion
  1288. }
  1289. }