//construct an instance of the class and parse list of zip entries public TinyUnzip(Stream stream, bool needDispose = true) { if (!stream.CanSeek) { throw new Exception("Unzip - stream should be able to perform Seek operation!"); } m_needDispose = needDispose; m_stream = stream; m_eocd = EOCD.ReadEOCD(m_stream); m_stream.Seek(m_eocd.offsetToCentralDir, SeekOrigin.Begin); m_reader = new BinaryReader(m_stream); m_centralDirEntries = new CentralDirectory[m_eocd.centralDirsTotalNum]; m_entries = new List <Entry>(m_eocd.centralDirsTotalNum); for (int i = 0; i < m_centralDirEntries.Length; i++) { m_centralDirEntries[i] = CentralDirectory.Read(m_reader); Entry entry = new Entry(m_centralDirEntries[i].fileName, m_centralDirEntries[i].comment, m_centralDirEntries[i].compressedSize, m_centralDirEntries[i].uncompressedSize, m_centralDirEntries[i].fileLastModificationTime, m_centralDirEntries[i].fileLastModificationDate, m_centralDirEntries[i].crc32); m_entries.Add(entry); } }
static async Task <(bool success, EOCD eocd)> GetEOCD(HttpClient httpClient, Uri url, long fileSize) { long fileOffset = fileSize - EndFileChunkSize; var req = new HttpRequestMessage(HttpMethod.Get, url); req.Headers.ConnectionClose = true; req.Headers.Range = new RangeHeaderValue(fileOffset, fileSize); HttpResponseMessage resp = await httpClient.SendAsync(req).ConfigureAwait(false); if (!resp.IsSuccessStatusCode) { return(false, null); } using (var eocdStream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false)) { using (var sr = new BinaryReader(eocdStream)) { byte[] expected = { 0x50, 0x4b, 0x05, 0x06 }; int expectedPos = 0; for (int i = 0; i < eocdStream.Length; i++) { byte b = sr.ReadByte(); if (b != expected [expectedPos]) { expectedPos = 0; continue; } if (expectedPos == expected.Length - 1) { // We've found the signature var eocd = new EOCD(); eocd.Signature = 0x06054b50; eocd.DiskNumber = sr.ReadUInt16(); eocd.CDStartDisk = sr.ReadUInt16(); eocd.TotalEntriesThisDisk = sr.ReadUInt16(); eocd.TotalEntries = sr.ReadUInt16(); eocd.CDSize = sr.ReadUInt32(); eocd.CDOffset = sr.ReadUInt32(); eocd.CommentLength = sr.ReadUInt16(); return(true, eocd); } expectedPos++; if (expectedPos >= expected.Length) { expectedPos = 0; } } } } return(false, null); }
static async Task <bool> ProcessEntries(HttpClient httpClient, Uri url, EOCD eocd, Stream centralDirectory, HashSet <string> neededFiles, string destinationDirectory) { long foundEntries = 0; using (var br = new BinaryReader(centralDirectory)) { long nread = 0; long nentries = 1; while (nread < centralDirectory.Length && nentries <= eocd.TotalEntries) { (bool success, CDHeader cdh) = ReadCDHeader(br, centralDirectory.Length, ref nread); nentries++; if (!success) { Console.Error.WriteLine($"Failed to read a Central Directory file header for entry {nentries}"); return(false); } if (!neededFiles.Contains(cdh.FileName)) { continue; } if (!await ReadEntry(httpClient, url, cdh, br, destinationDirectory)) { return(false); } foundEntries++; } } if (foundEntries < neededFiles.Count) { Console.WriteLine($"Could not find all required binaries. Found {foundEntries} out of {neededFiles.Count}"); return(false); } return(true); }
static EOCD ReadEOCD(BinaryReader reader) { EOCD eocd = new EOCD(); eocd.signature = reader.ReadUInt32(); eocd.numberOfDisk = reader.ReadUInt16(); eocd.disk = reader.ReadUInt16(); eocd.curDiskCentralDirsNum = reader.ReadUInt16(); eocd.centralDirsTotalNum = reader.ReadUInt16(); eocd.sizeOfCentralDir = reader.ReadUInt32(); eocd.offsetToCentralDir = reader.ReadUInt32(); ushort commentLength = reader.ReadUInt16(); eocd.comment = ReadAsString(reader, commentLength); if (eocd.signature != 0x06054b50) { throw new Exception("Broken signature in zip archive"); } return(eocd); }