public void ExtractEntry(DLCEntry entry, Stream input, Stream output) { input.JumpTo(entry.dataOffset); if (entry.compressedBlockSizesIndex == -1) { output.WriteFromStream(input, entry.uncomprSize); } else { var uncompressedBlockBuffers = new List <byte[]>(); var compressedBlockBuffers = new List <byte[]>(); var blockBytesLeft = new List <long>(); long bytesLeft = entry.uncomprSize; for (int j = 0; j < entry.numBlocks; j++) { blockBytesLeft.Add(bytesLeft); int compressedBlockSize = blockSizes[entry.compressedBlockSizesIndex + j]; int uncompressedBlockSize = (int)Math.Min(bytesLeft, maxBlockSize); if (compressedBlockSize == 0) { compressedBlockSize = (int)maxBlockSize; } compressedBlockBuffers.Add(input.ReadToBuffer(compressedBlockSize)); uncompressedBlockBuffers.Add(null); bytesLeft -= uncompressedBlockSize; } Parallel.For(0, entry.numBlocks, j => { int compressedBlockSize = blockSizes[entry.compressedBlockSizesIndex + (int)j]; int uncompressedBlockSize = (int)Math.Min(blockBytesLeft[(int)j], maxBlockSize); if (compressedBlockSize == 0 || compressedBlockSize == blockBytesLeft[(int)j]) { uncompressedBlockBuffers[(int)j] = compressedBlockBuffers[(int)j]; } else { uncompressedBlockBuffers[(int)j] = LZMA.Decompress(compressedBlockBuffers[(int)j], (uint)uncompressedBlockSize); if (uncompressedBlockBuffers[(int)j].Length == 0) { throw new Exception(); } } }); for (int j = 0; j < entry.numBlocks; j++) { output.WriteFromBuffer(uncompressedBlockBuffers[j]); } } }
private void LoadHeader(Stream stream) { uint tag = stream.ReadUInt32(); if (tag != SfarTag) { throw new Exception("Wrong SFAR tag"); } uint sfarVersion = stream.ReadUInt32(); if (sfarVersion != SfarVersion) { throw new Exception("Wrong SFAR version"); } uint dataOffset = stream.ReadUInt32(); uint entriesOffset = stream.ReadUInt32(); TotalFilesInDLC = stream.ReadUInt32(); uint sizesArrayOffset = stream.ReadUInt32(); maxBlockSize = stream.ReadUInt32(); uint compressionTag = stream.ReadUInt32(); if (compressionTag != LZMATag) { throw new Exception("Not LZMA compression for SFAR file"); } uint numBlockSizes = 0; stream.JumpTo(entriesOffset); filesList = new List <DLCEntry>(); for (int i = 0; i < TotalFilesInDLC; i++) { DLCEntry file = new DLCEntry { filenameHash = stream.ReadToBuffer(16), compressedBlockSizesIndex = stream.ReadInt32(), uncomprSize = stream.ReadUInt32() }; file.uncomprSize |= (long)stream.ReadByte() << 32; file.dataOffset = stream.ReadUInt32(); file.dataOffset |= (long)stream.ReadByte() << 32; file.numBlocks = (uint)((file.uncomprSize + maxBlockSize - 1) / maxBlockSize); filesList.Add(file); numBlockSizes += file.numBlocks; UncompressedSize += file.uncomprSize; } stream.JumpTo(sizesArrayOffset); blockSizes = new List <ushort>(); for (int i = 0; i < numBlockSizes; i++) { blockSizes.Add(stream.ReadUInt16()); } filenamesIndex = -1; for (int i = 0; i < TotalFilesInDLC; i++) { if (StructuralComparisons.StructuralEqualityComparer.Equals(filesList[i].filenameHash, FileListHash)) { stream.JumpTo(filesList[i].dataOffset); int compressedBlockSize = blockSizes[filesList[i].compressedBlockSizesIndex]; byte[] inBuf = stream.ReadToBuffer(compressedBlockSize); byte[] outBuf = LZMA.Decompress(inBuf, (uint)filesList[i].uncomprSize); if (outBuf.Length == 0) { throw new Exception(); } StreamReader filenamesStream = new StreamReader(new MemoryStream(outBuf)); while (filenamesStream.EndOfStream == false) { string name = filenamesStream.ReadLine(); byte[] hash = MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(name.ToLowerInvariant())); for (int l = 0; l < TotalFilesInDLC; l++) { if (StructuralComparisons.StructuralEqualityComparer.Equals(filesList[l].filenameHash, hash)) { DLCEntry f = filesList[l]; f.filenamePath = name.Replace('/', '\\'); filesList[l] = f; } } } filenamesIndex = i; break; } } }