/// <summary> /// Don't forget to wrap semaphore. /// </summary> /// <param name="entry"></param> /// <param name="destStream"></param> private void ExtractToStreamInternal(BA2GeneralFileEntry entry, Stream destStream) { // DeflateStream throws exception when // reads zlib compressed file header const int zlibHeaderLength = 2; // offset to file data UInt64 dataOffset = entry.IsCompressed() ? entry.Offset + zlibHeaderLength : entry.Offset; m_archiveStream.Seek((long)dataOffset, SeekOrigin.Begin); byte[] rawData = new byte[entry.UnpackedLength]; if (entry.IsCompressed()) { using (var uncompressStream = new DeflateStream(m_archiveStream, CompressionMode.Decompress, leaveOpen: true)) { var bytesReaden = uncompressStream.Read(rawData, 0, (int)entry.UnpackedLength); } } else { // TODO // entry.UnpackedLength => dataLength; m_archiveStream.Read(rawData, 0, (int)entry.UnpackedLength); } destStream.Write(rawData, 0, rawData.Length); destStream.Flush(); destStream.Seek(0, SeekOrigin.Begin); }
/// <summary> /// Extract single file to specified directory by index. /// </summary> /// <param name="index">The index.</param> /// <param name="destination">Absolute or relative directory path where extracted file will be placed.</param> /// <param name="overwriteFile">Overwrite existing file in directory with extracted one?</param> /// <exception cref="IndexOutOfRangeException"> /// <c>index</c> is less than 0 or more than total files in archive. /// </exception> /// <exception cref="ArgumentNullException" /> /// <exception cref="BA2ExtractionException"></exception> public override void Extract(int index, string destination, bool overwriteFile) { CheckDisposed(); if (index < 0 || index > this.TotalFiles) { throw new IndexOutOfRangeException(nameof(index)); } if (destination == null) { throw new ArgumentNullException(nameof(destination)); } BA2GeneralFileEntry entry = fileEntries[index]; string extractPath = CreateDirectoryAndGetPath(entry, destination, overwriteFile); using (var stream = File.Create(extractPath, 4096, FileOptions.SequentialScan)) { try { accessSemaphore.Wait(); ExtractToStreamInternal(entry, stream); } finally { accessSemaphore.Release(); } } }
private void ExtractFilesInternal(BA2GeneralFileEntry[] entries, string destination, CancellationToken cancellationToken, IProgress <int> progress, bool overwriteFiles) { try { accessSemaphore.Wait(cancellationToken); if (string.IsNullOrWhiteSpace(destination)) { throw new ArgumentException(nameof(destination)); } int totalEntries = entries.Count(); bool shouldUpdate = cancellationToken != null || progress != null; int counter = 0; int updateFrequency = Math.Max(1, totalEntries / 100); int nextUpdate = updateFrequency; BlockingCollection <string> readyFilenames = new BlockingCollection <string>(totalEntries); Action createDirs = () => CreateDirectoriesForFiles(entries, readyFilenames, cancellationToken, destination, overwriteFiles); if (IsMultithreaded) { Task.Run(createDirs, cancellationToken); } else { createDirs(); } for (int i = 0; i < totalEntries; i++) { BA2GeneralFileEntry entry = entries[i]; using (var stream = File.Create(readyFilenames.Take(), 4096, FileOptions.SequentialScan)) { ExtractToStreamInternal(entry, stream); } counter++; if (shouldUpdate && counter >= nextUpdate) { cancellationToken.ThrowIfCancellationRequested(); progress?.Report(counter); nextUpdate += updateFrequency; } } } finally { accessSemaphore.Release(); } }
private BA2GeneralFileEntry[] ConstructEntriesFromIndexes(IEnumerable <int> indexes) { BA2GeneralFileEntry[] entries = new BA2GeneralFileEntry[indexes.Count()]; int i = 0; foreach (int index in indexes) { // TODO throw new IndexOutOfRange entries[i] = fileEntries[index]; i++; } return(entries); }
/// <summary> /// Preloads the data. /// </summary> /// <remarks> /// Do not call base.PreloadData(). /// </remarks> /// <param name="reader">The reader.</param> internal override void PreloadData(BinaryReader reader) { CheckDisposed(); try { accessSemaphore.Wait(); this.BuildFileList(); m_archiveStream.Seek(BA2Loader.HeaderSize, SeekOrigin.Begin); fileEntries = new BA2GeneralFileEntry[TotalFiles]; for (int i = 0; i < TotalFiles; i++) { BA2GeneralFileEntry entry = new BA2GeneralFileEntry() { Unknown0 = reader.ReadUInt32(), Extension = Encoding.ASCII.GetChars(reader.ReadBytes(4)), Unknown1 = reader.ReadUInt32(), Unknown2 = reader.ReadUInt32(), Offset = reader.ReadUInt64(), PackedLength = reader.ReadUInt32(), UnpackedLength = reader.ReadUInt32(), Unknown3 = reader.ReadUInt32(), Index = i }; // 3131961357 = 0xBAADF00D as uint little-endian (0x0DF0ADBA) // System.Diagnostics.Debug.Assert(entry.Unknown3 == 3131961357); fileEntries[i] = entry; } } finally { accessSemaphore.Release(); } }