private IReadOnlyCollection <CabinetVolume> ReadVolumes(Stream cabinetStream) { var cabinetBinaryReader = new BinaryReader(cabinetStream); List <CabinetVolume> volumes = new List <CabinetVolume>(); for (int i = 0; i < cabinetHeader.CabinetFileHeader.cFolders; i++) { CabinetVolume volume = new CabinetVolume() { CabinetFileVolume = cabinetStream.ReadStruct <CFFOLDER>(), AdditionalApplicationData = cabinetHeader.VolumeAdditionalApplicationDataSize > 0 ? cabinetBinaryReader.ReadBytes(cabinetHeader.VolumeAdditionalApplicationDataSize) : new byte[0] }; volumes.Add(volume); if (volume.CabinetFileVolume.typeCompress != CFFOLDER.CFTYPECOMPRESS.TYPE_LZX && volume.CabinetFileVolume.typeCompress != CFFOLDER.CFTYPECOMPRESS.TYPE_MSZIP && volume.CabinetFileVolume.typeCompress != CFFOLDER.CFTYPECOMPRESS.TYPE_NONE) { throw new Exception("Unsupported Cabinet: Only LZX, MSZip and Store is currently supported"); } if (volume.CabinetFileVolume.typeCompress == CFFOLDER.CFTYPECOMPRESS.TYPE_LZX) { if (volume.CabinetFileVolume.typeCompressOption < 15 || volume.CabinetFileVolume.typeCompressOption > 21) { throw new Exception("Unsupported Cabinet: LZX variable does not fall in supported ranges"); } } } return(volumes); }
public string ExtractFile(string FileName) { var destination = Path.GetTempFileName(); // Do the extraction for (int volumeIndex = 0; volumeIndex < volumes.Count; volumeIndex++) { CabinetVolume volume = volumes.ElementAt(volumeIndex); LzxDecoder lzx = null; Inflater inf = null; if (volume.CabinetFileVolume.typeCompress == CFFOLDER.CFTYPECOMPRESS.TYPE_LZX) { lzx = new LzxDecoder(volume.CabinetFileVolume.typeCompressOption); } if (volume.CabinetFileVolume.typeCompress == CFFOLDER.CFTYPECOMPRESS.TYPE_MSZIP) { inf = new Inflater(true); } List <(CFDATA dataStruct, int dataOffsetCabinet, int beginFolderOffset, int endFolderOffset, int index)> datas = new List <(CFDATA dataStruct, int dataOffsetCabinet, int beginFolderOffset, int endFolderOffset, int index)>(); List <(CabinetVolumeFile file, int startingBlock, int startingBlockOffset, int endingBlock, int endingBlockOffset)> fileBlockMap = new List <(CabinetVolumeFile file, int startingBlock, int startingBlockOffset, int endingBlock, int endingBlockOffset)>(); // Build Data Map using (var cabinetFileStream = File.OpenRead(InputFile)) { var cabinetBinaryReader = new BinaryReader(cabinetFileStream); cabinetFileStream.Seek(volume.CabinetFileVolume.firstDataBlockOffset, SeekOrigin.Begin); int offset = 0; for (int i = 0; i < volume.CabinetFileVolume.dataBlockCount; i++) { CFDATA CabinetData = cabinetBinaryReader.BaseStream.ReadStruct <CFDATA>(); cabinetBinaryReader.BaseStream.Seek(cabinetHeader.DataAdditionalApplicationDataSize, SeekOrigin.Current); datas.Add((CabinetData, (int)cabinetBinaryReader.BaseStream.Position, offset, offset + CabinetData.cbUncomp - 1, i)); cabinetBinaryReader.BaseStream.Seek(CabinetData.cbData, SeekOrigin.Current); offset += CabinetData.cbUncomp; } // Build Block Map foreach (CabinetVolumeFile file in files) { if (file.CabinetFileVolumeFile.iFolder != volumeIndex) { continue; } var FirstBlock = datas.First(x => x.beginFolderOffset <= file.CabinetFileVolumeFile.uoffFolderStart && file.CabinetFileVolumeFile.uoffFolderStart <= x.endFolderOffset); var LastBlock = datas.First(x => x.beginFolderOffset <= (file.CabinetFileVolumeFile.uoffFolderStart + file.CabinetFileVolumeFile.cbFile - 1) && (file.CabinetFileVolumeFile.uoffFolderStart + file.CabinetFileVolumeFile.cbFile - 1) <= x.endFolderOffset); int fileBeginFolderOffset = (int)file.CabinetFileVolumeFile.uoffFolderStart; int fileEndFolderOffset = (int)file.CabinetFileVolumeFile.uoffFolderStart + (int)file.CabinetFileVolumeFile.cbFile - 1; int start = (int)file.CabinetFileVolumeFile.uoffFolderStart - FirstBlock.beginFolderOffset; int end = fileEndFolderOffset - LastBlock.beginFolderOffset; fileBlockMap.Add((file, FirstBlock.index, start, LastBlock.index, end)); } int fcount = 0; for (int i = 0; i < volume.CabinetFileVolume.dataBlockCount; i++) { var CabinetData = datas[i]; cabinetBinaryReader.BaseStream.Seek(CabinetData.dataOffsetCabinet, SeekOrigin.Begin); byte[] uncompressedDataBlock = new byte[CabinetData.dataStruct.cbUncomp]; byte[] compressedDataBlock = null; if (volume.CabinetFileVolume.typeCompress == CFFOLDER.CFTYPECOMPRESS.TYPE_MSZIP) { var magic = cabinetBinaryReader.ReadBytes(2); if (StructuralComparisons.StructuralComparer.Compare(magic, new byte[] { (byte)'C', (byte)'K' }) != 0) { throw new Exception("Bad Cabinet: Invalid Signature for MSZIP block"); } compressedDataBlock = cabinetBinaryReader.ReadBytes(CabinetData.dataStruct.cbData - 2); } else { compressedDataBlock = cabinetBinaryReader.ReadBytes(CabinetData.dataStruct.cbData); } using (var uncompressedDataBlockStream = new MemoryStream(uncompressedDataBlock)) using (var compressedDataBlockStream = new MemoryStream(compressedDataBlock)) { ExpandBlock(uncompressedDataBlockStream, compressedDataBlockStream, volume.CabinetFileVolume.typeCompress, lzx, inf); } foreach (CabinetVolumeFile file in files) { if (file.CabinetFileVolumeFile.iFolder != volumeIndex) { continue; } if (file.FileName.ToLower() != FileName.ToLower()) { continue; } var mapping = fileBlockMap.First(x => x.file.FileName == file.FileName); // This block contains this file if (mapping.startingBlock <= i && i <= mapping.endingBlock) { int start = 0; int end = CabinetData.dataStruct.cbUncomp - 1; bool IsFirstBlock = mapping.startingBlock == i; bool IsLastBlock = mapping.endingBlock == i; if (IsFirstBlock) { start = mapping.startingBlockOffset; } if (IsLastBlock) { end = mapping.endingBlockOffset; fcount++; } int count = end - start + 1; using (var uncompressedDataStream = File.Open(destination, FileMode.OpenOrCreate)) { uncompressedDataStream.Seek(0, SeekOrigin.End); uncompressedDataStream.Write(uncompressedDataBlock, start, count); } } } } } } return(destination); }