static async Task <bool> ReadEntry(HttpClient httpClient, Uri url, CDHeader cdh, BinaryReader br, string destinationDirectory) { string compressedFilePath = Path.Combine(destinationDirectory, $"{Path.GetFileName (cdh.FileName)}.deflated"); Console.WriteLine($" {cdh.FileName} (offset: {cdh.RelativeOffsetOfLocalHeader})"); (bool success, Stream contentStream) = await ReadFileData(httpClient, url, cdh); if (!success) { Console.Error.WriteLine("Failed to read file data"); return(false); } using (var destFile = new BinaryWriter(File.OpenWrite(compressedFilePath))) { using (var fbr = new BinaryReader(contentStream)) { if (!await DownloadAndExtract(fbr, contentStream, destFile, compressedFilePath)) { return(CleanupAndReturn(false)); } } } return(CleanupAndReturn(true)); bool CleanupAndReturn(bool retval) { if (File.Exists(compressedFilePath)) { File.Delete(compressedFilePath); } return(retval); } }
static (bool success, CDHeader cdh) ReadCDHeader(BinaryReader cdr, long dataLength, ref long nread) { var cdh = new CDHeader(); bool worked; string whatFailed = null; cdh.Signature = ReadUInt(cdr, dataLength, ref nread, out worked); if (!worked || cdh.Signature != CDHeaderSignature) { whatFailed = "Signature ({cdh.Signature:x} != {CDHeaderSignature:x})"; goto failed; } cdh.VersionMadeBy = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "VersionMadeBy"; goto failed; } cdh.VersionNeededToExtract = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "VersionNeededToExtract"; goto failed; } cdh.GeneralPurposeBitFlag = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "GeneralPurposeBitFlag"; goto failed; } cdh.CompressionMethod = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "CompressionMethod"; goto failed; } cdh.LastModFileTime = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "LastModFileTime"; goto failed; } cdh.LastModFileDate = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "LastModFileDate"; goto failed; } cdh.CRC32 = ReadUInt(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "CRC32"; goto failed; } cdh.CompressedSize = ReadUInt(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "CompressedSize"; goto failed; } cdh.UncompressedSize = ReadUInt(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "UncompressedSize"; goto failed; } cdh.FileNameLength = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "FileNameLength"; goto failed; } cdh.ExtraFieldLength = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "ExtraFieldLength"; goto failed; } cdh.FileCommentLength = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "FileCommentLength"; goto failed; } cdh.DiskNumberStart = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "DiskNumberStart"; goto failed; } cdh.InternalFileAttributes = ReadUShort(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "InternalFileAttributes"; goto failed; } cdh.ExternalFileAttributes = ReadUInt(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "ExternalFileAttributes"; goto failed; } cdh.RelativeOffsetOfLocalHeader = ReadUInt(cdr, dataLength, ref nread, out worked); if (!worked) { whatFailed = "RelativeOffsetOfLocalHeader"; goto failed; } byte[] bytes = ReadBytes(cdr, cdh.FileNameLength, dataLength, ref nread, out worked); if (!worked) { whatFailed = "FileName (bytes)"; goto failed; } cdh.FileName = Encoding.ASCII.GetString(bytes); if (!worked) { whatFailed = "FileName (ASCII decode)"; goto failed; } if (cdh.ExtraFieldLength > 0) { cdh.ExtraField = ReadBytes(cdr, cdh.ExtraFieldLength, dataLength, ref nread, out worked); if (!worked) { whatFailed = "ExtraField"; goto failed; } } if (cdh.FileCommentLength > 0) { bytes = ReadBytes(cdr, cdh.FileCommentLength, dataLength, ref nread, out worked); if (!worked) { whatFailed = "FileComment (bytes)"; goto failed; } cdh.FileComment = Encoding.ASCII.GetString(bytes); } return(true, cdh); failed: if (!String.IsNullOrEmpty(whatFailed)) { Console.Error.WriteLine($"Failed to read a central directory header field: {whatFailed}"); } return(false, null); }
static async Task <(bool success, Stream data)> ReadFileData(HttpClient httpClient, Uri url, CDHeader cdh) { long fileOffset = cdh.RelativeOffsetOfLocalHeader; long dataSize = cdh.CompressedSize + 30 + // local file header size, the static portion cdh.FileName.Length + // They're the same in both haders cdh.ExtraFieldLength + // This may differ between headers... 16384; // ...so we add some extra padding var req = new HttpRequestMessage(HttpMethod.Get, url); req.Headers.ConnectionClose = true; req.Headers.Range = new RangeHeaderValue(fileOffset, fileOffset + dataSize); HttpResponseMessage resp = await httpClient.SendAsync(req).ConfigureAwait(false); if (!resp.IsSuccessStatusCode) { Console.Error.WriteLine($"Failed to read file data: HTTP error {resp.StatusCode}"); return(false, null); } Stream s = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false); if (s.Length < dataSize) { Console.Error.WriteLine($"Failed to read file data: invalid data length ({s.Length} < {dataSize})"); s.Dispose(); return(false, null); } return(true, s); }