ZipCentralDirectoryEntry.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using SharpCompress.Common;
  5. using SharpCompress.Common.Zip;
  6. using SharpCompress.Common.Zip.Headers;
  7. using SharpCompress.Converters;
  8. namespace SharpCompress.Writers.Zip
  9. {
  10. internal class ZipCentralDirectoryEntry
  11. {
  12. private readonly ZipCompressionMethod compression;
  13. private readonly string fileName;
  14. private readonly ArchiveEncoding archiveEncoding;
  15. public ZipCentralDirectoryEntry(ZipCompressionMethod compression, string fileName, ulong headerOffset, ArchiveEncoding archiveEncoding)
  16. {
  17. this.compression = compression;
  18. this.fileName = fileName;
  19. HeaderOffset = headerOffset;
  20. this.archiveEncoding = archiveEncoding;
  21. }
  22. internal DateTime? ModificationTime { get; set; }
  23. internal string Comment { get; set; }
  24. internal uint Crc { get; set; }
  25. internal ulong Compressed { get; set; }
  26. internal ulong Decompressed { get; set; }
  27. internal ushort Zip64HeaderOffset { get; set; }
  28. internal ulong HeaderOffset { get; }
  29. internal uint Write(Stream outputStream)
  30. {
  31. byte[] encodedFilename = archiveEncoding.Encode(fileName);
  32. byte[] encodedComment = archiveEncoding.Encode(Comment);
  33. var zip64_stream = Compressed >= uint.MaxValue || Decompressed >= uint.MaxValue;
  34. var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue || Zip64HeaderOffset != 0;
  35. var compressedvalue = zip64 ? uint.MaxValue : (uint)Compressed;
  36. var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed;
  37. var headeroffsetvalue = zip64 ? uint.MaxValue : (uint)HeaderOffset;
  38. var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0;
  39. var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption
  40. HeaderFlags flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.Efs : HeaderFlags.None;
  41. if (!outputStream.CanSeek)
  42. {
  43. // Cannot use data descriptors with zip64:
  44. // https://blogs.oracle.com/xuemingshen/entry/is_zipinput_outputstream_handling_of
  45. // We check that streams are not written too large in the ZipWritingStream,
  46. // so this extra guard is not required, but kept to simplify changing the code
  47. // once the zip64 post-data issue is resolved
  48. if (!zip64_stream)
  49. flags |= HeaderFlags.UsePostDataDescriptor;
  50. if (compression == ZipCompressionMethod.LZMA)
  51. {
  52. flags |= HeaderFlags.Bit1; // eos marker
  53. }
  54. }
  55. //constant sig, then version made by, then version to extract
  56. outputStream.Write(new byte[] { 80, 75, 1, 2, version, 0, version, 0 }, 0, 8);
  57. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)flags), 0, 2);
  58. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)compression), 0, 2); // zipping method
  59. outputStream.Write(DataConverter.LittleEndian.GetBytes(ModificationTime.DateTimeToDosTime()), 0, 4);
  60. // zipping date and time
  61. outputStream.Write(DataConverter.LittleEndian.GetBytes(Crc), 0, 4); // file CRC
  62. outputStream.Write(DataConverter.LittleEndian.GetBytes(compressedvalue), 0, 4); // compressed file size
  63. outputStream.Write(DataConverter.LittleEndian.GetBytes(decompressedvalue), 0, 4); // uncompressed file size
  64. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)encodedFilename.Length), 0, 2); // Filename in zip
  65. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)extralength), 0, 2); // extra length
  66. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)encodedComment.Length), 0, 2);
  67. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0), 0, 2); // disk=0
  68. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0), 0, 2); // file type: binary
  69. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0), 0, 2); // Internal file attributes
  70. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0x8100), 0, 2);
  71. // External file attributes (normal/readable)
  72. outputStream.Write(DataConverter.LittleEndian.GetBytes(headeroffsetvalue), 0, 4); // Offset of header
  73. outputStream.Write(encodedFilename, 0, encodedFilename.Length);
  74. if (zip64)
  75. {
  76. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0x0001), 0, 2);
  77. outputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)(extralength - 4)), 0, 2);
  78. outputStream.Write(DataConverter.LittleEndian.GetBytes(Decompressed), 0, 8);
  79. outputStream.Write(DataConverter.LittleEndian.GetBytes(Compressed), 0, 8);
  80. outputStream.Write(DataConverter.LittleEndian.GetBytes(HeaderOffset), 0, 8);
  81. outputStream.Write(DataConverter.LittleEndian.GetBytes(0), 0, 4); // VolumeNumber = 0
  82. }
  83. outputStream.Write(encodedComment, 0, encodedComment.Length);
  84. return (uint)(8 + 2 + 2 + 4 + 4 + 4 + 4 + 2 + 2 + 2
  85. + 2 + 2 + 2 + 2 + 4 + encodedFilename.Length + extralength + encodedComment.Length);
  86. }
  87. }
  88. }