From 019f9a88d4c4a9a3dd09e556a801c9cd11ef5551 Mon Sep 17 00:00:00 2001 From: alcexhim Date: Thu, 4 Feb 2016 15:22:37 -0500 Subject: [PATCH] Correctly (I hope) implement reading from the central directory --- .../FileSystem/ZIP/ZIPDataFormat.cs | 153 +++++++++++++++++- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/ZIP/ZIPDataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/ZIP/ZIPDataFormat.cs index 1a5653f2..05d58e8e 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/ZIP/ZIPDataFormat.cs +++ b/CSharp/Plugins/UniversalEditor.Plugins.FileSystem/DataFormats/FileSystem/ZIP/ZIPDataFormat.cs @@ -19,6 +19,9 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP { private ZIPSettings mvarSettings = new ZIPSettings(); + private static readonly byte[] SIG_CENTRAL_DIRECTORY_ENTRY = new byte[] { 0x50, 0x4B, 0x01, 0x02 }; + private static readonly byte[] SIG_END_OF_CENTRAL_DIRECTORY = new byte[] { 0x50, 0x4B, 0x05, 0x06 }; + private string mvarComment = String.Empty; public string Comment { get { return mvarComment; } set { mvarComment = value; } } @@ -37,6 +40,120 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP if (fsom == null) return; IO.Reader br = base.Accessor.Reader; + + + long eocdOffset = zip_FindEndOfCentralDirectory(br); + if (eocdOffset != -1) + { + // let's work with the central directory since it's more reliable than the file header + Internal.ZIPCentralDirectoryFooter footer = ReadZIPCentralDirectoryFooter(br); + if (footer.centralDirectoryOffset > 0 && footer.centralDirectoryOffset < br.Accessor.Length) + { + br.Seek(footer.centralDirectoryOffset, SeekOrigin.Begin); + long pos = br.Accessor.Position; + + while (br.Accessor.Position < footer.centralDirectoryLength + pos) + { + byte[] centralDirectorySignature = br.ReadBytes(4); + if (!centralDirectorySignature.Match(SIG_CENTRAL_DIRECTORY_ENTRY)) + { + Console.WriteLine("zip: @" + br.Accessor.Position.ToString() + " - invalid central directory entry, shit might get real now"); + } + + ushort unknown1 = br.ReadUInt16(); + ushort unknown2 = br.ReadUInt16(); + ushort unknown3 = br.ReadUInt16(); + ushort unknown4 = br.ReadUInt16(); + uint unknown5 = br.ReadUInt32(); + uint unknown6 = br.ReadUInt32(); + uint compressedLength = br.ReadUInt32(); + uint decompressedLength = br.ReadUInt32(); + uint fileNameLength = br.ReadUInt32(); + ushort unknown7 = br.ReadUInt16(); + ushort unknown8 = br.ReadUInt16(); + ushort unknown9 = br.ReadUInt16(); + ushort unknown10 = br.ReadUInt16(); + ushort unknown11 = br.ReadUInt16(); + uint fileOffset = br.ReadUInt32(); + string fileName = br.ReadFixedLengthString(fileNameLength); + + long curpos = br.Accessor.Position; + br.Accessor.Position = fileOffset; + + byte[] headerSignature = br.ReadBytes(4); + if (!headerSignature.Match(new byte[] { 0x50, 0x4B, 0x03, 0x04 })) + { + + } + + CompressionMethod method = CompressionMethod.None; + short iMinimumVersionNeededToExtract = br.ReadInt16(); + short iGeneralPurposeBitFlag = br.ReadInt16(); + short iCompressionMethod = br.ReadInt16(); + switch (iCompressionMethod) + { + case 8: + { + method = CompressionMethod.Deflate; + break; + } + case 9: + { + method = CompressionMethod.Deflate64; + break; + } + case 12: + { + method = CompressionMethod.Bzip2; + break; + } + case 14: + { + method = CompressionMethod.LZMA; + break; + } + } + short iFileLastModificationTime = br.ReadInt16(); + short iFileLastModificationDate = br.ReadInt16(); + bool isEncrypted = false; + int iCRC32 = br.ReadInt32(); + int packedFileLength = br.ReadInt32(); + int unpackedFileLength = br.ReadInt32(); + short local_fileNameLength = br.ReadInt16(); + short extraFieldLength = br.ReadInt16(); + string local_fileName = br.ReadFixedLengthString(fileNameLength); + long local_pos = br.Accessor.Position; + while (br.Accessor.Position < (local_pos + extraFieldLength)) + { + short chunkIDCode = br.ReadInt16(); + short chunkLength = br.ReadInt16(); + byte[] data = br.ReadBytes(chunkLength); + } + + + byte[] compressedData = br.ReadBytes(compressedLength); + br.Accessor.Position = curpos; + + byte[] decompressedData = compressedData; + if (compressedLength != decompressedLength) + { + decompressedData = UniversalEditor.Compression.CompressionModules.Deflate.Decompress(compressedData); + if (decompressedData.Length != decompressedLength) + { + Console.WriteLine("zip: sanity check - decompressed data length (" + decompressedData.Length.ToString() + ") does not match expected value (" + decompressedLength.ToString() + ")"); + } + } + + File file = fsom.AddFile(fileName, decompressedData); + } + return; + } + else + { + Console.WriteLine("zip: central directory offset " + footer.centralDirectoryOffset.ToString() + " out of bounds for file (" + br.Accessor.Length.ToString() + ")"); + } + } + int firstFile = this.zip_FindFirstFileOffset(br); if (firstFile == -1) { @@ -49,7 +166,8 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP File file = null; try { - file = (zip_ReadFile(br) as File); + zip_ReadFile(br, fsom); + /* if (file != null) { if (file.Name.Contains("/")) @@ -106,6 +224,7 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP { break; } + */ } catch (System.Security.SecurityException) { @@ -114,6 +233,18 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP } } + private static Internal.ZIPCentralDirectoryFooter ReadZIPCentralDirectoryFooter(Reader reader) + { + Internal.ZIPCentralDirectoryFooter item = new Internal.ZIPCentralDirectoryFooter(); + item.unknown1 = reader.ReadUInt32(); + item.unknown2 = reader.ReadUInt16(); + item.unknown3 = reader.ReadUInt16(); + item.centralDirectoryLength = reader.ReadUInt32(); + item.centralDirectoryOffset = reader.ReadUInt32(); + item.unknown4 = reader.ReadUInt16(); + return item; + } + private bool readCentralDirectory(IO.Reader br) { int ctdir = this.seekCentralDirectory(br); @@ -373,8 +504,23 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP } return -1; } + + private long zip_FindEndOfCentralDirectory(IO.Reader reader) + { + reader.Seek(-4, SeekOrigin.End); + while (true) + { + byte[] test = reader.ReadBytes(4); + if (test.Match(SIG_END_OF_CENTRAL_DIRECTORY)) + { + return reader.Accessor.Position; + } + reader.Seek(-5, SeekOrigin.Current); + } + return -1; + } - private File zip_ReadFile(IO.Reader br) + private File zip_ReadFile(IO.Reader br, IFileSystemContainer fsom) { byte[] unpackedData; CompressionMethod method = CompressionMethod.Deflate; @@ -446,9 +592,8 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP HostApplication.Messages.Add(HostApplicationMessageSeverity.Error, "File size mismatch, source archive may be corrupted", fileName); } - File f = new File(); + File f = fsom.AddFile(fileName); f.SetData(unpackedData); - f.Name = fileName; return f; }