using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using Compress.SevenZip.Compress.BZip2; using Compress.SevenZip.Compress.LZMA; using Compress.SevenZip.Compress.PPmd; using Compress.SevenZip.Filters; using Compress.SevenZip.Structure; using Compress.Utils; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; namespace Compress.SevenZip { public class SevenZ : ICompress { private List _localFiles = new List(); private FileInfo _zipFileInfo; private Stream _zipFs; private SignatureHeader _signatureHeader; private bool _compressed = true; private long _baseOffset; public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : ""; public long TimeStamp => _zipFileInfo?.LastWriteTime ?? 0; public ZipOpenType ZipOpen { get; private set; } public ZipStatus ZipStatus { get; private set; } public int LocalFilesCount() { return _localFiles.Count; } public string Filename(int i) { return _localFiles[i].FileName; } public ulong? LocalHeader(int i) { return 0; } public ulong UncompressedSize(int i) { return _localFiles[i].UncompressedSize; } public int StreamIndex(int i) { return _localFiles[i].StreamIndex; } public ZipReturn FileStatus(int i) { return _localFiles[i].FileStatus; } public byte[] CRC32(int i) { return _localFiles[i].CRC; } public void ZipFileCloseFailed() { switch (ZipOpen) { case ZipOpenType.Closed: return; case ZipOpenType.OpenRead: ZipFileCloseReadStream(); if (_zipFs != null) { _zipFs.Close(); _zipFs.Dispose(); } break; case ZipOpenType.OpenWrite: _zipFs.Flush(); _zipFs.Close(); _zipFs.Dispose(); if (_zipFileInfo != null) RVIO.File.Delete(_zipFileInfo.FullName); _zipFileInfo = null; break; } ZipOpen = ZipOpenType.Closed; } public bool IsDirectory(int i) { return _localFiles[i].IsDirectory; } public void ZipFileAddDirectory(string filename) { string fName = filename; if (fName.Substring(fName.Length - 1, 1) == @"/") fName = fName.Substring(0, fName.Length - 1); LocalFile lf = new LocalFile { FileName = fName, UncompressedSize = 0, IsDirectory = true, StreamOffset = 0 }; _localFiles.Add(lf); } private class LocalFile { public string FileName; public ulong UncompressedSize; public bool IsDirectory; public byte[] CRC; public int StreamIndex; public ulong StreamOffset; public ZipReturn FileStatus = ZipReturn.ZipUntested; } #region open 7z files public ZipReturn ZipFileOpen(string filename, long timestamp, bool readHeaders) { ZipFileClose(); Debug.WriteLine(filename); #region open file stream try { if (!RVIO.File.Exists(filename)) { ZipFileClose(); return ZipReturn.ZipErrorFileNotFound; } _zipFileInfo = new FileInfo(filename); if ((timestamp != -1) && (_zipFileInfo.LastWriteTime != timestamp)) { ZipFileClose(); return ZipReturn.ZipErrorTimeStamp; } int errorCode = FileStream.OpenFileRead(filename, out _zipFs); if (errorCode != 0) { ZipFileClose(); return ZipReturn.ZipErrorOpeningFile; } } catch (PathTooLongException) { ZipFileClose(); return ZipReturn.ZipFileNameToLong; } catch (IOException) { ZipFileClose(); return ZipReturn.ZipErrorOpeningFile; } #endregion ZipOpen = ZipOpenType.OpenRead; ZipStatus = ZipStatus.None; return ZipFileReadHeaders(); } public ZipReturn ZipFileOpen(Stream inStream) { ZipFileClose(); _zipFileInfo = null; _zipFs = inStream; ZipOpen = ZipOpenType.OpenRead; ZipStatus = ZipStatus.None; return ZipFileReadHeaders(); } private ZipReturn ZipFileReadHeaders() { try { SignatureHeader signatureHeader = new SignatureHeader(); if (!signatureHeader.Read(_zipFs)) { return ZipReturn.ZipSignatureError; } _baseOffset = _zipFs.Position; //_zipFs.Seek(_baseOffset + (long)signatureHeader.NextHeaderOffset, SeekOrigin.Begin); //byte[] mainHeader = new byte[signatureHeader.NextHeaderSize]; //_zipFs.Read(mainHeader, 0, (int)signatureHeader.NextHeaderSize); //if (!CRC.VerifyDigest(signatureHeader.NextHeaderCRC, mainHeader, 0, (uint)signatureHeader.NextHeaderSize)) // return ZipReturn.Zip64EndOfCentralDirError; if (signatureHeader.NextHeaderSize != 0) { _zipFs.Seek(_baseOffset + (long)signatureHeader.NextHeaderOffset, SeekOrigin.Begin); ZipReturn zr = Header.ReadHeaderOrPackedHeader(_zipFs, _baseOffset, out _header); if (zr != ZipReturn.ZipGood) { return zr; } } _zipFs.Seek(_baseOffset + (long)(signatureHeader.NextHeaderOffset + signatureHeader.NextHeaderSize), SeekOrigin.Begin); ZipStatus = ZipStatus.None; ZipStatus |= IsRomVault7Z() ? ZipStatus.TrrntZip : ZipStatus.None; ZipStatus |= Istorrent7Z() ? ZipStatus.Trrnt7Zip : ZipStatus.None; PopulateLocalFiles(out _localFiles); return ZipReturn.ZipGood; } catch { ZipFileClose(); return ZipReturn.ZipErrorReadingFile; } } private void PopulateLocalFiles(out List localFiles) { int emptyFileIndex = 0; int folderIndex = 0; int unpackedStreamsIndex = 0; ulong streamOffset = 0; localFiles = new List(); if (_header == null) return; for (int i = 0; i < _header.FileInfo.Names.Length; i++) { LocalFile lf = new LocalFile { FileName = _header.FileInfo.Names[i] }; if ((_header.FileInfo.EmptyStreamFlags == null) || !_header.FileInfo.EmptyStreamFlags[i]) { lf.StreamIndex = folderIndex; lf.StreamOffset = streamOffset; lf.UncompressedSize = _header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].UnpackedSize; lf.CRC = Util.uinttobytes(_header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].Crc); streamOffset += lf.UncompressedSize; unpackedStreamsIndex++; if (unpackedStreamsIndex >= _header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo.Length) { folderIndex++; unpackedStreamsIndex = 0; streamOffset = 0; } } else { lf.UncompressedSize = 0; lf.CRC = new byte[] { 0, 0, 0, 0 }; lf.IsDirectory = (_header.FileInfo.EmptyFileFlags == null) || !_header.FileInfo.EmptyFileFlags[emptyFileIndex++]; if (lf.IsDirectory) { if (lf.FileName.Substring(lf.FileName.Length - 1, 1) != "/") { lf.FileName += "/"; } } } localFiles.Add(lf); } } public void ZipFileClose() { switch (ZipOpen) { case ZipOpenType.Closed: return; case ZipOpenType.OpenRead: ZipFileCloseReadStream(); if (_zipFs != null) { _zipFs.Close(); _zipFs.Dispose(); } ZipOpen = ZipOpenType.Closed; return; case ZipOpenType.OpenWrite: CloseWriting7Zip(); if (_zipFileInfo != null) _zipFileInfo = new FileInfo(_zipFileInfo.FullName); break; } ZipOpen = ZipOpenType.Closed; } private Header _header; public StringBuilder HeaderReport() { StringBuilder sb = new StringBuilder(); if (_header == null) { sb.AppendLine("Null Header"); return sb; } _header.Report(ref sb); return sb; } // not finalized yet, so do not use private void WriteRomVault7Zip(BinaryWriter bw, ulong headerPos, ulong headerLength, uint headerCRC) { const string sig = "RomVault7Z01"; byte[] RV7Zid = Util.Enc.GetBytes(sig); // RomVault 7Zip torrent header // 12 bytes : RomVault7Zip // 4 bytes : HeaderCRC // 8 bytes : HeaderPos // 8 bytes : HeaderLength bw.Write(RV7Zid); bw.Write(headerCRC); bw.Write(headerPos); bw.Write(headerLength); ZipStatus = ZipStatus.TrrntZip; } private bool IsRomVault7Z() { long length = _zipFs.Length; if (length < 32) { return false; } _zipFs.Seek(length - 32, SeekOrigin.Begin); const string sig = "RomVault7Z01"; byte[] rv7Zid = Util.Enc.GetBytes(sig); byte[] header = new byte[12]; _zipFs.Read(header, 0, 12); for (int i = 0; i < 12; i++) { if (header[i] != rv7Zid[i]) { return false; } } uint headerCRC; ulong headerOffset; ulong headerSize; using (BinaryReader br = new BinaryReader(_zipFs, Encoding.UTF8, true)) { headerCRC = br.ReadUInt32(); headerOffset = br.ReadUInt64(); headerSize = br.ReadUInt64(); } if ((ulong)length < headerOffset) { return false; } _zipFs.Seek((long)headerOffset, SeekOrigin.Begin); byte[] mainHeader = new byte[headerSize]; int bytesread = _zipFs.Read(mainHeader, 0, (int)headerSize); return ((ulong)bytesread == headerSize) && Utils.CRC.VerifyDigest(headerCRC, mainHeader, 0, (uint)headerSize); } private bool Istorrent7Z() { const int crcsz = 128; const int t7ZsigSize = 16 + 1 + 9 + 4 + 4; byte[] kSignature = { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C }; int kSignatureSize = kSignature.Length; const string sig = "\xa9\x9f\xd1\x57\x08\xa9\xd7\xea\x29\x64\xb2\x36\x1b\x83\x52\x33\x01torrent7z_0.9beta"; byte[] t7Zid = Util.Enc.GetBytes(sig); int t7ZidSize = t7Zid.Length; const int tmpbufsize = 256 + t7ZsigSize + 8 + 4; byte[] buffer = new byte[tmpbufsize]; // read fist 128 bytes, pad with zeros if less bytes int bufferPos = 0; _zipFs.Seek(0, SeekOrigin.Begin); int ar = _zipFs.Read(buffer, bufferPos, crcsz); if (ar < crcsz) { Util.memset(buffer, bufferPos + ar, 0, crcsz - ar); } bufferPos = crcsz; long foffs = _zipFs.Length; int endReadLength = crcsz + t7ZsigSize + 4; foffs = foffs < endReadLength ? 0 : foffs - endReadLength; _zipFs.Seek(foffs, SeekOrigin.Begin); ar = _zipFs.Read(buffer, bufferPos, endReadLength); if (ar < endReadLength) { if (ar >= t7ZsigSize + 4) { ar -= t7ZsigSize + 4; } if (ar < kSignatureSize) { ar = kSignatureSize; } Util.memset(buffer, bufferPos + ar, 0, crcsz - ar); Util.memcpyr(buffer, crcsz * 2 + 8, buffer, bufferPos + ar, t7ZsigSize + 4); } else { Util.memcpyr(buffer, crcsz * 2 + 8, buffer, crcsz * 2, t7ZsigSize + 4); } foffs = _zipFs.Length; foffs -= t7ZsigSize + 4; //memcpy(buffer, crcsz * 2, &foffs, 8); buffer[crcsz * 2 + 0] = (byte)((foffs >> 0) & 0xff); buffer[crcsz * 2 + 1] = (byte)((foffs >> 8) & 0xff); buffer[crcsz * 2 + 2] = (byte)((foffs >> 16) & 0xff); buffer[crcsz * 2 + 3] = (byte)((foffs >> 24) & 0xff); buffer[crcsz * 2 + 4] = 0; buffer[crcsz * 2 + 5] = 0; buffer[crcsz * 2 + 6] = 0; buffer[crcsz * 2 + 7] = 0; if (Util.memcmp(buffer, 0, kSignature, kSignatureSize)) { t7Zid[16] = buffer[crcsz * 2 + 4 + 8 + 16]; if (Util.memcmp(buffer, crcsz * 2 + 4 + 8, t7Zid, t7ZidSize)) { uint inCrc32 = (uint)(buffer[crcsz * 2 + 8 + 0] + (buffer[crcsz * 2 + 8 + 1] << 8) + (buffer[crcsz * 2 + 8 + 2] << 16) + (buffer[crcsz * 2 + 8 + 3] << 24)); buffer[crcsz * 2 + 8 + 0] = 0xff; buffer[crcsz * 2 + 8 + 1] = 0xff; buffer[crcsz * 2 + 8 + 2] = 0xff; buffer[crcsz * 2 + 8 + 3] = 0xff; uint calcCrc32 = Utils.CRC.CalculateDigest(buffer, 0, crcsz * 2 + 8 + t7ZsigSize + 4); if (inCrc32 == calcCrc32) { return true; } } } return false; } #endregion #region read 7z file private int _streamIndex = -1; private Stream _stream; public ZipReturn ZipFileOpenReadStream(int index, out Stream stream, out ulong unCompressedSize) { Debug.WriteLine("Opening File " + _localFiles[index].FileName); stream = null; unCompressedSize = 0; try { if (ZipOpen != ZipOpenType.OpenRead) { return ZipReturn.ZipErrorGettingDataStream; } if (IsDirectory(index)) { return ZipReturn.ZipTryingToAccessADirectory; } unCompressedSize = _localFiles[index].UncompressedSize; int thisStreamIndex = _localFiles[index].StreamIndex; ulong streamOffset = _localFiles[index].StreamOffset; if ((thisStreamIndex == _streamIndex) && (streamOffset >= (ulong)_stream.Position)) { stream = _stream; stream.Seek((long)_localFiles[index].StreamOffset - _stream.Position, SeekOrigin.Current); return ZipReturn.ZipGood; } ZipFileCloseReadStream(); _streamIndex = thisStreamIndex; Folder folder = _header.StreamsInfo.Folders[_streamIndex]; // first make the List of Decompressors streams int codersNeeded = folder.Coders.Length; List allInputStreams = new List(); for (int i = 0; i < codersNeeded; i++) { folder.Coders[i].DecoderStream = null; allInputStreams.AddRange(folder.Coders[i].InputStreamsSourceInfo); } // now use the binding pairs to links the outputs to the inputs int bindPairsCount = folder.BindPairs.Length; for (int i = 0; i < bindPairsCount; i++) { allInputStreams[(int)folder.BindPairs[i].InIndex].InStreamSource = InStreamSource.CompStreamOutput; allInputStreams[(int)folder.BindPairs[i].InIndex].InStreamIndex = folder.BindPairs[i].OutIndex; folder.Coders[(int)folder.BindPairs[i].OutIndex].OutputUsedInternally = true; } // next use the stream indises to connect the remaining input streams from the sourcefile int packedStreamsCount = folder.PackedStreamIndices.Length; for (int i = 0; i < packedStreamsCount; i++) { ulong packedStreamIndex = (ulong)i + folder.PackedStreamIndexBase; // create and open the source file stream if needed if (_header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream == null) { _header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream = CloneStream(_zipFs); } _header.StreamsInfo.PackedStreams[packedStreamIndex].PackedStream.Seek( _baseOffset + (long)_header.StreamsInfo.PackedStreams[packedStreamIndex].StreamPosition, SeekOrigin.Begin); allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamSource = InStreamSource.FileStream; allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamIndex = packedStreamIndex; } List inputCoders = new List(); bool allCodersComplete = false; while (!allCodersComplete) { allCodersComplete = true; for (int i = 0; i < codersNeeded; i++) { Coder coder = folder.Coders[i]; // check is decoder already processed if (coder.DecoderStream != null) { continue; } inputCoders.Clear(); for (int j = 0; j < (int)coder.NumInStreams; j++) { if (coder.InputStreamsSourceInfo[j].InStreamSource == InStreamSource.FileStream) { inputCoders.Add(_header.StreamsInfo.PackedStreams[coder.InputStreamsSourceInfo[j].InStreamIndex].PackedStream); } else if (coder.InputStreamsSourceInfo[j].InStreamSource == InStreamSource.CompStreamOutput) { if (folder.Coders[coder.InputStreamsSourceInfo[j].InStreamIndex].DecoderStream == null) { break; } inputCoders.Add(folder.Coders[coder.InputStreamsSourceInfo[j].InStreamIndex].DecoderStream); } else { // unknown input type so error return ZipReturn.ZipDecodeError; } } if (inputCoders.Count == (int)coder.NumInStreams) { // all inputs streams are available to make the decoder stream switch (coder.DecoderType) { case DecompressType.Stored: coder.DecoderStream = inputCoders[0]; break; case DecompressType.Delta: coder.DecoderStream = new Delta(folder.Coders[i].Properties, inputCoders[0]); break; case DecompressType.LZMA: coder.DecoderStream = new LzmaStream(folder.Coders[i].Properties, inputCoders[0]); break; case DecompressType.LZMA2: coder.DecoderStream = new LzmaStream(folder.Coders[i].Properties, inputCoders[0]); break; case DecompressType.PPMd: coder.DecoderStream = new PpmdStream(new PpmdProperties(folder.Coders[i].Properties), inputCoders[0], false); break; case DecompressType.BZip2: coder.DecoderStream = new CBZip2InputStream(inputCoders[0], false); break; case DecompressType.BCJ: coder.DecoderStream = new BCJFilter(false, inputCoders[0]); break; case DecompressType.BCJ2: coder.DecoderStream = new BCJ2Filter(inputCoders[0], inputCoders[1], inputCoders[2], inputCoders[3]); break; default: return ZipReturn.ZipDecodeError; } } // if skipped a coder need to loop round again if (coder.DecoderStream == null) { allCodersComplete = false; } } } // find the final output stream and return it. int outputStream = -1; for (int i = 0; i < codersNeeded; i++) { Coder coder = folder.Coders[i]; if (!coder.OutputUsedInternally) { outputStream = i; } } stream = folder.Coders[outputStream].DecoderStream; stream.Seek((long)_localFiles[index].StreamOffset, SeekOrigin.Current); _stream = stream; return ZipReturn.ZipGood; } catch (Exception e) { return ZipReturn.ZipErrorGettingDataStream; } } private Stream CloneStream(Stream s) { switch (s) { case System.IO.FileStream _: int errorCode = FileStream.OpenFileRead(ZipFilename, out Stream streamOut); return errorCode != 0 ? null : streamOut; case MemoryStream memStream: long pos = memStream.Position; memStream.Position = 0; byte[] newStream = new byte[memStream.Length]; memStream.Read(newStream, 0, (int)memStream.Length); MemoryStream ret = new MemoryStream(newStream, false); memStream.Position = pos; return ret; } return null; } public ZipReturn ZipFileCloseReadStream() { if (_streamIndex != -1) { Folder folder = _header.StreamsInfo.Folders[_streamIndex]; foreach (Coder c in folder.Coders) { Stream ds = c?.DecoderStream; if (ds == null) { continue; } ds.Close(); ds.Dispose(); c.DecoderStream = null; } } _streamIndex = -1; if (_header?.StreamsInfo != null) { foreach (PackedStreamInfo psi in _header.StreamsInfo.PackedStreams) { if (psi?.PackedStream == null) { continue; } psi.PackedStream.Close(); psi.PackedStream.Dispose(); psi.PackedStream = null; } } return ZipReturn.ZipGood; } #endregion #region write 7z File private LzmaStream _lzmaStream; private ulong _packStreamStart; private ulong _packStreamSize; private ulong _unpackedStreamSize; private byte[] _codeMSbytes; public void ZipFileAddDirectory() { // do nothing here for 7zip } public ZipReturn ZipFileCreate(string newFilename) { return ZipFileCreate(newFilename, true); } public ZipReturn ZipFileCreateFromUncompressedSize(string newFilename, ulong unCompressedSize) { return ZipFileCreate(newFilename,true, GetDictionarySizeFromUncompressedSize(unCompressedSize)); } public ZipReturn ZipFileCreate(string newFilename, bool compressOutput, int dictionarySize = 1 << 24, int numFastBytes = 64) { if (ZipOpen != ZipOpenType.Closed) { return ZipReturn.ZipFileAlreadyOpen; } DirUtil.CreateDirForFile(newFilename); _zipFileInfo = new FileInfo(newFilename); int errorCode = FileStream.OpenFileWrite(newFilename, out _zipFs); if (errorCode != 0) { ZipFileClose(); return ZipReturn.ZipErrorOpeningFile; } ZipOpen = ZipOpenType.OpenWrite; _signatureHeader = new SignatureHeader(); _header = new Header(); using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) { _signatureHeader.Write(bw); } _compressed = compressOutput; _unpackedStreamSize = 0; if (_compressed) { LzmaEncoderProperties ep = new LzmaEncoderProperties(true, dictionarySize, numFastBytes); _lzmaStream = new LzmaStream(ep, false, _zipFs); _codeMSbytes = _lzmaStream.Properties; _packStreamStart = (ulong)_zipFs.Position; } return ZipReturn.ZipGood; } public ZipReturn ZipFileOpenWriteStream(bool raw, bool trrntzip, string filename, ulong uncompressedSize, ushort compressionMethod, out Stream stream) { return ZipFileOpenWriteStream(filename, uncompressedSize, out stream); } private ZipReturn ZipFileOpenWriteStream(string filename, ulong uncompressedSize, out Stream stream) { LocalFile lf = new LocalFile { FileName = filename, UncompressedSize = uncompressedSize, StreamOffset = (ulong)(_zipFs.Position - _signatureHeader.BaseOffset) }; _unpackedStreamSize += uncompressedSize; _localFiles.Add(lf); stream = _compressed ? _lzmaStream : _zipFs; return ZipReturn.ZipGood; } public ZipReturn ZipFileCloseWriteStream(byte[] crc32) { _localFiles[_localFiles.Count - 1].CRC = new[] { crc32[3], crc32[2], crc32[1], crc32[0] }; return ZipReturn.ZipGood; } private void CloseWriting7Zip() { if (_compressed) { _lzmaStream.Close(); } _packStreamSize = (ulong)_zipFs.Position - _packStreamStart; Create7ZStructure(); byte[] newHeaderByte; using (Stream headerMem = new MemoryStream()) { using (BinaryWriter headerBw = new BinaryWriter(headerMem, Encoding.UTF8, true)) { _header.WriteHeader(headerBw); newHeaderByte = new byte[headerMem.Length]; headerMem.Position = 0; headerMem.Read(newHeaderByte, 0, newHeaderByte.Length); } } uint mainHeaderCRC = Utils.CRC.CalculateDigest(newHeaderByte, 0, (uint)newHeaderByte.Length); ulong headerpos = (ulong)_zipFs.Position; _zipFs.Write(newHeaderByte,0,newHeaderByte.Length); using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) { _signatureHeader.WriteFinal(bw, headerpos, (ulong)newHeaderByte.Length, mainHeaderCRC); WriteRomVault7Zip(bw, headerpos, (ulong)newHeaderByte.Length, mainHeaderCRC); } _zipFs.Flush(); _zipFs.Close(); _zipFs.Dispose(); } private void Create7ZStructure() { int fileCount = _localFiles.Count; //FileInfo _header.FileInfo = new Structure.FileInfo { Names = new string[fileCount] }; ulong emptyStreamCount = 0; ulong emptyFileCount = 0; for (int i = 0; i < fileCount; i++) { _header.FileInfo.Names[i] = _localFiles[i].FileName; if (_localFiles[i].UncompressedSize != 0) { continue; } if (!_localFiles[i].IsDirectory) { emptyFileCount += 1; } emptyStreamCount += 1; } ulong outFileCount = (ulong)_localFiles.Count - emptyStreamCount; _header.FileInfo.EmptyStreamFlags = null; _header.FileInfo.EmptyFileFlags = null; _header.FileInfo.Attributes = null; if (emptyStreamCount > 0) { if (emptyStreamCount != emptyFileCount) //then we found directories and need to set the attributes { _header.FileInfo.Attributes = new uint[fileCount]; } if (emptyFileCount > 0) { _header.FileInfo.EmptyFileFlags = new bool[emptyStreamCount]; } emptyStreamCount = 0; _header.FileInfo.EmptyStreamFlags = new bool[fileCount]; for (int i = 0; i < fileCount; i++) { if (_localFiles[i].UncompressedSize != 0) { continue; } if (_localFiles[i].IsDirectory) { if (_header.FileInfo.Attributes != null) _header.FileInfo.Attributes[i] = 0x10; // set attributes to directory } else { if (_header.FileInfo.EmptyFileFlags != null) _header.FileInfo.EmptyFileFlags[emptyStreamCount] = true; // set empty file flag } _header.FileInfo.EmptyStreamFlags[i] = true; emptyStreamCount += 1; } } //StreamsInfo _header.StreamsInfo = new StreamsInfo { PackPosition = 0 }; //StreamsInfo.PackedStreamsInfo if (_compressed) { _header.StreamsInfo.PackedStreams = new PackedStreamInfo[1]; _header.StreamsInfo.PackedStreams[0] = new PackedStreamInfo { PackedSize = _packStreamSize }; } else { _header.StreamsInfo.PackedStreams = new PackedStreamInfo[outFileCount]; int fileIndex = 0; for (int i = 0; i < fileCount; i++) { if (_localFiles[i].UncompressedSize == 0) { continue; } _header.StreamsInfo.PackedStreams[fileIndex++] = new PackedStreamInfo { PackedSize = _localFiles[i].UncompressedSize }; } } //StreamsInfo.PackedStreamsInfo, no CRC or StreamPosition required if (_compressed) { //StreamsInfo.Folders _header.StreamsInfo.Folders = new Folder[1]; Folder folder = new Folder { Coders = new Coder[1] }; //StreamsInfo.Folders.Coder // flags 0x23 folder.Coders[0] = new Coder { Method = new byte[] { 3, 1, 1 }, NumInStreams = 1, NumOutStreams = 1, Properties = _codeMSbytes }; folder.BindPairs = null; folder.PackedStreamIndices = new[] { (ulong)0 }; folder.UnpackedStreamSizes = new[] { _unpackedStreamSize }; folder.UnpackCRC = null; folder.UnpackedStreamInfo = new UnpackedStreamInfo[outFileCount]; int fileIndex = 0; for (int i = 0; i < fileCount; i++) { if (_localFiles[i].UncompressedSize == 0) { continue; } UnpackedStreamInfo unpackedStreamInfo = new UnpackedStreamInfo { UnpackedSize = _localFiles[i].UncompressedSize, Crc = Util.bytestouint(_localFiles[i].CRC) }; folder.UnpackedStreamInfo[fileIndex++] = unpackedStreamInfo; } _header.StreamsInfo.Folders[0] = folder; } else { _header.StreamsInfo.Folders = new Folder[outFileCount]; int fileIndex = 0; for (int i = 0; i < fileCount; i++) { if (_localFiles[i].UncompressedSize == 0) { continue; } Folder folder = new Folder { Coders = new Coder[1] }; //StreamsInfo.Folders.Coder // flags 0x01 folder.Coders[0] = new Coder { Method = new byte[] { 0 }, NumInStreams = 1, NumOutStreams = 1, Properties = null }; folder.BindPairs = null; folder.PackedStreamIndices = new[] { (ulong)i }; folder.UnpackedStreamSizes = new[] { _localFiles[i].UncompressedSize }; folder.UnpackCRC = null; folder.UnpackedStreamInfo = new UnpackedStreamInfo[1]; UnpackedStreamInfo unpackedStreamInfo = new UnpackedStreamInfo { UnpackedSize = _localFiles[i].UncompressedSize, Crc = Util.bytestouint(_localFiles[i].CRC) }; folder.UnpackedStreamInfo[0] = unpackedStreamInfo; _header.StreamsInfo.Folders[fileIndex++] = folder; } } } #endregion private static readonly int[] DictionarySizes = { 0x10000, 0x18000, 0x20000, 0x30000, 0x40000, 0x60000, 0x80000, 0xc0000, 0x100000, 0x180000, 0x200000, 0x300000, 0x400000, 0x600000, 0x800000, 0xc00000, 0x1000000, 0x1800000, 0x2000000, 0x3000000, 0x4000000, 0x6000000 }; private static int GetDictionarySizeFromUncompressedSize(ulong unCompressedSize) { foreach (int v in DictionarySizes) { if ((ulong)v >= unCompressedSize) return v; } return DictionarySizes[DictionarySizes.Length - 1]; } } }