diff --git a/SabreTools.Helper/Tools/FileTools.cs b/SabreTools.Helper/Tools/FileTools.cs index 640c787d..f4911873 100644 --- a/SabreTools.Helper/Tools/FileTools.cs +++ b/SabreTools.Helper/Tools/FileTools.cs @@ -174,6 +174,104 @@ namespace SabreTools.Helper return true; } + /// + /// Write an existing zip archive construct to a torrent zip file + /// + /// ZipArchiveStruct representing the zipfile + /// Name of the file to write out to + /// Logger object for file and console output + public static void WriteTorrentZip(ZipArchiveStruct zae, string output, Logger logger) + { + // First, rearrange entries by name + List entries = zae.Entries; + entries.Sort((i, j) => + { + return Style.CompareNumeric(i.FileName.ToLowerInvariant(), j.FileName.ToLowerInvariant()); + }); + + using (BinaryWriter bw = new BinaryWriter(File.Open(output, FileMode.Create))) + { + List offsets = new List(); + + // First, write out entries + foreach (ZipArchiveEntryStruct zaes in zae.Entries) + { + offsets.Add(bw.BaseStream.Position); + bw.Write(new byte[] { 0x50, 0x4b, 0x03, 0x04 }); // local file header signature + bw.Write((ushort)zaes.VersionNeeded); + bw.Write((ushort)zaes.GeneralPurposeBitFlag); + bw.Write((ushort)zaes.CompressionMethod); + bw.Write(zaes.LastModFileTime); + bw.Write(zaes.LastModFileDate); + bw.Write(zaes.CRC); + bw.Write(zaes.CompressedSize); + bw.Write(zaes.UncompressedSize); + bw.Write((ushort)zaes.FileName.Length); + bw.Write((ushort)zaes.ExtraField.Length); + bw.Write(zaes.FileName); + bw.Write(zaes.ExtraField); + bw.Write(zaes.Data); + if ((zaes.GeneralPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) != 0) + { + bw.Write(zaes.CRC); + bw.Write(zaes.CompressedSize); + bw.Write(zaes.UncompressedSize); + } + } + + // Then write out the central directory + int index = 0; + zae.SOCDOffset = (int)bw.BaseStream.Position; + foreach (ZipArchiveEntryStruct zaes in zae.Entries) + { + bw.Write(new byte[] { 0x50, 0x4b, 0x01, 0x02 }); // central file header signature + bw.Write((ushort)zaes.VersionMadeBy); + bw.Write((ushort)zaes.VersionNeeded); + bw.Write((ushort)zaes.GeneralPurposeBitFlag); + bw.Write((ushort)zaes.CompressionMethod); + bw.Write(zaes.LastModFileTime); + bw.Write(zaes.LastModFileDate); + bw.Write((short)zaes.FileName.Length); + bw.Write((short)zaes.ExtraField.Length); + bw.Write((short)zaes.Comment.Length); + bw.Write((short)0); + bw.Write((short)zaes.InternalFileAttributes); + bw.Write(zaes.ExternalFileAttributes); + bw.Write((int)offsets[index]); + bw.Write(zaes.FileName); + bw.Write(zaes.ExtraField); + bw.Write(zaes.Comment); + index++; + } + zae.EOCDOffset = (int)bw.BaseStream.Position; + + // Finally, write out the end record + bw.Write(new byte[] { 0x50, 0x4b, 0x05, 0x06 }); // end of central dir signature + bw.Write((short)0); + bw.Write((short)0); + bw.Write((short)zae.Entries.Count); + bw.Write((int)(zae.EOCDOffset - zae.SOCDOffset)); + bw.Write((int)(zae.EOCDOffset - zae.SOCDOffset)); + bw.Write((short)22); + bw.Write("TORRENTZIPPED-"); + } + + using (BinaryReader br = new BinaryReader(File.OpenRead(output))) + { + br.BaseStream.Seek(zae.SOCDOffset, SeekOrigin.Begin); + byte[] cd = br.ReadBytes(zae.EOCDOffset - zae.SOCDOffset); + + OptimizedCRC ocrc = new OptimizedCRC(); + ocrc.Update(cd, 0, cd.Length); + zae.CentralDirectoryCRC = ocrc.Value; + } + + using (BinaryWriter bw = new BinaryWriter(File.Open(output, FileMode.Append))) + { + bw.Write(zae.CentralDirectoryCRC); + } + } + #endregion #region Archive Extraction