From 444fc1696be6cad808a525eb700710ee6818ac76 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 15 Sep 2016 21:25:58 -0700 Subject: [PATCH] [FileTools] More fiddling with Tzip --- SabreTools.Helper/Objects/ZipFIle.cs | 2 +- SabreTools.Helper/Objects/ZipFileEntry.cs | 193 +++++++++++----------- SabreTools.Helper/Tools/FileTools.cs | 47 ++++-- 3 files changed, 129 insertions(+), 113 deletions(-) diff --git a/SabreTools.Helper/Objects/ZipFIle.cs b/SabreTools.Helper/Objects/ZipFIle.cs index 5acf9666..92eb0979 100644 --- a/SabreTools.Helper/Objects/ZipFIle.cs +++ b/SabreTools.Helper/Objects/ZipFIle.cs @@ -397,7 +397,7 @@ namespace SabreTools.Helper } // Now try to open the file for reading - _zipstream = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite); + _zipstream = new FileStream(filename, FileMode.Open, FileAccess.Read); int read = _zipstream.Read(new byte[1], 0, 1); if (read != 1) { diff --git a/SabreTools.Helper/Objects/ZipFileEntry.cs b/SabreTools.Helper/Objects/ZipFileEntry.cs index 7e8eb0bd..cf20dcd9 100644 --- a/SabreTools.Helper/Objects/ZipFileEntry.cs +++ b/SabreTools.Helper/Objects/ZipFileEntry.cs @@ -174,110 +174,109 @@ namespace SabreTools.Helper try { // Open the stream for reading - using (BinaryReader br = new BinaryReader(_zipstream)) + BinaryReader br = new BinaryReader(_zipstream); + + // If the first bytes aren't a central directory header, log and return + if (br.ReadUInt32() != Constants.CentralDirectoryHeaderSignature) { - // If the first bytes aren't a central directory header, log and return - if (br.ReadUInt32() != Constants.CentralDirectoryHeaderSignature) + return ZipReturn.ZipCentralDirError; + } + + // Now read in available information, skipping the unnecessary + _versionMadeBy = (ArchiveVersion)br.ReadUInt16(); + _versionNeeded = (ArchiveVersion)br.ReadUInt16(); + _generalPurposeBitFlag = (GeneralPurposeBitFlag)br.ReadUInt16(); + _compressionMethod = (CompressionMethod)br.ReadUInt16(); + + // If we have an unsupported compression method, log and return + if (_compressionMethod != CompressionMethod.Stored && _compressionMethod != CompressionMethod.Deflated) + { + return ZipReturn.ZipCentralDirError; + } + + // Keep reading available information, skipping the unnecessary + _lastModFileTime = br.ReadUInt16(); + _lastModFileDate = br.ReadUInt16(); + _crc = br.ReadUInt32(); + _compressedSize = br.ReadUInt32(); + _uncompressedSize = br.ReadUInt32(); + + // Now store some temp vars to find the filename, extra field, and comment + ushort fileNameLength = br.ReadUInt16(); + ushort extraFieldLength = br.ReadUInt16(); + ushort fileCommentLength = br.ReadUInt16(); + + // Even more reading available information, skipping the unnecessary + br.ReadUInt16(); // Disk number start + _internalFileAttributes = (InternalFileAttributes)br.ReadUInt16(); + _externalFileAttributes = br.ReadUInt32(); + _relativeOffset = br.ReadUInt32(); + byte[] fileNameBytes = br.ReadBytes(fileNameLength); + _fileName = ((_generalPurposeBitFlag & GeneralPurposeBitFlag.LanguageEncodingFlag) == 0 + ? Encoding.ASCII.GetString(fileNameBytes) + : Encoding.UTF8.GetString(fileNameBytes, 0, fileNameLength)); + _extraField = br.ReadBytes(extraFieldLength); + _comment = br.ReadBytes(fileCommentLength); + + /* + Full disclosure: this next section is in GordonJ's work but I honestly + have no idea everything that it does. It seems to do something to figure + out if it's Zip64, or possibly check for random things but it uses the + extra field for this, which I do not fully understand. It's copied in + its entirety below in the hope that it makes things better... + */ + + int pos = 0; + while (extraFieldLength > pos) + { + ushort type = BitConverter.ToUInt16(_extraField, pos); + pos += 2; + ushort blockLength = BitConverter.ToUInt16(_extraField, pos); + pos += 2; + switch (type) { - return ZipReturn.ZipCentralDirError; - } + case 0x0001: + Zip64 = true; + if (UncompressedSize == 0xffffffff) + { + UncompressedSize = BitConverter.ToUInt64(_extraField, pos); + pos += 8; + } + if (_compressedSize == 0xffffffff) + { + _compressedSize = BitConverter.ToUInt64(_extraField, pos); + pos += 8; + } + if (_relativeOffset == 0xffffffff) + { + _relativeOffset = BitConverter.ToUInt64(_extraField, pos); + pos += 8; + } + break; + case 0x7075: + //byte version = extraField[pos]; + pos += 1; + uint nameCRC32 = BitConverter.ToUInt32(_extraField, pos); + pos += 4; - // Now read in available information, skipping the unnecessary - _versionMadeBy = (ArchiveVersion)br.ReadUInt16(); - _versionNeeded = (ArchiveVersion)br.ReadUInt16(); - _generalPurposeBitFlag = (GeneralPurposeBitFlag)br.ReadUInt16(); - _compressionMethod = (CompressionMethod)br.ReadUInt16(); + CRC32 crcTest = new CRC32(); + crcTest.SlurpBlock(fileNameBytes, 0, fileNameLength); + uint fCRC = (uint)crcTest.Crc32Result; - // If we have an unsupported compression method, log and return - if (_compressionMethod != CompressionMethod.Stored && _compressionMethod != CompressionMethod.Deflated) - { - return ZipReturn.ZipCentralDirError; - } + if (nameCRC32 != fCRC) + { + return ZipReturn.ZipCentralDirError; + } - // Keep reading available information, skipping the unnecessary - _lastModFileTime = br.ReadUInt16(); - _lastModFileDate = br.ReadUInt16(); - _crc = br.ReadUInt32(); - _compressedSize = br.ReadUInt32(); - _uncompressedSize = br.ReadUInt32(); + int charLen = blockLength - 5; - // Now store some temp vars to find the filename, extra field, and comment - ushort fileNameLength = br.ReadUInt16(); - ushort extraFieldLength = br.ReadUInt16(); - ushort fileCommentLength = br.ReadUInt16(); + _fileName = Encoding.UTF8.GetString(_extraField, pos, charLen); + pos += charLen; - // Even more reading available information, skipping the unnecessary - br.ReadUInt16(); // Disk number start - _internalFileAttributes = (InternalFileAttributes)br.ReadUInt16(); - _externalFileAttributes = br.ReadUInt16(); - _relativeOffset = br.ReadUInt32(); - byte[] fileNameBytes = br.ReadBytes(fileNameLength); - _fileName = ((_generalPurposeBitFlag & GeneralPurposeBitFlag.LanguageEncodingFlag) == 0 - ? Encoding.ASCII.GetString(fileNameBytes) - : Encoding.UTF8.GetString(fileNameBytes, 0, fileNameLength)); - _extraField = br.ReadBytes(extraFieldLength); - _comment = br.ReadBytes(fileCommentLength); - - /* - Full disclosure: this next section is in GordonJ's work but I honestly - have no idea everything that it does. It seems to do something to figure - out if it's Zip64, or possibly check for random things but it uses the - extra field for this, which I do not fully understand. It's copied in - its entirety below in the hope that it makes things better... - */ - - int pos = 0; - while (extraFieldLength > pos) - { - ushort type = BitConverter.ToUInt16(_extraField, pos); - pos += 2; - ushort blockLength = BitConverter.ToUInt16(_extraField, pos); - pos += 2; - switch (type) - { - case 0x0001: - Zip64 = true; - if (UncompressedSize == 0xffffffff) - { - UncompressedSize = BitConverter.ToUInt64(_extraField, pos); - pos += 8; - } - if (_compressedSize == 0xffffffff) - { - _compressedSize = BitConverter.ToUInt64(_extraField, pos); - pos += 8; - } - if (_relativeOffset == 0xffffffff) - { - _relativeOffset = BitConverter.ToUInt64(_extraField, pos); - pos += 8; - } - break; - case 0x7075: - //byte version = extraField[pos]; - pos += 1; - uint nameCRC32 = BitConverter.ToUInt32(_extraField, pos); - pos += 4; - - CRC32 crcTest = new CRC32(); - crcTest.SlurpBlock(fileNameBytes, 0, fileNameLength); - uint fCRC = (uint)crcTest.Crc32Result; - - if (nameCRC32 != fCRC) - { - return ZipReturn.ZipCentralDirError; - } - - int charLen = blockLength - 5; - - _fileName = Encoding.UTF8.GetString(_extraField, pos, charLen); - pos += charLen; - - break; - default: - pos += blockLength; - break; - } + break; + default: + pos += blockLength; + break; } } } diff --git a/SabreTools.Helper/Tools/FileTools.cs b/SabreTools.Helper/Tools/FileTools.cs index a2f3d5b9..2f46771d 100644 --- a/SabreTools.Helper/Tools/FileTools.cs +++ b/SabreTools.Helper/Tools/FileTools.cs @@ -117,6 +117,7 @@ namespace SabreTools.Helper // Set internal variables Stream readStream = null; Stream writeStream = null; + ZipFile oldZipFile = new ZipFile(); ZipFile zipFile = new ZipFile(); ZipReturn zipReturn = ZipReturn.ZipGood; @@ -135,27 +136,35 @@ namespace SabreTools.Helper // Open or create the archive if (!File.Exists(archiveFileName)) { - zipReturn = zipFile.Create(archiveFileName); + zipReturn = zipFile.Create(archiveFileName + ".new"); } else { - zipReturn = zipFile.Open(archiveFileName, new FileInfo(archiveFileName).LastWriteTime.Ticks, false); - zipFile.ZipOpen = ZipOpenType.OpenWrite; - } - - if (zipReturn != ZipReturn.ZipGood) - { - zipFile.Dispose(); - return success; + // Open the old archive for reading + oldZipFile.Open(archiveFileName, new FileInfo(archiveFileName).LastWriteTime.Ticks, false); + zipFile.Create(archiveFileName + ".new"); + + // Copy over all files to the new archive + for (int i = 0; i < oldZipFile.EntriesCount; i++) + { + // Instantiate the streams + CompressionMethod icompressionMethod = CompressionMethod.Stored; + ulong istreamSize = 0; + oldZipFile.OpenReadStream(i, true, out readStream, out istreamSize, out icompressionMethod); + zipFile.OpenWriteStream(true, true, oldZipFile.Filename(i), streamSize, CompressionMethod.Deflated, out writeStream); + + // Copy the input stream to the output + byte[] ibuffer = new byte[8 * 1024]; + int ilen; + while ((ilen = readStream.Read(ibuffer, 0, ibuffer.Length)) > 0) + { + writeStream.Write(ibuffer, 0, ilen); + } + } } - // Open the stream for writing + // Now open the write stream for the new rom zipReturn = zipFile.OpenWriteStream(false, true, rom.Name, streamSize, CompressionMethod.Deflated, out writeStream); - if (zipReturn != ZipReturn.ZipGood) - { - zipFile.Dispose(); - return success; - } // Copy the input stream to the output byte[] buffer = new byte[8 * 1024]; @@ -186,8 +195,16 @@ namespace SabreTools.Helper finally { zipFile.Dispose(); + oldZipFile.Dispose(); } + // If the old file exists, delete it and replace + if (File.Exists(archiveFileName)) + { + File.Delete(archiveFileName); + } + File.Move(archiveFileName + ".new", archiveFileName); + return success; }