diff --git a/SabreTools.FileTypes/Archives/GZipArchive.cs b/SabreTools.FileTypes/Archives/GZipArchive.cs index 7e2813dc..545ac1c2 100644 --- a/SabreTools.FileTypes/Archives/GZipArchive.cs +++ b/SabreTools.FileTypes/Archives/GZipArchive.cs @@ -9,7 +9,7 @@ using SabreTools.Core.Tools; using SabreTools.IO; using Compress; using Compress.gZip; -using Compress.ZipFile.ZLib; +using Compress.Support.Compression.Deflate; namespace SabreTools.FileTypes.Archives { @@ -240,7 +240,7 @@ namespace SabreTools.FileTypes.Archives ZipReturn ret = gz.ZipFileOpen(this.Filename); ret = gz.ZipFileOpenReadStream(0, out Stream gzstream, out ulong streamSize); gzipEntryRom = GetInfo(gzstream, hashes: this.AvailableHashes); - gzipEntryRom.Filename = gz.Filename(0); + gzipEntryRom.Filename = gz.GetLocalFile(0).Filename; gzipEntryRom.Parent = gamename; gzipEntryRom.Date = (gz.TimeStamp > 0 ? gz.TimeStamp.ToString() : null); gzstream.Dispose(); diff --git a/SabreTools.FileTypes/Archives/SevenZipArchive.cs b/SabreTools.FileTypes/Archives/SevenZipArchive.cs index f566c212..a36e27d9 100644 --- a/SabreTools.FileTypes/Archives/SevenZipArchive.cs +++ b/SabreTools.FileTypes/Archives/SevenZipArchive.cs @@ -7,8 +7,6 @@ using SabreTools.Core; using SabreTools.Core.Tools; using Compress; using Compress.SevenZip; -using Compress.Utils; -using Compress.ZipFile; using NaturalSort; namespace SabreTools.FileTypes.Archives @@ -75,7 +73,7 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++) @@ -84,19 +82,19 @@ namespace SabreTools.FileTypes.Archives zr = zf.ZipFileOpenReadStream(i, out Stream readStream, out ulong streamsize); // Create the rest of the path, if needed - if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Filename(i)))) - Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.Filename(i)))); + if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.GetLocalFile(i).Filename))) + Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.GetLocalFile(i).Filename))); // If the entry ends with a directory separator, continue to the next item, if any - if (zf.Filename(i).EndsWith(Path.DirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.AltDirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.PathSeparator.ToString())) + if (zf.GetLocalFile(i).Filename.EndsWith(Path.DirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.AltDirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.PathSeparator.ToString())) { zf.ZipFileCloseReadStream(); continue; } - FileStream writeStream = File.Create(Path.Combine(outDir, zf.Filename(i))); + FileStream writeStream = File.Create(Path.Combine(outDir, zf.GetLocalFile(i).Filename)); // If the stream is smaller than the buffer, just run one loop through to avoid issues if (streamsize < _bufferSize) @@ -198,15 +196,15 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++) { - if (zf.Filename(i).Contains(entryName)) + if (zf.GetLocalFile(i).Filename.Contains(entryName)) { // Open the read stream - realEntry = zf.Filename(i); + realEntry = zf.GetLocalFile(i).Filename; zr = zf.ZipFileOpenReadStream(i, out Stream readStream, out ulong streamsize); // If the stream is smaller than the buffer, just run one loop through to avoid issues @@ -267,16 +265,16 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.LocalFilesCount(); i++) { // If the entry is a directory (or looks like a directory), we don't want to open it - if (zf.IsDirectory(i) - || zf.Filename(i).EndsWith(Path.DirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.AltDirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.PathSeparator.ToString())) + if (zf.GetLocalFile(i).IsDirectory + || zf.GetLocalFile(i).Filename.EndsWith(Path.DirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.AltDirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.PathSeparator.ToString())) { continue; } @@ -298,17 +296,17 @@ namespace SabreTools.FileTypes.Archives // Perform a quickscan, if flagged to if (this.AvailableHashes == Hash.CRC) { - zipEntryRom.Size = (long)zf.UncompressedSize(i); - zipEntryRom.CRC = zf.CRC32(i); + zipEntryRom.Size = (long)zf.GetLocalFile(i).UncompressedSize; + zipEntryRom.CRC = zf.GetLocalFile(i).CRC; } // Otherwise, use the stream directly else { - zipEntryRom = GetInfo(readStream, size: (long)zf.UncompressedSize(i), hashes: this.AvailableHashes, keepReadOpen: true); + zipEntryRom = GetInfo(readStream, size: (long)zf.GetLocalFile(i).UncompressedSize, hashes: this.AvailableHashes, keepReadOpen: true); } // Fill in comon details and add to the list - zipEntryRom.Filename = zf.Filename(i); + zipEntryRom.Filename = zf.GetLocalFile(i).Filename; zipEntryRom.Parent = gamename; found.Add(zipEntryRom); } @@ -337,13 +335,13 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } List<(string, bool)> zipEntries = new(); for (int i = 0; i < zf.LocalFilesCount(); i++) { - zipEntries.Add((zf.Filename(i), zf.IsDirectory(i))); + zipEntries.Add((zf.GetLocalFile(i).Filename, zf.GetLocalFile(i).IsDirectory)); } zipEntries = zipEntries.OrderBy(p => p.Item1, new NaturalReversedComparer()).ToList(); @@ -381,7 +379,7 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } return zf.ZipStatus == ZipStatus.Trrnt7Zip; @@ -474,7 +472,7 @@ namespace SabreTools.FileTypes.Archives var oldZipFileContents = new List(); for (int i = 0; i < oldZipFile.LocalFilesCount(); i++) { - oldZipFileContents.Add(oldZipFile.Filename(i)); + oldZipFileContents.Add(oldZipFile.GetLocalFile(i).Filename); } // If the old one doesn't contain the new file, then add it @@ -486,7 +484,7 @@ namespace SabreTools.FileTypes.Archives // Then add all of the old entries to it too for (int i = 0; i < oldZipFile.LocalFilesCount(); i++) { - inputIndexMap.Add(oldZipFile.Filename(i), i); + inputIndexMap.Add(oldZipFile.GetLocalFile(i).Filename, i); } // If the number of entries is the same as the old archive, skip out @@ -501,7 +499,7 @@ namespace SabreTools.FileTypes.Archives // Get the order for the entries with the new file List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Copy over all files to the new archive foreach (string key in keys) @@ -544,7 +542,7 @@ namespace SabreTools.FileTypes.Archives { // Instantiate the streams oldZipFile.ZipFileOpenReadStream(index, out Stream zreadStream, out ulong istreamSize); - zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.Filename(index), istreamSize, 0, out writeStream, null); + zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.GetLocalFile(index).Filename, istreamSize, 0, out writeStream, null); // Copy the input stream to the output byte[] ibuffer = new byte[_bufferSize]; @@ -556,7 +554,7 @@ namespace SabreTools.FileTypes.Archives } oldZipFile.ZipFileCloseReadStream(); - zipFile.ZipFileCloseWriteStream(oldZipFile.CRC32(index)); + zipFile.ZipFileCloseWriteStream(oldZipFile.GetLocalFile(index).CRC); } } } @@ -643,7 +641,7 @@ namespace SabreTools.FileTypes.Archives // Sort the keys in TZIP order List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Now add all of the files in order foreach (string key in keys) @@ -694,7 +692,7 @@ namespace SabreTools.FileTypes.Archives var oldZipFileContents = new List(); for (int j = 0; j < oldZipFile.LocalFilesCount(); j++) { - oldZipFileContents.Add(oldZipFile.Filename(j)); + oldZipFileContents.Add(oldZipFile.GetLocalFile(j).Filename); } // If the old one contains the new file, then just skip out @@ -709,7 +707,7 @@ namespace SabreTools.FileTypes.Archives // Then add all of the old entries to it too for (int i = 0; i < oldZipFile.LocalFilesCount(); i++) { - inputIndexMap.Add(oldZipFile.Filename(i), i); + inputIndexMap.Add(oldZipFile.GetLocalFile(i).Filename, i); } // If the number of entries is the same as the old archive, skip out @@ -724,7 +722,7 @@ namespace SabreTools.FileTypes.Archives // Get the order for the entries with the new file List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Copy over all files to the new archive foreach (string key in keys) @@ -768,7 +766,7 @@ namespace SabreTools.FileTypes.Archives { // Instantiate the streams oldZipFile.ZipFileOpenReadStream(index, out Stream zreadStream, out ulong istreamSize); - zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.Filename(index), istreamSize, 0, out writeStream, null); + zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.GetLocalFile(index).Filename, istreamSize, 0, out writeStream, null); // Copy the input stream to the output byte[] ibuffer = new byte[_bufferSize]; @@ -779,7 +777,7 @@ namespace SabreTools.FileTypes.Archives writeStream.Flush(); } - zipFile.ZipFileCloseWriteStream(oldZipFile.CRC32(index)); + zipFile.ZipFileCloseWriteStream(oldZipFile.GetLocalFile(index).CRC); } } } diff --git a/SabreTools.FileTypes/Archives/TapeArchive.cs b/SabreTools.FileTypes/Archives/TapeArchive.cs index 160b6cf6..fcc63a61 100644 --- a/SabreTools.FileTypes/Archives/TapeArchive.cs +++ b/SabreTools.FileTypes/Archives/TapeArchive.cs @@ -5,7 +5,7 @@ using System.Linq; using SabreTools.Core; using SabreTools.Core.Tools; -using Compress.ZipFile; +using Compress; using SharpCompress.Archives; using SharpCompress.Archives.Tar; using SharpCompress.Common; @@ -330,7 +330,7 @@ namespace SabreTools.FileTypes.Archives // Get the order for the entries with the new file List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Copy over all files to the new archive foreach (string key in keys) @@ -444,7 +444,7 @@ namespace SabreTools.FileTypes.Archives // Sort the keys in TZIP order List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Now add all of the files in order foreach (string key in keys) @@ -499,7 +499,7 @@ namespace SabreTools.FileTypes.Archives // Get the order for the entries with the new file List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Copy over all files to the new archive foreach (string key in keys) diff --git a/SabreTools.FileTypes/Archives/ZipArchive.cs b/SabreTools.FileTypes/Archives/ZipArchive.cs index 65878b44..568fa62e 100644 --- a/SabreTools.FileTypes/Archives/ZipArchive.cs +++ b/SabreTools.FileTypes/Archives/ZipArchive.cs @@ -6,7 +6,6 @@ using System.Linq; using SabreTools.Core; using SabreTools.Core.Tools; using Compress; -using Compress.Utils; using Compress.ZipFile; using NaturalSort; @@ -84,7 +83,7 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++) @@ -93,21 +92,21 @@ namespace SabreTools.FileTypes.Archives zr = zf.ZipFileOpenReadStream(i, false, out Stream readStream, out ulong streamsize, out ushort cm); // Create the rest of the path, if needed - if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Filename(i)))) + if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.GetLocalFile(i).Filename))) { - Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.Filename(i)))); + Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.GetLocalFile(i).Filename))); } // If the entry ends with a directory separator, continue to the next item, if any - if (zf.Filename(i).EndsWith(Path.DirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.AltDirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.PathSeparator.ToString())) + if (zf.GetLocalFile(i).Filename.EndsWith(Path.DirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.AltDirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.PathSeparator.ToString())) { zf.ZipFileCloseReadStream(); continue; } - FileStream writeStream = File.Create(Path.Combine(outDir, zf.Filename(i))); + FileStream writeStream = File.Create(Path.Combine(outDir, zf.GetLocalFile(i).Filename)); // If the stream is smaller than the buffer, just run one loop through to avoid issues if (streamsize < _bufferSize) @@ -209,15 +208,15 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++) { - if (zf.Filename(i).Contains(entryName)) + if (zf.GetLocalFile(i).Filename.Contains(entryName)) { // Open the read stream - realEntry = zf.Filename(i); + realEntry = zf.GetLocalFile(i).Filename; zr = zf.ZipFileOpenReadStream(i, false, out Stream readStream, out ulong streamsize, out ushort cm); // If the stream is smaller than the buffer, just run one loop through to avoid issues @@ -278,16 +277,16 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } for (int i = 0; i < zf.LocalFilesCount(); i++) { // If the entry is a directory (or looks like a directory), we don't want to open it - if (zf.IsDirectory(i) - || zf.Filename(i).EndsWith(Path.DirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.AltDirectorySeparatorChar.ToString()) - || zf.Filename(i).EndsWith(Path.PathSeparator.ToString())) + if (zf.GetLocalFile(i).IsDirectory + || zf.GetLocalFile(i).Filename.EndsWith(Path.DirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.AltDirectorySeparatorChar.ToString()) + || zf.GetLocalFile(i).Filename.EndsWith(Path.PathSeparator.ToString())) { continue; } @@ -309,19 +308,19 @@ namespace SabreTools.FileTypes.Archives // Perform a quickscan, if flagged to if (this.AvailableHashes == Hash.CRC) { - zipEntryRom.Size = (long)zf.UncompressedSize(i); - zipEntryRom.CRC = zf.CRC32(i); + zipEntryRom.Size = (long)zf.GetLocalFile(i).UncompressedSize; + zipEntryRom.CRC = zf.GetLocalFile(i).CRC; } // Otherwise, use the stream directly else { - zipEntryRom = GetInfo(readStream, size: (long)zf.UncompressedSize(i), hashes: this.AvailableHashes, keepReadOpen: true); + zipEntryRom = GetInfo(readStream, size: (long)zf.GetLocalFile(i).UncompressedSize, hashes: this.AvailableHashes, keepReadOpen: true); } // Fill in comon details and add to the list - zipEntryRom.Filename = zf.Filename(i); + zipEntryRom.Filename = zf.GetLocalFile(i).Filename; zipEntryRom.Parent = gamename; - zipEntryRom.Date = zf.LastModified(i).ToString("yyyy/MM/dd hh:mm:ss"); + zipEntryRom.Date = zf.GetLocalFile(i).LastModified.ToString("yyyy/MM/dd hh:mm:ss"); found.Add(zipEntryRom); } @@ -349,13 +348,13 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } List<(string, bool)> zipEntries = new(); for (int i = 0; i < zf.LocalFilesCount(); i++) { - zipEntries.Add((zf.Filename(i), zf.IsDirectory(i))); + zipEntries.Add((zf.GetLocalFile(i).Filename, zf.GetLocalFile(i).IsDirectory)); } zipEntries = zipEntries.OrderBy(p => p.Item1, new NaturalReversedComparer()).ToList(); @@ -393,7 +392,7 @@ namespace SabreTools.FileTypes.Archives ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); if (zr != ZipReturn.ZipGood) { - throw new Exception(ZipUtils.ZipErrorMessageText(zr)); + throw new Exception(CompressUtils.ZipErrorMessageText(zr)); } return zf.ZipStatus == ZipStatus.TrrntZip; @@ -486,7 +485,7 @@ namespace SabreTools.FileTypes.Archives var oldZipFileContents = new List(); for (int i = 0; i < oldZipFile.LocalFilesCount(); i++) { - oldZipFileContents.Add(oldZipFile.Filename(i)); + oldZipFileContents.Add(oldZipFile.GetLocalFile(i).Filename); } // If the old one doesn't contain the new file, then add it @@ -496,7 +495,7 @@ namespace SabreTools.FileTypes.Archives // Then add all of the old entries to it too for (int i = 0; i < oldZipFile.LocalFilesCount(); i++) { - inputIndexMap.Add(oldZipFile.Filename(i), i); + inputIndexMap.Add(oldZipFile.GetLocalFile(i).Filename, i); } // If the number of entries is the same as the old archive, skip out @@ -511,7 +510,7 @@ namespace SabreTools.FileTypes.Archives // Get the order for the entries with the new file List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Copy over all files to the new archive foreach (string key in keys) @@ -554,9 +553,9 @@ namespace SabreTools.FileTypes.Archives { // Instantiate the streams oldZipFile.ZipFileOpenReadStream(index, false, out Stream zreadStream, out ulong istreamSize, out ushort icompressionMethod); - long msDosDateTime = oldZipFile.LastModified(index); + long msDosDateTime = oldZipFile.GetLocalFile(index).LastModified; TimeStamps ts = new() { ModTime = msDosDateTime }; - zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.Filename(index), istreamSize, (ushort)CompressionMethod.Deflated, out writeStream, ts); + zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.GetLocalFile(index).Filename, istreamSize, (ushort)CompressionMethod.Deflated, out writeStream, ts); // Copy the input stream to the output byte[] ibuffer = new byte[_bufferSize]; @@ -568,7 +567,7 @@ namespace SabreTools.FileTypes.Archives } oldZipFile.ZipFileCloseReadStream(); - zipFile.ZipFileCloseWriteStream(oldZipFile.CRC32(index)); + zipFile.ZipFileCloseWriteStream(oldZipFile.GetLocalFile(index).CRC); } } } @@ -655,7 +654,7 @@ namespace SabreTools.FileTypes.Archives // Sort the keys in TZIP order List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Now add all of the files in order foreach (string key in keys) @@ -706,7 +705,7 @@ namespace SabreTools.FileTypes.Archives var oldZipFileContents = new List(); for (int j = 0; j < oldZipFile.LocalFilesCount(); j++) { - oldZipFileContents.Add(oldZipFile.Filename(j)); + oldZipFileContents.Add(oldZipFile.GetLocalFile(j).Filename); } // If the old one contains the new file, then just skip out @@ -721,7 +720,7 @@ namespace SabreTools.FileTypes.Archives // Then add all of the old entries to it too for (int i = 0; i < oldZipFile.LocalFilesCount(); i++) { - inputIndexMap.Add(oldZipFile.Filename(i), i); + inputIndexMap.Add(oldZipFile.GetLocalFile(i).Filename, i); } // If the number of entries is the same as the old archive, skip out @@ -736,7 +735,7 @@ namespace SabreTools.FileTypes.Archives // Get the order for the entries with the new file List keys = inputIndexMap.Keys.ToList(); - keys.Sort(ZipUtils.TrrntZipStringCompare); + keys.Sort(CompressUtils.TrrntZipStringCompare); // Copy over all files to the new archive foreach (string key in keys) @@ -780,9 +779,9 @@ namespace SabreTools.FileTypes.Archives { // Instantiate the streams oldZipFile.ZipFileOpenReadStream(index, false, out Stream zreadStream, out ulong istreamSize, out ushort icompressionMethod); - long msDosDateTime = oldZipFile.LastModified(index); + long msDosDateTime = oldZipFile.GetLocalFile(index).LastModified; TimeStamps ts = new() { ModTime = msDosDateTime }; - zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.Filename(index), istreamSize, (ushort)CompressionMethod.Deflated, out writeStream, ts); + zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.GetLocalFile(index).Filename, istreamSize, (ushort)CompressionMethod.Deflated, out writeStream, ts); // Copy the input stream to the output byte[] ibuffer = new byte[_bufferSize]; @@ -793,7 +792,7 @@ namespace SabreTools.FileTypes.Archives writeStream.Flush(); } - zipFile.ZipFileCloseWriteStream(oldZipFile.CRC32(index)); + zipFile.ZipFileCloseWriteStream(oldZipFile.GetLocalFile(index).CRC); } } } diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipUtils.cs b/SabreTools.FileTypes/Compress/CompressUtils.cs similarity index 71% rename from SabreTools.FileTypes/Compress/ZipFile/ZipUtils.cs rename to SabreTools.FileTypes/Compress/CompressUtils.cs index 30936cf0..108ea801 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipUtils.cs +++ b/SabreTools.FileTypes/Compress/CompressUtils.cs @@ -1,12 +1,31 @@ using System; using System.Text; +using RVIO; -namespace Compress.ZipFile +namespace Compress { - public static class ZipUtils + public static class CompressUtils { - // according to the zip documents, zip filesname are stored as MS-DOS Code Page 437. - // (Unless the uncode flag is set, in which case they are stored as UTF-8. + + public static void CreateDirForFile(string sFilename) + { + string strTemp = Path.GetDirectoryName(sFilename); + + if (string.IsNullOrEmpty(strTemp)) + { + return; + } + + if (Directory.Exists(strTemp)) + { + return; + } + + Directory.CreateDirectory(strTemp); + } + + // according to the zip documents, zip filenames are stored as MS-DOS Code Page 437. + // (Unless the unicode flag is set, in which case they are stored as UTF-8. private static Encoding enc = null; public static void EncodeSetup() @@ -154,7 +173,7 @@ namespace Compress.ZipFile ret = ""; break; case ZipReturn.ZipFileCountError: - ret = "The number of file in the Zip does not mach the number of files in the Zips Centeral Directory"; + ret = "The number of file in the Zip does not mach the number of files in the Zips Central Directory"; break; case ZipReturn.ZipSignatureError: ret = "An unknown Signature Block was found in the Zip"; @@ -163,19 +182,19 @@ namespace Compress.ZipFile ret = "Extra Data was found on the end of the Zip"; break; case ZipReturn.ZipUnsupportedCompression: - ret = "An unsupported Compression method was found in the Zip, if you recompress this zip it will be usable"; + ret = "An unsupported Compression method was found in the Zip, if you re-compress this zip it will be usable"; break; case ZipReturn.ZipLocalFileHeaderError: ret = "Error reading a zipped file header information"; break; case ZipReturn.ZipCentralDirError: - ret = "There is an error in the Zip Centeral Directory"; + ret = "There is an error in the Zip Central Directory"; break; case ZipReturn.ZipReadingFromOutputFile: ret = "Trying to write to a Zip file open for output only"; break; case ZipReturn.ZipWritingToInputFile: - ret = "Tring to read from a Zip file open for input only"; + ret = "Trying to read from a Zip file open for input only"; break; case ZipReturn.ZipErrorGettingDataStream: ret = "Error creating Data Stream"; @@ -190,32 +209,34 @@ namespace Compress.ZipFile return ret; } - public const long fileTimeToUTCTime = 504911232000000000; - public const long epochTimeToUTCTime = 621355968000000000; - public const long trrntzipDateTime = 629870671200000000; + + + private const long FileTimeToUtcTime = 504911232000000000; + private const long EpochTimeToUtcTime = 621355968000000000; + public const long TrrntzipDateTime = 629870671200000000; private const long TicksPerMillisecond = 10000; private const long TicksPerSecond = TicksPerMillisecond * 1000; - public static void SetDateTime(long ticks, out ushort DosFileDate, out ushort DosFileTime) + public static void UtcTicksToDosDateTime(long ticks, out ushort dosFileDate, out ushort dosFileTime) { - DateTime DateTime = new DateTime(ticks, DateTimeKind.Unspecified); - DosFileDate = (ushort)((DateTime.Day & 0x1f) | ((DateTime.Month & 0x0f) << 5) | (((DateTime.Year - 1980) & 0x7f) << 9)); - DosFileTime = (ushort)(((DateTime.Second >> 1) & 0x1f) | ((DateTime.Minute & 0x3f) << 5) | ((DateTime.Hour & 0x1f) << 11)); + DateTime dateTime = new(ticks, DateTimeKind.Unspecified); + dosFileDate = (ushort)((dateTime.Day & 0x1f) | ((dateTime.Month & 0x0f) << 5) | (((dateTime.Year - 1980) & 0x7f) << 9)); + dosFileTime = (ushort)(((dateTime.Second >> 1) & 0x1f) | ((dateTime.Minute & 0x3f) << 5) | ((dateTime.Hour & 0x1f) << 11)); } - public static long SetDateTime(ushort DosFileDate, ushort DosFileTime) + public static long UtcTicksFromDosDateTime(ushort dosFileDate, ushort dosFileTime) { - if (DosFileDate == 0) + if (dosFileDate == 0) return 0; - int second = (DosFileTime & 0x1f) << 1; - int minute = (DosFileTime >> 5) & 0x3f; - int hour = (DosFileTime >> 11) & 0x1f; + int second = (dosFileTime & 0x1f) << 1; + int minute = (dosFileTime >> 5) & 0x3f; + int hour = (dosFileTime >> 11) & 0x1f; - int day = DosFileDate & 0x1f; - int month = (DosFileDate >> 5) & 0x0f; - int year = ((DosFileDate >> 9) & 0x7f) + 1980; + int day = dosFileDate & 0x1f; + int month = (dosFileDate >> 5) & 0x0f; + int year = ((dosFileDate >> 9) & 0x7f) + 1980; // valid hours 0 to 23 // valid minutes 0 to 59 @@ -235,16 +256,25 @@ namespace Compress.ZipFile return 0; } } - - public static long FileTimeToUTCTime(long ticks) + public static long UtcTicksToNtfsDateTime(long ticks) { - return ticks + fileTimeToUTCTime; + return ticks - FileTimeToUtcTime; } - public static long SetDateTimeFromUnixSeconds(int seconds) + public static long UtcTicksFromNtfsDateTime(long ntfsTicks) { - return seconds * TicksPerSecond + epochTimeToUTCTime; + return ntfsTicks + FileTimeToUtcTime; + } + + public static int UtcTicksToUnixDateTime(long ticks) + { + return (int)((ticks - EpochTimeToUtcTime) / TicksPerSecond); + } + + public static long UtcTicksFromUnixDateTime(int linuxSeconds) + { + return linuxSeconds * TicksPerSecond + EpochTimeToUtcTime; } } -} +} \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/File/File.cs b/SabreTools.FileTypes/Compress/File/File.cs index 18eecac1..2087d036 100644 --- a/SabreTools.FileTypes/Compress/File/File.cs +++ b/SabreTools.FileTypes/Compress/File/File.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Compress.Utils; +using Compress.Support.Utils; using Path = RVIO.Path; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; @@ -27,41 +27,22 @@ namespace Compress.File return 1; } - public string Filename(int i) + public LocalFile GetLocalFile(int i) { - return Path.GetFileName(ZipFilename); + LocalFile lf = new() + { + Filename = Path.GetFileName(ZipFilename), + UncompressedSize = _fileInfo != null ? (ulong)_fileInfo.Length : (ulong)_inStream.Length, + CRC = _crc, + IsDirectory = RVIO.Directory.Exists(ZipFilename), + ModifiedTime = _fileInfo?.LastWriteTime, + AccessedTime = _fileInfo?.LastAccessTime, + CreatedTime = _fileInfo?.CreationTime + + }; + return lf; } - public bool IsDirectory(int i) - { - return RVIO.Directory.Exists(ZipFilename); - } - - public ulong UncompressedSize(int i) - { - return _fileInfo != null ? (ulong)_fileInfo.Length : (ulong)_inStream.Length; - } - - public ulong? LocalHeader(int i) - { - return 0; - } - - public ZipReturn FileStatus(int i) - { - return ZipReturn.ZipGood; - } - - public byte[] CRC32(int i) - { - return _crc; - } - - public long LastModified(int i) - { - return _fileInfo.LastWriteTime; - } - public ZipReturn ZipFileCreate(string newFilename) { if (ZipOpen != ZipOpenType.Closed) @@ -69,7 +50,7 @@ namespace Compress.File return ZipReturn.ZipFileAlreadyOpen; } - DirUtil.CreateDirForFile(newFilename); + CompressUtils.CreateDirForFile(newFilename); _fileInfo = new FileInfo(newFilename); int errorCode = FileStream.OpenFileWrite(newFilename, out _inStream); @@ -139,7 +120,7 @@ namespace Compress.File if (errorCode != 0) { ZipFileClose(); - if (errorCode == 32) + if (errorCode == 32 || errorCode==5) { return ZipReturn.ZipFileLocked; } @@ -158,12 +139,6 @@ namespace Compress.File } ZipOpen = ZipOpenType.OpenRead; - if (!readHeaders) - { - return ZipReturn.ZipGood; - } - - //return ZipFileReadHeaders(); return ZipReturn.ZipGood; } @@ -195,7 +170,7 @@ namespace Compress.File public void ZipFileCloseFailed() { - throw new NotImplementedException(); + //throw new NotImplementedException(); } public ZipReturn ZipFileOpenReadStream(int index, out Stream stream, out ulong streamSize) diff --git a/SabreTools.FileTypes/Compress/ICompress.cs b/SabreTools.FileTypes/Compress/ICompress.cs index 8b81fe2a..241eb668 100644 --- a/SabreTools.FileTypes/Compress/ICompress.cs +++ b/SabreTools.FileTypes/Compress/ICompress.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress { @@ -8,15 +8,8 @@ namespace Compress { int LocalFilesCount(); - string Filename(int i); - ulong? LocalHeader(int i); - ulong UncompressedSize(int i); - byte[] CRC32(int i); - - long LastModified(int i); - - bool IsDirectory(int i); - + LocalFile GetLocalFile(int i); + ZipOpenType ZipOpen { get; } ZipReturn ZipFileOpen(string newFilename, long timestamp = -1, bool readHeaders = true); diff --git a/SabreTools.FileTypes/Compress/LocalFile.cs b/SabreTools.FileTypes/Compress/LocalFile.cs new file mode 100644 index 00000000..92f7285b --- /dev/null +++ b/SabreTools.FileTypes/Compress/LocalFile.cs @@ -0,0 +1,52 @@ +using System; + +namespace Compress +{ + public class LocalFile + { + internal LocalFile() + { } + + public string Filename { get; internal set; } + public ulong UncompressedSize { get; internal set; } + public byte[] CRC { get; internal set; } + + public bool IsDirectory { get; internal set; } + + public long LastModified => ModifiedTime ?? HeaderLastModified; + + internal long HeaderLastModified { get; set; } + internal long? ModifiedTime { get; set; } + + public long? CreatedTime { get; internal set; } + public long? AccessedTime { get; internal set; } + + + private LocalFileStatus _status=LocalFileStatus.Nothing; + internal void SetStatus(LocalFileStatus lfs,bool set=true) + { + if (set) + _status |= lfs; + else + _status &= ~lfs; + } + + public bool GetStatus(LocalFileStatus lfs) + { + return (_status & lfs) != 0; + } + + public virtual ulong? LocalHead => null; + } + + [Flags] + public enum LocalFileStatus + { + Nothing = 0x00000, + Zip64 = 0x00001, + TrrntZip = 0x00002, + FilenameMisMatch = 0x00010, + DirectoryLengthError = 0x00020, + DateTimeMisMatch = 0x00040 + } +} diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardDictionary.cs b/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardDictionary.cs deleted file mode 100644 index 7705c5a2..00000000 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardDictionary.cs +++ /dev/null @@ -1,194 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using Interop = Compress.SevenZip.Compress.ZSTD.ZstandardInterop; - -namespace Compress.SevenZip.Compress.ZSTD -{ - /// - /// A Zstandard dictionary improves the compression ratio and speed on small data dramatically. - /// - /// - /// A Zstandard dictionary is calculated with a high number of small sample data. - /// Please refer to the Zstandard documentation for more details. - /// - /// - public sealed class ZstandardDictionary : IDisposable - { - private byte[] dictionary; - private IntPtr ddict; - private Dictionary cdicts = new Dictionary(); - private object lockObject = new object(); - private bool isDisposed = false; - - /// - /// Initializes a new instance of the class. - /// - /// The dictionary raw data. - public ZstandardDictionary(byte[] dictionary) - { - this.dictionary = dictionary; - } - - /// - /// Initializes a new instance of the class. - /// - /// The dictionary path. - public ZstandardDictionary(string dictionaryPath) - { - this.dictionary = System.IO.File.ReadAllBytes(dictionaryPath); - } - - /// - /// Initializes a new instance of the class. - /// - /// The dictionary stream. - public ZstandardDictionary(Stream dictionaryStream) - { - using (var memoryStream = new MemoryStream()) - { - dictionaryStream.CopyTo(memoryStream); - this.dictionary = memoryStream.ToArray(); - } - } - - /// - /// Finalizes an instance of the class. - /// - ~ZstandardDictionary() - { - this.Dispose(false); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - private void Dispose(bool dispose) - { - if (this.isDisposed == false) - { - this.isDisposed = true; - - if (this.ddict != IntPtr.Zero) - { - Interop.ZSTD_freeDDict(this.ddict); - this.ddict = IntPtr.Zero; - } - - foreach (var kv in this.cdicts.ToList()) - { - Interop.ZSTD_freeCDict(kv.Value); - this.cdicts.Remove(kv.Key); - } - } - } - - /// - /// Gets the compression dictionary for the specified compression level. - /// - /// The compression level. - /// - /// The IntPtr to the compression dictionary. - /// - /// ZstandardDictionary - internal IntPtr GetCompressionDictionary(int compressionLevel) - { - if (this.isDisposed) - { - throw new ObjectDisposedException(nameof(ZstandardDictionary)); - } - - lock (this.lockObject) - { - if (this.cdicts.TryGetValue(compressionLevel, out var cdict) == false) - { - this.cdicts[compressionLevel] = cdict = this.CreateCompressionDictionary(compressionLevel); - } - - return cdict; - } - } - - /// - /// Gets the decompression dictionary. - /// - /// - /// The IntPtr to the decompression dictionary. - /// - /// ZstandardDictionary - internal IntPtr GetDecompressionDictionary() - { - if (this.isDisposed) - { - throw new ObjectDisposedException(nameof(ZstandardDictionary)); - } - - lock (this.lockObject) - { - if (this.ddict == IntPtr.Zero) - { - this.ddict = this.CreateDecompressionDictionary(); - } - - return this.ddict; - } - } - - /// - /// Creates a new compression dictionary. - /// - /// The compression level. - /// - /// The IntPtr to the compression dictionary. - /// - private IntPtr CreateCompressionDictionary(int compressionLevel) - { - var alloc = GCHandle.Alloc(this.dictionary, GCHandleType.Pinned); - - try - { - var dictBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(this.dictionary, 0); - var dictSize = new UIntPtr((uint)this.dictionary.Length); - return Interop.ZSTD_createCDict(dictBuffer, dictSize, compressionLevel); - } - finally - { - alloc.Free(); - } - } - - /// - /// Creates a new decompression dictionary. - /// - /// - /// The IntPtr to the decompression dictionary. - /// - private IntPtr CreateDecompressionDictionary() - { - var alloc = GCHandle.Alloc(this.dictionary, GCHandleType.Pinned); - - try - { - var dictBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(this.dictionary, 0); - var dictSize = new UIntPtr((uint)this.dictionary.Length); - return Interop.ZSTD_createDDict(dictBuffer, dictSize); - } - finally - { - alloc.Free(); - } - } - } -} \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardInterop.cs b/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardInterop.cs deleted file mode 100644 index df469631..00000000 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardInterop.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace Compress.SevenZip.Compress.ZSTD -{ - internal static class ZstandardInterop - { - static ZstandardInterop() - { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - var root = Path.GetDirectoryName(typeof(ZstandardInterop).Assembly.Location); - var path = Environment.Is64BitProcess ? "x64" : "x86"; - var file = Path.Combine(root, path, "libzstd.dll"); - LoadLibraryEx(file, IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_SEARCH_APPLICATION_DIR); - } - } - - [StructLayout(LayoutKind.Sequential)] - public class Buffer - { - public IntPtr Data = IntPtr.Zero; - public UIntPtr Size = UIntPtr.Zero; - public UIntPtr Position = UIntPtr.Zero; - } - - public static void ThrowIfError(UIntPtr code) - { - if (ZSTD_isError(code)) - { - var errorPtr = ZSTD_getErrorName(code); - var errorMsg = Marshal.PtrToStringAnsi(errorPtr); - throw new IOException(errorMsg); - } - } - - //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- - - [Flags] - private enum LoadLibraryFlags : uint - { - DONT_RESOLVE_DLL_REFERENCES = 0x00000001, - LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, - LOAD_LIBRARY_AS_DATAFILE = 0x00000002, - LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, - LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, - LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200, - LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000, - LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100, - LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800, - LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400, - LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 - } - - [DllImport("kernel32", SetLastError = true)] - private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); - - //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern uint ZSTD_versionNumber(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern int ZSTD_maxCLevel(); - - //----------------------------------------------------------------------------------------- - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ZSTD_createCStream(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_initCStream(IntPtr zcs, int compressionLevel); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_freeCStream(IntPtr zcs); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_CStreamInSize(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_CStreamOutSize(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_compressStream(IntPtr zcs, [MarshalAs(UnmanagedType.LPStruct)] Buffer outputBuffer, [MarshalAs(UnmanagedType.LPStruct)] Buffer inputBuffer); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ZSTD_createCDict(IntPtr dictBuffer, UIntPtr dictSize, int compressionLevel); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_freeCDict(IntPtr cdict); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_initCStream_usingCDict(IntPtr zcs, IntPtr cdict); - - //----------------------------------------------------------------------------------------- - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ZSTD_createDStream(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_initDStream(IntPtr zds); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_freeDStream(IntPtr zds); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_DStreamInSize(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_DStreamOutSize(); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_decompressStream(IntPtr zds, [MarshalAs(UnmanagedType.LPStruct)] Buffer outputBuffer, [MarshalAs(UnmanagedType.LPStruct)] Buffer inputBuffer); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ZSTD_createDDict(IntPtr dictBuffer, UIntPtr dictSize); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_freeDDict(IntPtr ddict); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_initDStream_usingDDict(IntPtr zds, IntPtr ddict); - - //----------------------------------------------------------------------------------------- - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_flushStream(IntPtr zcs, [MarshalAs(UnmanagedType.LPStruct)] Buffer outputBuffer); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr ZSTD_endStream(IntPtr zcs, [MarshalAs(UnmanagedType.LPStruct)] Buffer outputBuffer); - - //----------------------------------------------------------------------------------------- - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - internal static extern bool ZSTD_isError(UIntPtr code); - - [DllImport("libzstd", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr ZSTD_getErrorName(UIntPtr code); - } -} \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardStream.cs b/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardStream.cs deleted file mode 100644 index 71e86bf1..00000000 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/ZSTD/ZstandardStream.cs +++ /dev/null @@ -1,397 +0,0 @@ -using System; -using System.IO; -using System.IO.Compression; -using System.Runtime.InteropServices; -using Interop = Compress.SevenZip.Compress.ZSTD.ZstandardInterop; - -namespace Compress.SevenZip.Compress.ZSTD -{ - /// - /// Provides methods and properties for compressing and decompressing streams by using the Zstandard algorithm. - /// - public class ZstandardStream : Stream - { - private Stream stream; - private CompressionMode mode; - private Boolean leaveOpen; - private Boolean isClosed = false; - private Boolean isDisposed = false; - private Boolean isInitialized = false; - - private IntPtr zstream; - private uint zstreamInputSize; - private uint zstreamOutputSize; - - private byte[] data; - private bool dataDepleted = false; - private bool dataSkipRead = false; - private int dataPosition = 0; - private int dataSize = 0; - - private long position = 0; - - private Interop.Buffer outputBuffer = new Interop.Buffer(); - private Interop.Buffer inputBuffer = new Interop.Buffer(); - - /// - /// Initializes a new instance of the class by using the specified stream and compression mode, and optionally leaves the stream open. - /// - /// The stream to compress. - /// One of the enumeration values that indicates whether to compress or decompress the stream. - /// true to leave the stream open after disposing the object; otherwise, false. - public ZstandardStream(Stream stream, CompressionMode mode, bool leaveOpen = false) - { - this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); - this.mode = mode; - this.leaveOpen = leaveOpen; - position = 0; - - if (mode == CompressionMode.Compress) - { - this.zstreamInputSize = Interop.ZSTD_CStreamInSize().ToUInt32(); - this.zstreamOutputSize = Interop.ZSTD_CStreamOutSize().ToUInt32(); - this.zstream = Interop.ZSTD_createCStream(); - this.data = new byte[(int)this.zstreamOutputSize]; - } - - if (mode == CompressionMode.Decompress) - { - this.zstreamInputSize = Interop.ZSTD_DStreamInSize().ToUInt32(); - this.zstreamOutputSize = Interop.ZSTD_DStreamOutSize().ToUInt32(); - this.zstream = Interop.ZSTD_createDStream(); - this.data = new byte[(int)this.zstreamInputSize]; - } - } - - /// - /// Initializes a new instance of the class by using the specified stream and compression level, and optionally leaves the stream open. - /// - /// The stream to compress. - /// The compression level. - /// true to leave the stream open after disposing the object; otherwise, false. - public ZstandardStream(Stream stream, int compressionLevel, bool leaveOpen = false) : this(stream, CompressionMode.Compress, leaveOpen) - { - this.CompressionLevel = compressionLevel; - } - - //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- - - /// - /// The version of the native Zstd library. - /// - public static Version Version - { - get - { - var version = (int)Interop.ZSTD_versionNumber(); - return new Version((version / 10000) % 100, (version / 100) % 100, version % 100); - } - } - - /// - /// The maximum compression level supported by the native Zstd library. - /// - public static int MaxCompressionLevel - { - get - { - return Interop.ZSTD_maxCLevel(); - } - } - - //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- - - /// - /// Gets or sets the compression level to use, the default is 6. - /// - /// - /// To get the maximum compression level see . - /// - public int CompressionLevel { get; set; } = 6; - - /// - /// Gets or sets the compression dictionary tp use, the default is null. - /// - /// - /// The compression dictionary. - /// - public ZstandardDictionary CompressionDictionary { get; set; } = null; - - /// - /// Gets whether the current stream supports reading. - /// - public override bool CanRead => this.stream.CanRead && this.mode == CompressionMode.Decompress; - - /// - /// Gets whether the current stream supports writing. - /// - public override bool CanWrite => this.stream.CanWrite && this.mode == CompressionMode.Compress; - - /// - /// Gets whether the current stream supports seeking. - /// - public override bool CanSeek => false; - - /// - /// Gets the length in bytes of the stream. - /// - public override long Length => throw new NotSupportedException(); - - /// - /// Gets or sets the position within the current stream. - /// - public override long Position - { - get => position; - set => throw new NotSupportedException(); - } - - //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this.isDisposed == false) - { - if (!this.isClosed) ReleaseResources(flushStream: false); - this.isDisposed = true; - this.data = null; - } - } - - public override void Close() - { - if (this.isClosed) return; - - try - { - ReleaseResources(flushStream: true); - } - finally - { - this.isClosed = true; - base.Close(); - } - } - - private void ReleaseResources(bool flushStream) - { - if (this.mode == CompressionMode.Compress) - { - try - { - if (flushStream) - { - this.ProcessStream((zcs, buffer) => Interop.ThrowIfError(Interop.ZSTD_flushStream(zcs, buffer))); - this.ProcessStream((zcs, buffer) => Interop.ThrowIfError(Interop.ZSTD_endStream(zcs, buffer))); - this.stream.Flush(); - } - } - finally - { - Interop.ZSTD_freeCStream(this.zstream); - if (!this.leaveOpen) this.stream.Close(); - } - } - else if (this.mode == CompressionMode.Decompress) - { - Interop.ZSTD_freeDStream(this.zstream); - if (!this.leaveOpen) this.stream.Close(); - } - } - - public override void Flush() - { - if (this.mode == CompressionMode.Compress) - { - this.ProcessStream((zcs, buffer) => Interop.ThrowIfError(Interop.ZSTD_flushStream(zcs, buffer))); - this.stream.Flush(); - } - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (this.CanRead == false) throw new NotSupportedException(); - - // prevent the buffers from being moved around by the garbage collector - var alloc1 = GCHandle.Alloc(buffer, GCHandleType.Pinned); - var alloc2 = GCHandle.Alloc(this.data, GCHandleType.Pinned); - - try - { - var length = 0; - - if (this.isInitialized == false) - { - this.isInitialized = true; - - var result = this.CompressionDictionary == null - ? Interop.ZSTD_initDStream(this.zstream) - : Interop.ZSTD_initDStream_usingDDict(this.zstream, this.CompressionDictionary.GetDecompressionDictionary()); - } - - while (count > 0) - { - var inputSize = this.dataSize - this.dataPosition; - - // read data from input stream - if (inputSize <= 0 && !this.dataDepleted && !this.dataSkipRead) - { - this.dataSize = this.stream.Read(this.data, 0, (int)this.zstreamInputSize); - this.dataDepleted = this.dataSize <= 0; - this.dataPosition = 0; - inputSize = this.dataDepleted ? 0 : this.dataSize; - - // skip stream.Read until the internal buffer is depleted - // avoids a Read timeout for applications that know the exact number of bytes in the stream - this.dataSkipRead = true; - } - - // configure the inputBuffer - this.inputBuffer.Data = inputSize <= 0 ? IntPtr.Zero : Marshal.UnsafeAddrOfPinnedArrayElement(this.data, this.dataPosition); - this.inputBuffer.Size = inputSize <= 0 ? UIntPtr.Zero : new UIntPtr((uint)inputSize); - this.inputBuffer.Position = UIntPtr.Zero; - - // configure the outputBuffer - this.outputBuffer.Data = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset); - this.outputBuffer.Size = new UIntPtr((uint)count); - this.outputBuffer.Position = UIntPtr.Zero; - - // decompress inputBuffer to outputBuffer - Interop.ThrowIfError(Interop.ZSTD_decompressStream(this.zstream, this.outputBuffer, this.inputBuffer)); - - // calculate progress in outputBuffer - var outputBufferPosition = (int)this.outputBuffer.Position.ToUInt32(); - if (outputBufferPosition == 0) - { - // the internal buffer is depleted, we're either done - if (this.dataDepleted) break; - - // or we need more bytes - this.dataSkipRead = false; - } - length += outputBufferPosition; - offset += outputBufferPosition; - count -= outputBufferPosition; - - // calculate progress in inputBuffer - var inputBufferPosition = (int)inputBuffer.Position.ToUInt32(); - this.dataPosition += inputBufferPosition; - } - - position += length; - return length; - } - finally - { - alloc1.Free(); - alloc2.Free(); - } - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (this.CanWrite == false) throw new NotSupportedException(); - - // prevent the buffers from being moved around by the garbage collector - var alloc1 = GCHandle.Alloc(buffer, GCHandleType.Pinned); - var alloc2 = GCHandle.Alloc(this.data, GCHandleType.Pinned); - - try - { - if (this.isInitialized == false) - { - this.isInitialized = true; - - var result = this.CompressionDictionary == null - ? Interop.ZSTD_initCStream(this.zstream, this.CompressionLevel) - : Interop.ZSTD_initCStream_usingCDict(this.zstream, this.CompressionDictionary.GetCompressionDictionary(this.CompressionLevel)); - - Interop.ThrowIfError(result); - } - - while (count > 0) - { - var inputSize = Math.Min((uint)count, this.zstreamInputSize); - - // configure the outputBuffer - this.outputBuffer.Data = Marshal.UnsafeAddrOfPinnedArrayElement(this.data, 0); - this.outputBuffer.Size = new UIntPtr(this.zstreamOutputSize); - this.outputBuffer.Position = UIntPtr.Zero; - - // configure the inputBuffer - this.inputBuffer.Data = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset); - this.inputBuffer.Size = new UIntPtr((uint)inputSize); - this.inputBuffer.Position = UIntPtr.Zero; - - // compress inputBuffer to outputBuffer - Interop.ThrowIfError(Interop.ZSTD_compressStream(this.zstream, this.outputBuffer, this.inputBuffer)); - - // write data to output stream - var outputBufferPosition = (int)this.outputBuffer.Position.ToUInt32(); - this.stream.Write(this.data, 0, outputBufferPosition); - - // calculate progress in inputBuffer - var inputBufferPosition = (int)this.inputBuffer.Position.ToUInt32(); - offset += inputBufferPosition; - count -= inputBufferPosition; - } - } - finally - { - alloc1.Free(); - alloc2.Free(); - } - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (origin != SeekOrigin.Current) - throw new NotImplementedException(); - - byte[] tmpBuff = new byte[1024]; - long sizeToGo = offset; - while (sizeToGo > 0) - { - int sizenow = sizeToGo > 1024 ? 1024 : (int)sizeToGo; - Read(tmpBuff, 0, sizenow); - sizeToGo -= sizenow; - } - - return offset; - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - //----------------------------------------------------------------------------------------- - //----------------------------------------------------------------------------------------- - - private void ProcessStream(Action outputAction) - { - var alloc = GCHandle.Alloc(this.data, GCHandleType.Pinned); - - try - { - this.outputBuffer.Data = Marshal.UnsafeAddrOfPinnedArrayElement(this.data, 0); - this.outputBuffer.Size = new UIntPtr(this.zstreamOutputSize); - this.outputBuffer.Position = UIntPtr.Zero; - - outputAction(this.zstream, this.outputBuffer); - - var outputBufferPosition = (int)this.outputBuffer.Position.ToUInt32(); - this.stream.Write(this.data, 0, outputBufferPosition); - } - finally - { - alloc.Free(); - } - } - } -} \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/SevenZip/SevenZip.cs b/SabreTools.FileTypes/Compress/SevenZip/SevenZip.cs index e5f9e6be..7c1cd479 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/SevenZip.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/SevenZip.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; -using Compress.SevenZip.Compress.ZSTD; using Compress.SevenZip.Structure; using FileInfo = RVIO.FileInfo; @@ -11,47 +9,21 @@ namespace Compress.SevenZip public partial class SevenZ : ICompress { - public enum sevenZipCompressType + public enum SevenZipCompressType { uncompressed, lzma, zstd } - public static bool supportZstd + private class SevenZipLocalFile : LocalFile { - get; - private set; - } - - public static void TestForZstd() - { - supportZstd = false; - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - var root = Path.GetDirectoryName(typeof(ZstandardInterop).Assembly.Location); - var path = Environment.Is64BitProcess ? "x64" : "x86"; - var file = Path.Combine(root, path, "libzstd.dll"); - supportZstd = RVIO.File.Exists(file); - } - } - - - - private class LocalFile - { - public string FileName; - public ulong UncompressedSize; - public bool IsDirectory; - public byte[] CRC; public int StreamIndex; public ulong StreamOffset; - public long LastModified; - public ZipReturn FileStatus = ZipReturn.ZipUntested; } - private List _localFiles = new List(); + private List _localFiles = new(); private FileInfo _zipFileInfo; @@ -59,7 +31,6 @@ namespace Compress.SevenZip private SignatureHeader _signatureHeader; - private sevenZipCompressType _compressed = sevenZipCompressType.lzma; private long _baseOffset; @@ -75,41 +46,10 @@ namespace Compress.SevenZip { return _localFiles.Count; } - - public string Filename(int i) + + public LocalFile GetLocalFile(int i) { - return _localFiles[i].FileName; - } - - public ulong? LocalHeader(int i) - { - return 0; - } - - public ulong UncompressedSize(int i) - { - return _localFiles[i].UncompressedSize; - } - - public int StreamIndex(int i) - { - return _localFiles[i].StreamIndex; - - } - - public ZipReturn FileStatus(int i) - { - return _localFiles[i].FileStatus; - } - - public byte[] CRC32(int i) - { - return _localFiles[i].CRC; - } - - public long LastModified(int i) - { - return _localFiles[i].LastModified; + return _localFiles[i]; } public void ZipFileCloseFailed() @@ -138,21 +78,7 @@ namespace Compress.SevenZip ZipOpen = ZipOpenType.Closed; } - - public bool IsDirectory(int i) - { - return _localFiles[i].IsDirectory; - } - - - - - - - - - - + public void ZipFileClose() { @@ -184,7 +110,7 @@ namespace Compress.SevenZip public StringBuilder HeaderReport() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); if (_header == null) { diff --git a/SabreTools.FileTypes/Compress/SevenZip/SevenZipRead.cs b/SabreTools.FileTypes/Compress/SevenZip/SevenZipRead.cs index 16dbfa48..ff7c7d32 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/SevenZipRead.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/SevenZipRead.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using Compress.SevenZip.Structure; -using Compress.Utils; +using Compress.Support.Utils; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; @@ -73,7 +73,7 @@ namespace Compress.SevenZip { try { - SignatureHeader signatureHeader = new SignatureHeader(); + SignatureHeader signatureHeader = new(); if (!signatureHeader.Read(_zipFs)) { return ZipReturn.ZipSignatureError; @@ -115,27 +115,27 @@ namespace Compress.SevenZip } - private void PopulateLocalFiles(out List localFiles) + private void PopulateLocalFiles(out List localFiles) { int emptyFileIndex = 0; int folderIndex = 0; int unpackedStreamsIndex = 0; ulong streamOffset = 0; - localFiles = new List(); + localFiles = new List(); if (_header == null) return; for (int i = 0; i < _header.FileInfo.Names.Length; i++) { - LocalFile lf = new LocalFile { FileName = _header.FileInfo.Names[i] }; + SevenZipLocalFile lf = new() { Filename = _header.FileInfo.Names[i] }; if ((_header.FileInfo.EmptyStreamFlags == null) || !_header.FileInfo.EmptyStreamFlags[i]) { lf.StreamIndex = folderIndex; lf.StreamOffset = streamOffset; lf.UncompressedSize = _header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].UnpackedSize; - lf.CRC = Util.uinttobytes(_header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].Crc); + lf.CRC = Util.UIntToBytes(_header.StreamsInfo.Folders[folderIndex].UnpackedStreamInfo[unpackedStreamsIndex].Crc); streamOffset += lf.UncompressedSize; unpackedStreamsIndex++; @@ -155,17 +155,19 @@ namespace Compress.SevenZip if (lf.IsDirectory) { - if (lf.FileName.Substring(lf.FileName.Length - 1, 1) != "/") + if (lf.Filename.Substring(lf.Filename.Length - 1, 1) != "/") { - lf.FileName += "/"; + lf.Filename += "/"; } } } - + if (_header.FileInfo.TimeLastWrite != null) - { - lf.LastModified = DateTime.FromFileTimeUtc((long)_header.FileInfo.TimeLastWrite[i]).Ticks; - } + lf.ModifiedTime = DateTime.FromFileTimeUtc((long)_header.FileInfo.TimeLastWrite[i]).Ticks; + if (_header.FileInfo.TimeCreation != null) + lf.CreatedTime = DateTime.FromFileTimeUtc((long)_header.FileInfo.TimeCreation[i]).Ticks; + if (_header.FileInfo.TimeLastAccess != null) + lf.AccessedTime = DateTime.FromFileTimeUtc((long)_header.FileInfo.TimeLastAccess[i]).Ticks; localFiles.Add(lf); } diff --git a/SabreTools.FileTypes/Compress/SevenZip/SevenZipReadStream.cs b/SabreTools.FileTypes/Compress/SevenZip/SevenZipReadStream.cs index 90df92f9..96912708 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/SevenZipReadStream.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/SevenZipReadStream.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; -using Compress.SevenZip.Compress.BZip2; -using Compress.SevenZip.Compress.LZMA; -using Compress.SevenZip.Compress.PPmd; -using Compress.SevenZip.Compress.ZSTD; -using Compress.SevenZip.Filters; using Compress.SevenZip.Structure; +using Compress.Support.Compression.BZip2; +using Compress.Support.Compression.LZMA; +using Compress.Support.Compression.PPmd; +using Compress.Support.Compression.zStd; +using Compress.Support.Filters; using FileStream = RVIO.FileStream; namespace Compress.SevenZip @@ -20,7 +20,7 @@ namespace Compress.SevenZip public ZipReturn ZipFileOpenReadStream(int index, out Stream stream, out ulong unCompressedSize) { - Debug.WriteLine("Opening File " + _localFiles[index].FileName); + Debug.WriteLine("Opening File " + _localFiles[index].Filename); stream = null; unCompressedSize = 0; @@ -31,7 +31,7 @@ namespace Compress.SevenZip return ZipReturn.ZipErrorGettingDataStream; } - if (IsDirectory(index)) + if (GetLocalFile(index).IsDirectory) { return ZipReturn.ZipTryingToAccessADirectory; } @@ -50,7 +50,7 @@ namespace Compress.SevenZip ZipFileCloseReadStream(); _streamIndex = thisStreamIndex; - if (_header.StreamsInfo==null) + if (_header.StreamsInfo == null) { stream = null; return ZipReturn.ZipGood; @@ -61,7 +61,7 @@ namespace Compress.SevenZip // first make the List of Decompressors streams int codersNeeded = folder.Coders.Length; - List allInputStreams = new List(); + List allInputStreams = new(); for (int i = 0; i < codersNeeded; i++) { folder.Coders[i].DecoderStream = null; @@ -96,7 +96,7 @@ namespace Compress.SevenZip allInputStreams[(int)folder.PackedStreamIndices[i]].InStreamIndex = packedStreamIndex; } - List inputCoders = new List(); + List inputCoders = new(); bool allCodersComplete = false; while (!allCodersComplete) @@ -164,7 +164,7 @@ namespace Compress.SevenZip coder.DecoderStream = new BCJ2Filter(inputCoders[0], inputCoders[1], inputCoders[2], inputCoders[3]); break; case DecompressType.ZSTD: - coder.DecoderStream = new ZstandardStream(inputCoders[0], CompressionMode.Decompress, true); + coder.DecoderStream =new zStdSharp(inputCoders[0]); break; default: return ZipReturn.ZipDecodeError; @@ -217,7 +217,7 @@ namespace Compress.SevenZip memStream.Position = 0; byte[] newStream = new byte[memStream.Length]; memStream.Read(newStream, 0, (int)memStream.Length); - MemoryStream ret = new MemoryStream(newStream, false); + MemoryStream ret = new(newStream, false); memStream.Position = pos; return ret; } diff --git a/SabreTools.FileTypes/Compress/SevenZip/SevenZipTorrent.cs b/SabreTools.FileTypes/Compress/SevenZip/SevenZipTorrent.cs index 4679a5da..439edfc0 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/SevenZipTorrent.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/SevenZipTorrent.cs @@ -1,5 +1,6 @@ using System.IO; using System.Text; +using Compress.Support.Utils; namespace Compress.SevenZip { @@ -36,9 +37,10 @@ namespace Compress.SevenZip const string sig = "RomVault7Z01"; byte[] rv7Zid = Util.Enc.GetBytes(sig); - byte[] header = new byte[12]; _zipFs.Read(header, 0, 12); + + for (int i = 0; i < 12; i++) { if (header[i] != rv7Zid[i]) @@ -50,7 +52,7 @@ namespace Compress.SevenZip uint headerCRC; ulong headerOffset; // is location of header in file ulong headerSize; - using (BinaryReader br = new BinaryReader(_zipFs, Encoding.UTF8, true)) + using (BinaryReader br = new(_zipFs, Encoding.UTF8, true)) { headerCRC = br.ReadUInt32(); headerOffset = br.ReadUInt64(); @@ -65,6 +67,7 @@ namespace Compress.SevenZip return headerSize == testHeaderLength; } + private bool Istorrent7Z() { const int crcsz = 128; @@ -84,7 +87,7 @@ namespace Compress.SevenZip int ar = _zipFs.Read(buffer, bufferPos, crcsz); if (ar < crcsz) { - Util.memset(buffer, bufferPos + ar, 0, crcsz - ar); + Util.MemSet(buffer, bufferPos + ar, 0, crcsz - ar); } bufferPos = crcsz; @@ -105,12 +108,12 @@ namespace Compress.SevenZip { ar = kSignatureSize; } - Util.memset(buffer, bufferPos + ar, 0, crcsz - ar); - Util.memcpyr(buffer, crcsz * 2 + 8, buffer, bufferPos + ar, t7ZsigSize + 4); + Util.MemSet(buffer, bufferPos + ar, 0, crcsz - ar); + Util.MemCrypt(buffer, crcsz * 2 + 8, buffer, bufferPos + ar, t7ZsigSize + 4); } else { - Util.memcpyr(buffer, crcsz * 2 + 8, buffer, crcsz * 2, t7ZsigSize + 4); + Util.MemCrypt(buffer, crcsz * 2 + 8, buffer, crcsz * 2, t7ZsigSize + 4); } foffs = _zipFs.Length; @@ -121,15 +124,15 @@ namespace Compress.SevenZip buffer[crcsz * 2 + 1] = (byte)((foffs >> 8) & 0xff); buffer[crcsz * 2 + 2] = (byte)((foffs >> 16) & 0xff); buffer[crcsz * 2 + 3] = (byte)((foffs >> 24) & 0xff); - buffer[crcsz * 2 + 4] = 0; - buffer[crcsz * 2 + 5] = 0; - buffer[crcsz * 2 + 6] = 0; - buffer[crcsz * 2 + 7] = 0; + buffer[crcsz * 2 + 4] = (byte)((foffs >> 32) & 0xff); + buffer[crcsz * 2 + 5] = (byte)((foffs >> 40) & 0xff); + buffer[crcsz * 2 + 6] = (byte)((foffs >> 48) & 0xff); + buffer[crcsz * 2 + 7] = (byte)((foffs >> 56) & 0xff); - if (Util.memcmp(buffer, 0, kSignature, kSignatureSize)) + if (Util.MemCmp(buffer, 0, kSignature, kSignatureSize)) { t7Zid[16] = buffer[crcsz * 2 + 4 + 8 + 16]; - if (Util.memcmp(buffer, crcsz * 2 + 4 + 8, t7Zid, t7ZidSize)) + if (Util.MemCmp(buffer, crcsz * 2 + 4 + 8, t7Zid, t7ZidSize)) { uint inCrc32 = (uint)(buffer[crcsz * 2 + 8 + 0] + (buffer[crcsz * 2 + 8 + 1] << 8) + @@ -141,7 +144,7 @@ namespace Compress.SevenZip buffer[crcsz * 2 + 8 + 2] = 0xff; buffer[crcsz * 2 + 8 + 3] = 0xff; - uint calcCrc32 = Utils.CRC.CalculateDigest(buffer, 0, crcsz * 2 + 8 + t7ZsigSize + 4); + uint calcCrc32 = CRC.CalculateDigest(buffer, 0, crcsz * 2 + 8 + t7ZsigSize + 4); if (inCrc32 == calcCrc32) { diff --git a/SabreTools.FileTypes/Compress/SevenZip/SevenZipWrite.cs b/SabreTools.FileTypes/Compress/SevenZip/SevenZipWrite.cs index 01ad80e1..f168d2c1 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/SevenZipWrite.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/SevenZipWrite.cs @@ -1,9 +1,8 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Text; -using Compress.SevenZip.Compress.LZMA; -using Compress.SevenZip.Compress.ZSTD; using Compress.SevenZip.Structure; -using Compress.Utils; +using Compress.Support.Compression.LZMA; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; @@ -11,37 +10,40 @@ namespace Compress.SevenZip { public partial class SevenZ { - private Stream _lzmaStream; - private ulong _packStreamStart; - private ulong _packStreamSize; - private ulong _unpackedStreamSize; - private byte[] _codeMSbytes; + private Stream _compressStream; + public class outStreams + { + public SevenZipCompressType compType; + public byte[] Method; + public byte[] Properties; + public ulong packedStart; + public ulong packedSize; + public List unpackedStreams; + } + + public List _packedOutStreams; public ZipReturn ZipFileCreate(string newFilename) { - return ZipFileCreate(newFilename, sevenZipCompressType.lzma); + return ZipFileCreate(newFilename, SevenZipCompressType.lzma); } - - public ZipReturn ZipFileCreateFromUncompressedSize(string newFilename, sevenZipCompressType ctype, ulong unCompressedSize) - { - if (ctype == sevenZipCompressType.zstd) - { - if (!supportZstd) - ctype = sevenZipCompressType.lzma; - } + public ZipReturn ZipFileCreateFromUncompressedSize(string newFilename, SevenZipCompressType ctype, ulong unCompressedSize) + { return ZipFileCreate(newFilename, ctype, GetDictionarySizeFromUncompressedSize(unCompressedSize)); } - public ZipReturn ZipFileCreate(string newFilename, sevenZipCompressType compressOutput, int dictionarySize = 1 << 24, int numFastBytes = 64) + private SevenZipCompressType _compType; + + public ZipReturn ZipFileCreate(string newFilename, SevenZipCompressType compressOutput, int dictionarySize = 1 << 24, int numFastBytes = 64) { if (ZipOpen != ZipOpenType.Closed) { return ZipReturn.ZipFileAlreadyOpen; } - DirUtil.CreateDirForFile(newFilename); + CompressUtils.CreateDirForFile(newFilename); _zipFileInfo = new FileInfo(newFilename); int errorCode = FileStream.OpenFileWrite(newFilename, out _zipFs); @@ -55,31 +57,56 @@ namespace Compress.SevenZip _signatureHeader = new SignatureHeader(); _header = new Header(); - using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) + using (BinaryWriter bw = new(_zipFs, Encoding.UTF8, true)) { _signatureHeader.Write(bw); } - _baseOffset = _zipFs.Position; - _compressed = compressOutput; - _unpackedStreamSize = 0; - if (_compressed == sevenZipCompressType.lzma) + _packedOutStreams = new List(); + + _compType = compressOutput; + +#if solid + outStreams newStream = new() { - LzmaEncoderProperties ep = new LzmaEncoderProperties(true, dictionarySize, numFastBytes); - LzmaStream lzs = new LzmaStream(ep, false, _zipFs); - _codeMSbytes = lzs.Properties; - _lzmaStream = lzs; - _packStreamStart = (ulong)_zipFs.Position; - } - else if (_compressed == sevenZipCompressType.zstd) + packedStart = (ulong)_zipFs.Position, + compType = compressOutput, + packedSize = 0, + unpackedStreams = new List() + }; + switch (compressOutput) { - ZstandardStream zss = new ZstandardStream(_zipFs, 18, true); - _codeMSbytes = new byte[] { 1, 4, 18, 0, 0 }; - _lzmaStream = zss; - _packStreamStart = (ulong)_zipFs.Position; + case SevenZipCompressType.lzma: + LzmaEncoderProperties ep = new(true, dictionarySize, numFastBytes); + LzmaStream lzs = new(ep, false, _zipFs); + + newStream.Method = new byte[] { 3, 1, 1 }; + newStream.Properties = lzs.Properties; + _compressStream = lzs; + break; + + + case SevenZipCompressType.zstd: + + + Stream zss = new ZstdSharp.CompressionStream(_zipFs, 18); + + newStream.Method = new byte[] { 4, 247, 17, 1 }; + newStream.Properties = new byte[] { 1, 5, 19, 0, 0 }; + _compressStream = zss; + break; + + case SevenZipCompressType.uncompressed: + newStream.Method = new byte[] { 0 }; + newStream.Properties = null; + _compressStream = _zipFs; + break; } + + _packedOutStreams.Add(newStream); +#endif return ZipReturn.ZipGood; } @@ -89,14 +116,14 @@ namespace Compress.SevenZip if (fName.Substring(fName.Length - 1, 1) == @"/") fName = fName.Substring(0, fName.Length - 1); - LocalFile lf = new LocalFile + SevenZipLocalFile lf = new() { - FileName = fName, + Filename = fName, UncompressedSize = 0, - IsDirectory = true, - StreamOffset = 0 + IsDirectory = true }; _localFiles.Add(lf); + unpackedStreamInfo = null; } public void ZipFileAddZeroLengthFile() @@ -104,36 +131,100 @@ namespace Compress.SevenZip // do nothing here for 7zip } + + + UnpackedStreamInfo unpackedStreamInfo; public ZipReturn ZipFileOpenWriteStream(bool raw, bool trrntzip, string filename, ulong uncompressedSize, ushort compressionMethod, out Stream stream, TimeStamps dateTime) { - return ZipFileOpenWriteStream(filename, uncompressedSize, out stream); - } - - private ZipReturn ZipFileOpenWriteStream(string filename, ulong uncompressedSize, out Stream stream) - { - LocalFile lf = new LocalFile - { - FileName = filename, - UncompressedSize = uncompressedSize, - StreamOffset = (ulong)(_zipFs.Position - _signatureHeader.BaseOffset) - }; + // check if we are writing a directory if (uncompressedSize == 0 && filename.Substring(filename.Length - 1, 1) == "/") { - lf.FileName = filename.Substring(0, filename.Length - 1); - lf.IsDirectory = true; + ZipFileAddDirectory(filename); + stream = null; + return ZipReturn.ZipGood; } - _unpackedStreamSize += uncompressedSize; + SevenZipLocalFile localFile = new() + { + Filename = filename, + UncompressedSize = uncompressedSize + }; + _localFiles.Add(localFile); - _localFiles.Add(lf); - stream = _compressed == sevenZipCompressType.uncompressed ? _zipFs : _lzmaStream; + if (uncompressedSize == 0) + { + unpackedStreamInfo = null; + stream = null; + return ZipReturn.ZipGood; + } + + +#if !solid + + outStreams newStream = new() + { + packedStart = (ulong)_zipFs.Position, + compType = _compType, + packedSize = 0, + unpackedStreams = new List() + }; + switch (_compType) + { + case SevenZipCompressType.lzma: + + LzmaEncoderProperties ep = new(true, GetDictionarySizeFromUncompressedSize(uncompressedSize), 64); + LzmaStream lzs = new(ep, false, _zipFs); + newStream.Method = new byte[] { 3, 1, 1 }; + newStream.Properties = lzs.Properties; + _compressStream = lzs; + break; + + case SevenZipCompressType.zstd: + + ZstdSharp.CompressionStream zss = new(_zipFs, 19); + newStream.Method = new byte[] { 4, 247, 17, 1 }; + newStream.Properties = new byte[] { 1, 5, 19, 0, 0 }; + _compressStream = zss; + break; + + case SevenZipCompressType.uncompressed: + newStream.Method = new byte[] { 0 }; + newStream.Properties = null; + _compressStream = _zipFs; + break; + } + + _packedOutStreams.Add(newStream); +#endif + + unpackedStreamInfo = new UnpackedStreamInfo { UnpackedSize = uncompressedSize }; + _packedOutStreams[_packedOutStreams.Count - 1].unpackedStreams.Add(unpackedStreamInfo); + + stream = _compressStream; return ZipReturn.ZipGood; } public ZipReturn ZipFileCloseWriteStream(byte[] crc32) { - _localFiles[_localFiles.Count - 1].CRC = new[] { crc32[3], crc32[2], crc32[1], crc32[0] }; + SevenZipLocalFile localFile = _localFiles[_localFiles.Count - 1]; + localFile.CRC = new[] { crc32[3], crc32[2], crc32[1], crc32[0] }; + + if (unpackedStreamInfo != null) + unpackedStreamInfo.Crc = Util.BytesToUint(localFile.CRC); + +#if !solid + if (unpackedStreamInfo != null) + { + if (_packedOutStreams[_packedOutStreams.Count - 1].compType != SevenZipCompressType.uncompressed) + { + _compressStream.Flush(); + _compressStream.Close(); + } + _packedOutStreams[_packedOutStreams.Count - 1].packedSize = (ulong)_zipFs.Position - _packedOutStreams[_packedOutStreams.Count - 1].packedStart; + } +#endif + return ZipReturn.ZipGood; } diff --git a/SabreTools.FileTypes/Compress/SevenZip/SevenZipWriteClose.cs b/SabreTools.FileTypes/Compress/SevenZip/SevenZipWriteClose.cs index 3907851c..9dc2ba0c 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/SevenZipWriteClose.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/SevenZipWriteClose.cs @@ -1,9 +1,8 @@ using System.IO; using System.Text; -using Compress.SevenZip.Compress.LZMA; -using Compress.SevenZip.Compress.ZSTD; using Compress.SevenZip.Structure; -using Compress.Utils; +using Compress.Support.Compression.LZMA; +using Compress.Support.Utils; namespace Compress.SevenZip { @@ -23,7 +22,7 @@ namespace Compress.SevenZip ulong emptyFileCount = 0; for (int i = 0; i < fileCount; i++) { - _header.FileInfo.Names[i] = _localFiles[i].FileName; + _header.FileInfo.Names[i] = _localFiles[i].Filename; if (_localFiles[i].UncompressedSize != 0) { @@ -37,7 +36,6 @@ namespace Compress.SevenZip emptyStreamCount += 1; } - ulong outFileCount = (ulong)_localFiles.Count - emptyStreamCount; _header.FileInfo.EmptyStreamFlags = null; _header.FileInfo.EmptyFileFlags = null; @@ -82,158 +80,75 @@ namespace Compress.SevenZip //StreamsInfo + _header.StreamsInfo = new StreamsInfo { PackPosition = 0 }; - //StreamsInfo.PackedStreamsInfo - if (_compressed!=sevenZipCompressType.uncompressed) + _header.StreamsInfo.PackedStreams = new PackedStreamInfo[_packedOutStreams.Count]; + for (int i = 0; i < _packedOutStreams.Count; i++) { - _header.StreamsInfo.PackedStreams = new PackedStreamInfo[1]; - _header.StreamsInfo.PackedStreams[0] = new PackedStreamInfo { PackedSize = _packStreamSize }; + _header.StreamsInfo.PackedStreams[i] = new PackedStreamInfo { PackedSize = _packedOutStreams[i].packedSize }; } - else + + _header.StreamsInfo.Folders = new Folder[_packedOutStreams.Count]; + for (int i = 0; i < _packedOutStreams.Count; i++) { - _header.StreamsInfo.PackedStreams = new PackedStreamInfo[outFileCount]; - int fileIndex = 0; - for (int i = 0; i < fileCount; i++) - { - if (_localFiles[i].UncompressedSize == 0) - { - continue; - } - _header.StreamsInfo.PackedStreams[fileIndex++] = new PackedStreamInfo { PackedSize = _localFiles[i].UncompressedSize }; - } - } - //StreamsInfo.PackedStreamsInfo, no CRC or StreamPosition required + ulong unpackedStreamSize = 0; + foreach (UnpackedStreamInfo v in _packedOutStreams[i].unpackedStreams) + unpackedStreamSize += v.UnpackedSize; - if (_compressed != sevenZipCompressType.uncompressed) - { - //StreamsInfo.Folders - _header.StreamsInfo.Folders = new Folder[1]; - - //StreamsInfo.Folders.Coder - // flags 0x23 - - Folder folder = new Folder + _header.StreamsInfo.Folders[i] = new Folder() { BindPairs = null, - Coders = new[] { + Coders = new Coder[] { new Coder { - Method = new byte[] { 3, 1, 1 }, + Method = _packedOutStreams[i].Method, NumInStreams = 1, NumOutStreams = 1, - Properties = _codeMSbytes + Properties = _packedOutStreams[i].Properties } }, - PackedStreamIndices = new[] { (ulong)0 }, - UnpackedStreamSizes = new[] { _unpackedStreamSize }, - UnpackedStreamInfo = new UnpackedStreamInfo[outFileCount], + PackedStreamIndices = new ulong[] { (ulong)i }, + UnpackedStreamSizes = new ulong[] { unpackedStreamSize }, + UnpackedStreamInfo = _packedOutStreams[i].unpackedStreams.ToArray(), UnpackCRC = null }; - - switch (_lzmaStream) - { - case LzmaStream _: - folder.Coders[0].Method = new byte[] { 3, 1, 1 }; - break; - case ZstandardStream _: - folder.Coders[0].Method = new byte[] { 4, 247, 17, 1 }; - break; - } - - - int fileIndex = 0; - for (int i = 0; i < fileCount; i++) - { - if (_localFiles[i].UncompressedSize == 0) - { - continue; - } - UnpackedStreamInfo unpackedStreamInfo = new UnpackedStreamInfo - { - UnpackedSize = _localFiles[i].UncompressedSize, - Crc = Util.bytestouint(_localFiles[i].CRC) - }; - folder.UnpackedStreamInfo[fileIndex++] = unpackedStreamInfo; - } - _header.StreamsInfo.Folders[0] = folder; - } - else - { - _header.StreamsInfo.Folders = new Folder[outFileCount]; - int fileIndex = 0; - for (int i = 0; i < fileCount; i++) - { - if (_localFiles[i].UncompressedSize == 0) - { - continue; - } - - Folder folder = new Folder - { - BindPairs = null, - Coders = new[] { - new Coder { - Method = new byte[] {0}, - NumInStreams = 1, - NumOutStreams = 1, - Properties = null - } - }, - PackedStreamIndices = new[] { (ulong)i }, - UnpackedStreamSizes = new[] { _localFiles[i].UncompressedSize }, - UnpackCRC = null, - - UnpackedStreamInfo = new[] { - new UnpackedStreamInfo { - UnpackedSize = _localFiles[i].UncompressedSize, - Crc = Util.bytestouint(_localFiles[i].CRC) - } - } - }; - - _header.StreamsInfo.Folders[fileIndex++] = folder; - } } } - - private void CloseWriting7Zip() { - if (_compressed != sevenZipCompressType.uncompressed) +#if solid + if (_packedOutStreams[0].compType != SevenZipCompressType.uncompressed) { - _lzmaStream.Flush(); - _lzmaStream.Close(); + _compressStream.Flush(); + _compressStream.Close(); } - - _packStreamSize = (ulong)_zipFs.Position - _packStreamStart; - + _packedOutStreams[0].packedSize = (ulong)_zipFs.Position - _packedOutStreams[0].packedStart; +#endif Create7ZStructure(); byte[] newHeaderByte; using (Stream headerMem = new MemoryStream()) { - using (BinaryWriter headerBw = new BinaryWriter(headerMem, Encoding.UTF8, true)) - { - _header.WriteHeader(headerBw); + using BinaryWriter headerBw = new(headerMem, Encoding.UTF8, true); + _header.WriteHeader(headerBw); - newHeaderByte = new byte[headerMem.Length]; - headerMem.Position = 0; - headerMem.Read(newHeaderByte, 0, newHeaderByte.Length); - } + newHeaderByte = new byte[headerMem.Length]; + headerMem.Position = 0; + headerMem.Read(newHeaderByte, 0, newHeaderByte.Length); } uint mainHeaderCRC = CRC.CalculateDigest(newHeaderByte, 0, (uint)newHeaderByte.Length); - #region Header Compression +#region Header Compression long packedHeaderPos = _zipFs.Position; - LzmaEncoderProperties ep = new LzmaEncoderProperties(true, GetDictionarySizeFromUncompressedSize((ulong)newHeaderByte.Length), 64); - LzmaStream lzs = new LzmaStream(ep, false, _zipFs); + LzmaEncoderProperties ep = new(true, GetDictionarySizeFromUncompressedSize((ulong)newHeaderByte.Length), 64); + LzmaStream lzs = new(ep, false, _zipFs); byte[] lzmaStreamProperties = lzs.Properties; lzs.Write(newHeaderByte, 0, newHeaderByte.Length); lzs.Close(); - StreamsInfo streamsInfo = new StreamsInfo + StreamsInfo streamsInfo = new() { PackPosition = (ulong)(packedHeaderPos - _baseOffset), Folders = new[] { @@ -262,22 +177,19 @@ namespace Compress.SevenZip using (Stream headerMem = new MemoryStream()) { - using (BinaryWriter bw = new BinaryWriter(headerMem, Encoding.UTF8, true)) - { - bw.Write((byte)HeaderProperty.kEncodedHeader); - streamsInfo.WriteHeader(bw); + using BinaryWriter bw = new(headerMem, Encoding.UTF8, true); + bw.Write((byte)HeaderProperty.kEncodedHeader); + streamsInfo.WriteHeader(bw); - newHeaderByte = new byte[headerMem.Length]; - headerMem.Position = 0; - headerMem.Read(newHeaderByte, 0, newHeaderByte.Length); - - } + newHeaderByte = new byte[headerMem.Length]; + headerMem.Position = 0; + headerMem.Read(newHeaderByte, 0, newHeaderByte.Length); } mainHeaderCRC = CRC.CalculateDigest(newHeaderByte, 0, (uint)newHeaderByte.Length); - #endregion +#endregion - using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) + using (BinaryWriter bw = new(_zipFs, Encoding.UTF8, true)) { ulong headerPosition = (ulong)_zipFs.Position + 32; //tzip header is 32 bytes WriteRomVault7Zip(bw, headerPosition, (ulong)newHeaderByte.Length, mainHeaderCRC); diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/Coder.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/Coder.cs index f06aea87..bccf0cd7 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/Coder.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/Coder.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Text; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress.SevenZip.Structure { @@ -106,7 +106,7 @@ namespace Compress.SevenZip.Structure { DecoderType = DecompressType.LZMA2; } - else if (SevenZ.supportZstd && Method.Length == 4 && Method[0] == 4 && Method[1] == 247 && Method[2] == 17 && Method[3] == 1) + else if (Method.Length == 4 && Method[0] == 4 && Method[1] == 247 && Method[2] == 17 && Method[3] == 1) { DecoderType = DecompressType.ZSTD; } diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/FileInfo.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/FileInfo.cs index 5d356af1..7a469308 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/FileInfo.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/FileInfo.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using System.Text; namespace Compress.SevenZip.Structure @@ -97,20 +96,18 @@ namespace Compress.SevenZip.Structure byte[] namebyte; - using (MemoryStream nameMem = new MemoryStream()) + using (MemoryStream nameMem = new()) { - using (BinaryWriter nameBw = new BinaryWriter(nameMem, Encoding.UTF8, true)) + using BinaryWriter nameBw = new(nameMem, Encoding.UTF8, true); + nameBw.Write((byte)0); //not external + foreach (string name in Names) { - nameBw.Write((byte)0); //not external - foreach (string name in Names) - { - nameBw.WriteName(name); - } - - namebyte = new byte[nameMem.Length]; - nameMem.Position = 0; - nameMem.Read(namebyte, 0, namebyte.Length); + nameBw.WriteName(name); } + + namebyte = new byte[nameMem.Length]; + nameMem.Position = 0; + nameMem.Read(namebyte, 0, namebyte.Length); } bw.Write((byte)HeaderProperty.kName); diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/Folder.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/Folder.cs index d869be07..30cf4484 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/Folder.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/Folder.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Text; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress.SevenZip.Structure { @@ -175,8 +175,7 @@ namespace Compress.SevenZip.Structure case HeaderProperty.kCRC: { - uint?[] crcs; - Util.UnPackCRCs(br, (ulong)Folders.Length, out crcs); + Util.UnPackCRCs(br, (ulong)Folders.Length, out uint?[] crcs); for (int i = 0; i < Folders.Length; i++) { Folders[i].UnpackCRC = crcs[i]; @@ -250,8 +249,10 @@ namespace Compress.SevenZip.Structure if (folder.UnpackedStreamInfo == null) { folder.UnpackedStreamInfo = new UnpackedStreamInfo[1]; - folder.UnpackedStreamInfo[0] = new UnpackedStreamInfo(); - folder.UnpackedStreamInfo[0].UnpackedSize = folder.GetUnpackSize(); + folder.UnpackedStreamInfo[0] = new UnpackedStreamInfo + { + UnpackedSize = folder.GetUnpackSize() + }; } if ((folder.UnpackedStreamInfo.Length != 1) || !folder.UnpackCRC.HasValue) @@ -261,8 +262,7 @@ namespace Compress.SevenZip.Structure } int crcIndex = 0; - uint?[] crc; - Util.UnPackCRCs(br, numCRC, out crc); + Util.UnPackCRCs(br, numCRC, out uint?[] crc); for (uint i = 0; i < Folders.Length; i++) { Folder folder = Folders[i]; @@ -384,7 +384,7 @@ namespace Compress.SevenZip.Structure Folder folder = Folders[f]; for (int i = 0; i < folder.UnpackedStreamInfo.Length; i++) { - bw.Write(Util.uinttobytes(folder.UnpackedStreamInfo[i].Crc)); + bw.Write(Util.UIntToBytes(folder.UnpackedStreamInfo[i].Crc)); } } diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/Header.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/Header.cs index f2bf21a8..35dc24e1 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/Header.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/Header.cs @@ -1,8 +1,7 @@ using System; using System.IO; using System.Text; -using Compress.SevenZip.Compress.LZMA; -using Compress.Utils; +using Compress.Support.Compression.LZMA; namespace Compress.SevenZip.Structure { @@ -50,56 +49,54 @@ namespace Compress.SevenZip.Structure { header = null; - using (BinaryReader br = new BinaryReader(stream, Encoding.UTF8, true)) + using BinaryReader br = new(stream, Encoding.UTF8, true); + HeaderProperty hp = (HeaderProperty)br.ReadByte(); + switch (hp) { - HeaderProperty hp = (HeaderProperty)br.ReadByte(); - switch (hp) - { - case HeaderProperty.kEncodedHeader: + case HeaderProperty.kEncodedHeader: + { + StreamsInfo streamsInfo = new(); + streamsInfo.Read(br); + + if (streamsInfo.Folders.Length > 1) { - StreamsInfo streamsInfo = new StreamsInfo(); - streamsInfo.Read(br); - - if (streamsInfo.Folders.Length > 1) - { - return ZipReturn.ZipUnsupportedCompression; - } - - Folder firstFolder = streamsInfo.Folders[0]; - if (firstFolder.Coders.Length > 1) - { - return ZipReturn.ZipUnsupportedCompression; - } - - byte[] method = firstFolder.Coders[0].Method; - if (!((method.Length == 3) && (method[0] == 3) && (method[1] == 1) && (method[2] == 1))) // LZMA - { - return ZipReturn.ZipUnsupportedCompression; - } - - stream.Seek(baseOffset + (long)streamsInfo.PackPosition, SeekOrigin.Begin); - using (LzmaStream decoder = new LzmaStream(firstFolder.Coders[0].Properties, stream)) - { - ZipReturn zr = ReadHeaderOrPackedHeader(decoder, baseOffset, out header); - if (zr != ZipReturn.ZipGood) - { - return zr; - } - } - - return ZipReturn.ZipGood; + return ZipReturn.ZipUnsupportedCompression; } - case HeaderProperty.kHeader: + Folder firstFolder = streamsInfo.Folders[0]; + if (firstFolder.Coders.Length > 1) { - header = new Header(); - header.Read(br); - return ZipReturn.ZipGood; + return ZipReturn.ZipUnsupportedCompression; } - } - return ZipReturn.ZipCentralDirError; + byte[] method = firstFolder.Coders[0].Method; + if (!((method.Length == 3) && (method[0] == 3) && (method[1] == 1) && (method[2] == 1))) // LZMA + { + return ZipReturn.ZipUnsupportedCompression; + } + + stream.Seek(baseOffset + (long)streamsInfo.PackPosition, SeekOrigin.Begin); + using (LzmaStream decoder = new(firstFolder.Coders[0].Properties, stream)) + { + ZipReturn zr = ReadHeaderOrPackedHeader(decoder, baseOffset, out header); + if (zr != ZipReturn.ZipGood) + { + return zr; + } + } + + return ZipReturn.ZipGood; + } + + case HeaderProperty.kHeader: + { + header = new Header(); + header.Read(br); + return ZipReturn.ZipGood; + } } + + return ZipReturn.ZipCentralDirError; } public void Report(ref StringBuilder sb) diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/PackedStreamInfo.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/PackedStreamInfo.cs index 66d6bb03..68bee870 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/PackedStreamInfo.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/PackedStreamInfo.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Text; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress.SevenZip.Structure { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/SignatureHeader.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/SignatureHeader.cs index 1ca52a86..1a4586a6 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/SignatureHeader.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/SignatureHeader.cs @@ -1,14 +1,12 @@ using System.IO; using System.Text; +using Compress.Support.Utils; namespace Compress.SevenZip.Structure { internal class SignatureHeader { - private static readonly byte[] Signature = {(byte) '7', (byte) 'z', 0xBC, 0xAF, 0x27, 0x1C}; - - private byte _major; - private byte _minor; + private static readonly byte[] Signature = { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C }; private uint _startHeaderCRC; @@ -22,34 +20,32 @@ namespace Compress.SevenZip.Structure public bool Read(Stream stream) { - using (BinaryReader br = new BinaryReader(stream, Encoding.UTF8, true)) + using BinaryReader br = new(stream, Encoding.UTF8, true); + byte[] signatureBytes = br.ReadBytes(6); + if (!signatureBytes.Compare(Signature)) { - byte[] signatureBytes = br.ReadBytes(6); - if (!signatureBytes.Compare(Signature)) - { - return false; - } - - _major = br.ReadByte(); - _minor = br.ReadByte(); - - _startHeaderCRC = br.ReadUInt32(); - - long pos = br.BaseStream.Position; - byte[] mainHeader = new byte[8 + 8 + 4]; - br.BaseStream.Read(mainHeader, 0, mainHeader.Length); - if (!Utils.CRC.VerifyDigest(_startHeaderCRC, mainHeader, 0, (uint) mainHeader.Length)) - { - return false; - } - - br.BaseStream.Seek(pos, SeekOrigin.Begin); - - NextHeaderOffset = br.ReadUInt64(); - NextHeaderSize = br.ReadUInt64(); - NextHeaderCRC = br.ReadUInt32(); - return true; + return false; } + + br.ReadByte(); // major version + br.ReadByte(); // minor version + + _startHeaderCRC = br.ReadUInt32(); + + long pos = br.BaseStream.Position; + byte[] mainHeader = new byte[8 + 8 + 4]; + br.BaseStream.Read(mainHeader, 0, mainHeader.Length); + if (!CRC.VerifyDigest(_startHeaderCRC, mainHeader, 0, (uint)mainHeader.Length)) + { + return false; + } + + br.BaseStream.Seek(pos, SeekOrigin.Begin); + + NextHeaderOffset = br.ReadUInt64(); + NextHeaderSize = br.ReadUInt64(); + NextHeaderCRC = br.ReadUInt32(); + return true; } public void Write(BinaryWriter bw) @@ -61,18 +57,18 @@ namespace Compress.SevenZip.Structure //ArchiveVersion //{ - bw.Write((byte) 0); // BYTE Major - bw.Write((byte) 3); // BYTE Minor + bw.Write((byte)0); // BYTE Major + bw.Write((byte)3); // BYTE Minor //}; _crcOffset = bw.BaseStream.Position; - bw.Write((uint) 0); //HeaderCRC + bw.Write((uint)0); //HeaderCRC //StartHeader //{ - bw.Write((ulong) 0); //NextHeaderOffset - bw.Write((ulong) 0); //NextHeaderSize - bw.Write((uint) 0); //NextHeaderCRC + bw.Write((ulong)0); //NextHeaderOffset + bw.Write((ulong)0); //NextHeaderSize + bw.Write((uint)0); //NextHeaderCRC //} BaseOffset = bw.BaseStream.Position; @@ -84,21 +80,19 @@ namespace Compress.SevenZip.Structure byte[] sigHeaderBytes; - using (MemoryStream sigHeaderMem = new MemoryStream()) + using (MemoryStream sigHeaderMem = new()) { - using (BinaryWriter sigHeaderBw = new BinaryWriter(sigHeaderMem,Encoding.UTF8,true)) - { - sigHeaderBw.Write((ulong) ((long) headerpos - BaseOffset)); //NextHeaderOffset - sigHeaderBw.Write(headerLength); //NextHeaderSize - sigHeaderBw.Write(headerCRC); //NextHeaderCRC + using BinaryWriter sigHeaderBw = new(sigHeaderMem, Encoding.UTF8, true); + sigHeaderBw.Write((ulong)((long)headerpos - BaseOffset)); //NextHeaderOffset + sigHeaderBw.Write(headerLength); //NextHeaderSize + sigHeaderBw.Write(headerCRC); //NextHeaderCRC - sigHeaderBytes = new byte[sigHeaderMem.Length]; - sigHeaderMem.Position = 0; - sigHeaderMem.Read(sigHeaderBytes, 0, sigHeaderBytes.Length); - } + sigHeaderBytes = new byte[sigHeaderMem.Length]; + sigHeaderMem.Position = 0; + sigHeaderMem.Read(sigHeaderBytes, 0, sigHeaderBytes.Length); } - uint sigHeaderCRC = Utils.CRC.CalculateDigest(sigHeaderBytes, 0, (uint) sigHeaderBytes.Length); + uint sigHeaderCRC = CRC.CalculateDigest(sigHeaderBytes, 0, (uint)sigHeaderBytes.Length); bw.BaseStream.Position = _crcOffset; bw.Write(sigHeaderCRC); //Header CRC diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/StreamsInfo.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/StreamsInfo.cs index d440dc97..b9196d03 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/StreamsInfo.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/StreamsInfo.cs @@ -1,8 +1,6 @@ using System; using System.IO; -using System.Security.Permissions; using System.Text; -using Compress.Utils; namespace Compress.SevenZip.Structure { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Structure/UnpackedStreamInfo.cs b/SabreTools.FileTypes/Compress/SevenZip/Structure/UnpackedStreamInfo.cs index 9eceb3c7..d2394926 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Structure/UnpackedStreamInfo.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Structure/UnpackedStreamInfo.cs @@ -1,5 +1,5 @@ using System.Text; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress.SevenZip.Structure { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Util.cs b/SabreTools.FileTypes/Compress/SevenZip/Util.cs index a4c748d1..8f8491f1 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Util.cs +++ b/SabreTools.FileTypes/Compress/SevenZip/Util.cs @@ -47,7 +47,7 @@ namespace Compress.SevenZip { public static readonly Encoding Enc = Encoding.GetEncoding(28591); - public static void memset(byte[] buffer, int start, byte val, int len) + public static void MemSet(byte[] buffer, int start, byte val, int len) { for (int i = 0; i < len; i++) { @@ -55,7 +55,7 @@ namespace Compress.SevenZip } } - public static void memcpyr(byte[] destBuffer, int destPoint, byte[] sourceBuffer, int sourcePoint, int len) + public static void MemCrypt(byte[] destBuffer, int destPoint, byte[] sourceBuffer, int sourcePoint, int len) { for (int i = len - 1; i >= 0; i--) { @@ -64,7 +64,7 @@ namespace Compress.SevenZip } - public static bool memcmp(byte[] buffer1, int offset, byte[] buffer2, int len) + public static bool MemCmp(byte[] buffer1, int offset, byte[] buffer2, int len) { for (int i = 0; i < len; i++) { @@ -147,7 +147,7 @@ namespace Compress.SevenZip public static string ReadName(this BinaryReader br) { - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new(); for (; ; ) { char c = (char)br.ReadUInt16(); @@ -334,7 +334,7 @@ namespace Compress.SevenZip } } - public static byte[] uinttobytes(uint? crc) + public static byte[] UIntToBytes(uint? crc) { if (crc == null) { @@ -350,7 +350,7 @@ namespace Compress.SevenZip return b; } - public static uint? bytestouint(byte[] crc) + public static uint? BytesToUint(byte[] crc) { if (crc == null) { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/BZip2Constants.cs b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/BZip2Constants.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/BZip2Constants.cs rename to SabreTools.FileTypes/Compress/Support/Compression/BZip2/BZip2Constants.cs index c945d571..b8460e5f 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/BZip2Constants.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/BZip2Constants.cs @@ -20,7 +20,7 @@ * great code. */ -namespace Compress.SevenZip.Compress.BZip2 +namespace Compress.Support.Compression.BZip2 { /** * Base class for both the compress and decompress classes. diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CBZip2InputStream.cs b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/CBZip2InputStream.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CBZip2InputStream.cs rename to SabreTools.FileTypes/Compress/Support/Compression/BZip2/CBZip2InputStream.cs index da697686..003cbcd6 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CBZip2InputStream.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/CBZip2InputStream.cs @@ -23,7 +23,7 @@ using System.IO; * great code. */ -namespace Compress.SevenZip.Compress.BZip2 +namespace Compress.Support.Compression.BZip2 { /** * An input stream that decompresses from the BZip2 format (with the file @@ -119,7 +119,6 @@ namespace Compress.SevenZip.Compress.BZip2 private int[] minLens = new int[BZip2Constants.N_GROUPS]; private Stream bsStream; - private bool leaveOpen; private bool streamEnd = false; @@ -342,19 +341,7 @@ namespace Compress.SevenZip.Compress.BZip2 private void BsFinishedWithStream() { - try - { - if (this.bsStream != null) - { - if (!leaveOpen) - this.bsStream.Dispose(); - this.bsStream = null; - } - } - catch - { - //ignore - } + bsStream = null; } private void BsSetStream(Stream f) diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CBZip2OutputStream.cs b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/CBZip2OutputStream.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CBZip2OutputStream.cs rename to SabreTools.FileTypes/Compress/Support/Compression/BZip2/CBZip2OutputStream.cs index 24b763d7..29b0f8f4 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CBZip2OutputStream.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/CBZip2OutputStream.cs @@ -22,7 +22,7 @@ * great code. */ -namespace Compress.SevenZip.Compress.BZip2 +namespace Compress.Support.Compression.BZip2 { /** * An output stream that compresses into the BZip2 format (with the file diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CRC.cs b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/CRC.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CRC.cs rename to SabreTools.FileTypes/Compress/Support/Compression/BZip2/CRC.cs index e6457051..5ea30128 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/BZip2/CRC.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/BZip2/CRC.cs @@ -21,7 +21,7 @@ * great code. */ -namespace Compress.SevenZip.Compress.BZip2 +namespace Compress.Support.Compression.BZip2 { /** * A simple class the hold and calculate the CRC for sanity checking diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Deflate.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Deflate.cs similarity index 98% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/Deflate.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/Deflate.cs index 83830228..2b3b05e1 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Deflate.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Deflate.cs @@ -69,7 +69,7 @@ using System; -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { internal enum BlockState @@ -545,10 +545,10 @@ namespace Compress.ZipFile.ZLib internal void send_tree(short[] tree, int max_code) { int n; // iterates over all tree elements - int prevlen = -1; // last emitted length + int prevlen = -1; // last emitted length int curlen; // length of current code - int nextlen = tree[0 * 2 + 1]; // length of next code - int count = 0; // repeat count of the current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code int max_count = 7; // max repeat count int min_count = 4; // min repeat count @@ -731,23 +731,23 @@ namespace Compress.ZipFile.ZLib * * * ************************************************************* */ - if (false) //CompSettings + //CompSettings + /* + if ((last_lit & 0x1fff) == 0 && (int)compressionLevel > 2) { - if ((last_lit & 0x1fff) == 0 && (int)compressionLevel > 2) + // Compute an upper bound for the compressed length + int out_length = last_lit << 3; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < InternalConstants.D_CODES; dcode++) { - // Compute an upper bound for the compressed length - int out_length = last_lit << 3; - int in_length = strstart - block_start; - int dcode; - for (dcode = 0; dcode < InternalConstants.D_CODES; dcode++) - { - out_length = (int)(out_length + (int)dyn_dtree[dcode * 2] * (5L + Tree.ExtraDistanceBits[dcode])); - } - out_length >>= 3; - if ((matches < (last_lit / 2)) && out_length < in_length / 2) - return true; + out_length = (int)(out_length + (int)dyn_dtree[dcode * 2] * (5L + Tree.ExtraDistanceBits[dcode])); } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) + return true; } + */ return (last_lit == lit_bufsize - 1) || (last_lit == lit_bufsize); // dinoch - wraparound? diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/InfTree.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/InfTree.cs similarity index 95% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/InfTree.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/InfTree.cs index 587f9c10..611afed6 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/InfTree.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/InfTree.cs @@ -62,7 +62,7 @@ using System; -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { sealed class InfTree @@ -83,25 +83,19 @@ namespace Compress.ZipFile.ZLib internal const int fixed_bl = 9; internal const int fixed_bd = 5; - //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" internal static readonly int[] fixed_tl = new int[]{96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255}; - //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" internal static readonly int[] fixed_td = new int[]{80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577}; // Tables for deflate from PKZIP's appnote.txt. - //UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" internal static readonly int[] cplens = new int[]{3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; // see note #13 above about 258 - //UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" internal static readonly int[] cplext = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; - //UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" internal static readonly int[] cpdist = new int[]{1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; - //UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" internal static readonly int[] cpdext = new int[]{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; // If BMAX needs to be larger than 16, then h and x[] should be uLong. diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Inflate.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Inflate.cs similarity index 99% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/Inflate.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/Inflate.cs index 37c62e21..db6d6a51 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Inflate.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Inflate.cs @@ -64,7 +64,7 @@ using System; -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { sealed class InflateBlocks { diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Tree.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Tree.cs similarity index 99% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/Tree.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/Tree.cs index 98441bd7..0475081e 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Tree.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Tree.cs @@ -61,7 +61,7 @@ // ----------------------------------------------------------------------- -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { sealed class Tree { diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Zlib.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Zlib.cs similarity index 99% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/Zlib.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/Zlib.cs index 368a0455..ab0685b5 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/Zlib.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/Zlib.cs @@ -89,7 +89,7 @@ using System.Runtime.InteropServices; -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { /// diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibBaseStream.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibBaseStream.cs similarity index 99% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibBaseStream.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibBaseStream.cs index bb48fcb6..8874f8e9 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibBaseStream.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibBaseStream.cs @@ -25,9 +25,9 @@ // ------------------------------------------------------------------ using System; -using System.IO; +using Compress.Support.Utils; -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { public enum ZlibStreamFlavor { ZLIB = 1950, DEFLATE = 1951, GZIP = 1952 } @@ -50,7 +50,7 @@ namespace Compress.ZipFile.ZLib protected internal CompressionStrategy Strategy = CompressionStrategy.Default; // workitem 7159 - Compress.Utils.CRC crc; + CRC crc; protected internal string _GzipFileName; protected internal string _GzipComment; protected internal DateTime _GzipMtime; @@ -75,7 +75,7 @@ namespace Compress.ZipFile.ZLib // workitem 7159 if (flavor == ZlibStreamFlavor.GZIP) { - this.crc = new Compress.Utils.CRC(); + this.crc = new CRC(); } } diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibCodec.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibCodec.cs similarity index 99% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibCodec.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibCodec.cs index 42bf00f7..3b9ed3af 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibCodec.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibCodec.cs @@ -68,7 +68,7 @@ using System; using System.Runtime.InteropServices; using Interop=System.Runtime.InteropServices; -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { /// /// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951). diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibConstants.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibConstants.cs similarity index 99% rename from SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibConstants.cs rename to SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibConstants.cs index 332684aa..38000961 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZLib/ZlibConstants.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate/ZlibConstants.cs @@ -61,7 +61,7 @@ // ----------------------------------------------------------------------- -namespace Compress.ZipFile.ZLib +namespace Compress.Support.Compression.Deflate { /// /// A bunch of constants used in the Zlib interface. diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/BlockType.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/BlockType.cs new file mode 100644 index 00000000..6230e9fe --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/BlockType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Compress.Support.Compression.Deflate64 +{ + internal enum BlockType + { + Uncompressed = 0, + Static = 1, + Dynamic = 2 + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/Deflate64Stream.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/Deflate64Stream.cs new file mode 100644 index 00000000..c53ce9c5 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/Deflate64Stream.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Runtime.CompilerServices; + +namespace Compress.Support.Compression.Deflate64 +{ + public enum ZipCompressionMethod + { + Deflate64, + Deflate + } + + public sealed class Deflate64Stream : Stream + { + private const int DEFAULT_BUFFER_SIZE = 8192; + + private Stream _stream; + private CompressionMode _mode; + private InflaterManaged _inflater; + private byte[] _buffer; + + public Deflate64Stream(Stream stream, CompressionMode mode) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (mode != CompressionMode.Decompress) + { + throw new NotImplementedException("Deflate64: this implementation only supports decompression"); + } + + if (!stream.CanRead) + { + throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream)); + } + + InitializeInflater(stream, ZipCompressionMethod.Deflate64); + } + + /// + /// Sets up this DeflateManagedStream to be used for Inflation/Decompression + /// + private void InitializeInflater(Stream stream, ZipCompressionMethod method = ZipCompressionMethod.Deflate) + { + Debug.Assert(stream != null); + Debug.Assert(method == ZipCompressionMethod.Deflate || method == ZipCompressionMethod.Deflate64); + if (!stream.CanRead) + { + throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream)); + } + + _inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64); + + _stream = stream; + _mode = CompressionMode.Decompress; + _buffer = new byte[DEFAULT_BUFFER_SIZE]; + } + + public override bool CanRead + { + get + { + if (_stream is null) + { + return false; + } + + return (_mode == CompressionMode.Decompress && _stream.CanRead); + } + } + + public override bool CanWrite + { + get + { + if (_stream is null) + { + return false; + } + + return (_mode == CompressionMode.Compress && _stream.CanWrite); + } + } + + public override bool CanSeek => false; + + public override long Length + { + get { throw new NotSupportedException("Deflate64: not supported"); } + } + + public override long Position + { + get { throw new NotSupportedException("Deflate64: not supported"); } + set { throw new NotSupportedException("Deflate64: not supported"); } + } + + public override void Flush() + { + EnsureNotDisposed(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Deflate64: not supported"); + } + + public override void SetLength(long value) + { + throw new NotSupportedException("Deflate64: not supported"); + } + + public override int Read(byte[] array, int offset, int count) + { + EnsureDecompressionMode(); + ValidateParameters(array, offset, count); + EnsureNotDisposed(); + + int bytesRead; + int currentOffset = offset; + int remainingCount = count; + + while (true) + { + bytesRead = _inflater.Inflate(array, currentOffset, remainingCount); + currentOffset += bytesRead; + remainingCount -= bytesRead; + + if (remainingCount == 0) + { + break; + } + + if (_inflater.Finished()) + { + // if we finished decompressing, we can't have anything left in the outputwindow. + Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!"); + break; + } + + int bytes = _stream.Read(_buffer, 0, _buffer.Length); + if (bytes <= 0) + { + break; + } + else if (bytes > _buffer.Length) + { + // The stream is either malicious or poorly implemented and returned a number of + // bytes larger than the buffer supplied to it. + throw new InvalidDataException("Deflate64: invalid data"); + } + + _inflater.SetInput(_buffer, 0, bytes); + } + + return count - remainingCount; + } + + private void ValidateParameters(byte[] array, int offset, int count) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (array.Length - offset < count) + { + throw new ArgumentException("Deflate64: invalid offset/count combination"); + } + } + + private void EnsureNotDisposed() + { + if (_stream is null) + { + ThrowStreamClosedException(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowStreamClosedException() + { + throw new ObjectDisposedException(null, "Deflate64: stream has been disposed"); + } + + private void EnsureDecompressionMode() + { + if (_mode != CompressionMode.Decompress) + { + ThrowCannotReadFromDeflateManagedStreamException(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowCannotReadFromDeflateManagedStreamException() + { + throw new InvalidOperationException("Deflate64: cannot read from this stream"); + } + + private void EnsureCompressionMode() + { + if (_mode != CompressionMode.Compress) + { + ThrowCannotWriteToDeflateManagedStreamException(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowCannotWriteToDeflateManagedStreamException() + { + throw new InvalidOperationException("Deflate64: cannot write to this stream"); + } + + public override void Write(byte[] array, int offset, int count) + { + ThrowCannotWriteToDeflateManagedStreamException(); + } + + // This is called by Dispose: + private void PurgeBuffers(bool disposing) + { + if (!disposing) + { + return; + } + + if (_stream is null) + { + return; + } + + Flush(); + } + + protected override void Dispose(bool disposing) + { + try + { + PurgeBuffers(disposing); + } + finally + { + // Close the underlying stream even if PurgeBuffers threw. + // Stream.Close() may throw here (may or may not be due to the same error). + // In this case, we still need to clean up internal resources, hence the inner finally blocks. + try + { + if (disposing) + { + //_stream?.Dispose(); + } + } + finally + { + _stream = null; + + try + { + _inflater?.Dispose(); + } + finally + { + _inflater = null; + base.Dispose(disposing); + } + } + } + } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/DeflateInput.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/DeflateInput.cs new file mode 100644 index 00000000..14dcd0bf --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/DeflateInput.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Compress.Support.Compression.Deflate64 +{ + internal sealed class DeflateInput + { + public DeflateInput(byte[] buffer) + { + Buffer = buffer; + } + + public byte[] Buffer { get; } + public int Count { get; set; } + public int StartIndex { get; set; } + + internal void ConsumeBytes(int n) + { + Debug.Assert(n <= Count, "Should use more bytes than what we have in the buffer"); + StartIndex += n; + Count -= n; + Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!"); + } + + internal InputState DumpState() => new InputState(Count, StartIndex); + + internal void RestoreState(InputState state) + { + Count = state._count; + StartIndex = state._startIndex; + } + + internal /*readonly */struct InputState + { + internal readonly int _count; + internal readonly int _startIndex; + + internal InputState(int count, int startIndex) + { + _count = count; + _startIndex = startIndex; + } + } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/FastEncoderStatus.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/FastEncoderStatus.cs new file mode 100644 index 00000000..f7cd81c7 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/FastEncoderStatus.cs @@ -0,0 +1,249 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Compress.Support.Compression.Deflate64 +{ + internal static class FastEncoderStatics + { + // static information for encoding, DO NOT MODIFY + + internal static readonly byte[] FAST_ENCODER_TREE_STRUCTURE_DATA = + { + 0xec,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca, + 0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60, + 0x13,0x24,0xd8,0x90,0x40,0x10,0xec,0xc1,0x88,0xcd,0xe6,0x92, + 0xec,0x1d,0x69,0x47,0x23,0x29,0xab,0x2a,0x81,0xca,0x65,0x56, + 0x65,0x5d,0x66,0x16,0x40,0xcc,0xed,0x9d,0xbc,0xf7,0xde,0x7b, + 0xef,0xbd,0xf7,0xde,0x7b,0xef,0xbd,0xf7,0xba,0x3b,0x9d,0x4e, + 0x27,0xf7,0xdf,0xff,0x3f,0x5c,0x66,0x64,0x01,0x6c,0xf6,0xce, + 0x4a,0xda,0xc9,0x9e,0x21,0x80,0xaa,0xc8,0x1f,0x3f,0x7e,0x7c, + 0x1f,0x3f + }; + + internal static readonly byte[] B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA = + { + 0xed,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca, + 0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60, + 0x13,0x24,0xd8,0x90,0x40,0x10,0xec,0xc1,0x88,0xcd,0xe6,0x92, + 0xec,0x1d,0x69,0x47,0x23,0x29,0xab,0x2a,0x81,0xca,0x65,0x56, + 0x65,0x5d,0x66,0x16,0x40,0xcc,0xed,0x9d,0xbc,0xf7,0xde,0x7b, + 0xef,0xbd,0xf7,0xde,0x7b,0xef,0xbd,0xf7,0xba,0x3b,0x9d,0x4e, + 0x27,0xf7,0xdf,0xff,0x3f,0x5c,0x66,0x64,0x01,0x6c,0xf6,0xce, + 0x4a,0xda,0xc9,0x9e,0x21,0x80,0xaa,0xc8,0x1f,0x3f,0x7e,0x7c, + 0x1f,0x3f + }; + + // Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos + // + // Optimisation: unlike the other encoders, here we have an array of codes for each currentMatch + // length (not just each currentMatch length slot), complete with all the extra bits filled in, in + // a single array element. + // + // There are many advantages to doing this: + // + // 1. A single array lookup on g_FastEncoderLiteralCodeInfo, instead of separate array lookups + // on g_LengthLookup (to get the length slot), g_FastEncoderLiteralTreeLength, + // g_FastEncoderLiteralTreeCode, g_ExtraLengthBits, and g_BitMask + // + // 2. The array is an array of ULONGs, so no access penalty, unlike for accessing those USHORT + // code arrays in the other encoders (although they could be made into ULONGs with some + // modifications to the source). + // + // Note, if we could guarantee that codeLen <= 16 always, then we could skip an if statement here. + // + // A completely different optimisation is used for the distance codes since, obviously, a table for + // all 8192 distances combining their extra bits is not feasible. The distance codeinfo table is + // made up of code[], len[] and # extraBits for this code. + // + // The advantages are similar to the above; a ULONG array instead of a USHORT and BYTE array, better + // cache locality, fewer memory operations. + // + + + // Encoding information for literal and Length. + // The least 5 significant bits are the length + // and the rest is the code bits. + + internal static readonly uint[] FAST_ENCODER_LITERAL_CODE_INFO = + { + 0x0000d7ee,0x0004d7ee,0x0002d7ee,0x0006d7ee,0x0001d7ee,0x0005d7ee,0x0003d7ee, + 0x0007d7ee,0x000037ee,0x0000c7ec,0x00000126,0x000437ee,0x000237ee,0x000637ee, + 0x000137ee,0x000537ee,0x000337ee,0x000737ee,0x0000b7ee,0x0004b7ee,0x0002b7ee, + 0x0006b7ee,0x0001b7ee,0x0005b7ee,0x0003b7ee,0x0007b7ee,0x000077ee,0x000477ee, + 0x000277ee,0x000677ee,0x000017ed,0x000177ee,0x00000526,0x000577ee,0x000023ea, + 0x0001c7ec,0x000377ee,0x000777ee,0x000217ed,0x000063ea,0x00000b68,0x00000ee9, + 0x00005beb,0x000013ea,0x00000467,0x00001b68,0x00000c67,0x00002ee9,0x00000768, + 0x00001768,0x00000f68,0x00001ee9,0x00001f68,0x00003ee9,0x000053ea,0x000001e9, + 0x000000e8,0x000021e9,0x000011e9,0x000010e8,0x000031e9,0x000033ea,0x000008e8, + 0x0000f7ee,0x0004f7ee,0x000018e8,0x000009e9,0x000004e8,0x000029e9,0x000014e8, + 0x000019e9,0x000073ea,0x0000dbeb,0x00000ce8,0x00003beb,0x0002f7ee,0x000039e9, + 0x00000bea,0x000005e9,0x00004bea,0x000025e9,0x000027ec,0x000015e9,0x000035e9, + 0x00000de9,0x00002bea,0x000127ec,0x0000bbeb,0x0006f7ee,0x0001f7ee,0x0000a7ec, + 0x00007beb,0x0005f7ee,0x0000fbeb,0x0003f7ee,0x0007f7ee,0x00000fee,0x00000326, + 0x00000267,0x00000a67,0x00000667,0x00000726,0x00001ce8,0x000002e8,0x00000e67, + 0x000000a6,0x0001a7ec,0x00002de9,0x000004a6,0x00000167,0x00000967,0x000002a6, + 0x00000567,0x000117ed,0x000006a6,0x000001a6,0x000005a6,0x00000d67,0x000012e8, + 0x00000ae8,0x00001de9,0x00001ae8,0x000007eb,0x000317ed,0x000067ec,0x000097ed, + 0x000297ed,0x00040fee,0x00020fee,0x00060fee,0x00010fee,0x00050fee,0x00030fee, + 0x00070fee,0x00008fee,0x00048fee,0x00028fee,0x00068fee,0x00018fee,0x00058fee, + 0x00038fee,0x00078fee,0x00004fee,0x00044fee,0x00024fee,0x00064fee,0x00014fee, + 0x00054fee,0x00034fee,0x00074fee,0x0000cfee,0x0004cfee,0x0002cfee,0x0006cfee, + 0x0001cfee,0x0005cfee,0x0003cfee,0x0007cfee,0x00002fee,0x00042fee,0x00022fee, + 0x00062fee,0x00012fee,0x00052fee,0x00032fee,0x00072fee,0x0000afee,0x0004afee, + 0x0002afee,0x0006afee,0x0001afee,0x0005afee,0x0003afee,0x0007afee,0x00006fee, + 0x00046fee,0x00026fee,0x00066fee,0x00016fee,0x00056fee,0x00036fee,0x00076fee, + 0x0000efee,0x0004efee,0x0002efee,0x0006efee,0x0001efee,0x0005efee,0x0003efee, + 0x0007efee,0x00001fee,0x00041fee,0x00021fee,0x00061fee,0x00011fee,0x00051fee, + 0x00031fee,0x00071fee,0x00009fee,0x00049fee,0x00029fee,0x00069fee,0x00019fee, + 0x00059fee,0x00039fee,0x00079fee,0x00005fee,0x00045fee,0x00025fee,0x00065fee, + 0x00015fee,0x00055fee,0x00035fee,0x00075fee,0x0000dfee,0x0004dfee,0x0002dfee, + 0x0006dfee,0x0001dfee,0x0005dfee,0x0003dfee,0x0007dfee,0x00003fee,0x00043fee, + 0x00023fee,0x00063fee,0x00013fee,0x00053fee,0x00033fee,0x00073fee,0x0000bfee, + 0x0004bfee,0x0002bfee,0x0006bfee,0x0001bfee,0x0005bfee,0x0003bfee,0x0007bfee, + 0x00007fee,0x00047fee,0x00027fee,0x00067fee,0x00017fee,0x000197ed,0x000397ed, + 0x000057ed,0x00057fee,0x000257ed,0x00037fee,0x000157ed,0x00077fee,0x000357ed, + 0x0000ffee,0x0004ffee,0x0002ffee,0x0006ffee,0x0001ffee,0x00000084,0x00000003, + 0x00000184,0x00000044,0x00000144,0x000000c5,0x000002c5,0x000001c5,0x000003c6, + 0x000007c6,0x00000026,0x00000426,0x000003a7,0x00000ba7,0x000007a7,0x00000fa7, + 0x00000227,0x00000627,0x00000a27,0x00000e27,0x00000068,0x00000868,0x00001068, + 0x00001868,0x00000369,0x00001369,0x00002369,0x00003369,0x000006ea,0x000026ea, + 0x000046ea,0x000066ea,0x000016eb,0x000036eb,0x000056eb,0x000076eb,0x000096eb, + 0x0000b6eb,0x0000d6eb,0x0000f6eb,0x00003dec,0x00007dec,0x0000bdec,0x0000fdec, + 0x00013dec,0x00017dec,0x0001bdec,0x0001fdec,0x00006bed,0x0000ebed,0x00016bed, + 0x0001ebed,0x00026bed,0x0002ebed,0x00036bed,0x0003ebed,0x000003ec,0x000043ec, + 0x000083ec,0x0000c3ec,0x000103ec,0x000143ec,0x000183ec,0x0001c3ec,0x00001bee, + 0x00009bee,0x00011bee,0x00019bee,0x00021bee,0x00029bee,0x00031bee,0x00039bee, + 0x00041bee,0x00049bee,0x00051bee,0x00059bee,0x00061bee,0x00069bee,0x00071bee, + 0x00079bee,0x000167f0,0x000367f0,0x000567f0,0x000767f0,0x000967f0,0x000b67f0, + 0x000d67f0,0x000f67f0,0x001167f0,0x001367f0,0x001567f0,0x001767f0,0x001967f0, + 0x001b67f0,0x001d67f0,0x001f67f0,0x000087ef,0x000187ef,0x000287ef,0x000387ef, + 0x000487ef,0x000587ef,0x000687ef,0x000787ef,0x000887ef,0x000987ef,0x000a87ef, + 0x000b87ef,0x000c87ef,0x000d87ef,0x000e87ef,0x000f87ef,0x0000e7f0,0x0002e7f0, + 0x0004e7f0,0x0006e7f0,0x0008e7f0,0x000ae7f0,0x000ce7f0,0x000ee7f0,0x0010e7f0, + 0x0012e7f0,0x0014e7f0,0x0016e7f0,0x0018e7f0,0x001ae7f0,0x001ce7f0,0x001ee7f0, + 0x0005fff3,0x000dfff3,0x0015fff3,0x001dfff3,0x0025fff3,0x002dfff3,0x0035fff3, + 0x003dfff3,0x0045fff3,0x004dfff3,0x0055fff3,0x005dfff3,0x0065fff3,0x006dfff3, + 0x0075fff3,0x007dfff3,0x0085fff3,0x008dfff3,0x0095fff3,0x009dfff3,0x00a5fff3, + 0x00adfff3,0x00b5fff3,0x00bdfff3,0x00c5fff3,0x00cdfff3,0x00d5fff3,0x00ddfff3, + 0x00e5fff3,0x00edfff3,0x00f5fff3,0x00fdfff3,0x0003fff3,0x000bfff3,0x0013fff3, + 0x001bfff3,0x0023fff3,0x002bfff3,0x0033fff3,0x003bfff3,0x0043fff3,0x004bfff3, + 0x0053fff3,0x005bfff3,0x0063fff3,0x006bfff3,0x0073fff3,0x007bfff3,0x0083fff3, + 0x008bfff3,0x0093fff3,0x009bfff3,0x00a3fff3,0x00abfff3,0x00b3fff3,0x00bbfff3, + 0x00c3fff3,0x00cbfff3,0x00d3fff3,0x00dbfff3,0x00e3fff3,0x00ebfff3,0x00f3fff3, + 0x00fbfff3,0x0007fff3,0x000ffff3,0x0017fff3,0x001ffff3,0x0027fff3,0x002ffff3, + 0x0037fff3,0x003ffff3,0x0047fff3,0x004ffff3,0x0057fff3,0x005ffff3,0x0067fff3, + 0x006ffff3,0x0077fff3,0x007ffff3,0x0087fff3,0x008ffff3,0x0097fff3,0x009ffff3, + 0x00a7fff3,0x00affff3,0x00b7fff3,0x00bffff3,0x00c7fff3,0x00cffff3,0x00d7fff3, + 0x00dffff3,0x00e7fff3,0x00effff3,0x00f7fff3,0x00fffff3,0x0001e7f1,0x0003e7f1, + 0x0005e7f1,0x0007e7f1,0x0009e7f1,0x000be7f1,0x000de7f1,0x000fe7f1,0x0011e7f1, + 0x0013e7f1,0x0015e7f1,0x0017e7f1,0x0019e7f1,0x001be7f1,0x001de7f1,0x001fe7f1, + 0x0021e7f1,0x0023e7f1,0x0025e7f1,0x0027e7f1,0x0029e7f1,0x002be7f1,0x002de7f1, + 0x002fe7f1,0x0031e7f1,0x0033e7f1,0x0035e7f1,0x0037e7f1,0x0039e7f1,0x003be7f1, + 0x003de7f1,0x000047eb + }; + + internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO = + { + 0x00000f06,0x0001ff0a,0x0003ff0b,0x0007ff0b,0x0000ff19,0x00003f18,0x0000bf28, + 0x00007f28,0x00001f37,0x00005f37,0x00000d45,0x00002f46,0x00000054,0x00001d55, + 0x00000864,0x00000365,0x00000474,0x00001375,0x00000c84,0x00000284,0x00000a94, + 0x00000694,0x00000ea4,0x000001a4,0x000009b4,0x00000bb5,0x000005c4,0x00001bc5, + 0x000007d5,0x000017d5,0x00000000,0x00000100 + }; + + internal static readonly uint[] BIT_MASK = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 }; + internal static readonly byte[] EXTRA_LENGTH_BITS = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + internal static readonly byte[] EXTRA_DISTANCE_BITS = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; + internal const int NUM_CHARS = 256; + internal const int NUM_LENGTH_BASE_CODES = 29; + internal const int NUM_DIST_BASE_CODES = 30; + + internal const uint FAST_ENCODER_POST_TREE_BIT_BUF = 0x0022; + internal const int FAST_ENCODER_POST_TREE_BIT_COUNT = 9; + + internal const uint NO_COMPRESSION_HEADER = 0x0; + internal const int NO_COMPRESSION_HEADER_BIT_COUNT = 3; + internal const uint B_FINAL_NO_COMPRESSION_HEADER = 0x1; + internal const int B_FINAL_NO_COMPRESSION_HEADER_BIT_COUNT = 3; + internal const int MAX_CODE_LEN = 16; + + private static readonly byte[] S_DIST_LOOKUP = CreateDistanceLookup(); + + private static byte[] CreateDistanceLookup() + { + byte[] result = new byte[512]; + + // Generate the global slot tables which allow us to convert a distance + // (0..32K) to a distance slot (0..29) + // + // Distance table + // Extra Extra Extra + // Code Bits Dist Code Bits Dist Code Bits Distance + // ---- ---- ---- ---- ---- ------ ---- ---- -------- + // 0 0 1 10 4 33-48 20 9 1025-1536 + // 1 0 2 11 4 49-64 21 9 1537-2048 + // 2 0 3 12 5 65-96 22 10 2049-3072 + // 3 0 4 13 5 97-128 23 10 3073-4096 + // 4 1 5,6 14 6 129-192 24 11 4097-6144 + // 5 1 7,8 15 6 193-256 25 11 6145-8192 + // 6 2 9-12 16 7 257-384 26 12 8193-12288 + // 7 2 13-16 17 7 385-512 27 12 12289-16384 + // 8 3 17-24 18 8 513-768 28 13 16385-24576 + // 9 3 25-32 19 8 769-1024 29 13 24577-32768 + + // Initialize the mapping length (0..255) -> length code (0..28) + //int length = 0; + //for (code = 0; code < FastEncoderStatics.NumLengthBaseCodes-1; code++) { + // for (int n = 0; n < (1 << FastEncoderStatics.ExtraLengthBits[code]); n++) + // lengthLookup[length++] = (byte) code; + //} + //lengthLookup[length-1] = (byte) code; + + // Initialize the mapping dist (0..32K) -> dist code (0..29) + int dist = 0; + int code; + for (code = 0; code < 16; code++) + { + for (int n = 0; n < (1 << EXTRA_DISTANCE_BITS[code]); n++) + { + result[dist++] = (byte)code; + } + } + + dist >>= 7; // from now on, all distances are divided by 128 + + for (; code < NUM_DIST_BASE_CODES; code++) + { + for (int n = 0; n < (1 << (EXTRA_DISTANCE_BITS[code] - 7)); n++) + { + result[256 + dist++] = (byte)code; + } + } + + return result; + } + + // Return the position slot (0...29) of a match offset (0...32767) + internal static int GetSlot(int pos) => + S_DIST_LOOKUP[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))]; + + // Reverse 'length' of the bits in code + public static uint BitReverse(uint code, int length) + { + uint newCode = 0; + + Debug.Assert(length > 0 && length <= 16, "Invalid len"); + do + { + newCode |= (code & 1); + newCode <<= 1; + code >>= 1; + } while (--length > 0); + + return newCode >> 1; + } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/HuffmanTree.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/HuffmanTree.cs new file mode 100644 index 00000000..6a0986fe --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/HuffmanTree.cs @@ -0,0 +1,318 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.IO; + +namespace Compress.Support.Compression.Deflate64 +{ + // Strictly speaking this class is not a HuffmanTree, this class is + // a lookup table combined with a HuffmanTree. The idea is to speed up + // the lookup for short symbols (they should appear more frequently ideally.) + // However we don't want to create a huge table since it might take longer to + // build the table than decoding (Deflate usually generates new tables frequently.) + // + // Jean-loup Gailly and Mark Adler gave a very good explanation about this. + // The full text (algorithm.txt) can be found inside + // ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib.zip. + // + // Following paper explains decoding in details: + // Hirschberg and Lelewer, "Efficient decoding of prefix codes," + // Comm. ACM, 33,4, April 1990, pp. 449-459. + // + + internal sealed class HuffmanTree + { + internal const int MAX_LITERAL_TREE_ELEMENTS = 288; + internal const int MAX_DIST_TREE_ELEMENTS = 32; + internal const int END_OF_BLOCK_CODE = 256; + internal const int NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS = 19; + + private readonly int _tableBits; + private readonly short[] _table; + private readonly short[] _left; + private readonly short[] _right; + private readonly byte[] _codeLengthArray; + + private readonly int _tableMask; + + // huffman tree for static block + public static HuffmanTree StaticLiteralLengthTree { get; } = new HuffmanTree(GetStaticLiteralTreeLength()); + + public static HuffmanTree StaticDistanceTree { get; } = new HuffmanTree(GetStaticDistanceTreeLength()); + + public HuffmanTree(byte[] codeLengths) + { + Debug.Assert( + codeLengths.Length == MAX_LITERAL_TREE_ELEMENTS || + codeLengths.Length == MAX_DIST_TREE_ELEMENTS || + codeLengths.Length == NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS, + "we only expect three kinds of Length here"); + _codeLengthArray = codeLengths; + + if (_codeLengthArray.Length == MAX_LITERAL_TREE_ELEMENTS) + { + // bits for Literal/Length tree table + _tableBits = 9; + } + else + { + // bits for distance tree table and code length tree table + _tableBits = 7; + } + _tableMask = (1 << _tableBits) - 1; + + _table = new short[1 << _tableBits]; + + // I need to find proof that left and right array will always be + // enough. I think they are. + _left = new short[2 * _codeLengthArray.Length]; + _right = new short[2 * _codeLengthArray.Length]; + + CreateTable(); + } + + // Generate the array contains huffman codes lengths for static huffman tree. + // The data is in RFC 1951. + private static byte[] GetStaticLiteralTreeLength() + { + byte[] literalTreeLength = new byte[MAX_LITERAL_TREE_ELEMENTS]; + for (int i = 0; i <= 143; i++) + { + literalTreeLength[i] = 8; + } + + for (int i = 144; i <= 255; i++) + { + literalTreeLength[i] = 9; + } + + for (int i = 256; i <= 279; i++) + { + literalTreeLength[i] = 7; + } + + for (int i = 280; i <= 287; i++) + { + literalTreeLength[i] = 8; + } + + return literalTreeLength; + } + + private static byte[] GetStaticDistanceTreeLength() + { + byte[] staticDistanceTreeLength = new byte[MAX_DIST_TREE_ELEMENTS]; + for (int i = 0; i < MAX_DIST_TREE_ELEMENTS; i++) + { + staticDistanceTreeLength[i] = 5; + } + return staticDistanceTreeLength; + } + + // Calculate the huffman code for each character based on the code length for each character. + // This algorithm is described in standard RFC 1951 + private uint[] CalculateHuffmanCode() + { + uint[] bitLengthCount = new uint[17]; + foreach (int codeLength in _codeLengthArray) + { + bitLengthCount[codeLength]++; + } + bitLengthCount[0] = 0; // clear count for length 0 + + uint[] nextCode = new uint[17]; + uint tempCode = 0; + for (int bits = 1; bits <= 16; bits++) + { + tempCode = (tempCode + bitLengthCount[bits - 1]) << 1; + nextCode[bits] = tempCode; + } + + uint[] code = new uint[MAX_LITERAL_TREE_ELEMENTS]; + for (int i = 0; i < _codeLengthArray.Length; i++) + { + int len = _codeLengthArray[i]; + + if (len > 0) + { + code[i] = FastEncoderStatics.BitReverse(nextCode[len], len); + nextCode[len]++; + } + } + return code; + } + + private void CreateTable() + { + uint[] codeArray = CalculateHuffmanCode(); + + short avail = (short)_codeLengthArray.Length; + + for (int ch = 0; ch < _codeLengthArray.Length; ch++) + { + // length of this code + int len = _codeLengthArray[ch]; + if (len > 0) + { + // start value (bit reversed) + int start = (int)codeArray[ch]; + + if (len <= _tableBits) + { + // If a particular symbol is shorter than nine bits, + // then that symbol's translation is duplicated + // in all those entries that start with that symbol's bits. + // For example, if the symbol is four bits, then it's duplicated + // 32 times in a nine-bit table. If a symbol is nine bits long, + // it appears in the table once. + // + // Make sure that in the loop below, code is always + // less than table_size. + // + // On last iteration we store at array index: + // initial_start_at + (locs-1)*increment + // = initial_start_at + locs*increment - increment + // = initial_start_at + (1 << tableBits) - increment + // = initial_start_at + table_size - increment + // + // Therefore we must ensure: + // initial_start_at + table_size - increment < table_size + // or: initial_start_at < increment + // + int increment = 1 << len; + if (start >= increment) + { + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + // Note the bits in the table are reverted. + int locs = 1 << (_tableBits - len); + for (int j = 0; j < locs; j++) + { + _table[start] = (short)ch; + start += increment; + } + } + else + { + // For any code which has length longer than num_elements, + // build a binary tree. + + int overflowBits = len - _tableBits; // the nodes we need to respent the data. + int codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table) + + // the left, right table is used to repesent the + // the rest bits. When we got the first part (number bits.) and look at + // tbe table, we will need to follow the tree to find the real character. + // This is in place to avoid bloating the table if there are + // a few ones with long code. + int index = start & ((1 << _tableBits) - 1); + short[] array = _table; + + do + { + short value = array[index]; + + if (value == 0) + { + // set up next pointer if this node is not used before. + array[index] = (short)-avail; // use next available slot. + value = (short)-avail; + avail++; + } + + if (value > 0) + { + // prevent an IndexOutOfRangeException from array[index] + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!"); + + if ((start & codeBitMask) == 0) + { + // if current bit is 0, go change the left array + array = _left; + } + else + { + // if current bit is 1, set value in the right array + array = _right; + } + index = -value; // go to next node + + codeBitMask <<= 1; + overflowBits--; + } while (overflowBits != 0); + + array[index] = (short)ch; + } + } + } + } + + // + // This function will try to get enough bits from input and + // try to decode the bits. + // If there are no enought bits in the input, this function will return -1. + // + public int GetNextSymbol(InputBuffer input) + { + // Try to load 16 bits into input buffer if possible and get the bitBuffer value. + // If there aren't 16 bits available we will return all we have in the + // input buffer. + uint bitBuffer = input.TryLoad16Bits(); + if (input.AvailableBits == 0) + { // running out of input. + return -1; + } + + // decode an element + int symbol = _table[bitBuffer & _tableMask]; + if (symbol < 0) + { // this will be the start of the binary tree + // navigate the tree + uint mask = (uint)1 << _tableBits; + do + { + symbol = -symbol; + if ((bitBuffer & mask) == 0) + { + symbol = _left[symbol]; + } + else + { + symbol = _right[symbol]; + } + + mask <<= 1; + } while (symbol < 0); + } + + int codeLength = _codeLengthArray[symbol]; + + // huffman code lengths must be at least 1 bit long + if (codeLength <= 0) + { + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + // + // If this code is longer than the # bits we had in the bit buffer (i.e. + // we read only part of the code), we can hit the entry in the table or the tree + // for another symbol. However the length of another symbol will not match the + // available bits count. + if (codeLength > input.AvailableBits) + { + // We already tried to load 16 bits and maximum length is 15, + // so this means we are running out of input. + return -1; + } + + input.SkipBits(codeLength); + return symbol; + } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InflaterManaged.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InflaterManaged.cs new file mode 100644 index 00000000..57905fa2 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InflaterManaged.cs @@ -0,0 +1,740 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.2.1, November 17th, 2003 +// +// Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// + +using System; +using System.Diagnostics; +using System.IO; + +namespace Compress.Support.Compression.Deflate64 +{ + internal sealed class InflaterManaged + { + // const tables used in decoding: + + // Extra bits for length code 257 - 285. + private static readonly byte[] S_EXTRA_LENGTH_BITS = new byte[] + { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,16 }; + + // The base length for length code 257 - 285. + // The formula to get the real length for a length code is lengthBase[code - 257] + (value stored in extraBits) + private static readonly int[] S_LENGTH_BASE = + { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,3}; + + // The base distance for distance code 0 - 31 + // The real distance for a distance code is distanceBasePosition[code] + (value stored in extraBits) + private static readonly int[] S_DISTANCE_BASE_POSITION = + { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,32769,49153 }; + + // code lengths for code length alphabet is stored in following order + private static readonly byte[] S_CODE_ORDER = new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static readonly byte[] S_STATIC_DISTANCE_TREE_TABLE = new byte[] + { + 0x00,0x10,0x08,0x18,0x04,0x14,0x0c,0x1c,0x02,0x12,0x0a,0x1a, + 0x06,0x16,0x0e,0x1e,0x01,0x11,0x09,0x19,0x05,0x15,0x0d,0x1d, + 0x03,0x13,0x0b,0x1b,0x07,0x17,0x0f,0x1f + }; + + private readonly OutputWindow _output; + private readonly InputBuffer _input; + private HuffmanTree _literalLengthTree; + private HuffmanTree _distanceTree; + + private InflaterState _state; + //private bool _hasFormatReader; + private int _bfinal; + private BlockType _blockType; + + // uncompressed block + private readonly byte[] _blockLengthBuffer = new byte[4]; + private int _blockLength; + + // compressed block + private int _length; + private int _distanceCode; + private int _extraBits; + + private int _loopCounter; + private int _literalLengthCodeCount; + private int _distanceCodeCount; + private int _codeLengthCodeCount; + private int _codeArraySize; + private int _lengthCode; + + private readonly byte[] _codeList; // temporary array to store the code length for literal/Length and distance + private readonly byte[] _codeLengthTreeCodeLength; + private readonly bool _deflate64; + private HuffmanTree _codeLengthTree; + + //private IFileFormatReader _formatReader; // class to decode header and footer (e.g. gzip) + + internal InflaterManaged(/*IFileFormatReader reader, */bool deflate64) + { + _output = new OutputWindow(); + _input = new InputBuffer(); + + _codeList = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS + HuffmanTree.MAX_DIST_TREE_ELEMENTS]; + _codeLengthTreeCodeLength = new byte[HuffmanTree.NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS]; + _deflate64 = deflate64; + //if (reader != null) + //{ + // _formatReader = reader; + // _hasFormatReader = true; + //} + Reset(); + } + + private void Reset() + { + _state = //_hasFormatReader ? + //InflaterState.ReadingHeader : // start by reading Header info + InflaterState.ReadingBFinal; // start by reading BFinal bit + } + + public void SetInput(byte[] inputBytes, int offset, int length) => + _input.SetInput(inputBytes, offset, length); // append the bytes + + public bool Finished() => _state == InflaterState.Done || _state == InflaterState.VerifyingFooter; + + public int AvailableOutput => _output.AvailableBytes; + + public int Inflate(byte[] bytes, int offset, int length) + { + // copy bytes from output to outputbytes if we have available bytes + // if buffer is not filled up. keep decoding until no input are available + // if decodeBlock returns false. Throw an exception. + int count = 0; + do + { + int copied = _output.CopyTo(bytes, offset, length); + if (copied > 0) + { + //if (_hasFormatReader) + //{ + // _formatReader.UpdateWithBytesRead(bytes, offset, copied); + //} + + offset += copied; + count += copied; + length -= copied; + } + + if (length == 0) + { // filled in the bytes array + break; + } + // Decode will return false when more input is needed + } while (!Finished() && Decode()); + + if (_state == InflaterState.VerifyingFooter) + { // finished reading CRC + // In this case finished is true and output window has all the data. + // But some data in output window might not be copied out. + if (_output.AvailableBytes == 0) + { + //_formatReader.Validate(); + } + } + + return count; + } + + //Each block of compressed data begins with 3 header bits + // containing the following data: + // first bit BFINAL + // next 2 bits BTYPE + // Note that the header bits do not necessarily begin on a byte + // boundary, since a block does not necessarily occupy an integral + // number of bytes. + // BFINAL is set if and only if this is the last block of the data + // set. + // BTYPE specifies how the data are compressed, as follows: + // 00 - no compression + // 01 - compressed with fixed Huffman codes + // 10 - compressed with dynamic Huffman codes + // 11 - reserved (error) + // The only difference between the two compressed cases is how the + // Huffman codes for the literal/length and distance alphabets are + // defined. + // + // This function returns true for success (end of block or output window is full,) + // false if we are short of input + // + private bool Decode() + { + bool eob = false; + bool result = false; + + if (Finished()) + { + return true; + } + + //if (_hasFormatReader) + //{ + // if (_state == InflaterState.ReadingHeader) + // { + // if (!_formatReader.ReadHeader(_input)) + // { + // return false; + // } + // _state = InflaterState.ReadingBFinal; + // } + // else if (_state == InflaterState.StartReadingFooter || _state == InflaterState.ReadingFooter) + // { + // if (!_formatReader.ReadFooter(_input)) + // return false; + + // _state = InflaterState.VerifyingFooter; + // return true; + // } + //} + + if (_state == InflaterState.ReadingBFinal) + { + // reading bfinal bit + // Need 1 bit + if (!_input.EnsureBitsAvailable(1)) + { + return false; + } + + _bfinal = _input.GetBits(1); + _state = InflaterState.ReadingBType; + } + + if (_state == InflaterState.ReadingBType) + { + // Need 2 bits + if (!_input.EnsureBitsAvailable(2)) + { + _state = InflaterState.ReadingBType; + return false; + } + + _blockType = (BlockType)_input.GetBits(2); + if (_blockType == BlockType.Dynamic) + { + _state = InflaterState.ReadingNumLitCodes; + } + else if (_blockType == BlockType.Static) + { + _literalLengthTree = HuffmanTree.StaticLiteralLengthTree; + _distanceTree = HuffmanTree.StaticDistanceTree; + _state = InflaterState.DecodeTop; + } + else if (_blockType == BlockType.Uncompressed) + { + _state = InflaterState.UncompressedAligning; + } + else + { + throw new InvalidDataException("Deflate64: unknown block type"); + } + } + + if (_blockType == BlockType.Dynamic) + { + if (_state < InflaterState.DecodeTop) + { + // we are reading the header + result = DecodeDynamicBlockHeader(); + } + else + { + result = DecodeBlock(out eob); // this can returns true when output is full + } + } + else if (_blockType == BlockType.Static) + { + result = DecodeBlock(out eob); + } + else if (_blockType == BlockType.Uncompressed) + { + result = DecodeUncompressedBlock(out eob); + } + else + { + throw new InvalidDataException("Deflate64: unknown block type"); + } + + // + // If we reached the end of the block and the block we were decoding had + // bfinal=1 (final block) + // + if (eob && (_bfinal != 0)) + { + //if (_hasFormatReader) + // _state = InflaterState.StartReadingFooter; + //else + _state = InflaterState.Done; + } + return result; + } + + + // Format of Non-compressed blocks (BTYPE=00): + // + // Any bits of input up to the next byte boundary are ignored. + // The rest of the block consists of the following information: + // + // 0 1 2 3 4... + // +---+---+---+---+================================+ + // | LEN | NLEN |... LEN bytes of literal data...| + // +---+---+---+---+================================+ + // + // LEN is the number of data bytes in the block. NLEN is the + // one's complement of LEN. + private bool DecodeUncompressedBlock(out bool endOfBlock) + { + endOfBlock = false; + while (true) + { + switch (_state) + { + case InflaterState.UncompressedAligning: // initial state when calling this function + // we must skip to a byte boundary + _input.SkipToByteBoundary(); + _state = InflaterState.UncompressedByte1; + goto case InflaterState.UncompressedByte1; + + case InflaterState.UncompressedByte1: // decoding block length + case InflaterState.UncompressedByte2: + case InflaterState.UncompressedByte3: + case InflaterState.UncompressedByte4: + int bits = _input.GetBits(8); + if (bits < 0) + { + return false; + } + + _blockLengthBuffer[_state - InflaterState.UncompressedByte1] = (byte)bits; + if (_state == InflaterState.UncompressedByte4) + { + _blockLength = _blockLengthBuffer[0] + ((int)_blockLengthBuffer[1]) * 256; + int blockLengthComplement = _blockLengthBuffer[2] + ((int)_blockLengthBuffer[3]) * 256; + + // make sure complement matches + if ((ushort)_blockLength != (ushort)(~blockLengthComplement)) + { + throw new InvalidDataException("Deflate64: invalid block length"); + } + } + + _state += 1; + break; + + case InflaterState.DecodingUncompressed: // copying block data + + // Directly copy bytes from input to output. + int bytesCopied = _output.CopyFrom(_input, _blockLength); + _blockLength -= bytesCopied; + + if (_blockLength == 0) + { + // Done with this block, need to re-init bit buffer for next block + _state = InflaterState.ReadingBFinal; + endOfBlock = true; + return true; + } + + // We can fail to copy all bytes for two reasons: + // Running out of Input + // running out of free space in output window + if (_output.FreeBytes == 0) + { + return true; + } + + return false; + + default: + Debug./*Fail*/Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + } + } + + private bool DecodeBlock(out bool endOfBlockCodeSeen) + { + endOfBlockCodeSeen = false; + + int freeBytes = _output.FreeBytes; // it is a little bit faster than frequently accessing the property + while (freeBytes > 65536) + { + // With Deflate64 we can have up to a 64kb length, so we ensure at least that much space is available + // in the OutputWindow to avoid overwriting previous unflushed output data. + + int symbol; + switch (_state) + { + case InflaterState.DecodeTop: + // decode an element from the literal tree + + // TODO: optimize this!!! + symbol = _literalLengthTree.GetNextSymbol(_input); + if (symbol < 0) + { + // running out of input + return false; + } + + if (symbol < 256) + { + // literal + _output.Write((byte)symbol); + --freeBytes; + } + else if (symbol == 256) + { + // end of block + endOfBlockCodeSeen = true; + // Reset state + _state = InflaterState.ReadingBFinal; + return true; + } + else + { + // length/distance pair + symbol -= 257; // length code started at 257 + if (symbol < 8) + { + symbol += 3; // match length = 3,4,5,6,7,8,9,10 + _extraBits = 0; + } + else if (!_deflate64 && symbol == 28) + { + // extra bits for code 285 is 0 + symbol = 258; // code 285 means length 258 + _extraBits = 0; + } + else + { + if (symbol < 0 || symbol >= S_EXTRA_LENGTH_BITS.Length) + { + throw new InvalidDataException("Deflate64: invalid data"); + } + _extraBits = S_EXTRA_LENGTH_BITS[symbol]; + Debug.Assert(_extraBits != 0, "We handle other cases separately!"); + } + _length = symbol; + goto case InflaterState.HaveInitialLength; + } + break; + + case InflaterState.HaveInitialLength: + if (_extraBits > 0) + { + _state = InflaterState.HaveInitialLength; + int bits = _input.GetBits(_extraBits); + if (bits < 0) + { + return false; + } + + if (_length < 0 || _length >= S_LENGTH_BASE.Length) + { + throw new InvalidDataException("Deflate64: invalid data"); + } + _length = S_LENGTH_BASE[_length] + bits; + } + _state = InflaterState.HaveFullLength; + goto case InflaterState.HaveFullLength; + + case InflaterState.HaveFullLength: + if (_blockType == BlockType.Dynamic) + { + _distanceCode = _distanceTree.GetNextSymbol(_input); + } + else + { + // get distance code directly for static block + _distanceCode = _input.GetBits(5); + if (_distanceCode >= 0) + { + _distanceCode = S_STATIC_DISTANCE_TREE_TABLE[_distanceCode]; + } + } + + if (_distanceCode < 0) + { + // running out input + return false; + } + + _state = InflaterState.HaveDistCode; + goto case InflaterState.HaveDistCode; + + case InflaterState.HaveDistCode: + // To avoid a table lookup we note that for distanceCode > 3, + // extra_bits = (distanceCode-2) >> 1 + int offset; + if (_distanceCode > 3) + { + _extraBits = (_distanceCode - 2) >> 1; + int bits = _input.GetBits(_extraBits); + if (bits < 0) + { + return false; + } + offset = S_DISTANCE_BASE_POSITION[_distanceCode] + bits; + } + else + { + offset = _distanceCode + 1; + } + + _output.WriteLengthDistance(_length, offset); + freeBytes -= _length; + _state = InflaterState.DecodeTop; + break; + + default: + Debug./*Fail*/Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + } + + return true; + } + + + // Format of the dynamic block header: + // 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) + // 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) + // 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) + // + // (HCLEN + 4) x 3 bits: code lengths for the code length + // alphabet given just above, in the order: 16, 17, 18, + // 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + // + // These code lengths are interpreted as 3-bit integers + // (0-7); as above, a code length of 0 means the + // corresponding symbol (literal/length or distance code + // length) is not used. + // + // HLIT + 257 code lengths for the literal/length alphabet, + // encoded using the code length Huffman code + // + // HDIST + 1 code lengths for the distance alphabet, + // encoded using the code length Huffman code + // + // The code length repeat codes can cross from HLIT + 257 to the + // HDIST + 1 code lengths. In other words, all code lengths form + // a single sequence of HLIT + HDIST + 258 values. + private bool DecodeDynamicBlockHeader() + { + switch (_state) + { + case InflaterState.ReadingNumLitCodes: + _literalLengthCodeCount = _input.GetBits(5); + if (_literalLengthCodeCount < 0) + { + return false; + } + _literalLengthCodeCount += 257; + _state = InflaterState.ReadingNumDistCodes; + goto case InflaterState.ReadingNumDistCodes; + + case InflaterState.ReadingNumDistCodes: + _distanceCodeCount = _input.GetBits(5); + if (_distanceCodeCount < 0) + { + return false; + } + _distanceCodeCount += 1; + _state = InflaterState.ReadingNumCodeLengthCodes; + goto case InflaterState.ReadingNumCodeLengthCodes; + + case InflaterState.ReadingNumCodeLengthCodes: + _codeLengthCodeCount = _input.GetBits(4); + if (_codeLengthCodeCount < 0) + { + return false; + } + _codeLengthCodeCount += 4; + _loopCounter = 0; + _state = InflaterState.ReadingCodeLengthCodes; + goto case InflaterState.ReadingCodeLengthCodes; + + case InflaterState.ReadingCodeLengthCodes: + while (_loopCounter < _codeLengthCodeCount) + { + int bits = _input.GetBits(3); + if (bits < 0) + { + return false; + } + _codeLengthTreeCodeLength[S_CODE_ORDER[_loopCounter]] = (byte)bits; + ++_loopCounter; + } + + for (int i = _codeLengthCodeCount; i < S_CODE_ORDER.Length; i++) + { + _codeLengthTreeCodeLength[S_CODE_ORDER[i]] = 0; + } + + // create huffman tree for code length + _codeLengthTree = new HuffmanTree(_codeLengthTreeCodeLength); + _codeArraySize = _literalLengthCodeCount + _distanceCodeCount; + _loopCounter = 0; // reset loop count + + _state = InflaterState.ReadingTreeCodesBefore; + goto case InflaterState.ReadingTreeCodesBefore; + + case InflaterState.ReadingTreeCodesBefore: + case InflaterState.ReadingTreeCodesAfter: + while (_loopCounter < _codeArraySize) + { + if (_state == InflaterState.ReadingTreeCodesBefore) + { + if ((_lengthCode = _codeLengthTree.GetNextSymbol(_input)) < 0) + { + return false; + } + } + + // The alphabet for code lengths is as follows: + // 0 - 15: Represent code lengths of 0 - 15 + // 16: Copy the previous code length 3 - 6 times. + // The next 2 bits indicate repeat length + // (0 = 3, ... , 3 = 6) + // Example: Codes 8, 16 (+2 bits 11), + // 16 (+2 bits 10) will expand to + // 12 code lengths of 8 (1 + 6 + 5) + // 17: Repeat a code length of 0 for 3 - 10 times. + // (3 bits of length) + // 18: Repeat a code length of 0 for 11 - 138 times + // (7 bits of length) + if (_lengthCode <= 15) + { + _codeList[_loopCounter++] = (byte)_lengthCode; + } + else + { + int repeatCount; + if (_lengthCode == 16) + { + if (!_input.EnsureBitsAvailable(2)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + if (_loopCounter == 0) + { + // can't have "prev code" on first code + throw new InvalidDataException(); + } + + byte previousCode = _codeList[_loopCounter - 1]; + repeatCount = _input.GetBits(2) + 3; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (int j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = previousCode; + } + } + else if (_lengthCode == 17) + { + if (!_input.EnsureBitsAvailable(3)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + repeatCount = _input.GetBits(3) + 3; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (int j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = 0; + } + } + else + { + // code == 18 + if (!_input.EnsureBitsAvailable(7)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + repeatCount = _input.GetBits(7) + 11; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (int j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = 0; + } + } + } + _state = InflaterState.ReadingTreeCodesBefore; // we want to read the next code. + } + break; + + default: + Debug./*Fail*/Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + + byte[] literalTreeCodeLength = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS]; + byte[] distanceTreeCodeLength = new byte[HuffmanTree.MAX_DIST_TREE_ELEMENTS]; + + // Create literal and distance tables + Array.Copy(_codeList, literalTreeCodeLength, _literalLengthCodeCount); + Array.Copy(_codeList, _literalLengthCodeCount, distanceTreeCodeLength, 0, _distanceCodeCount); + + // Make sure there is an end-of-block code, otherwise how could we ever end? + if (literalTreeCodeLength[HuffmanTree.END_OF_BLOCK_CODE] == 0) + { + throw new InvalidDataException(); + } + + _literalLengthTree = new HuffmanTree(literalTreeCodeLength); + _distanceTree = new HuffmanTree(distanceTreeCodeLength); + _state = InflaterState.DecodeTop; + return true; + } + + public void Dispose() { } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InflaterState.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InflaterState.cs new file mode 100644 index 00000000..5c3fef78 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InflaterState.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Compress.Support.Compression.Deflate64 +{ + // Do not rearrange the enum values. + internal enum InflaterState + { + ReadingHeader = 0, // Only applies to GZIP + + ReadingBFinal = 2, // About to read bfinal bit + ReadingBType = 3, // About to read blockType bits + + ReadingNumLitCodes = 4, // About to read # literal codes + ReadingNumDistCodes = 5, // About to read # dist codes + ReadingNumCodeLengthCodes = 6, // About to read # code length codes + ReadingCodeLengthCodes = 7, // In the middle of reading the code length codes + ReadingTreeCodesBefore = 8, // In the middle of reading tree codes (loop top) + ReadingTreeCodesAfter = 9, // In the middle of reading tree codes (extension; code > 15) + + DecodeTop = 10, // About to decode a literal (char/match) in a compressed block + HaveInitialLength = 11, // Decoding a match, have the literal code (base length) + HaveFullLength = 12, // Ditto, now have the full match length (incl. extra length bits) + HaveDistCode = 13, // Ditto, now have the distance code also, need extra dist bits + + /* uncompressed blocks */ + UncompressedAligning = 15, + UncompressedByte1 = 16, + UncompressedByte2 = 17, + UncompressedByte3 = 18, + UncompressedByte4 = 19, + DecodingUncompressed = 20, + + // These three apply only to GZIP + StartReadingFooter = 21, // (Initialisation for reading footer) + ReadingFooter = 22, + VerifyingFooter = 23, + + Done = 24 // Finished + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InputBuffer.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InputBuffer.cs new file mode 100644 index 00000000..dd9be6c2 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/InputBuffer.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace Compress.Support.Compression.Deflate64 +{ + // This class can be used to read bits from an byte array quickly. + // Normally we get bits from 'bitBuffer' field and bitsInBuffer stores + // the number of bits available in 'BitBuffer'. + // When we used up the bits in bitBuffer, we will try to get byte from + // the byte array and copy the byte to appropiate position in bitBuffer. + // + // The byte array is not reused. We will go from 'start' to 'end'. + // When we reach the end, most read operations will return -1, + // which means we are running out of input. + + internal sealed class InputBuffer + { + private byte[] _buffer; // byte array to store input + private int _start; // start poisition of the buffer + private int _end; // end position of the buffer + private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer + private int _bitsInBuffer = 0; // number of bits available in bitBuffer + + /// Total bits available in the input buffer. + public int AvailableBits => _bitsInBuffer; + + /// Total bytes available in the input buffer. + public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8); + + /// Ensure that count bits are in the bit buffer. + /// Can be up to 16. + /// Returns false if input is not sufficient to make this true. + public bool EnsureBitsAvailable(int count) + { + Debug.Assert(0 < count && count <= 16, "count is invalid."); + + // manual inlining to improve perf + if (_bitsInBuffer < count) + { + if (NeedsInput()) + { + return false; + } + // insert a byte to bitbuffer + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + + if (_bitsInBuffer < count) + { + if (NeedsInput()) + { + return false; + } + // insert a byte to bitbuffer + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + } + + return true; + } + + /// + /// This function will try to load 16 or more bits into bitBuffer. + /// It returns whatever is contained in bitBuffer after loading. + /// The main difference between this and GetBits is that this will + /// never return -1. So the caller needs to check AvailableBits to + /// see how many bits are available. + /// + public uint TryLoad16Bits() + { + if (_bitsInBuffer < 8) + { + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + } + else if (_bitsInBuffer < 16) + { + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + } + + return _bitBuffer; + } + + private uint GetBitMask(int count) => ((uint)1 << count) - 1; + + /// Gets count bits from the input buffer. Returns -1 if not enough bits available. + public int GetBits(int count) + { + Debug.Assert(0 < count && count <= 16, "count is invalid."); + + if (!EnsureBitsAvailable(count)) + { + return -1; + } + + int result = (int)(_bitBuffer & GetBitMask(count)); + _bitBuffer >>= count; + _bitsInBuffer -= count; + return result; + } + + /// + /// Copies length bytes from input buffer to output buffer starting at output[offset]. + /// You have to make sure, that the buffer is byte aligned. If not enough bytes are + /// available, copies fewer bytes. + /// + /// Returns the number of bytes copied, 0 if no byte is available. + public int CopyTo(byte[] output, int offset, int length) + { + Debug.Assert(output != null); + Debug.Assert(offset >= 0); + Debug.Assert(length >= 0); + Debug.Assert(offset <= output.Length - length); + Debug.Assert((_bitsInBuffer % 8) == 0); + + // Copy the bytes in bitBuffer first. + int bytesFromBitBuffer = 0; + while (_bitsInBuffer > 0 && length > 0) + { + output[offset++] = (byte)_bitBuffer; + _bitBuffer >>= 8; + _bitsInBuffer -= 8; + length--; + bytesFromBitBuffer++; + } + + if (length == 0) + { + return bytesFromBitBuffer; + } + + int avail = _end - _start; + if (length > avail) + { + length = avail; + } + + Array.Copy(_buffer, _start, output, offset, length); + _start += length; + return bytesFromBitBuffer + length; + } + + /// + /// Return true is all input bytes are used. + /// This means the caller can call SetInput to add more input. + /// + public bool NeedsInput() => _start == _end; + + /// + /// Set the byte array to be processed. + /// All the bits remained in bitBuffer will be processed before the new bytes. + /// We don't clone the byte array here since it is expensive. + /// The caller should make sure after a buffer is passed in. + /// It will not be changed before calling this function again. + /// + public void SetInput(byte[] buffer, int offset, int length) + { + Debug.Assert(buffer != null); + Debug.Assert(offset >= 0); + Debug.Assert(length >= 0); + Debug.Assert(offset <= buffer.Length - length); + Debug.Assert(_start == _end); + + _buffer = buffer; + _start = offset; + _end = offset + length; + } + + /// Skip n bits in the buffer. + public void SkipBits(int n) + { + Debug.Assert(_bitsInBuffer >= n, "No enough bits in the buffer, Did you call EnsureBitsAvailable?"); + _bitBuffer >>= n; + _bitsInBuffer -= n; + } + + /// Skips to the next byte boundary. + public void SkipToByteBoundary() + { + _bitBuffer >>= (_bitsInBuffer % 8); + _bitsInBuffer = _bitsInBuffer - (_bitsInBuffer % 8); + } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/Match.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/Match.cs new file mode 100644 index 00000000..ce0cbadc --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/Match.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Compress.Support.Compression.Deflate64 +{ + /// + /// This class represents a match in the history window. + /// + internal sealed class Match + { + internal MatchState State { get; set; } + internal int Position { get; set; } + internal int Length { get; set; } + internal byte Symbol { get; set; } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/MatchState.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/MatchState.cs new file mode 100644 index 00000000..72c0139d --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/MatchState.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Compress.Support.Compression.Deflate64 +{ + internal enum MatchState + { + HasSymbol = 1, + HasMatch = 2, + HasSymbolAndMatch = 3 + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/OutputWindow.cs b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/OutputWindow.cs new file mode 100644 index 00000000..339626c1 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/Deflate64/OutputWindow.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace Compress.Support.Compression.Deflate64 +{ + /// + /// This class maintains a window for decompressed output. + /// We need to keep this because the decompressed information can be + /// a literal or a length/distance pair. For length/distance pair, + /// we need to look back in the output window and copy bytes from there. + /// We use a byte array of WindowSize circularly. + /// + internal sealed class OutputWindow + { + // With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at + // least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without + // overwriting existing data. OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18. + private const int WINDOW_SIZE = 262144; + private const int WINDOW_MASK = 262143; + + private readonly byte[] _window = new byte[WINDOW_SIZE]; // The window is 2^18 bytes + private int _end; // this is the position to where we should write next byte + private int _bytesUsed; // The number of bytes in the output window which is not consumed. + + /// Add a byte to output window. + public void Write(byte b) + { + Debug.Assert(_bytesUsed < WINDOW_SIZE, "Can't add byte when window is full!"); + _window[_end++] = b; + _end &= WINDOW_MASK; + ++_bytesUsed; + } + + public void WriteLengthDistance(int length, int distance) + { + Debug.Assert((_bytesUsed + length) <= WINDOW_SIZE, "No Enough space"); + + // move backwards distance bytes in the output stream, + // and copy length bytes from this position to the output stream. + _bytesUsed += length; + int copyStart = (_end - distance) & WINDOW_MASK; // start position for coping. + + int border = WINDOW_SIZE - length; + if (copyStart <= border && _end < border) + { + if (length <= distance) + { + Array.Copy(_window, copyStart, _window, _end, length); + _end += length; + } + else + { + // The referenced string may overlap the current + // position; for example, if the last 2 bytes decoded have values + // X and Y, a string reference with + // adds X,Y,X,Y,X to the output stream. + while (length-- > 0) + { + _window[_end++] = _window[copyStart++]; + } + } + } + else + { + // copy byte by byte + while (length-- > 0) + { + _window[_end++] = _window[copyStart++]; + _end &= WINDOW_MASK; + copyStart &= WINDOW_MASK; + } + } + } + + /// + /// Copy up to length of bytes from input directly. + /// This is used for uncompressed block. + /// + public int CopyFrom(InputBuffer input, int length) + { + length = Math.Min(Math.Min(length, WINDOW_SIZE - _bytesUsed), input.AvailableBytes); + int copied; + + // We might need wrap around to copy all bytes. + int tailLen = WINDOW_SIZE - _end; + if (length > tailLen) + { + // copy the first part + copied = input.CopyTo(_window, _end, tailLen); + if (copied == tailLen) + { + // only try to copy the second part if we have enough bytes in input + copied += input.CopyTo(_window, 0, length - tailLen); + } + } + else + { + // only one copy is needed if there is no wrap around. + copied = input.CopyTo(_window, _end, length); + } + + _end = (_end + copied) & WINDOW_MASK; + _bytesUsed += copied; + return copied; + } + + /// Free space in output window. + public int FreeBytes => WINDOW_SIZE - _bytesUsed; + + /// Bytes not consumed in output window. + public int AvailableBytes => _bytesUsed; + + /// Copy the decompressed bytes to output array. + public int CopyTo(byte[] output, int offset, int length) + { + int copyEnd; + + if (length > _bytesUsed) + { + // we can copy all the decompressed bytes out + copyEnd = _end; + length = _bytesUsed; + } + else + { + copyEnd = (_end - _bytesUsed + length) & WINDOW_MASK; // copy length of bytes + } + + int copied = length; + + int tailLen = length - copyEnd; + if (tailLen > 0) + { + // this means we need to copy two parts separately + // copy tailLen bytes from the end of output window + Array.Copy(_window, WINDOW_SIZE - tailLen, + output, offset, tailLen); + offset += tailLen; + length = copyEnd; + } + Array.Copy(_window, copyEnd - length, output, offset, length); + _bytesUsed -= copied; + Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have"); + return copied; + } + } +} diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzBinTree.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZ/LzBinTree.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzBinTree.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZ/LzBinTree.cs index 4ce018eb..7511850b 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzBinTree.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZ/LzBinTree.cs @@ -1,7 +1,6 @@ using System; -using Compress.SevenZip.Common; -namespace Compress.SevenZip.Compress.LZ +namespace Compress.Support.Compression.LZ { internal class BinTree : InWindow { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzInWindow.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZ/LzInWindow.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzInWindow.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZ/LzInWindow.cs index 2fd6642a..43cb8d66 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzInWindow.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZ/LzInWindow.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.SevenZip.Compress.LZ +namespace Compress.Support.Compression.LZ { internal class InWindow { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzOutWindow.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZ/LzOutWindow.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzOutWindow.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZ/LzOutWindow.cs index f5b4a9f8..b54650d5 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZ/LzOutWindow.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZ/LzOutWindow.cs @@ -1,6 +1,6 @@ -using Compress.SevenZip.Common; +using Compress.Support.Compression.LZMA; -namespace Compress.SevenZip.Compress.LZ +namespace Compress.Support.Compression.LZ { internal class OutWindow { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Common/ICoder.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/ICoder.cs similarity index 67% rename from SabreTools.FileTypes/Compress/SevenZip/Common/ICoder.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZMA/ICoder.cs index 2677e52f..ad5f493f 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Common/ICoder.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/ICoder.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.SevenZip.Common +namespace Compress.Support.Compression.LZMA { /// /// The exception that is thrown when an error in input stream occurs during decoding. @@ -32,40 +32,6 @@ namespace Compress.SevenZip.Common void SetProgress(Int64 inSize, Int64 outSize); }; - internal interface ICoder - { - /// - /// Codes streams. - /// - /// - /// input Stream. - /// - /// - /// output Stream. - /// - /// - /// input Size. -1 if unknown. - /// - /// - /// output Size. -1 if unknown. - /// - /// - /// callback progress reference. - /// - void Code(System.IO.Stream inStream, System.IO.Stream outStream, - Int64 inSize, Int64 outSize, ICodeProgress progress); - }; - - /* - public interface ICoder2 - { - void Code(ISequentialInStream []inStreams, - const UInt64 []inSizes, - ISequentialOutStream []outStreams, - UInt64 []outSizes, - ICodeProgress progress); - }; - */ /// /// Provides the fields that represent properties idenitifiers for compressing. @@ -134,19 +100,4 @@ namespace Compress.SevenZip.Common EndMarker }; - - internal interface ISetCoderProperties - { - void SetCoderProperties(CoderPropID[] propIDs, object[] properties); - }; - - internal interface IWriteCoderProperties - { - void WriteCoderProperties(System.IO.Stream outStream); - } - - internal interface ISetDecoderProperties - { - void SetDecoderProperties(byte[] properties); - } } diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaBase.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaBase.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaBase.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaBase.cs index a24fd1f5..ca70f1bf 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaBase.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaBase.cs @@ -1,4 +1,4 @@ -namespace Compress.SevenZip.Compress.LZMA +namespace Compress.Support.Compression.LZMA { internal abstract class Base { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaDecoder.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaDecoder.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaDecoder.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaDecoder.cs index 42705b2a..c01a5e4c 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaDecoder.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaDecoder.cs @@ -1,10 +1,9 @@ using System; -using Compress.SevenZip.Common; -using Compress.SevenZip.Compress.RangeCoder; +using Compress.Support.Compression.RangeCoder; -namespace Compress.SevenZip.Compress.LZMA +namespace Compress.Support.Compression.LZMA { - internal class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream + internal class Decoder { class LenDecoder { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaEncoder.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaEncoder.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaEncoder.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaEncoder.cs index 61340432..df4de66d 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaEncoder.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaEncoder.cs @@ -1,11 +1,10 @@ using System; -using Compress.SevenZip.Common; -using Compress.SevenZip.Compress.RangeCoder; +using Compress.Support.Compression.RangeCoder; -namespace Compress.SevenZip.Compress.LZMA +namespace Compress.Support.Compression.LZMA { - internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + internal class Encoder { enum EMatchFinderType { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaEncoderProperties.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaEncoderProperties.cs similarity index 94% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaEncoderProperties.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaEncoderProperties.cs index db866ebf..9b200b78 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaEncoderProperties.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaEncoderProperties.cs @@ -1,6 +1,4 @@ -using Compress.SevenZip.Common; - -namespace Compress.SevenZip.Compress.LZMA +namespace Compress.Support.Compression.LZMA { public class LzmaEncoderProperties { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaStream.cs b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaStream.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaStream.cs rename to SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaStream.cs index 564e162a..3447ec44 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/LZMA/LzmaStream.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/LZMA/LzmaStream.cs @@ -1,9 +1,8 @@ using System; using System.IO; -using Compress.SevenZip.Common; -using Compress.SevenZip.Compress.LZ; +using Compress.Support.Compression.LZ; -namespace Compress.SevenZip.Compress.LZMA +namespace Compress.Support.Compression.LZMA { public class LzmaStream : Stream { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/FreqData.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/FreqData.cs similarity index 97% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/FreqData.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/FreqData.cs index 5dfcd62e..a0da398b 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/FreqData.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/FreqData.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class FreqData : Pointer { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/ModelPPM.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/ModelPPM.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/ModelPPM.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/ModelPPM.cs index 081a70d5..d19815fb 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/ModelPPM.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/ModelPPM.cs @@ -1,8 +1,8 @@ using System.IO; using System.Text; -using Decoder = Compress.SevenZip.Compress.RangeCoder.Decoder; +using Decoder = Compress.Support.Compression.RangeCoder.Decoder; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class ModelPPM { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/PPMContext.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/PPMContext.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/PPMContext.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/PPMContext.cs index 10cc6be4..4d88d41f 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/PPMContext.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/PPMContext.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class PPMContext : Pointer { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/Pointer.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/Pointer.cs similarity index 93% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/Pointer.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/Pointer.cs index edca08ca..089571fb 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/Pointer.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/Pointer.cs @@ -1,5 +1,5 @@ -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal abstract class Pointer { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RangeCoder.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RangeCoder.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RangeCoder.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RangeCoder.cs index da085256..bf491649 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RangeCoder.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RangeCoder.cs @@ -1,7 +1,7 @@ using System.IO; using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class RangeCoder { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RarMemBlock.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RarMemBlock.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RarMemBlock.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RarMemBlock.cs index f583f56f..83d0fff2 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RarMemBlock.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RarMemBlock.cs @@ -1,5 +1,5 @@ -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class RarMemBlock : Pointer { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RarNode.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RarNode.cs similarity index 96% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RarNode.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RarNode.cs index 168fe24e..7794f014 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/RarNode.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/RarNode.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class RarNode : Pointer { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/SEE2Context.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/SEE2Context.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/SEE2Context.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/SEE2Context.cs index 6d53f26a..a27c7576 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/SEE2Context.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/SEE2Context.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class SEE2Context { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/State.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/State.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/State.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/State.cs index 6a9f005b..2209048c 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/State.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/State.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class State : Pointer { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/StateRef.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/StateRef.cs similarity index 97% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/StateRef.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/StateRef.cs index 23a52883..a9e968ae 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/StateRef.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/StateRef.cs @@ -1,6 +1,6 @@ using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class StateRef { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/SubAllocator.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/SubAllocator.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/SubAllocator.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/SubAllocator.cs index 957b8205..bac4e2ed 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/H/SubAllocator.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/H/SubAllocator.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace Compress.SevenZip.Compress.PPmd.H +namespace Compress.Support.Compression.PPmd.H { internal class SubAllocator { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Allocator.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Allocator.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Allocator.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Allocator.cs index e85afa88..252fc774 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Allocator.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Allocator.cs @@ -4,7 +4,7 @@ #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// Allocate a single, large array and then provide sections of this array to callers. Callers are provided with diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Coder.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Coder.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Coder.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Coder.cs index c243ae60..5510d93e 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Coder.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Coder.cs @@ -4,7 +4,7 @@ using System.IO; #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// A simple range coder. diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/MemoryNode.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/MemoryNode.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/MemoryNode.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/MemoryNode.cs index bde225f8..0758d7bc 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/MemoryNode.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/MemoryNode.cs @@ -4,7 +4,7 @@ #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// A structure containing a single address. The address represents a location in the diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Model.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Model.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Model.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Model.cs index f464c626..2834650a 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Model.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Model.cs @@ -7,7 +7,7 @@ using System.IO; // This is a port of Dmitry Shkarin's PPMd Variant I Revision 1. // Ported by Michael Bone (mjbone03@yahoo.com.au). -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// The model. diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/ModelRestorationMethod.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/ModelRestorationMethod.cs similarity index 92% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/ModelRestorationMethod.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/ModelRestorationMethod.cs index e7a7cdef..339ee3ef 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/ModelRestorationMethod.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/ModelRestorationMethod.cs @@ -4,7 +4,7 @@ #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// The method used to adjust the model when the memory limit is reached. diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Pointer.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Pointer.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Pointer.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Pointer.cs index 2df401a0..dac6aa23 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/Pointer.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/Pointer.cs @@ -4,7 +4,7 @@ using System; #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// A structure containing a single address representing a position in the array. This diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/PpmContext.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/PpmContext.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/PpmContext.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/PpmContext.cs index 81aad5e1..f3cd7854 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/PpmContext.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/PpmContext.cs @@ -4,7 +4,7 @@ #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// The PPM context structure. This is tightly coupled with . diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/PpmState.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/PpmState.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/PpmState.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/PpmState.cs index 79b509f6..056bf9b3 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/PpmState.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/PpmState.cs @@ -4,7 +4,7 @@ #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// PPM state. diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/See2Context.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/See2Context.cs similarity index 97% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/See2Context.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/See2Context.cs index 970a0889..1602a0eb 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/I1/See2Context.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/I1/See2Context.cs @@ -4,7 +4,7 @@ #endregion -namespace Compress.SevenZip.Compress.PPmd.I1 +namespace Compress.Support.Compression.PPmd.I1 { /// /// SEE2 (secondary escape estimation) contexts for PPM contexts with masked symbols. diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/PpmdProperties.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/PpmdProperties.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/PpmdProperties.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/PpmdProperties.cs index 2e5adbfd..6054e727 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/PpmdProperties.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/PpmdProperties.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.SevenZip.Compress.PPmd +namespace Compress.Support.Compression.PPmd { public enum PpmdVersion { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/PpmdStream.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/PpmdStream.cs similarity index 97% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/PpmdStream.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/PpmdStream.cs index f5db7540..7db77be2 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/PpmdStream.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/PpmdStream.cs @@ -1,8 +1,8 @@ using System; using System.IO; -using Compress.SevenZip.Compress.RangeCoder; +using Compress.Support.Compression.RangeCoder; -namespace Compress.SevenZip.Compress.PPmd +namespace Compress.Support.Compression.PPmd { public class PpmdStream : Stream { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/Utility.cs b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/Utility.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/Utility.cs rename to SabreTools.FileTypes/Compress/Support/Compression/PPmd/Utility.cs index 934f4fbb..c66be9d7 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/PPmd/Utility.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/PPmd/Utility.cs @@ -4,7 +4,7 @@ using System.Collections.ObjectModel; using System.IO; using System.Linq; -namespace Compress.SevenZip.Compress.PPmd +namespace Compress.Support.Compression.PPmd { internal static class Utility { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoder.cs b/SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoder.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoder.cs rename to SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoder.cs index 10f2e09f..1a13cf8a 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoder.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoder.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.SevenZip.Compress.RangeCoder +namespace Compress.Support.Compression.RangeCoder { internal class Encoder { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoderBit.cs b/SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoderBit.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoderBit.cs rename to SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoderBit.cs index 357a3e79..0ddc702a 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoderBit.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoderBit.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.SevenZip.Compress.RangeCoder +namespace Compress.Support.Compression.RangeCoder { internal struct BitEncoder { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoderBitTree.cs b/SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoderBitTree.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoderBitTree.cs rename to SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoderBitTree.cs index 06a814ef..254ecfbb 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Compress/RangeCoder/RangeCoderBitTree.cs +++ b/SabreTools.FileTypes/Compress/Support/Compression/RangeCoder/RangeCoderBitTree.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.SevenZip.Compress.RangeCoder +namespace Compress.Support.Compression.RangeCoder { internal struct BitTreeEncoder { diff --git a/SabreTools.FileTypes/Compress/Support/Compression/SimpleInflate/Inflate.cs b/SabreTools.FileTypes/Compress/Support/Compression/SimpleInflate/Inflate.cs new file mode 100644 index 00000000..e15b12e2 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/SimpleInflate/Inflate.cs @@ -0,0 +1,279 @@ +namespace Compress.Support.Compression.SimpleInflate +{ + + public class Tree + { + public int[] Codes = new int[288]; + public int[] num = new int[288]; + public int[] bitLen = new int[288]; + public int max; + + public void Build(byte[] lens, int lensOffset, int symcount) + { + unchecked + { + int[] codes = new int[16]; + int[] first = new int[16]; + int[] counts = new int[16]; + + int endcount = lensOffset + symcount; + // Frequency count. + for (int n = lensOffset; n < endcount; n++) + counts[lens[n]]++; + + // Distribute codes. + counts[0] = codes[0] = first[0] = 0; + for (int n = 1; n <= 15; n++) + { + codes[n] = (codes[n - 1] + counts[n - 1]) << 1; + first[n] = first[n - 1] + counts[n - 1]; + } + + // Insert keys into the tree for each symbol. + int lensOffsetLocal = lensOffset; + for (int n = 0; n < symcount; n++) + { + int len = lens[lensOffsetLocal++]; + if (len == 0) continue; + + int code = codes[len]++; + int slot = first[len]++; + Codes[slot] = code << (16 - len); + num[slot] = n; + bitLen[slot] = len; + } + + max = first[15]; + } + } + + } + + public class Inflate + { + private int _bits, _count; + + private byte[] _bIn; + private int _indexIn; //public int endIn; + private byte[] _bOut; + private int _indexOut; //public int endOut; + + private readonly Tree _dLitCodes = new Tree(); + private readonly Tree _dDistCodes = new Tree(); + private readonly Tree _lenCodes = new Tree(); + + + private Tree _litCodes; + private Tree _distCodes; + private static readonly byte[] Order = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + private static readonly byte[] LenBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + private static readonly int[] LenBase = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + private static readonly byte[] DistBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; + private static readonly int[] DistBase = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + + // Table to bit-reverse a byte. + private static readonly byte[] ReverseTable = new byte[256]; + + // Static tables + private static readonly Tree SLitCodes = new Tree(); + private static readonly Tree SDistCodes = new Tree(); + static Inflate() + { + for (int i = 0; i < 256; i++) + { + ReverseTable[i] = (byte)( + ((i & 0x80) >> 7) | ((i & 0x40) >> 5) | ((i & 0x20) >> 3) | ((i & 0x10) >> 1) | + ((i & 0x08) << 1) | ((i & 0x04) << 3) | ((i & 0x02) << 5) | ((i & 0x01) << 7) + ); + } + + // Fixed set of Huffman codes. + byte[] lens = new byte[288 + 32]; + int n; + for (n = 0; n <= 143; n++) lens[n] = 8; + for (n = 144; n <= 255; n++) lens[n] = 9; + for (n = 256; n <= 279; n++) lens[n] = 7; + for (n = 280; n <= 287; n++) lens[n] = 8; + for (n = 0; n < 32; n++) lens[288 + n] = 5; + + SLitCodes.Build(lens, 0, 288); + SDistCodes.Build(lens, 288, 32); + } + + public static int Rev16(int n) + { + return (ReverseTable[n & 0xff] << 8) | ReverseTable[(n >> 8) & 0xff]; + } + + private int Bits(int n) + { + int v = _bits & ((1 << n) - 1); + _bits >>= n; + _count -= n; + while (_count < 16) + { + _bits |= _bIn[_indexIn++] << _count; + _count += 8; + } + return v; + } + + private void Copy(byte[] src, int index, int len) + { + while (len-- > 0) + { + _bOut[_indexOut++] = src[index++]; + } + } + + + private int Decode(Tree tree) + { + unchecked + { + + // Find the next prefix code. + int lo = 0; + int hi = tree.max; + + int search = Rev16(_bits); + while (lo < hi) + { + int guess = (lo + hi) >> 1; + if (search < tree.Codes[guess]) hi = guess; + else lo = guess + 1; + } + + Bits(tree.bitLen[lo - 1]); + return tree.num[lo - 1]; + } + + } + + private void Run(int sym) + { + int length = Bits(LenBits[sym]) + LenBase[sym]; + int dsym = Decode(_distCodes); + int offs = Bits(DistBits[dsym]) + DistBase[dsym]; + Copy(_bOut, _indexOut - offs, length); + } + + private void Block() + { + for (; ; ) + { + int sym = Decode(_litCodes); + if (sym < 256) + { + _bOut[_indexOut++] = (byte)sym; + } + else if (sym > 256) + { + Run(sym - 257); + } + else // == 256 + break; + } + } + + + private void Stored() + { + // Uncompressed data block. + + // skip any remaining unused bits to get back byte aligned. + Bits(_count & 7); + + // read the numbers of bytes to directly copy + int len = Bits(16); + + // copy the input stream to the output stream for len bytes + Copy(_bIn, _indexIn, len); + _indexIn += len; + + // reload the + Bits(16); + } + + private void Fixed() + { + _litCodes = SLitCodes; + _distCodes = SDistCodes; + } + + private void Dynamic() + { + unchecked + { + byte[] lenlens = new byte[19]; + byte[] lens = new byte[288 + 32]; + int nlit = 257 + Bits(5); + int ndist = 1 + Bits(5); + int nlen = 4 + Bits(4); + for (int n = 0; n < nlen; n++) + lenlens[Order[n]] = (byte)Bits(3); + + // Build the tree for decoding code lengths. + _lenCodes.Build(lenlens, 0, 19); + + // Decode code lengths. + for (int n = 0; n < nlit + ndist;) + { + int sym = Decode(_lenCodes); + switch (sym) + { + case 16: + for (int i = 3 + Bits(2); i > 0; i--, n++) + lens[n] = lens[n - 1]; + break; + case 17: + for (int i = 3 + Bits(3); i > 0; i--, n++) + lens[n] = 0; + break; + case 18: + for (int i = 11 + Bits(7); i > 0; i--, n++) + lens[n] = 0; + break; + default: + lens[n++] = (byte)sym; + break; + } + } + + // Build lit/dist trees. + _dLitCodes.Build(lens, 0, nlit); + _dDistCodes.Build(lens, nlit, ndist); + + _litCodes = _dLitCodes; + _distCodes = _dDistCodes; + } + } + + public int InflateBuffer(byte[] outbuffer, byte[] inbuffer) + { + int last; + // We assume we can buffer 2 extra bytes from off the end of 'in'. + _bIn = inbuffer; + _bOut = outbuffer; + _bits = 0; + _count = 0; + + Bits(0); + + do + { + last = Bits(1); + switch (Bits(2)) + { + case 0: Stored(); break; + case 1: Fixed(); Block(); break; + case 2: Dynamic(); Block(); break; // 87% block() + //case 3: + default: throw new System.InvalidOperationException("Invalid Initial bits"); + } + } while (last == 0); + + return 1; + } + } +} diff --git a/SabreTools.FileTypes/Compress/Support/Compression/SimpleInflate/InflateCPP.cs b/SabreTools.FileTypes/Compress/Support/Compression/SimpleInflate/InflateCPP.cs new file mode 100644 index 00000000..10258fef --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/SimpleInflate/InflateCPP.cs @@ -0,0 +1,219 @@ +/* +#include "tigr_internal.h" +#include +#include + +typedef struct { + unsigned bits, count; + const unsigned char *in, *inend; + unsigned char *out, *outend; + jmp_buf jmp; + unsigned litcodes[288], distcodes[32], LenCodes[19]; + int tlit, tdist, tlen; +} State; + +#define FAIL() longjmp(s->jmp, 1) +#define CHECK(X) if (!(X)) FAIL() + +// Built-in DEFLATE standard tables. +static char order[] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; +static char lenBits[29+2] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0, 0,0 }; +static int lenBase[29+2] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 0,0 }; +static char distBits[30+2] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13, 0,0 }; +static int distBase[30+2] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 }; + +// Table to bit-reverse a byte. +static const unsigned char reverseTable[256] = { +#define R2(n) n, n + 128, n + 64, n + 192 +#define R4(n) R2(n), R2(n + 32), R2(n + 16), R2(n + 48) +#define R6(n) R4(n), R4(n + 8), R4(n + 4), R4(n + 12) + R6(0), R6(2), R6(1), R6(3) +}; + +static unsigned rev16(unsigned n) { return (reverseTable[n&0xff] << 8) | reverseTable[(n>>8)&0xff]; } + +static int bits(State *s, int n) +{ + int v = s->bits & ((1 << n)-1); + s->bits >>= n; + s->count -= n; + while (s->count < 16) + { + CHECK(s->in != s->inend); + s->bits |= (*s->in++) << s->count; + s->count += 8; + } + return v; +} + +static unsigned char *emit(State *s, int len) +{ + s->out += len; + CHECK(s->out <= s->outend); + return s->out-len; +} + +static void copy(State *s, const unsigned char *src, int len) +{ + unsigned char *dest = emit(s, len); + while (len--) *dest++ = *src++; +} + +static int build(State *s, unsigned *tree, unsigned char *lens, int symcount) +{ + int n, codes[16], first[16], counts[16]={0}; + + // Frequency count. + for (n=0;nbits) << 16) | 0xffff; + while (lo < hi) { + unsigned guess = (lo + hi) / 2; + if (search < tree[guess]) hi = guess; + else lo = guess + 1; + } + + // Pull out the key and check it. + key = tree[lo-1]; + CHECK(((search^key) >> (32-(key&0xf))) == 0); + + bits(s, key & 0xf); + return (key >> 4) & 0xfff; +} + +static void run(State *s, int sym) +{ + int length = bits(s, lenBits[sym]) + lenBase[sym]; + int dsym = decode(s, s->distcodes, s->tdist); + int offs = bits(s, distBits[dsym]) + distBase[dsym]; + copy(s, s->out - offs, length); +} + +static void block(State *s) +{ + for (;;) { + int sym = decode(s, s->litcodes, s->tlit); + if (sym < 256) *emit(s, 1) = (unsigned char)sym; + else if (sym > 256) run(s, sym-257); + else break; + } +} + +static void stored(State *s) +{ + // Uncompressed data block. + int len; + bits(s, s->count & 7); + len = bits(s, 16); + CHECK(((len^s->bits)&0xffff) == 0xffff); + CHECK(s->in + len <= s->inend); + + copy(s, s->in, len); + s->in += len; + bits(s, 16); +} + +static void fixed(State *s) +{ + // Fixed set of Huffman codes. + int n; + unsigned char lens[288+32]; + for (n= 0;n<=143;n++) lens[n] = 8; + for (n=144;n<=255;n++) lens[n] = 9; + for (n=256;n<=279;n++) lens[n] = 7; + for (n=280;n<=287;n++) lens[n] = 8; + for (n=0;n<32;n++) lens[288+n] = 5; + + // Build lit/dist trees. + s->tlit = build(s, s->litcodes, lens, 288); + s->tdist = build(s, s->distcodes, lens+288, 32); +} + +static void dynamic(State *s) +{ + int n, i, nlit, ndist, nlen; + unsigned char lenlens[19] = {0}, lens[288+32]; + nlit = 257 + bits(s, 5); + ndist = 1 + bits(s, 5); + nlen = 4 + bits(s, 4); + for (n=0;ntlen = build(s, s->LenCodes, lenlens, 19); + + // Decode code lengths. + for (n=0;nLenCodes, s->tlen); + switch (sym) { + case 16: for (i = 3+bits(s,2); i; i--,n++) lens[n] = lens[n-1]; break; + case 17: for (i = 3+bits(s,3); i; i--,n++) lens[n] = 0; break; + case 18: for (i = 11+bits(s,7); i; i--,n++) lens[n] = 0; break; + default: lens[n++] = (unsigned char)sym; break; + } + } + + // Build lit/dist trees. + s->tlit = build(s, s->litcodes, lens, nlit); + s->tdist = build(s, s->distcodes, lens+nlit, ndist); +} + +int tigrInflate(void *out, unsigned outlen, const void *in, unsigned inlen) +{ + int last; + State *s = (State *)calloc(1, sizeof(State)); + + // We assume we can buffer 2 extra bytes from off the end of 'in'. + s->in = (unsigned char *)in; s->inend = s->in + inlen + 2; + s->out = (unsigned char *)out; s->outend = s->out + outlen; + s->bits = 0; s->count = 0; bits(s, 0); + + if (setjmp(s->jmp) == 1) { + free(s); + return 0; + } + + do { + last = bits(s, 1); + switch (bits(s, 2)) { + case 0: stored(s); break; + case 1: fixed(s); block(s); break; + case 2: dynamic(s); block(s); break; + case 3: FAIL(); + } + } while(!last); + + free(s); + return 1; +} + +#undef CHECK +#undef FAIL + + */ \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/Support/Compression/zStd/zStdSharp.cs b/SabreTools.FileTypes/Compress/Support/Compression/zStd/zStdSharp.cs new file mode 100644 index 00000000..bbd568de --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Compression/zStd/zStdSharp.cs @@ -0,0 +1,64 @@ +using System.IO; + +namespace Compress.Support.Compression.zStd +{ + // C# version of zstd + internal class zStdSharp : ZstdSharp.DecompressionStream + { + long pos = 0; + public zStdSharp(Stream stream, int bufferSize = 0) : base(stream, bufferSize) + { + pos = 0; + } + + + public override int Read(byte[] buffer, int offset, int count) + { + int read = base.Read(buffer, offset, count); + pos += read; + return read; + } + + public override bool CanSeek => true; + + public override long Position { get => pos; set => base.Position = value; } + + public override long Seek(long offset, SeekOrigin origin) + { + long readLen; + switch (origin) + { + case SeekOrigin.Begin: + { + if (offset < pos) + { + // error connot go backwards + return -1; + } + readLen = offset - pos; + break; + } + + case SeekOrigin.Current: + { + readLen = offset; + break; + } + default: + { + // unknown origin + return -1; + } + } + + byte[] buffer = new byte[4096]; + while(readLen>0) + { + int count = readLen > 4096 ? 4096 : (int)readLen; + int read = Read(buffer, 0, count); + readLen -= read; + } + return pos; + } + } +} diff --git a/SabreTools.FileTypes/Compress/SevenZip/Filters/BCJ2Filter.cs b/SabreTools.FileTypes/Compress/Support/Filters/BCJ2Filter.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Filters/BCJ2Filter.cs rename to SabreTools.FileTypes/Compress/Support/Filters/BCJ2Filter.cs index 09664ee5..9acd496b 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Filters/BCJ2Filter.cs +++ b/SabreTools.FileTypes/Compress/Support/Filters/BCJ2Filter.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Compress.SevenZip.Filters +namespace Compress.Support.Filters { public class BCJ2Filter : Stream { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Filters/BCJFilter.cs b/SabreTools.FileTypes/Compress/Support/Filters/BCJFilter.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Filters/BCJFilter.cs rename to SabreTools.FileTypes/Compress/Support/Filters/BCJFilter.cs index 666ce0ce..ba114c2f 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Filters/BCJFilter.cs +++ b/SabreTools.FileTypes/Compress/Support/Filters/BCJFilter.cs @@ -1,6 +1,6 @@ using System.IO; -namespace Compress.SevenZip.Filters +namespace Compress.Support.Filters { public class BCJFilter : Filter { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Filters/Delta.cs b/SabreTools.FileTypes/Compress/Support/Filters/Delta.cs similarity index 98% rename from SabreTools.FileTypes/Compress/SevenZip/Filters/Delta.cs rename to SabreTools.FileTypes/Compress/Support/Filters/Delta.cs index 400e03a4..d639f46b 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Filters/Delta.cs +++ b/SabreTools.FileTypes/Compress/Support/Filters/Delta.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Compress.SevenZip.Filters +namespace Compress.Support.Filters { public class Delta : Stream { diff --git a/SabreTools.FileTypes/Compress/SevenZip/Filters/Filter.cs b/SabreTools.FileTypes/Compress/Support/Filters/Filter.cs similarity index 99% rename from SabreTools.FileTypes/Compress/SevenZip/Filters/Filter.cs rename to SabreTools.FileTypes/Compress/Support/Filters/Filter.cs index cd1f80d9..d72ee944 100644 --- a/SabreTools.FileTypes/Compress/SevenZip/Filters/Filter.cs +++ b/SabreTools.FileTypes/Compress/Support/Filters/Filter.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Compress.SevenZip.Filters +namespace Compress.Support.Filters { public abstract class Filter : Stream { diff --git a/SabreTools.FileTypes/Compress/Support/Utils/ArchiveExtract.cs b/SabreTools.FileTypes/Compress/Support/Utils/ArchiveExtract.cs new file mode 100644 index 00000000..75c66841 --- /dev/null +++ b/SabreTools.FileTypes/Compress/Support/Utils/ArchiveExtract.cs @@ -0,0 +1,115 @@ +using Compress.SevenZip; +using Compress.ZipFile; +using System.IO; +using Directory = RVIO.Directory; +using FileStream = RVIO.FileStream; +using Path = RVIO.Path; + +namespace Compress.Support.Utils +{ + public delegate void MessageBack(string message); + + public class ArchiveExtract + { + public MessageBack MessageCallBack = null; + + public ArchiveExtract() + { } + public ArchiveExtract(MessageBack messageCallBack) + { + MessageCallBack=messageCallBack; + } + + public bool FullExtract(string filename, string outDir) + { + MessageCallBack?.Invoke($"Processing file: {filename}"); + if (!string.IsNullOrEmpty(outDir)) + MessageCallBack?.Invoke($"Output dir: {outDir}"); + + string ext = Path.GetExtension(filename); + + ICompress z = null; + switch (ext.ToLower()) + { + case ".zip": + z = new Zip(); + break; + case ".7z": + z = new SevenZ(); + break; + + } + + if (z == null) + { + MessageCallBack?.Invoke($"Unknown file type {ext}"); + return false; + } + + ZipReturn zRet = z.ZipFileOpen(filename); + if (zRet != ZipReturn.ZipGood) + { + MessageCallBack?.Invoke($"Error opening archive {zRet}"); + return false; + } + + ulong buflen = 409600; + byte[] buffer = new byte[buflen]; + + for (int i = 0; i < z.LocalFilesCount(); i++) + { + LocalFile lf = z.GetLocalFile(i); + byte[] cread = null; + string filenameOut = lf.Filename; + if (lf.IsDirectory) + { + string outFullDir = Path.Combine(outDir, filenameOut.Substring(0, filenameOut.Length - 1).Replace('/', '\\')); + Directory.CreateDirectory(outFullDir); + continue; + } + else + { + MessageCallBack?.Invoke($"Extracting {filenameOut}"); + string fOut = Path.Combine(outDir, filenameOut.Replace('/', '\\')); + string dOut = Path.GetDirectoryName(fOut); + if (!string.IsNullOrWhiteSpace(dOut) && !Directory.Exists(dOut)) + Directory.CreateDirectory(dOut); + + int errorCode = FileStream.OpenFileWrite(fOut, out Stream sWrite); + if (errorCode != 0) + { + MessageCallBack?.Invoke($"Error opening outputfile {fOut}"); + } + + z.ZipFileOpenReadStream(i, out Stream sRead, out _); + + CRC crc = new(); + ulong sizeToGo = lf.UncompressedSize; + + while (sizeToGo > 0) + { + ulong sizeNow = sizeToGo > buflen ? buflen : sizeToGo; + int sizeRead = sRead.Read(buffer, 0, (int)sizeNow); + + crc.SlurpBlock(buffer, 0, sizeRead); + sWrite.Write(buffer, 0, sizeRead); + sizeToGo -= (ulong)sizeRead; + } + + sWrite.Close(); + sWrite.Dispose(); + + cread = crc.Crc32ResultB; + } + + byte[] fread = lf.CRC; + if (cread[0] != fread[0] || cread[1] != fread[1] || cread[2] != fread[2] || cread[3] != fread[3]) + { + MessageCallBack?.Invoke($"CRC error. Expected {fread.ToHex()} found {cread.ToHex()}"); + return false; + } + } + return true; + } + } +} diff --git a/SabreTools.FileTypes/Compress/Utils/CRC.cs b/SabreTools.FileTypes/Compress/Support/Utils/CRC.cs similarity index 99% rename from SabreTools.FileTypes/Compress/Utils/CRC.cs rename to SabreTools.FileTypes/Compress/Support/Utils/CRC.cs index a39d2b14..6de946e8 100644 --- a/SabreTools.FileTypes/Compress/Utils/CRC.cs +++ b/SabreTools.FileTypes/Compress/Support/Utils/CRC.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.Utils +namespace Compress.Support.Utils { public class CRC { diff --git a/SabreTools.FileTypes/Compress/Utils/CRCStream.cs b/SabreTools.FileTypes/Compress/Support/Utils/CRCStream.cs similarity index 99% rename from SabreTools.FileTypes/Compress/Utils/CRCStream.cs rename to SabreTools.FileTypes/Compress/Support/Utils/CRCStream.cs index 5c416e82..55b8c9e7 100644 --- a/SabreTools.FileTypes/Compress/Utils/CRCStream.cs +++ b/SabreTools.FileTypes/Compress/Support/Utils/CRCStream.cs @@ -1,6 +1,6 @@ using System; -namespace Compress.Utils +namespace Compress.Support.Utils { class CRCStream { diff --git a/SabreTools.FileTypes/Compress/Utils/Reporter.cs b/SabreTools.FileTypes/Compress/Support/Utils/Reporter.cs similarity index 75% rename from SabreTools.FileTypes/Compress/Utils/Reporter.cs rename to SabreTools.FileTypes/Compress/Support/Utils/Reporter.cs index a6a60588..7f5690b9 100644 --- a/SabreTools.FileTypes/Compress/Utils/Reporter.cs +++ b/SabreTools.FileTypes/Compress/Support/Utils/Reporter.cs @@ -1,5 +1,5 @@  -namespace Compress.Utils +namespace Compress.Support.Utils { public static class Reporter { @@ -30,6 +30,20 @@ namespace Compress.Utils return ret; } + public static string ToHex(this byte[] arr) + { + if (arr == null) + return "NULL"; + + string ret = ""; + for (int i = 0; i < arr.Length; i++) + { + ret += arr[i].ToString("X2"); + } + + return ret; + } + public static string ToHex(this uint? v) { diff --git a/SabreTools.FileTypes/Compress/ThreadReaders/ThreadCRC.cs b/SabreTools.FileTypes/Compress/ThreadReaders/ThreadCRC.cs index 5be20fe8..8e042dd5 100644 --- a/SabreTools.FileTypes/Compress/ThreadReaders/ThreadCRC.cs +++ b/SabreTools.FileTypes/Compress/ThreadReaders/ThreadCRC.cs @@ -1,11 +1,12 @@ using System; using System.Threading; +using Compress.Support.Utils; namespace Compress.ThreadReaders { public class ThreadCRC : IDisposable { - private Utils.CRC crc; + private CRC crc; private readonly AutoResetEvent _waitEvent; private readonly AutoResetEvent _outEvent; private readonly Thread _tWorker; @@ -17,7 +18,7 @@ namespace Compress.ThreadReaders public ThreadCRC() { - crc=new Utils.CRC(); + crc=new CRC(); _waitEvent = new AutoResetEvent(false); _outEvent = new AutoResetEvent(false); _finished = false; @@ -56,6 +57,13 @@ namespace Compress.ThreadReaders _size = size; _waitEvent.Set(); } + public void TriggerOnce(byte[] buffer, int size) + { + crc.Reset(); + _buffer = buffer; + _size = size; + _waitEvent.Set(); + } public void Wait() { diff --git a/SabreTools.FileTypes/Compress/ThreadReaders/ThreadLoadBuffer.cs b/SabreTools.FileTypes/Compress/ThreadReaders/ThreadReadBuffer.cs similarity index 94% rename from SabreTools.FileTypes/Compress/ThreadReaders/ThreadLoadBuffer.cs rename to SabreTools.FileTypes/Compress/ThreadReaders/ThreadReadBuffer.cs index 10633554..38de4b30 100644 --- a/SabreTools.FileTypes/Compress/ThreadReaders/ThreadLoadBuffer.cs +++ b/SabreTools.FileTypes/Compress/ThreadReaders/ThreadReadBuffer.cs @@ -4,7 +4,7 @@ using System.Threading; namespace Compress.ThreadReaders { - public class ThreadLoadBuffer : IDisposable + public class ThreadReadBuffer : IDisposable { private readonly AutoResetEvent _waitEvent; private readonly AutoResetEvent _outEvent; @@ -18,7 +18,7 @@ namespace Compress.ThreadReaders public int SizeRead; - public ThreadLoadBuffer(Stream ds) + public ThreadReadBuffer(Stream ds) { _waitEvent = new AutoResetEvent(false); _outEvent = new AutoResetEvent(false); diff --git a/SabreTools.FileTypes/Compress/ThreadReaders/ThreadWriteBuffer.cs b/SabreTools.FileTypes/Compress/ThreadReaders/ThreadWriteBuffer.cs new file mode 100644 index 00000000..9fab0e1d --- /dev/null +++ b/SabreTools.FileTypes/Compress/ThreadReaders/ThreadWriteBuffer.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; +using System.Threading; + +namespace Compress.ThreadReaders +{ + public class ThreadWriteBuffer : IDisposable + { + private readonly AutoResetEvent _waitEvent; + private readonly AutoResetEvent _outEvent; + private readonly Thread _tWorker; + + private byte[] _buffer; + private int _size; + private readonly Stream _ds; + private bool _finished; + public bool errorState; + + public int SizeRead; + + public ThreadWriteBuffer(Stream ds) + { + _waitEvent = new AutoResetEvent(false); + _outEvent = new AutoResetEvent(false); + _finished = false; + _ds = ds; + errorState = false; + + _tWorker = new Thread(MainLoop); + _tWorker.Start(); + } + + public void Dispose() + { + _waitEvent.Close(); + _outEvent.Close(); + } + + private void MainLoop() + { + while (true) + { + _waitEvent.WaitOne(); + if (_finished) + { + break; + } + try + { + _ds.Write(_buffer, 0, _size); + } + catch (Exception) + { + errorState = true; + } + _outEvent.Set(); + } + } + + public void Trigger(byte[] buffer, int size) + { + _buffer = buffer; + _size = size; + _waitEvent.Set(); + } + + public void Wait() + { + _outEvent.WaitOne(); + } + + public void Finish() + { + _finished = true; + _waitEvent.Set(); + _tWorker.Join(); + } + } +} \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/Utils/TimeStamps.cs b/SabreTools.FileTypes/Compress/TimeStamps.cs similarity index 82% rename from SabreTools.FileTypes/Compress/Utils/TimeStamps.cs rename to SabreTools.FileTypes/Compress/TimeStamps.cs index 11359f82..dcfb7ea2 100644 --- a/SabreTools.FileTypes/Compress/Utils/TimeStamps.cs +++ b/SabreTools.FileTypes/Compress/TimeStamps.cs @@ -1,4 +1,4 @@ -namespace Compress.Utils +namespace Compress { public class TimeStamps { diff --git a/SabreTools.FileTypes/Compress/Utils/DirUtil.cs b/SabreTools.FileTypes/Compress/Utils/DirUtil.cs deleted file mode 100644 index 34437626..00000000 --- a/SabreTools.FileTypes/Compress/Utils/DirUtil.cs +++ /dev/null @@ -1,24 +0,0 @@ -using RVIO; - -namespace Compress.Utils -{ - public static class DirUtil - { - public static void CreateDirForFile(string sFilename) - { - string strTemp = Path.GetDirectoryName(sFilename); - - if (string.IsNullOrEmpty(strTemp)) - { - return; - } - - if (Directory.Exists(strTemp)) - { - return; - } - - Directory.CreateDirectory(strTemp); - } - } -} \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/ZipEnums.cs b/SabreTools.FileTypes/Compress/ZipEnums.cs index be209cfd..faac2af5 100644 --- a/SabreTools.FileTypes/Compress/ZipEnums.cs +++ b/SabreTools.FileTypes/Compress/ZipEnums.cs @@ -30,9 +30,19 @@ namespace Compress ZipErrorRollBackFile, ZipTryingToAccessADirectory, ZipErrorWritingToOutputStream, + ZipTrrntzipIncorrectCompressionUsed, + ZipTrrntzipIncorrectFileOrder, + ZipTrrntzipIncorrectDirectoryAddedToZip, + ZipTrrntZipIncorrectDataStream, ZipUntested } + public enum OutputZipType + { + None, + TrrntZip, + rvZip + } public enum ZipOpenType { @@ -46,8 +56,8 @@ namespace Compress public enum ZipStatus { None = 0x0, - TrrntZip = 0x1, + TrrntZip = 0x1, // for Zip this is a Trrntzip , for 7zip this is an rv7Zip ExtraData = 0x2, - Trrnt7Zip = 0x4 + Trrnt7Zip = 0x4 // used by 7zip for a t7z } } diff --git a/SabreTools.FileTypes/Compress/ZipFile/Explode/Original.c b/SabreTools.FileTypes/Compress/ZipFile/Explode/Original.c deleted file mode 100644 index 5ab16589..00000000 --- a/SabreTools.FileTypes/Compress/ZipFile/Explode/Original.c +++ /dev/null @@ -1,613 +0,0 @@ -/* - Copyright (c) 1990-2007 Info-ZIP. All rights reserved. - See the accompanying file LICENSE, version 2007-Mar-04 or later - (the contents of which are also included in unzip.h) for terms of use. - If, for some reason, all these files are missing, the Info-ZIP license - also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html -*/ -/* explode.c -- by Mark Adler - version c17d, 01 December 2007 */ - - - /* Copyright history: - - Starting with UnZip 5.41 of 16-April-2000, this source file - is covered by the Info-Zip LICENSE cited above. - - Prior versions of this source file, found in UnZip source packages - up to UnZip 5.40, were put in the public domain. - The original copyright note by Mark Adler was: - "You can do whatever you like with this source file, - though I would prefer that if you modify it and - redistribute it that you include comments to that effect - with your name and the date. Thank you." - History: - vers date who what - ---- --------- -------------- ------------------------------------ - c1 30 Mar 92 M. Adler explode that uses huft_build from inflate - (this gives over a 70% speed improvement - over the original unimplode.c, which - decoded a bit at a time) - c2 4 Apr 92 M. Adler fixed bug for file sizes a multiple of 32k. - c3 10 Apr 92 M. Adler added a little memory tracking if DEBUG - c4 11 Apr 92 M. Adler added NOMEMCPY do kill use of memcpy() - c5 21 Apr 92 M. Adler added the WSIZE #define to allow reducing - the 32K window size for specialized - applications. - c6 31 May 92 M. Adler added typecasts to eliminate some warnings - c7 27 Jun 92 G. Roelofs added more typecasts. - c8 17 Oct 92 G. Roelofs changed ULONG/UWORD/byte to ulg/ush/uch. - c9 19 Jul 93 J. Bush added more typecasts (to return values); - made l[256] array static for Amiga. - c10 8 Oct 93 G. Roelofs added used_csize for diagnostics; added - buf and unshrink arguments to flush(); - undef'd various macros at end for Turbo C; - removed NEXTBYTE macro (now in unzip.h) - and bytebuf variable (not used); changed - memset() to memzero(). - c11 9 Jan 94 M. Adler fixed incorrect used_csize calculation. - c12 9 Apr 94 G. Roelofs fixed split comments on preprocessor lines - to avoid bug in Encore compiler. - c13 25 Aug 94 M. Adler fixed distance-length comment (orig c9 fix) - c14 22 Nov 95 S. Maxwell removed unnecessary "static" on auto array - c15 6 Jul 96 W. Haidinger added ulg typecasts to flush() calls. - c16 8 Feb 98 C. Spieler added ZCONST modifiers to const tables - and #ifdef DEBUG around debugging code. - c16b 25 Mar 98 C. Spieler modified DLL code for slide redirection. - c16d 05 Jul 99 C. Spieler take care of flush() return values and - stop processing in case of errors - c17 04 Feb 01 C. Spieler reorganized code to reduce repetitions - of large code parts; adapted huft decoding - to the changes in inflate's huft_build() - due to support of deflate64; fixed memory - leaks (huft tables were not free'd when - get_tree() failed). - c17b 16 Feb 02 C. Spieler changed type of the "extra lengths" array - "extra" from ush into uch (to save space) - c17c 10 Aug 04 NN file sizes use zoff_t. - c17d 01 Dec 07 C. Spieler type for file sizes changed from zoff_t - into zusz_t. - */ - - - /* - Explode imploded (PKZIP method 6 compressed) data. This compression - method searches for as much of the current string of bytes (up to a length - of ~320) in the previous 4K or 8K bytes. If it doesn't find any matches - (of at least length 2 or 3), it codes the next byte. Otherwise, it codes - the length of the matched string and its distance backwards from the - current position. Single bytes ("literals") are preceded by a one (a - single bit) and are either uncoded (the eight bits go directly into the - compressed stream for a total of nine bits) or Huffman coded with a - supplied literal code tree. If literals are coded, then the minimum match - length is three, otherwise it is two. - There are therefore four kinds of imploded streams: 8K search with coded - literals (min match = 3), 4K search with coded literals (min match = 3), - 8K with uncoded literals (min match = 2), and 4K with uncoded literals - (min match = 2). The kind of stream is identified in two bits of a - general purpose bit flag that is outside of the compressed stream. - Distance-length pairs for matched strings are preceded by a zero bit (to - distinguish them from literals) and are always coded. The distance comes - first and is either the low six (4K) or low seven (8K) bits of the - distance (uncoded), followed by the high six bits of the distance coded. - Then the length is six bits coded (0..63 + min match length), and if the - maximum such length is coded, then it's followed by another eight bits - (uncoded) to be added to the coded length. This gives a match length - range of 2..320 or 3..321 bytes. - The literal, length, and distance codes are all represented in a slightly - compressed form themselves. What is sent are the lengths of the codes for - each value, which is sufficient to construct the codes. Each byte of the - code representation is the code length (the low four bits representing - 1..16), and the number of values sequentially with that length (the high - four bits also representing 1..16). There are 256 literal code values (if - literals are coded), 64 length code values, and 64 distance code values, - in that order at the beginning of the compressed stream. Each set of code - values is preceded (redundantly) with a byte indicating how many bytes are - in the code description that follows, in the range 1..256. - The codes themselves are decoded using tables made by huft_build() from - the bit lengths. That routine and its comments are in the inflate.c - module. - */ - -#define __EXPLODE_C /* identifies this source module */ -#define UNZIP_INTERNAL -#include "unzip.h" /* must supply slide[] (uch) array and NEXTBYTE macro */ - -#ifndef WSIZE -# define WSIZE 0x8000 /* window size--must be a power of two, and */ -#endif /* at least 8K for zip's implode method */ - -#if (defined(DLL) && !defined(NO_SLIDE_REDIR)) -# define wszimpl (unsigned)(G._wsize) -#else -# if defined(USE_DEFLATE64) && defined(INT_16BIT) -# define wszimpl (unsigned)(WSIZE>>1) -# else /* !(USE_DEFLATE64 && INT_16BIT) */ -# define wszimpl WSIZE -# endif /* !(USE_DEFLATE64 && INT_16BIT) */ -#endif - - /* routines here */ -static int get_tree OF((__GPRO__ unsigned* l, unsigned n)); -static int explode_lit OF((__GPRO__ struct huft* tb, struct huft* tl, - struct huft* td, unsigned bb, unsigned bl, - unsigned bd, unsigned bdl)); -static int explode_nolit OF((__GPRO__ struct huft* tl, struct huft* td, - unsigned bl, unsigned bd, unsigned bdl)); -int explode OF((__GPRO)); - - -/* The implode algorithm uses a sliding 4K or 8K byte window on the - uncompressed stream to find repeated byte strings. This is implemented - here as a circular buffer. The index is updated simply by incrementing - and then and'ing with 0x0fff (4K-1) or 0x1fff (8K-1). Here, the 32K - buffer of inflate is used, and it works just as well to always have - a 32K circular buffer, so the index is anded with 0x7fff. This is - done to allow the window to also be used as the output buffer. */ - /* This must be supplied in an external module useable like "uch slide[8192];" - or "uch *slide;", where the latter would be malloc'ed. In unzip, slide[] - is actually a 32K area for use by inflate, which uses a 32K sliding window. - */ - - -#define INVALID_CODE 99 -#define IS_INVALID_CODE(c) ((c) == INVALID_CODE) - - /* Tables for length and distance */ -static ZCONST ush cplen2[] = -{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65 }; -static ZCONST ush cplen3[] = -{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, -19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66 }; -static ZCONST uch extra[] = -{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -8 }; -static ZCONST ush cpdist4[] = -{ 1, 65, 129, 193, 257, 321, 385, 449, 513, 577, 641, 705, -769, 833, 897, 961, 1025, 1089, 1153, 1217, 1281, 1345, 1409, 1473, -1537, 1601, 1665, 1729, 1793, 1857, 1921, 1985, 2049, 2113, 2177, -2241, 2305, 2369, 2433, 2497, 2561, 2625, 2689, 2753, 2817, 2881, -2945, 3009, 3073, 3137, 3201, 3265, 3329, 3393, 3457, 3521, 3585, -3649, 3713, 3777, 3841, 3905, 3969, 4033 }; -static ZCONST ush cpdist8[] = -{ 1, 129, 257, 385, 513, 641, 769, 897, 1025, 1153, 1281, -1409, 1537, 1665, 1793, 1921, 2049, 2177, 2305, 2433, 2561, 2689, -2817, 2945, 3073, 3201, 3329, 3457, 3585, 3713, 3841, 3969, 4097, -4225, 4353, 4481, 4609, 4737, 4865, 4993, 5121, 5249, 5377, 5505, -5633, 5761, 5889, 6017, 6145, 6273, 6401, 6529, 6657, 6785, 6913, -7041, 7169, 7297, 7425, 7553, 7681, 7809, 7937, 8065 }; - - -/* Macros for inflate() bit peeking and grabbing. - The usage is: - NEEDBITS(j) - x = b & mask_bits[j]; - DUMPBITS(j) - where NEEDBITS makes sure that b has at least j bits in it, and - DUMPBITS removes the bits from b. The macros use the variable k - for the number of bits in b. Normally, b and k are register - variables for speed. - */ - -#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE)<>=(n);k-=(n);} - -#define DECODEHUFT(htab, bits, mask) {\ - NEEDBITS((unsigned)(bits))\ - t = (htab) + ((~(unsigned)b)&(mask));\ - while (1) {\ - DUMPBITS(t->b)\ - if ((e=t->e) <= 32) break;\ - if (IS_INVALID_CODE(e)) return 1;\ - e &= 31;\ - NEEDBITS(e)\ - t = t->v.t + ((~(unsigned)b)&mask_bits[e]);\ - }\ -} - - -static int get_tree(__G__ l, n) -__GDEF -unsigned* l; /* bit lengths */ -unsigned n; /* number expected */ -/* Get the bit lengths for a code representation from the compressed - stream. If get_tree() returns 4, then there is an error in the data. - Otherwise zero is returned. */ -{ - unsigned i; /* bytes remaining in list */ - unsigned k; /* lengths entered */ - unsigned j; /* number of codes */ - unsigned b; /* bit length for those codes */ - - - /* get bit lengths */ - i = NEXTBYTE + 1; /* length/count pairs to read */ - k = 0; /* next code */ - do { - b = ((j = NEXTBYTE) & 0xf) + 1; /* bits in code (1..16) */ - j = ((j & 0xf0) >> 4) + 1; /* codes with those bits (1..16) */ - if (k + j > n) - return 4; /* don't overflow l[] */ - do { - l[k++] = b; - } while (--j); - } while (--i); - return k != n ? 4 : 0; /* should have read n of them */ -} - - - -static int explode_lit(__G__ tb, tl, td, bb, bl, bd, bdl) -__GDEF -struct huft* tb, * tl, * td; /* literal, length, and distance tables */ -unsigned bb, bl, bd; /* number of bits decoded by those */ -unsigned bdl; /* number of distance low bits */ -/* Decompress the imploded data using coded literals and a sliding - window (of size 2^(6+bdl) bytes). */ -{ - zusz_t s; /* bytes to decompress */ - register unsigned e; /* table entry flag/number of extra bits */ - unsigned n, d; /* length and index for copy */ - unsigned w; /* current window position */ - struct huft* t; /* pointer to table entry */ - unsigned mb, ml, md; /* masks for bb, bl, and bd bits */ - unsigned mdl; /* mask for bdl (distance lower) bits */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - unsigned u; /* true if unflushed */ - int retval = 0; /* error code returned: initialized to "no error" */ - - - /* explode the coded data */ - b = k = w = 0; /* initialize bit buffer, window */ - u = 1; /* buffer unflushed */ - mb = mask_bits[bb]; /* precompute masks for speed */ - ml = mask_bits[bl]; - md = mask_bits[bd]; - mdl = mask_bits[bdl]; - s = G.lrec.ucsize; - while (s > 0) /* do until ucsize bytes uncompressed */ - { - NEEDBITS(1) - if (b & 1) /* then literal--decode it */ - { - DUMPBITS(1) - s--; - DECODEHUFT(tb, bb, mb) /* get coded literal */ - redirSlide[w++] = (uch)t->v.n; - if (w == wszimpl) - { - if ((retval = flush(__G__ redirSlide, (ulg)w, 0)) != 0) - return retval; - w = u = 0; - } - } - else /* else distance/length */ - { - DUMPBITS(1) - NEEDBITS(bdl) /* get distance low bits */ - d = (unsigned)b & mdl; - DUMPBITS(bdl) - DECODEHUFT(td, bd, md) /* get coded distance high bits */ - d = w - d - t->v.n; /* construct offset */ - DECODEHUFT(tl, bl, ml) /* get coded length */ - n = t->v.n; - if (e) /* get length extra bits */ - { - NEEDBITS(8) - n += (unsigned)b & 0xff; - DUMPBITS(8) - } - - /* do the copy */ - s = (s > (zusz_t)n ? s - (zusz_t)n : 0); - do { -#if (defined(DLL) && !defined(NO_SLIDE_REDIR)) - if (G.redirect_slide) { - /* &= w/ wszimpl not needed and wrong if redirect */ - if (d >= wszimpl) - return 1; - e = wszimpl - (d > w ? d : w); - } - else -#endif - e = wszimpl - ((d &= wszimpl - 1) > w ? d : w); - if (e > n) e = n; - n -= e; - if (u && w <= d) - { - memzero(redirSlide + w, e); - w += e; - d += e; - } - else -#ifndef NOMEMCPY - if (w - d >= e) /* (this test assumes unsigned comparison) */ - { - memcpy(redirSlide + w, redirSlide + d, e); - w += e; - d += e; - } - else /* do it slow to avoid memcpy() overlap */ -#endif /* !NOMEMCPY */ - do { - redirSlide[w++] = redirSlide[d++]; - } while (--e); - if (w == wszimpl) - { - if ((retval = flush(__G__ redirSlide, (ulg)w, 0)) != 0) - return retval; - w = u = 0; - } - } while (n); - } - } - - /* flush out redirSlide */ - if ((retval = flush(__G__ redirSlide, (ulg)w, 0)) != 0) - return retval; - if (G.csize + G.incnt + (k >> 3)) /* should have read csize bytes, but */ - { /* sometimes read one too many: k>>3 compensates */ - G.used_csize = G.lrec.csize - G.csize - G.incnt - (k >> 3); - return 5; - } - return 0; -} - - - -static int explode_nolit(__G__ tl, td, bl, bd, bdl) -__GDEF -struct huft* tl, * td; /* length and distance decoder tables */ -unsigned bl, bd; /* number of bits decoded by tl[] and td[] */ -unsigned bdl; /* number of distance low bits */ -/* Decompress the imploded data using uncoded literals and a sliding - window (of size 2^(6+bdl) bytes). */ -{ - zusz_t s; /* bytes to decompress */ - register unsigned e; /* table entry flag/number of extra bits */ - unsigned n, d; /* length and index for copy */ - unsigned w; /* current window position */ - struct huft* t; /* pointer to table entry */ - unsigned ml, md; /* masks for bl and bd bits */ - unsigned mdl; /* mask for bdl (distance lower) bits */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - unsigned u; /* true if unflushed */ - int retval = 0; /* error code returned: initialized to "no error" */ - - - /* explode the coded data */ - b = k = w = 0; /* initialize bit buffer, window */ - u = 1; /* buffer unflushed */ - ml = mask_bits[bl]; /* precompute masks for speed */ - md = mask_bits[bd]; - mdl = mask_bits[bdl]; - s = G.lrec.ucsize; - while (s > 0) /* do until ucsize bytes uncompressed */ - { - NEEDBITS(1) - if (b & 1) /* then literal--get eight bits */ - { - DUMPBITS(1) - s--; - NEEDBITS(8) - redirSlide[w++] = (uch)b; - if (w == wszimpl) - { - if ((retval = flush(__G__ redirSlide, (ulg)w, 0)) != 0) - return retval; - w = u = 0; - } - DUMPBITS(8) - } - else /* else distance/length */ - { - DUMPBITS(1) - NEEDBITS(bdl) /* get distance low bits */ - d = (unsigned)b & mdl; - DUMPBITS(bdl) - DECODEHUFT(td, bd, md) /* get coded distance high bits */ - d = w - d - t->v.n; /* construct offset */ - DECODEHUFT(tl, bl, ml) /* get coded length */ - n = t->v.n; - if (e) /* get length extra bits */ - { - NEEDBITS(8) - n += (unsigned)b & 0xff; - DUMPBITS(8) - } - - /* do the copy */ - s = (s > (zusz_t)n ? s - (zusz_t)n : 0); - do { -#if (defined(DLL) && !defined(NO_SLIDE_REDIR)) - if (G.redirect_slide) { - /* &= w/ wszimpl not needed and wrong if redirect */ - if (d >= wszimpl) - return 1; - e = wszimpl - (d > w ? d : w); - } - else -#endif - e = wszimpl - ((d &= wszimpl - 1) > w ? d : w); - if (e > n) e = n; - n -= e; - if (u && w <= d) - { - memzero(redirSlide + w, e); - w += e; - d += e; - } - else -#ifndef NOMEMCPY - if (w - d >= e) /* (this test assumes unsigned comparison) */ - { - memcpy(redirSlide + w, redirSlide + d, e); - w += e; - d += e; - } - else /* do it slow to avoid memcpy() overlap */ -#endif /* !NOMEMCPY */ - do { - redirSlide[w++] = redirSlide[d++]; - } while (--e); - if (w == wszimpl) - { - if ((retval = flush(__G__ redirSlide, (ulg)w, 0)) != 0) - return retval; - w = u = 0; - } - } while (n); - } - } - - /* flush out redirSlide */ - if ((retval = flush(__G__ redirSlide, (ulg)w, 0)) != 0) - return retval; - if (G.csize + G.incnt + (k >> 3)) /* should have read csize bytes, but */ - { /* sometimes read one too many: k>>3 compensates */ - G.used_csize = G.lrec.csize - G.csize - G.incnt - (k >> 3); - return 5; - } - return 0; -} - - - -int explode(__G) -__GDEF -/* Explode an imploded compressed stream. Based on the general purpose - bit flag, decide on coded or uncoded literals, and an 8K or 4K sliding - window. Construct the literal (if any), length, and distance codes and - the tables needed to decode them (using huft_build() from inflate.c), - and call the appropriate routine for the type of data in the remainder - of the stream. The four routines are nearly identical, differing only - in whether the literal is decoded or simply read in, and in how many - bits are read in, uncoded, for the low distance bits. */ -{ - unsigned r; /* return codes */ - struct huft* tb; /* literal code table */ - struct huft* tl; /* length code table */ - struct huft* td; /* distance code table */ - unsigned bb; /* bits for tb */ - unsigned bl; /* bits for tl */ - unsigned bd; /* bits for td */ - unsigned bdl; /* number of uncoded lower distance bits */ - unsigned l[256]; /* bit lengths for codes */ - -#if (defined(DLL) && !defined(NO_SLIDE_REDIR)) - if (G.redirect_slide) - /* For 16-bit systems, it has already been checked at DLL entrance that - * the buffer size in G.redirect_size does not exceed unsigned range. - */ - G._wsize = G.redirect_size, redirSlide = G.redirect_buffer; - else -#if defined(USE_DEFLATE64) && defined(INT_16BIT) - /* For systems using 16-bit ints, reduce the used buffer size below - * the limit of "unsigned int" numbers range. - */ - G._wsize = WSIZE >> 1, redirSlide = slide; -#else /* !(USE_DEFLATE64 && INT_16BIT) */ - G._wsize = WSIZE, redirSlide = slide; -#endif /* !(USE_DEFLATE64 && INT_16BIT) */ -#endif /* DLL && !NO_SLIDE_REDIR */ - - /* Tune base table sizes. Note: I thought that to truly optimize speed, - I would have to select different bl, bd, and bb values for different - compressed file sizes. I was surprised to find out that the values of - 7, 7, and 9 worked best over a very wide range of sizes, except that - bd = 8 worked marginally better for large compressed sizes. */ - bl = 7; - bd = (G.csize + G.incnt) > 200000L ? 8 : 7; - -#ifdef DEBUG - G.hufts = 0; /* initialize huft's malloc'ed */ -#endif - - if (G.lrec.general_purpose_bit_flag & 4) - /* With literal tree--minimum match length is 3 */ - { - bb = 9; /* base table size for literals */ - if ((r = get_tree(__G__ l, 256)) != 0) - return (int)r; - if ((r = huft_build(__G__ l, 256, 256, NULL, NULL, &tb, &bb)) != 0) - { - if (r == 1) - huft_free(tb); - return (int)r; - } - if ((r = get_tree(__G__ l, 64)) != 0) { - huft_free(tb); - return (int)r; - } - if ((r = huft_build(__G__ l, 64, 0, cplen3, extra, &tl, &bl)) != 0) - { - if (r == 1) - huft_free(tl); - huft_free(tb); - return (int)r; - } - } - else - /* No literal tree--minimum match length is 2 */ - { - tb = (struct huft*)NULL; - if ((r = get_tree(__G__ l, 64)) != 0) - return (int)r; - if ((r = huft_build(__G__ l, 64, 0, cplen2, extra, &tl, &bl)) != 0) - { - if (r == 1) - huft_free(tl); - return (int)r; - } - } - - if ((r = get_tree(__G__ l, 64)) != 0) { - huft_free(tl); - if (tb != (struct huft*)NULL) huft_free(tb); - return (int)r; - } - if (G.lrec.general_purpose_bit_flag & 2) /* true if 8K */ - { - bdl = 7; - r = huft_build(__G__ l, 64, 0, cpdist8, extra, &td, &bd); - } - else /* else 4K */ - { - bdl = 6; - r = huft_build(__G__ l, 64, 0, cpdist4, extra, &td, &bd); - } - if (r != 0) - { - if (r == 1) - huft_free(td); - huft_free(tl); - if (tb != (struct huft*)NULL) huft_free(tb); - return (int)r; - } - - if (tb != NULL) { - r = explode_lit(__G__ tb, tl, td, bb, bl, bd, bdl); - huft_free(tb); - } - else { - r = explode_nolit(__G__ tl, td, bl, bd, bdl); - } - - huft_free(td); - huft_free(tl); - Trace((stderr, "<%u > ", G.hufts)); - return (int)r; -} - -/* so explode.c and inflate.c can be compiled together into one object: */ -#undef DECODEHUFT -#undef NEEDBITS -#undef DUMPBITS -#undef wszimpl \ No newline at end of file diff --git a/SabreTools.FileTypes/Compress/ZipFile/Zip.cs b/SabreTools.FileTypes/Compress/ZipFile/Zip.cs index 52a69cc6..dd5c2770 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/Zip.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/Zip.cs @@ -11,108 +11,69 @@ namespace Compress.ZipFile { public partial class Zip : ICompress { - private readonly List _localFiles = new List(); - + private readonly List _localFiles = new(); private FileInfo _zipFileInfo; - - private byte[] _fileComment; private Stream _zipFs; private uint _localFilesCount; + private ulong _centralDirStart; + private ulong _centralDirSize; + private ulong _endOfCentralDir64; + private bool _zip64; public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : ""; public long TimeStamp => _zipFileInfo?.LastWriteTime ?? 0; - public ZipOpenType ZipOpen { get; private set; } - - - public ZipStatus ZipStatus { get; set; } + public byte[] FileComment { get; private set; } public Zip() { - ZipUtils.EncodeSetup(); + CompressUtils.EncodeSetup(); } + public ZipOpenType ZipOpen { get; private set; } + + + public ZipStatus ZipStatus { get; private set; } + + // If writeZipType == trrntzip then it will force a trrntzip file and error out if not. + // If writeZipType == None it will still try and make a trrntzip if everything is supplied matching trrntzip parameters. + private OutputZipType writeZipType = OutputZipType.None; + + private ulong offset = 0; + public int LocalFilesCount() { return _localFiles.Count; } - public string Filename(int i) + public LocalFile GetLocalFile(int i) { - return _localFiles[i].FileName; + return _localFiles[i]; } - - public ulong UncompressedSize(int i) - { - return _localFiles[i].UncompressedSize; - } - - public ulong? LocalHeader(int i) - { - return (_localFiles[i].GeneralPurposeBitFlag & 8) == 0 ? (ulong?)_localFiles[i].RelativeOffsetOfLocalHeader : null; - } - - public byte[] CRC32(int i) - { - return _localFiles[i].CRC; - } - - public bool IsDirectory(int i) - { - try - { - if (_localFiles[i].UncompressedSize != 0) - return false; - string filename = _localFiles[i].FileName; - char lastChar = filename[filename.Length - 1]; - return lastChar == '/' || lastChar == '\\'; - } - catch (Exception ex) - { - ArgumentException argEx = new ArgumentException("Error in file " + _zipFileInfo?.FullName + " : " + ex.Message, ex.InnerException); - throw argEx; - } - - } - - public long LastModified(int i) - { - return _localFiles[i].DateTime; - } - - public long? Created(int i) - { - return _localFiles[i].DateTimeCreate; - } - - public long? Accessed(int i) - { - return _localFiles[i].DateTimeAccess; - } - + public void ZipFileClose() { - if (ZipOpen == ZipOpenType.Closed) + switch (ZipOpen) { - return; - } + case ZipOpenType.Closed: + return; - if (ZipOpen == ZipOpenType.OpenRead) - { - zipFileCloseRead(); - return; - } + case ZipOpenType.OpenRead: + zipFileCloseRead(); + return; - zipFileCloseWrite(); + default: + zipFileCloseWrite(); + break; + } } - public byte[] Filecomment => _fileComment; /* public void BreakTrrntZip(string filename) diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipCentralDir.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipCentralDir.cs index e5b3e9ff..bb5575d9 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipCentralDir.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipCentralDir.cs @@ -56,65 +56,65 @@ namespace Compress.ZipFile private ZipReturn EndOfCentralDirRead() { - using (BinaryReader zipBr = new BinaryReader(_zipFs, Encoding.UTF8, true)) + using BinaryReader zipBr = new(_zipFs, Encoding.UTF8, true); + uint thisSignature = zipBr.ReadUInt32(); + if (thisSignature != EndOfCentralDirSignature) { - uint thisSignature = zipBr.ReadUInt32(); - if (thisSignature != EndOfCentralDirSignature) - { - return ZipReturn.ZipEndOfCentralDirectoryError; - } - - ushort tUShort = zipBr.ReadUInt16(); // NumberOfThisDisk - if (tUShort != 0) - { - return ZipReturn.ZipEndOfCentralDirectoryError; - } - - tUShort = zipBr.ReadUInt16(); // NumberOfThisDiskCenterDir - if (tUShort != 0) - { - return ZipReturn.ZipEndOfCentralDirectoryError; - } - - _localFilesCount = zipBr.ReadUInt16(); // TotalNumberOfEntriesDisk - - tUShort = zipBr.ReadUInt16(); // TotalNumber of entries in the central directory - if (tUShort != _localFilesCount) - { - return ZipReturn.ZipEndOfCentralDirectoryError; - } - - _centralDirSize = zipBr.ReadUInt32(); // SizeOfCentralDir - _centralDirStart = zipBr.ReadUInt32(); // Offset - - ushort zipFileCommentLength = zipBr.ReadUInt16(); - - _fileComment = zipBr.ReadBytes(zipFileCommentLength); - - if (_zipFs.Position != _zipFs.Length) - { - ZipStatus |= ZipStatus.ExtraData; - } - - return ZipReturn.ZipGood; + return ZipReturn.ZipEndOfCentralDirectoryError; } + + ushort tUShort = zipBr.ReadUInt16(); // NumberOfThisDisk + if (tUShort != 0) + { + return ZipReturn.ZipEndOfCentralDirectoryError; + } + + tUShort = zipBr.ReadUInt16(); // NumberOfThisDiskCenterDir + if (tUShort != 0) + { + return ZipReturn.ZipEndOfCentralDirectoryError; + } + + _localFilesCount = zipBr.ReadUInt16(); // TotalNumberOfEntriesDisk + + tUShort = zipBr.ReadUInt16(); // TotalNumber of entries in the central directory + if (tUShort != _localFilesCount) + { + return ZipReturn.ZipEndOfCentralDirectoryError; + } + + _centralDirSize = zipBr.ReadUInt32(); // SizeOfCentralDir + _centralDirStart = zipBr.ReadUInt32(); // Offset + + ushort zipFileCommentLength = zipBr.ReadUInt16(); + + FileComment = zipBr.ReadBytes(zipFileCommentLength); + + if (_zipFs.Position != _zipFs.Length) + { + ZipStatus |= ZipStatus.ExtraData; + } + if (offset != 0) + { + ZipStatus |= ZipStatus.ExtraData; + } + + return ZipReturn.ZipGood; } private void EndOfCentralDirWrite() { - using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) - { - bw.Write(EndOfCentralDirSignature); - bw.Write((ushort)0); // NumberOfThisDisk - bw.Write((ushort)0); // NumberOfThisDiskCenterDir - bw.Write((ushort)(_localFiles.Count >= 0xffff ? 0xffff : _localFiles.Count)); // TotalNumberOfEntriesDisk - bw.Write((ushort)(_localFiles.Count >= 0xffff ? 0xffff : _localFiles.Count)); // TotalNumber of entries in the central directory - bw.Write((uint)(_centralDirSize >= 0xffffffff ? 0xffffffff : _centralDirSize)); - bw.Write((uint)(_centralDirStart >= 0xffffffff ? 0xffffffff : _centralDirStart)); - bw.Write((ushort)_fileComment.Length); - bw.Write(_fileComment, 0, _fileComment.Length); - } + using BinaryWriter bw = new(_zipFs, Encoding.UTF8, true); + bw.Write(EndOfCentralDirSignature); + bw.Write((ushort)0); // NumberOfThisDisk + bw.Write((ushort)0); // NumberOfThisDiskCenterDir + bw.Write((ushort)(_localFiles.Count >= 0xffff ? 0xffff : _localFiles.Count)); // TotalNumberOfEntriesDisk + bw.Write((ushort)(_localFiles.Count >= 0xffff ? 0xffff : _localFiles.Count)); // TotalNumber of entries in the central directory + bw.Write((uint)(_centralDirSize >= 0xffffffff ? 0xffffffff : _centralDirSize)); + bw.Write((uint)(_centralDirStart >= 0xffffffff ? 0xffffffff : _centralDirStart)); + bw.Write((ushort)FileComment.Length); + bw.Write(FileComment, 0, FileComment.Length); } @@ -122,116 +122,106 @@ namespace Compress.ZipFile private ZipReturn Zip64EndOfCentralDirRead() { - using (BinaryReader zipBr = new BinaryReader(_zipFs, Encoding.UTF8, true)) + using BinaryReader zipBr = new(_zipFs, Encoding.UTF8, true); + uint thisSignature = zipBr.ReadUInt32(); + if (thisSignature != Zip64EndOfCentralDirSignature) { - uint thisSignature = zipBr.ReadUInt32(); - if (thisSignature != Zip64EndOfCentralDirSignature) - { - return ZipReturn.ZipEndOfCentralDirectoryError; - } - - //_zip64 = true; - ulong tULong = zipBr.ReadUInt64(); // Size of zip64 end of central directory record - if (tULong != 44) - { - return ZipReturn.Zip64EndOfCentralDirError; - } - - zipBr.ReadUInt16(); // version made by - - ushort tUShort = zipBr.ReadUInt16(); // version needed to extract - if (tUShort != 45) - { - return ZipReturn.Zip64EndOfCentralDirError; - } - - uint tUInt = zipBr.ReadUInt32(); // number of this disk - if (tUInt != 0) - { - return ZipReturn.Zip64EndOfCentralDirError; - } - - tUInt = zipBr.ReadUInt32(); // number of the disk with the start of the central directory - if (tUInt != 0) - { - return ZipReturn.Zip64EndOfCentralDirError; - } - - _localFilesCount = - (uint)zipBr.ReadUInt64(); // total number of entries in the central directory on this disk - - tULong = zipBr.ReadUInt64(); // total number of entries in the central directory - if (tULong != _localFilesCount) - { - return ZipReturn.Zip64EndOfCentralDirError; - } - - _zip64 = true; - _centralDirSize = zipBr.ReadUInt64(); // size of central directory - - _centralDirStart = zipBr.ReadUInt64(); // offset of start of central directory with respect to the starting disk number - - return ZipReturn.ZipGood; + return ZipReturn.ZipEndOfCentralDirectoryError; } + + ulong tULong = zipBr.ReadUInt64(); // Size of zip64 end of central directory record + if (tULong != 44) + { + return ZipReturn.Zip64EndOfCentralDirError; + } + + zipBr.ReadUInt16(); // version made by + + ushort tUShort = zipBr.ReadUInt16(); // version needed to extract + if (tUShort != 45) + { + return ZipReturn.Zip64EndOfCentralDirError; + } + + uint tUInt = zipBr.ReadUInt32(); // number of this disk + if (tUInt != 0) + { + return ZipReturn.Zip64EndOfCentralDirError; + } + + tUInt = zipBr.ReadUInt32(); // number of the disk with the start of the central directory + if (tUInt != 0) + { + return ZipReturn.Zip64EndOfCentralDirError; + } + + _localFilesCount = (uint)zipBr.ReadUInt64(); // total number of entries in the central directory on this disk + + tULong = zipBr.ReadUInt64(); // total number of entries in the central directory + if (tULong != _localFilesCount) + { + return ZipReturn.Zip64EndOfCentralDirError; + } + + _zip64 = true; + _centralDirSize = zipBr.ReadUInt64(); // size of central directory + + _centralDirStart = zipBr.ReadUInt64(); // offset of start of central directory with respect to the starting disk number + + return ZipReturn.ZipGood; } private void Zip64EndOfCentralDirWrite() { - using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) - { - bw.Write(Zip64EndOfCentralDirSignature); - bw.Write((ulong)44); // Size of zip64 end of central directory record - bw.Write((ushort)45); // version made by - bw.Write((ushort)45); // version needed to extract - bw.Write((uint)0); // number of this disk - bw.Write((uint)0); // number of the disk with the start of the central directory - bw.Write((ulong)_localFiles.Count); // total number of entries in the central directory on this disk - bw.Write((ulong)_localFiles.Count); // total number of entries in the central directory - bw.Write(_centralDirSize); // size of central directory - bw.Write(_centralDirStart); // offset of start of central directory with respect to the starting disk number - } + using BinaryWriter bw = new(_zipFs, Encoding.UTF8, true); + bw.Write(Zip64EndOfCentralDirSignature); + bw.Write((ulong)44); // Size of zip64 end of central directory record + bw.Write((ushort)45); // version made by + bw.Write((ushort)45); // version needed to extract + bw.Write((uint)0); // number of this disk + bw.Write((uint)0); // number of the disk with the start of the central directory + bw.Write((ulong)_localFiles.Count); // total number of entries in the central directory on this disk + bw.Write((ulong)_localFiles.Count); // total number of entries in the central directory + bw.Write(_centralDirSize); // size of central directory + bw.Write(_centralDirStart); // offset of start of central directory with respect to the starting disk number } private ZipReturn Zip64EndOfCentralDirectoryLocatorRead() { - using (BinaryReader zipBr = new BinaryReader(_zipFs, Encoding.UTF8, true)) + using BinaryReader zipBr = new(_zipFs, Encoding.UTF8, true); + uint thisSignature = zipBr.ReadUInt32(); + if (thisSignature != Zip64EndOfCentralDirectoryLocator) { - uint thisSignature = zipBr.ReadUInt32(); - if (thisSignature != Zip64EndOfCentralDirectoryLocator) - { - return ZipReturn.ZipEndOfCentralDirectoryError; - } - - uint tUInt = zipBr.ReadUInt32(); // number of the disk with the start of the zip64 end of central directory - if (tUInt != 0) - { - return ZipReturn.Zip64EndOfCentralDirectoryLocatorError; - } - - _endOfCenterDir64 = zipBr.ReadUInt64(); // relative offset of the zip64 end of central directory record - - tUInt = zipBr.ReadUInt32(); // total number of disks - if (tUInt != 1) - { - return ZipReturn.Zip64EndOfCentralDirectoryLocatorError; - } - - return ZipReturn.ZipGood; + return ZipReturn.ZipEndOfCentralDirectoryError; } + + uint tUInt = zipBr.ReadUInt32(); // number of the disk with the start of the zip64 end of central directory + if (tUInt != 0) + { + return ZipReturn.Zip64EndOfCentralDirectoryLocatorError; + } + + _endOfCentralDir64 = zipBr.ReadUInt64(); // relative offset of the zip64 end of central directory record + + tUInt = zipBr.ReadUInt32(); // total number of disks + if (tUInt > 1) + { + return ZipReturn.Zip64EndOfCentralDirectoryLocatorError; + } + + return ZipReturn.ZipGood; } private void Zip64EndOfCentralDirectoryLocatorWrite() { - using (BinaryWriter bw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) - { - bw.Write(Zip64EndOfCentralDirectoryLocator); - bw.Write((uint)0); // number of the disk with the start of the zip64 end of central directory - bw.Write(_endOfCenterDir64); // relative offset of the zip64 end of central directory record - bw.Write((uint)1); // total number of disks - } + using BinaryWriter bw = new(_zipFs, Encoding.UTF8, true); + bw.Write(Zip64EndOfCentralDirectoryLocator); + bw.Write((uint)0); // number of the disk with the start of the zip64 end of central directory + bw.Write(_endOfCentralDir64); // relative offset of the zip64 end of central directory record + bw.Write((uint)1); // total number of disks } diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipExtraField.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipExtraField.cs index 33df4e78..8c78db81 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipExtraField.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipExtraField.cs @@ -1,605 +1,601 @@ using System; using System.Diagnostics; using System.Text; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress.ZipFile { - internal partial class LocalFile + internal static class ZipExtraField { - private static class ZipExtraField + public static ZipReturn ReadExtraField(byte[] extraField, byte[] bFileName, + LocalFile lf, ref ulong compressedSize, ref ulong relativeOffsetOfLocalHeader, + bool centralDir) { - public static ZipReturn ReadLocalExtraField(byte[] extraField, byte[] bFileName, LocalFile lf, bool centralDir) + int extraFieldLength = extraField.Length; + + lf.SetStatus(LocalFileStatus.Zip64,false); + lf.ModifiedTime = null; + lf.AccessedTime = null; + lf.CreatedTime = null; + + int blockPos = 0; + + // the +4 is added as there must be at least 4 bytes more to read to get the next blocks type and blockLength + // this is added to handle some incorrectly format extra data fields. + + while (blockPos <= extraFieldLength - 4) { - int extraFieldLength = extraField.Length; + ushort type = BitConverter.ToUInt16(extraField, blockPos); + blockPos += 2; + ushort blockLength = BitConverter.ToUInt16(extraField, blockPos); + blockPos += 2; - lf.Zip64 = false; - int blockPos = 0; - while (extraFieldLength > blockPos) + int pos = blockPos; + switch (type) { - ushort type = BitConverter.ToUInt16(extraField, blockPos); - blockPos += 2; - ushort blockLength = BitConverter.ToUInt16(extraField, blockPos); - blockPos += 2; + /* + -Zip64 Extended Information Extra Field (0x0001): + =============================================== - int pos = blockPos; - switch (type) - { - /* - -Zip64 Extended Information Extra Field (0x0001): - =============================================== - - The following is the layout of the zip64 extended - information "extra" block. If one of the size or - offset fields in the Local or Central directory - record is too small to hold the required data, - a zip64 extended information record is created. - The order of the fields in the zip64 extended - information record is fixed, but the fields will - only appear if the corresponding Local or Central - directory record field is set to 0xFFFF or 0xFFFFFFFF. - - Note: all fields stored in Intel low-byte/high-byte order. - - Value Size Description - ----- ---- ----------- - (ZIP64) 0x0001 2 bytes Tag for this "extra" block type - Size 2 bytes Size of this "extra" block - Original - Size 8 bytes Original uncompressed file size - Compressed - Size 8 bytes Size of compressed data - Relative Header - Offset 8 bytes Offset of local header record - Disk Start - Number 4 bytes Number of the disk on which - this file starts - - This entry in the Local header must include BOTH original - and compressed file size fields. If encrypting the - central directory and bit 13 of the general purpose bit - flag is set indicating masking, the value stored in the - Local Header for the original file size will be zero. - */ - case 0x0001: - lf.Zip64 = true; - if (centralDir) + The following is the layout of the zip64 extended + information "extra" block. If one of the size or + offset fields in the Local or Central directory + record is too small to hold the required data, + a zip64 extended information record is created. + The order of the fields in the zip64 extended + information record is fixed, but the fields will + only appear if the corresponding Local or Central + directory record field is set to 0xFFFF or 0xFFFFFFFF. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- + (ZIP64) 0x0001 2 bytes Tag for this "extra" block type + Size 2 bytes Size of this "extra" block + Original + Size 8 bytes Original uncompressed file size + Compressed + Size 8 bytes Size of compressed data + Relative Header + Offset 8 bytes Offset of local header record + Disk Start + Number 4 bytes Number of the disk on which + this file starts + + This entry in the Local header must include BOTH original + and compressed file size fields. If encrypting the + central directory and bit 13 of the general purpose bit + flag is set indicating masking, the value stored in the + Local Header for the original file size will be zero. + */ + case 0x0001: + lf.SetStatus(LocalFileStatus.Zip64); + if (lf.UncompressedSize == 0xffffffff) + { + lf.UncompressedSize = BitConverter.ToUInt64(extraField, pos); + pos += 8; + } + if (compressedSize == 0xffffffff) + { + compressedSize = BitConverter.ToUInt64(extraField, pos); + pos += 8; + } + if (centralDir) + { + if (relativeOffsetOfLocalHeader == 0xffffffff) { - if (lf.UncompressedSize == 0xffffffff) - { - lf.UncompressedSize = BitConverter.ToUInt64(extraField, pos); - pos += 8; - } - if (lf._compressedSize == 0xffffffff) - { - lf._compressedSize = BitConverter.ToUInt64(extraField, pos); - pos += 8; - } - if (lf.RelativeOffsetOfLocalHeader == 0xffffffff) - { - lf.RelativeOffsetOfLocalHeader = BitConverter.ToUInt64(extraField, pos); - pos += 8; - } - } - else - { - if (lf._localHeaderUncompressedSize == 0xffffffff) - { - lf._localHeaderUncompressedSize = BitConverter.ToUInt64(extraField, pos); - pos += 8; - } - if (lf._localHeaderCompressedSize == 0xffffffff) - { - lf._localHeaderCompressedSize = BitConverter.ToUInt64(extraField, pos); - pos += 8; - } - } - - break; - - - /* PKWARE's authenticity verification */ - case 0x0007: // Not Needed - break; - - - /* - -PKWARE Win95/WinNT Extra Field (0x000a): - ======================================= - - The following description covers PKWARE's "NTFS" attributes - "extra" block, introduced with the release of PKZIP 2.50 for - Windows. (Last Revision 20001118) - - (Note: At this time the Mtime, Atime and Ctime values may - be used on any WIN32 system.) - [Info-ZIP note: In the current implementations, this field has - a fixed total data size of 32 bytes and is only stored as local - extra field.] - - Value Size Description - ----- ---- ----------- - (NTFS) 0x000a Short Tag for this "extra" block type - TSize Short Total Data Size for this block - Reserved Long for future use - Tag1 Short NTFS attribute tag value #1 - Size1 Short Size of attribute #1, in bytes - (var.) SubSize1 Attribute #1 data - . - . - . - TagN Short NTFS attribute tag value #N - SizeN Short Size of attribute #N, in bytes - (var.) SubSizeN Attribute #N data - - For NTFS, values for Tag1 through TagN are as follows: - (currently only one set of attributes is defined for NTFS) - - Tag Size Description - ----- ---- ----------- - 0x0001 2 bytes Tag for attribute #1 - Size1 2 bytes Size of attribute #1, in bytes (24) - Mtime 8 bytes 64-bit NTFS file last modification time - Atime 8 bytes 64-bit NTFS file last access time - Ctime 8 bytes 64-bit NTFS file creation time - - The total length for this block is 28 bytes, resulting in a - fixed size value of 32 for the TSize field of the NTFS block. - - The NTFS filetimes are 64-bit unsigned integers, stored in Intel - (least significant byte first) byte order. They determine the - number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch", - which is "01-Jan-1601 00:00:00 UTC". - */ - case 0x000a: - pos += 4; // Reserved Long for future use - int tag1 = BitConverter.ToInt16(extraField, pos); // Tag1 Short NTFS attribute tag value #1 - pos += 2; - int size1 = BitConverter.ToInt16(extraField, pos); // Size1 Short Size of attribute #1, in bytes - pos += 2; - if (tag1 == 0x0001 && size1 == 24 - ) // (currently only one set of attributes is defined for NTFS) - { - lf.mTime = ZipUtils.FileTimeToUTCTime(BitConverter.ToInt64(extraField, pos)); // Mtime 8 bytes 64-bit NTFS file last modification time + relativeOffsetOfLocalHeader = BitConverter.ToUInt64(extraField, pos); pos += 8; - lf.aTime = ZipUtils.FileTimeToUTCTime(BitConverter.ToInt64(extraField, pos)); // Atime 8 bytes 64-bit NTFS file last access time - pos += 8; - lf.cTime = ZipUtils.FileTimeToUTCTime(BitConverter.ToInt64(extraField, pos)); // Ctime 8 bytes 64-bit NTFS file creation time - pos += 8; - - Debug.WriteLine("modtime = " + new DateTime((long)lf.mTime)); - Debug.WriteLine("Acctime = " + new DateTime((long)lf.aTime)); - Debug.WriteLine("Cretime = " + new DateTime((long)lf.cTime)); } - - break; + } + break; - /* - -Windows NT Security Descriptor Extra Field (0x4453): - =================================================== - - The following is the layout of the NT Security Descriptor (another - type of ACL) extra block. (Last Revision 19960922) - - Local-header version: - - Value Size Description - ----- ---- ----------- - (SD) 0x4453 Short tag for this extra block type ("SD") - TSize Short total data size for this block - BSize Long uncompressed SD data size - Version Byte version of uncompressed SD data format - CType Short compression type - EACRC Long CRC value for uncompressed SD data - (var.) variable compressed SD data - - Central-header version: - - Value Size Description - ----- ---- ----------- - (SD) 0x4453 Short tag for this extra block type ("SD") - TSize Short total data size for this block (4) - BSize Long size of uncompressed local SD data - - The value of CType is interpreted according to the "compression - method" section above; i.e., 0 for stored, 8 for deflated, etc. - Version specifies how the compressed data are to be interpreted - and allows for future expansion of this extra field type. Currently - only version 0 is defined. - - For version 0, the compressed data are to be interpreted as a single - valid Windows NT SECURITY_DESCRIPTOR data structure, in self-relative - format. - - */ - case 0x4453: // Not Needed - break; + /* PKWARE's authenticity verification */ + case 0x0007: // Not Needed + break; - /* - -FWKCS MD5 Extra Field (0x4b46): - ============================== - - The FWKCS Contents_Signature System, used in automatically - identifying files independent of filename, optionally adds - and uses an extra field to support the rapid creation of - an enhanced contents_signature. - There is no local-header version; the following applies - only to the central header. (Last Revision 19961207) - - Central-header version: - - Value Size Description - ----- ---- ----------- - (MD5) 0x4b46 Short tag for this extra block type ("FK") - TSize Short total data size for this block (19) - "MD5" 3 bytes extra-field signature - MD5hash 16 bytes 128-bit MD5 hash of uncompressed data - (low byte first) - - When FWKCS revises a .ZIP file central directory to add - this extra field for a file, it also replaces the - central directory entry for that file's uncompressed - file length with a measured value. - - FWKCS provides an option to strip this extra field, if - present, from a .ZIP file central directory. In adding - this extra field, FWKCS preserves .ZIP file Authenticity - Verification; if stripping this extra field, FWKCS - preserves all versions of AV through PKZIP version 2.04g. - - FWKCS, and FWKCS Contents_Signature System, are - trademarks of Frederick W. Kantor. - - (1) R. Rivest, RFC1321.TXT, MIT Laboratory for Computer - Science and RSA Data Security, Inc., April 1992. - ll.76-77: "The MD5 algorithm is being placed in the - public domain for review and possible adoption as a - standard." - */ - case 0x4B46: // Not Needed - break; + /* + -PKWARE Win95/WinNT Extra Field (0x000a): + ======================================= + + The following description covers PKWARE's "NTFS" attributes + "extra" block, introduced with the release of PKZIP 2.50 for + Windows. (Last Revision 20001118) + + (Note: At this time the Mtime, Atime and Ctime values may + be used on any WIN32 system.) + [Info-ZIP note: In the current implementations, this field has + a fixed total data size of 32 bytes and is only stored as local + extra field.] + + Value Size Description + ----- ---- ----------- + (NTFS) 0x000a Short Tag for this "extra" block type + TSize Short Total Data Size for this block + Reserved Long for future use + Tag1 Short NTFS attribute tag value #1 + Size1 Short Size of attribute #1, in bytes + (var.) SubSize1 Attribute #1 data + . + . + . + TagN Short NTFS attribute tag value #N + SizeN Short Size of attribute #N, in bytes + (var.) SubSizeN Attribute #N data + + For NTFS, values for Tag1 through TagN are as follows: + (currently only one set of attributes is defined for NTFS) + + Tag Size Description + ----- ---- ----------- + 0x0001 2 bytes Tag for attribute #1 + Size1 2 bytes Size of attribute #1, in bytes (24) + Mtime 8 bytes 64-bit NTFS file last modification time + Atime 8 bytes 64-bit NTFS file last access time + Ctime 8 bytes 64-bit NTFS file creation time + + The total length for this block is 28 bytes, resulting in a + fixed size value of 32 for the TSize field of the NTFS block. + + The NTFS filetimes are 64-bit unsigned integers, stored in Intel + (least significant byte first) byte order. They determine the + number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch", + which is "01-Jan-1601 00:00:00 UTC". - /* - -Extended Timestamp Extra Field: - ============================== - - The following is the layout of the extended-timestamp extra block. - (Last Revision 19970118) - - Local-header version: - - Value Size Description - ----- ---- ----------- - (time) 0x5455 Short tag for this extra block type ("UT") - TSize Short total data size for this block - Flags Byte info bits - (ModTime) Long time of last modification (UTC/GMT) - (AcTime) Long time of last access (UTC/GMT) - (CrTime) Long time of original creation (UTC/GMT) - - Central-header version: - - Value Size Description - ----- ---- ----------- - (time) 0x5455 Short tag for this extra block type ("UT") - TSize Short total data size for this block - Flags Byte info bits (refers to local header!) - (ModTime) Long time of last modification (UTC/GMT) - - The central-header extra field contains the modification time only, - or no timestamp at all. TSize is used to flag its presence or - absence. But note: - - If "Flags" indicates that Modtime is present in the local header - field, it MUST be present in the central header field, too! - This correspondence is required because the modification time - value may be used to support trans-timezone freshening and - updating operations with zip archives. - - The time values are in standard Unix signed-long format, indicating - the number of seconds since 1 January 1970 00:00:00. The times - are relative to Coordinated Universal Time (UTC), also sometimes - referred to as Greenwich Mean Time (GMT). To convert to local time, - the software must know the local timezone offset from UTC/GMT. - - The lower three bits of Flags in both headers indicate which time- - stamps are present in the LOCAL extra field: - - bit 0 if set, modification time is present - bit 1 if set, access time is present - bit 2 if set, creation time is present - bits 3-7 reserved for additional timestamps; not set - - Those times that are present will appear in the order indicated, but - any combination of times may be omitted. (Creation time may be - present without access time, for example.) TSize should equal - (1 + 4*(number of set bits in Flags)), as the block is currently - defined. Other timestamps may be added in the future. - */ - case 0x5455: - byte flags = extraField[pos]; - pos += 1; - if ((flags & 1) == 1) + Additional Notes + ================ + WinRar only write this to the central Directory + */ + case 0x000a: + pos += 4; // Reserved Long for future use + int tag1 = BitConverter.ToInt16(extraField, pos); // Tag1 Short NTFS attribute tag value #1 + pos += 2; + int size1 = BitConverter.ToInt16(extraField, pos); // Size1 Short Size of attribute #1, in bytes + pos += 2; + if (tag1 == 0x0001 && size1 == 24 + ) // (currently only one set of attributes is defined for NTFS) + { + lf.ModifiedTime = CompressUtils.UtcTicksFromNtfsDateTime(BitConverter.ToInt64(extraField, pos)); // Mtime 8 bytes 64-bit NTFS file last modification time + pos += 8; + lf.AccessedTime = CompressUtils.UtcTicksFromNtfsDateTime(BitConverter.ToInt64(extraField, pos)); // Atime 8 bytes 64-bit NTFS file last access time + pos += 8; + lf.CreatedTime = CompressUtils.UtcTicksFromNtfsDateTime(BitConverter.ToInt64(extraField, pos)); // Ctime 8 bytes 64-bit NTFS file creation time + pos += 8; + + Debug.WriteLine("modtime = " + new DateTime((long)lf.ModifiedTime)); + Debug.WriteLine("Acctime = " + new DateTime((long)lf.AccessedTime)); + Debug.WriteLine("Cretime = " + new DateTime((long)lf.CreatedTime)); + } + + break; + + + /* + -Windows NT Security Descriptor Extra Field (0x4453): + =================================================== + + The following is the layout of the NT Security Descriptor (another + type of ACL) extra block. (Last Revision 19960922) + + Local-header version: + + Value Size Description + ----- ---- ----------- + (SD) 0x4453 Short tag for this extra block type ("SD") + TSize Short total data size for this block + BSize Long uncompressed SD data size + Version Byte version of uncompressed SD data format + CType Short compression type + EACRC Long CRC value for uncompressed SD data + (var.) variable compressed SD data + + Central-header version: + + Value Size Description + ----- ---- ----------- + (SD) 0x4453 Short tag for this extra block type ("SD") + TSize Short total data size for this block (4) + BSize Long size of uncompressed local SD data + + The value of CType is interpreted according to the "compression + method" section above; i.e., 0 for stored, 8 for deflated, etc. + Version specifies how the compressed data are to be interpreted + and allows for future expansion of this extra field type. Currently + only version 0 is defined. + + For version 0, the compressed data are to be interpreted as a single + valid Windows NT SECURITY_DESCRIPTOR data structure, in self-relative + format. + + */ + case 0x4453: // Not Needed + break; + + + /* + -FWKCS MD5 Extra Field (0x4b46): + ============================== + + The FWKCS Contents_Signature System, used in automatically + identifying files independent of filename, optionally adds + and uses an extra field to support the rapid creation of + an enhanced contents_signature. + There is no local-header version; the following applies + only to the central header. (Last Revision 19961207) + + Central-header version: + + Value Size Description + ----- ---- ----------- + (MD5) 0x4b46 Short tag for this extra block type ("FK") + TSize Short total data size for this block (19) + "MD5" 3 bytes extra-field signature + MD5hash 16 bytes 128-bit MD5 hash of uncompressed data + (low byte first) + + When FWKCS revises a .ZIP file central directory to add + this extra field for a file, it also replaces the + central directory entry for that file's uncompressed + file length with a measured value. + + FWKCS provides an option to strip this extra field, if + present, from a .ZIP file central directory. In adding + this extra field, FWKCS preserves .ZIP file Authenticity + Verification; if stripping this extra field, FWKCS + preserves all versions of AV through PKZIP version 2.04g. + + FWKCS, and FWKCS Contents_Signature System, are + trademarks of Frederick W. Kantor. + + (1) R. Rivest, RFC1321.TXT, MIT Laboratory for Computer + Science and RSA Data Security, Inc., April 1992. + ll.76-77: "The MD5 algorithm is being placed in the + public domain for review and possible adoption as a + standard." + */ + case 0x4B46: // Not Needed + break; + + + /* + -Extended Timestamp Extra Field: + ============================== + + The following is the layout of the extended-timestamp extra block. + (Last Revision 19970118) + + Local-header version: + + Value Size Description + ----- ---- ----------- + (time) 0x5455 Short tag for this extra block type ("UT") + TSize Short total data size for this block + Flags Byte info bits + (ModTime) Long time of last modification (UTC/GMT) + (AcTime) Long time of last access (UTC/GMT) + (CrTime) Long time of original creation (UTC/GMT) + + Central-header version: + + Value Size Description + ----- ---- ----------- + (time) 0x5455 Short tag for this extra block type ("UT") + TSize Short total data size for this block + Flags Byte info bits (refers to local header!) + (ModTime) Long time of last modification (UTC/GMT) + + The central-header extra field contains the modification time only, + or no timestamp at all. TSize is used to flag its presence or + absence. But note: + + If "Flags" indicates that Modtime is present in the local header + field, it MUST be present in the central header field, too! + This correspondence is required because the modification time + value may be used to support trans-timezone freshening and + updating operations with zip archives. + + The time values are in standard Unix signed-long format, indicating + the number of seconds since 1 January 1970 00:00:00. The times + are relative to Coordinated Universal Time (UTC), also sometimes + referred to as Greenwich Mean Time (GMT). To convert to local time, + the software must know the local timezone offset from UTC/GMT. + + The lower three bits of Flags in both headers indicate which time- + stamps are present in the LOCAL extra field: + + bit 0 if set, modification time is present + bit 1 if set, access time is present + bit 2 if set, creation time is present + bits 3-7 reserved for additional timestamps; not set + + Those times that are present will appear in the order indicated, but + any combination of times may be omitted. (Creation time may be + present without access time, for example.) TSize should equal + (1 + 4*(number of set bits in Flags)), as the block is currently + defined. Other timestamps may be added in the future. + */ + case 0x5455: + byte flags = extraField[pos]; + pos += 1; + if ((flags & 1) == 1) + { + lf.ModifiedTime = CompressUtils.UtcTicksFromUnixDateTime(BitConverter.ToInt32(extraField, pos)); // (ModTime) Long time of last modification (UTC/GMT) + Debug.WriteLine("Umodtime = " + new DateTime((long)lf.ModifiedTime)); + pos += 4; + } + + if (!centralDir) + { + if ((flags & 2) == 2) { - lf.mTime = ZipUtils.SetDateTimeFromUnixSeconds(BitConverter.ToInt32(extraField, pos)); // (ModTime) Long time of last modification (UTC/GMT) - Debug.WriteLine("Umodtime = " + new DateTime((long)lf.mTime)); + lf.AccessedTime = CompressUtils.UtcTicksFromUnixDateTime(BitConverter.ToInt32(extraField, pos)); // (AcTime) Long time of last access (UTC/GMT) + Debug.WriteLine("UAcctime = " + new DateTime((long)lf.AccessedTime)); pos += 4; } - if (!centralDir) + if ((flags & 4) == 4) { - if ((flags & 2) == 2) - { - lf.aTime = ZipUtils.SetDateTimeFromUnixSeconds(BitConverter.ToInt32(extraField, pos)); // (AcTime) Long time of last access (UTC/GMT) - Debug.WriteLine("UAcctime = " + new DateTime((long)lf.aTime)); - pos += 4; - } - - if ((flags & 4) == 4) - { - lf.cTime = ZipUtils.SetDateTimeFromUnixSeconds(BitConverter.ToInt32(extraField, pos)); // (CrTime) Long time of original creation (UTC/GMT) - Debug.WriteLine("UCretime = " + new DateTime((long)lf.cTime)); - pos += 4; - } + lf.CreatedTime = CompressUtils.UtcTicksFromUnixDateTime(BitConverter.ToInt32(extraField, pos)); // (CrTime) Long time of original creation (UTC/GMT) + Debug.WriteLine("UCretime = " + new DateTime((long)lf.CreatedTime)); + pos += 4; } + } - break; + break; - /* - -Info-ZIP Unix Extra Field (type 1): - ================================== - - The following is the layout of the old Info-ZIP extra block for - Unix. It has been replaced by the extended-timestamp extra block - (0x5455) and the Unix type 2 extra block (0x7855). - (Last Revision 19970118) - - Local-header version: - - Value Size Description - ----- ---- ----------- - (Unix1) 0x5855 Short tag for this extra block type ("UX") - TSize Short total data size for this block - AcTime Long time of last access (UTC/GMT) - ModTime Long time of last modification (UTC/GMT) - UID Short Unix user ID (optional) - GID Short Unix group ID (optional) - - Central-header version: - - Value Size Description - ----- ---- ----------- - (Unix1) 0x5855 Short tag for this extra block type ("UX") - TSize Short total data size for this block - AcTime Long time of last access (GMT/UTC) - ModTime Long time of last modification (GMT/UTC) - - The file access and modification times are in standard Unix signed- - long format, indicating the number of seconds since 1 January 1970 - 00:00:00. The times are relative to Coordinated Universal Time - (UTC), also sometimes referred to as Greenwich Mean Time (GMT). To - convert to local time, the software must know the local timezone - offset from UTC/GMT. The modification time may be used by non-Unix - systems to support inter-timezone freshening and updating of zip - archives. - - The local-header extra block may optionally contain UID and GID - info for the file. The local-header TSize value is the only - indication of this. Note that Unix UIDs and GIDs are usually - specific to a particular machine, and they generally require root - access to restore. - - This extra field type is obsolete, but it has been in use since - mid-1994. Therefore future archiving software should continue to - support it. Some guidelines: - - An archive member should either contain the old "Unix1" - extra field block or the new extra field types "time" and/or - "Unix2". - - If both the old "Unix1" block type and one or both of the new - block types "time" and "Unix2" are found, the "Unix1" block - should be considered invalid and ignored. - - Unarchiving software should recognize both old and new extra - field block types, but the info from new types overrides the - old "Unix1" field. - - Archiving software should recognize "Unix1" extra fields for - timestamp comparison but never create it for updated, freshened - or new archive members. When copying existing members to a new - archive, any "Unix1" extra field blocks should be converted to - the new "time" and/or "Unix2" types. - */ - case 0x5855: - lf.aTime = ZipUtils.SetDateTimeFromUnixSeconds(BitConverter.ToInt32(extraField, pos)); // AcTime Long time of last access (UTC/GMT) - Debug.WriteLine("UAcctime = " + new DateTime((long)lf.aTime)); - pos += 4; - lf.mTime = ZipUtils.SetDateTimeFromUnixSeconds(BitConverter.ToInt32(extraField, pos)); // ModTime Long time of last modification (UTC/GMT) - Debug.WriteLine("Umodtime = " + new DateTime((long)lf.mTime)); - pos += 4; - break; + /* + -Info-ZIP Unix Extra Field (type 1): + ================================== + + The following is the layout of the old Info-ZIP extra block for + Unix. It has been replaced by the extended-timestamp extra block + (0x5455) and the Unix type 2 extra block (0x7855). + (Last Revision 19970118) + + Local-header version: + + Value Size Description + ----- ---- ----------- + (Unix1) 0x5855 Short tag for this extra block type ("UX") + TSize Short total data size for this block + AcTime Long time of last access (UTC/GMT) + ModTime Long time of last modification (UTC/GMT) + UID Short Unix user ID (optional) + GID Short Unix group ID (optional) + + Central-header version: + + Value Size Description + ----- ---- ----------- + (Unix1) 0x5855 Short tag for this extra block type ("UX") + TSize Short total data size for this block + AcTime Long time of last access (GMT/UTC) + ModTime Long time of last modification (GMT/UTC) + + The file access and modification times are in standard Unix signed- + long format, indicating the number of seconds since 1 January 1970 + 00:00:00. The times are relative to Coordinated Universal Time + (UTC), also sometimes referred to as Greenwich Mean Time (GMT). To + convert to local time, the software must know the local timezone + offset from UTC/GMT. The modification time may be used by non-Unix + systems to support inter-timezone freshening and updating of zip + archives. + + The local-header extra block may optionally contain UID and GID + info for the file. The local-header TSize value is the only + indication of this. Note that Unix UIDs and GIDs are usually + specific to a particular machine, and they generally require root + access to restore. + + This extra field type is obsolete, but it has been in use since + mid-1994. Therefore future archiving software should continue to + support it. Some guidelines: + + An archive member should either contain the old "Unix1" + extra field block or the new extra field types "time" and/or + "Unix2". + + If both the old "Unix1" block type and one or both of the new + block types "time" and "Unix2" are found, the "Unix1" block + should be considered invalid and ignored. + + Unarchiving software should recognize both old and new extra + field block types, but the info from new types overrides the + old "Unix1" field. + + Archiving software should recognize "Unix1" extra fields for + timestamp comparison but never create it for updated, freshened + or new archive members. When copying existing members to a new + archive, any "Unix1" extra field blocks should be converted to + the new "time" and/or "Unix2" types. + */ + case 0x5855: + lf.AccessedTime = CompressUtils.UtcTicksFromUnixDateTime(BitConverter.ToInt32(extraField, pos)); // AcTime Long time of last access (UTC/GMT) + Debug.WriteLine("UAcctime = " + new DateTime((long)lf.AccessedTime)); + pos += 4; + lf.ModifiedTime = CompressUtils.UtcTicksFromUnixDateTime(BitConverter.ToInt32(extraField, pos)); // ModTime Long time of last modification (UTC/GMT) + Debug.WriteLine("Umodtime = " + new DateTime((long)lf.ModifiedTime)); + pos += 4; + break; - /* - -Info-ZIP Unicode Path Extra Field: - ================================= - - Stores the UTF-8 version of the entry path as stored in the - local header and central directory header. - (Last Revision 20070912) - - Value Size Description - ----- ---- ----------- - (UPath) 0x7075 Short tag for this extra block type ("up") - TSize Short total data size for this block - Version Byte version of this extra field, currently 1 - NameCRC32 Long CRC-32 checksum of standard name field - UnicodeName variable UTF-8 version of the entry file name - - Currently Version is set to the number 1. If there is a need - to change this field, the version will be incremented. Changes - may not be backward compatible so this extra field should not be - used if the version is not recognized. - - The NameCRC32 is the standard zip CRC32 checksum of the File Name - field in the header. This is used to verify that the header - File Name field has not changed since the Unicode Path extra field - was created. This can happen if a utility renames the entry but - does not update the UTF-8 path extra field. If the CRC check fails, - this UTF-8 Path Extra Field should be ignored and the File Name field - in the header should be used instead. - - The UnicodeName is the UTF-8 version of the contents of the File - Name field in the header, without any trailing NUL. The standard - name field in the Zip entry header remains filled with the entry - name coded in the local machine's extended ASCII system charset. - As UnicodeName is defined to be UTF-8, no UTF-8 byte order mark - (BOM) is used. The length of this field is determined by - subtracting the size of the previous fields from TSize. - If both the File Name and Comment fields are UTF-8, the new General - Purpose Bit Flag, bit 11 (Language encoding flag (EFS)), should be - used to indicate that both the header File Name and Comment fields - are UTF-8 and, in this case, the Unicode Path and Unicode Comment - extra fields are not needed and should not be created. Note that, - for backward compatibility, bit 11 should only be used if the native - character set of the paths and comments being zipped up are already - in UTF-8. The same method, either general purpose bit 11 or extra - fields, should be used in both the Local and Central Directory Header - for a file. - - Utilisation rules: - 1. This field shall never be created for names consisting solely of - 7-bit ASCII characters. - 2. On a system that already uses UTF-8 as system charset, this field - shall not repeat the string pattern already stored in the Zip - entry's standard name field. Instead, a field of exactly 9 bytes - (70 75 05 00 01 and 4 bytes CRC) should be created. - In this form with 5 data bytes, the field serves as indicator - for the UTF-8 encoding of the standard Zip header's name field. - 3. This field shall not be used whenever the calculated CRC-32 of - the entry's standard name field does not match the provided - CRC checksum value. A mismatch of the CRC check indicates that - the standard name field was changed by some non-"up"-aware - utility without synchronizing this UTF-8 name e.f. block. - */ - case 0x7075: - pos += 1; - uint nameCRC32 = BitConverter.ToUInt32(extraField, pos); - pos += 4; + /* + -Info-ZIP Unicode Path Extra Field: + ================================= - CRC crcTest = new CRC(); - crcTest.SlurpBlock(bFileName, 0, bFileName.Length); - uint fCRC = crcTest.Crc32ResultU; + Stores the UTF-8 version of the entry path as stored in the + local header and central directory header. + (Last Revision 20070912) - if (nameCRC32 == fCRC) - { - int charLen = blockLength - 5; - if (centralDir) - lf.FileName = Encoding.UTF8.GetString(extraField, pos, charLen); - else - lf._localHeaderFilename = Encoding.UTF8.GetString(extraField, pos, charLen); - } + Value Size Description + ----- ---- ----------- + (UPath) 0x7075 Short tag for this extra block type ("up") + TSize Short total data size for this block + Version Byte version of this extra field, currently 1 + NameCRC32 Long CRC-32 checksum of standard name field + UnicodeName variable UTF-8 version of the entry file name - break; + Currently Version is set to the number 1. If there is a need + to change this field, the version will be incremented. Changes + may not be backward compatible so this extra field should not be + used if the version is not recognized. + + The NameCRC32 is the standard zip CRC32 checksum of the File Name + field in the header. This is used to verify that the header + File Name field has not changed since the Unicode Path extra field + was created. This can happen if a utility renames the entry but + does not update the UTF-8 path extra field. If the CRC check fails, + this UTF-8 Path Extra Field should be ignored and the File Name field + in the header should be used instead. + + The UnicodeName is the UTF-8 version of the contents of the File + Name field in the header, without any trailing NUL. The standard + name field in the Zip entry header remains filled with the entry + name coded in the local machine's extended ASCII system charset. + As UnicodeName is defined to be UTF-8, no UTF-8 byte order mark + (BOM) is used. The length of this field is determined by + subtracting the size of the previous fields from TSize. + If both the File Name and Comment fields are UTF-8, the new General + Purpose Bit Flag, bit 11 (Language encoding flag (EFS)), should be + used to indicate that both the header File Name and Comment fields + are UTF-8 and, in this case, the Unicode Path and Unicode Comment + extra fields are not needed and should not be created. Note that, + for backward compatibility, bit 11 should only be used if the native + character set of the paths and comments being zipped up are already + in UTF-8. The same method, either general purpose bit 11 or extra + fields, should be used in both the Local and Central Directory Header + for a file. + + Utilisation rules: + 1. This field shall never be created for names consisting solely of + 7-bit ASCII characters. + 2. On a system that already uses UTF-8 as system charset, this field + shall not repeat the string pattern already stored in the Zip + entry's standard name field. Instead, a field of exactly 9 bytes + (70 75 05 00 01 and 4 bytes CRC) should be created. + In this form with 5 data bytes, the field serves as indicator + for the UTF-8 encoding of the standard Zip header's name field. + 3. This field shall not be used whenever the calculated CRC-32 of + the entry's standard name field does not match the provided + CRC checksum value. A mismatch of the CRC check indicates that + the standard name field was changed by some non-"up"-aware + utility without synchronizing this UTF-8 name e.f. block. + */ + case 0x7075: + byte version = extraField[pos]; + pos += 1; + uint nameCRC32 = BitConverter.ToUInt32(extraField, pos); + pos += 4; + + CRC crcTest = new(); + crcTest.SlurpBlock(bFileName, 0, bFileName.Length); + uint fCRC = crcTest.Crc32ResultU; + + if (nameCRC32 == fCRC) + { + int charLen = blockLength - 5; + lf.Filename = Encoding.UTF8.GetString(extraField, pos, charLen); + } + + break; - /* - -Info-ZIP UNIX Extra Field (type 2): - ================================== - - The following is the layout of the new Info-ZIP extra block for - Unix. (Last Revision 19960922) - - Local-header version: - - Value Size Description - ----- ---- ----------- - (Unix2) 0x7855 Short tag for this extra block type ("Ux") - TSize Short total data size for this block (4) - UID Short Unix user ID - GID Short Unix group ID - - Central-header version: - - Value Size Description - ----- ---- ----------- - (Unix2) 0x7855 Short tag for this extra block type ("Ux") - TSize Short total data size for this block (0) - - The data size of the central-header version is zero; it is used - solely as a flag that UID/GID info is present in the local-header - extra field. If additional fields are ever added to the local - version, the central version may be extended to indicate this. - - Note that Unix UIDs and GIDs are usually specific to a particular - machine, and they generally require root access to restore. - - */ - case 0x7855: // Not Needed - break; + /* + -Info-ZIP UNIX Extra Field (type 2): + ================================== + + The following is the layout of the new Info-ZIP extra block for + Unix. (Last Revision 19960922) + + Local-header version: + + Value Size Description + ----- ---- ----------- + (Unix2) 0x7855 Short tag for this extra block type ("Ux") + TSize Short total data size for this block (4) + UID Short Unix user ID + GID Short Unix group ID + + Central-header version: + + Value Size Description + ----- ---- ----------- + (Unix2) 0x7855 Short tag for this extra block type ("Ux") + TSize Short total data size for this block (0) + + The data size of the central-header version is zero; it is used + solely as a flag that UID/GID info is present in the local-header + extra field. If additional fields are ever added to the local + version, the central version may be extended to indicate this. + + Note that Unix UIDs and GIDs are usually specific to a particular + machine, and they generally require root access to restore. + + */ + case 0x7855: // Not Needed + break; - /* - -Info-ZIP New Unix Extra Field: - ==================================== - - Currently stores Unix UIDs/GIDs up to 32 bits. - (Last Revision 20080509) - - Value Size Description - ----- ---- ----------- - (UnixN) 0x7875 Short tag for this extra block type ("ux") - TSize Short total data size for this block - Version 1 byte version of this extra field, currently 1 - UIDSize 1 byte Size of UID field - UID Variable UID for this entry - GIDSize 1 byte Size of GID field - GID Variable GID for this entry - - Currently Version is set to the number 1. If there is a need - to change this field, the version will be incremented. Changes - may not be backward compatible so this extra field should not be - used if the version is not recognized. - - UIDSize is the size of the UID field in bytes. This size should - match the size of the UID field on the target OS. - - UID is the UID for this entry in standard little endian format. - - GIDSize is the size of the GID field in bytes. This size should - match the size of the GID field on the target OS. - - GID is the GID for this entry in standard little endian format. - - If both the old 16-bit Unix extra field (tag 0x7855, Info-ZIP Unix2) - and this extra field are present, the values in this extra field - supercede the values in that extra field. - */ - case 0x7875: // Not Needed - break; + /* + -Info-ZIP New Unix Extra Field: + ==================================== + + Currently stores Unix UIDs/GIDs up to 32 bits. + (Last Revision 20080509) + + Value Size Description + ----- ---- ----------- + (UnixN) 0x7875 Short tag for this extra block type ("ux") + TSize Short total data size for this block + Version 1 byte version of this extra field, currently 1 + UIDSize 1 byte Size of UID field + UID Variable UID for this entry + GIDSize 1 byte Size of GID field + GID Variable GID for this entry + + Currently Version is set to the number 1. If there is a need + to change this field, the version will be incremented. Changes + may not be backward compatible so this extra field should not be + used if the version is not recognized. + + UIDSize is the size of the UID field in bytes. This size should + match the size of the UID field on the target OS. + + UID is the UID for this entry in standard little endian format. + + GIDSize is the size of the GID field in bytes. This size should + match the size of the GID field on the target OS. + + GID is the GID for this entry in standard little endian format. + + If both the old 16-bit Unix extra field (tag 0x7855, Info-ZIP Unix2) + and this extra field are present, the values in this extra field + supercede the values in that extra field. + */ + case 0x7875: // Not Needed + break; - /* UNKNOWN */ - case 0xe57a: - break; + /* UNKNOWN */ + case 0xe57a: + break; - default: - break; - } - - blockPos += blockLength; - + default: + break; } - return ZipReturn.ZipGood; + blockPos += blockLength; + } + + return ZipReturn.ZipGood; } } -} \ No newline at end of file +} diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipExtraFieldWrite.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipExtraFieldWrite.cs new file mode 100644 index 00000000..b61ec723 --- /dev/null +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipExtraFieldWrite.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; + +namespace Compress.ZipFile +{ + internal class ZipExtraFieldWrite + { + private readonly List _extraField; + + public ZipExtraFieldWrite() + { + _extraField = new List(); + } + + public byte[] ExtraField => _extraField.ToArray(); + + public bool Zip64(ulong unCompressedSize, ulong compressedSize, ulong relativeOffsetOfLocalHeader, bool centralDir, + out uint headerUnCompressedSize, out uint headerCompressedSize, out uint headerRelativeOffsetOfLocalHeader) + { + List eZip64 = new(); + + if (!centralDir) + { + if (unCompressedSize >= 0xffffffff || compressedSize >= 0xffffffff) + { + eZip64.AddRange(BitConverter.GetBytes(unCompressedSize)); + eZip64.AddRange(BitConverter.GetBytes(compressedSize)); + headerUnCompressedSize = 0xffffffff; + headerCompressedSize = 0xffffffff; + } + else + { + headerUnCompressedSize = (uint)unCompressedSize; + headerCompressedSize = (uint)compressedSize; + } + + headerRelativeOffsetOfLocalHeader = 0; + } + else + { + if (unCompressedSize >= 0xffffffff) + { + headerUnCompressedSize = 0xffffffff; + eZip64.AddRange(BitConverter.GetBytes(unCompressedSize)); + } + else + { + headerUnCompressedSize = (uint)unCompressedSize; + } + if (compressedSize >= 0xffffffff) + { + headerCompressedSize = 0xffffffff; + eZip64.AddRange(BitConverter.GetBytes(compressedSize)); + } + else + { + headerCompressedSize = (uint)compressedSize; + } + + if (relativeOffsetOfLocalHeader >= 0xffffffff) + { + headerRelativeOffsetOfLocalHeader = 0xffffffff; + eZip64.AddRange(BitConverter.GetBytes(relativeOffsetOfLocalHeader)); + } + else + { + headerRelativeOffsetOfLocalHeader = (uint)relativeOffsetOfLocalHeader; + } + + } + + if (eZip64.Count == 0) + return false; + + _extraField.AddRange(BitConverter.GetBytes((ushort)0x0001)); + _extraField.AddRange(BitConverter.GetBytes((ushort)eZip64.Count)); + _extraField.AddRange(eZip64); + return true; + } + + + public void NtfsTime(long mTime, long aTime, long cTime) + { + _extraField.AddRange(BitConverter.GetBytes((ushort)0x000a)); + _extraField.AddRange(BitConverter.GetBytes((ushort)32)); // this block is 32 bytes long + _extraField.AddRange(BitConverter.GetBytes((uint)0)); // Reserved + _extraField.AddRange(BitConverter.GetBytes((ushort)0x0001)); // tag1 = 1 + _extraField.AddRange(BitConverter.GetBytes((ushort)24)); // size1 block size of date/times + + _extraField.AddRange(BitConverter.GetBytes(CompressUtils.UtcTicksToNtfsDateTime(mTime))); + _extraField.AddRange(BitConverter.GetBytes(CompressUtils.UtcTicksToNtfsDateTime(aTime))); + _extraField.AddRange(BitConverter.GetBytes(CompressUtils.UtcTicksToNtfsDateTime(cTime))); + } + + + public void LinuxTime(long? mTime, long? aTime, long? cTime, bool centralDir) + { + List eTime = new(); + byte flags = 0; + if (mTime != null) + { + flags |= 0x01; + eTime.AddRange(BitConverter.GetBytes(CompressUtils.UtcTicksToUnixDateTime((long)mTime))); + } + + if (!centralDir) + { + if (aTime != null) + { + flags |= 0x02; + eTime.AddRange(BitConverter.GetBytes(CompressUtils.UtcTicksToUnixDateTime((long)aTime))); + } + if (cTime != null) + { + flags |= 0x04; + eTime.AddRange(BitConverter.GetBytes(CompressUtils.UtcTicksToUnixDateTime((long)cTime))); + } + + } + + if (flags == 0) + return; + + _extraField.AddRange(BitConverter.GetBytes((ushort)0x5455)); + _extraField.AddRange(BitConverter.GetBytes((ushort)eTime.Count + 1)); + + _extraField.Add(flags); + _extraField.AddRange(eTime); + } + + } +} diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipFake.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipFake.cs index b59885f3..88b02b31 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipFake.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipFake.cs @@ -1,5 +1,5 @@ using System.IO; -using Compress.Utils; +using Compress.Support.Utils; namespace Compress.ZipFile { @@ -25,19 +25,19 @@ namespace Compress.ZipFile return; } - _zip64 = false; bool lTrrntzip = true; _zipFs = new MemoryStream(); _centralDirStart = fileOffset; - CrcCalculatorStream crcCs = new CrcCalculatorStream(_zipFs, true); - foreach (LocalFile t in _localFiles) + CrcCalculatorStream crcCs = new(_zipFs, true); + + foreach (ZipLocalFile t in _localFiles) { - t.CenteralDirectoryWrite(crcCs); - lTrrntzip &= t.TrrntZip; + t.CentralDirectoryWrite(crcCs); + lTrrntzip &= t.GetStatus(LocalFileStatus.TrrntZip); } crcCs.Flush(); @@ -45,7 +45,7 @@ namespace Compress.ZipFile _centralDirSize = (ulong)_zipFs.Position; - _fileComment = lTrrntzip ? ZipUtils.GetBytes("TORRENTZIPPED-" + crcCs.Crc.ToString("X8")) : new byte[0]; + FileComment = lTrrntzip ? CompressUtils.GetBytes("TORRENTZIPPED-" + crcCs.Crc.ToString("X8")) : new byte[0]; ZipStatus = lTrrntzip ? ZipStatus.TrrntZip : ZipStatus.None; crcCs.Dispose(); @@ -54,7 +54,7 @@ namespace Compress.ZipFile if (_zip64) { - _endOfCenterDir64 = fileOffset + (ulong)_zipFs.Position; + _endOfCentralDir64 = fileOffset + (ulong)_zipFs.Position; Zip64EndOfCentralDirWrite(); Zip64EndOfCentralDirectoryLocatorWrite(); } @@ -76,10 +76,10 @@ namespace Compress.ZipFile return ZipReturn.ZipWritingToInputFile; } - LocalFile lf = new LocalFile(filename); + ZipLocalFile lf = new(filename); _localFiles.Add(lf); - MemoryStream ms = new MemoryStream(); + MemoryStream ms = new(); lf.LocalFileHeaderFake(fileOffset, uncompressedSize, compressedSize, crc32, ms); localHeader = ms.ToArray(); diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipLocalFile.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipLocalFile.cs index 29600148..0debf100 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipLocalFile.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipLocalFile.cs @@ -1,148 +1,177 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Text; -using Compress.Utils; -using Compress.ZipFile.ZLib; +using Compress.Support.Compression.BZip2; +using Compress.Support.Compression.Deflate; +using Compress.Support.Compression.Deflate64; +using Compress.Support.Compression.LZMA; +using Compress.Support.Compression.PPmd; namespace Compress.ZipFile { - internal partial class LocalFile + internal class ZipLocalFile : LocalFile { private const uint LocalFileHeaderSignature = 0x04034b50; private const uint CentralDirectoryHeaderSignature = 0x02014b50; private ushort _compressionMethod; - private long _lastModFileTimeDate; - private long? mTime; - private long? cTime; - private long? aTime; - - private string _localHeaderFilename; private ulong _compressedSize; - private ulong _localHeaderCompressedSize; - private ulong _localHeaderUncompressedSize; + internal ulong RelativeOffsetOfLocalHeader; // only in central directory - public ulong RelativeOffsetOfLocalHeader; // only in centeral directory - - private ulong _crc32Location; private ulong _extraLocation; + private byte[] _extraField; private ulong _dataLocation; - public LocalFile() + internal ushort GeneralPurposeBitFlag { get; private set; } + + //internal bool TrrntZip { get; private set; } + + public override ulong? LocalHead => (GeneralPurposeBitFlag & 8) == 0 ? (ulong?)RelativeOffsetOfLocalHeader : null; + + internal ZipLocalFile() { } - public LocalFile(string filename, TimeStamps dateTime = null) + internal ZipLocalFile(string filename, TimeStamps dateTime = null) { - Zip64 = false; + SetStatus(LocalFileStatus.Zip64, false); GeneralPurposeBitFlag = 2; // Maximum Compression Deflating _compressionMethod = 8; // Compression Method Deflate if (dateTime?.ModTime == null) { - _lastModFileTimeDate = ZipUtils.trrntzipDateTime; + HeaderLastModified = CompressUtils.TrrntzipDateTime; } else { - _lastModFileTimeDate = (long)dateTime.ModTime; + HeaderLastModified = (long)dateTime.ModTime; } - FileName = filename; - } - - public string FileName { get; private set; } - public ushort GeneralPurposeBitFlag { get; private set; } - public byte[] CRC { get; private set; } - public ulong UncompressedSize { get; private set; } - - public bool Zip64 { get; private set; } - public bool TrrntZip { get; private set; } - - public long DateTime => mTime ?? _lastModFileTimeDate; - - public long? DateTimeCreate => cTime; - public long? DateTimeAccess => aTime; - - - public ulong LocalFilePos - { - get => RelativeOffsetOfLocalHeader; - set => RelativeOffsetOfLocalHeader = value; + Filename = filename; } - public ZipReturn CenteralDirectoryRead(Stream zipFs) + internal ZipReturn CentralDirectoryRead(Stream zipFs, ulong offset) { try { - using (BinaryReader br = new BinaryReader(zipFs, Encoding.UTF8, true)) + using BinaryReader br = new(zipFs, Encoding.UTF8, true); + uint thisSignature = br.ReadUInt32(); + if (thisSignature != CentralDirectoryHeaderSignature) { - uint thisSignature = br.ReadUInt32(); - if (thisSignature != CentralDirectoryHeaderSignature) - { - return ZipReturn.ZipCentralDirError; - } + return ZipReturn.ZipCentralDirError; + } - br.ReadUInt16(); // Version Made By + br.ReadUInt16(); // Version Made By - br.ReadUInt16(); // Version Needed To Extract + br.ReadUInt16(); // Version Needed To Extract - GeneralPurposeBitFlag = br.ReadUInt16(); - _compressionMethod = br.ReadUInt16(); - if (_compressionMethod != 8 && _compressionMethod != 0) - { - if (_compressionMethod != 6 && _compressionMethod != 5 && _compressionMethod != 1) - { - return ZipReturn.ZipUnsupportedCompression; - } + + GeneralPurposeBitFlag = br.ReadUInt16(); + _compressionMethod = br.ReadUInt16(); + + ushort lastModFileTime = br.ReadUInt16(); + ushort lastModFileDate = br.ReadUInt16(); + HeaderLastModified = CompressUtils.UtcTicksFromDosDateTime(lastModFileDate, lastModFileTime); + + CRC = ReadCRC(br); + + _compressedSize = br.ReadUInt32(); + UncompressedSize = br.ReadUInt32(); + + ushort fileNameLength = br.ReadUInt16(); + ushort extraFieldLength = br.ReadUInt16(); + ushort fileCommentLength = br.ReadUInt16(); + + br.ReadUInt16(); // diskNumberStart + br.ReadUInt16(); // internalFileAttributes + br.ReadUInt32(); // externalFileAttributes + + RelativeOffsetOfLocalHeader = br.ReadUInt32(); + + + byte[] bFileName = br.ReadBytes(fileNameLength); + Filename = (GeneralPurposeBitFlag & (1 << 11)) == 0 + ? CompressUtils.GetString(bFileName) + : Encoding.UTF8.GetString(bFileName, 0, fileNameLength); + + if (extraFieldLength > 0) + { + byte[] extraField = br.ReadBytes(extraFieldLength); + + ZipReturn zr = ZipExtraField.ReadExtraField(extraField, bFileName, this, ref _compressedSize, ref RelativeOffsetOfLocalHeader, true); + if (zr != ZipReturn.ZipGood) + return zr; + } + + RelativeOffsetOfLocalHeader += offset; + + if (CompressUtils.IsCodePage437(Filename) != ((GeneralPurposeBitFlag & (1 << 11)) == 0)) + SetStatus(LocalFileStatus.TrrntZip, false); + + if (fileCommentLength > 0) + { + byte[] fileComment = br.ReadBytes(fileCommentLength); + } + + if (Filename.Length > 0) + { + char lastChar = Filename[Filename.Length - 1]; + IsDirectory = (lastChar == '/' || lastChar == '\\'); + if (IsDirectory && UncompressedSize > 0) SetStatus(LocalFileStatus.DirectoryLengthError); + } + /* + 4.4.5 compression method: (2 bytes) + + 0 - (Supported) The file is stored (no compression) + 1 - The file is Shrunk + 2 - The file is Reduced with compression factor 1 + 3 - The file is Reduced with compression factor 2 + 4 - The file is Reduced with compression factor 3 + 5 - The file is Reduced with compression factor 4 + 6 - The file is Imploded + 7 - Reserved for Tokenizing compression algorithm + 8 - (Supported) The file is Deflated + 9 - (Supported) Enhanced Deflating using Deflate64(tm) + 10 - PKWARE Data Compression Library Imploding (old IBM TERSE) + 11 - Reserved by PKWARE + 12 - (Supported) File is compressed using BZIP2 algorithm + 13 - Reserved by PKWARE + 14 - (Supported) LZMA + 15 - Reserved by PKWARE + 16 - IBM z/OS CMPSC Compression + 17 - Reserved by PKWARE + 18 - File is compressed using IBM TERSE (new) + 19 - IBM LZ77 z Architecture + 20 - deprecated (use method 93 for zstd) + 93 - (Supported, with external DLL) Zstandard (zstd) Compression + 94 - MP3 Compression + 95 - XZ Compression + 96 - JPEG variant + 97 - WavPack compressed data + 98 - (Supported) PPMd version I, Rev 1 + 99 - AE-x encryption marker (see APPENDIX E) + */ + + switch (_compressionMethod) + { + case 0: // The file is stored (no compression) + + case 8: // The file is Deflated + case 9: // Enhanced Deflating using Deflate64(tm) + case 12: // The file is BZIP2 algorithm. + case 14: // LZMA + return ZipReturn.ZipGood; + + case 20: + case 93: // Zstandard (zstd) Compression + return ZipReturn.ZipGood; + + case 98: // PPMd version I, Rev 1 + return ZipReturn.ZipGood; + + default: return ZipReturn.ZipUnsupportedCompression; - } - - ushort lastModFileTime = br.ReadUInt16(); - ushort lastModFileDate = br.ReadUInt16(); - _lastModFileTimeDate = ZipUtils.SetDateTime(lastModFileDate, lastModFileTime); - - CRC = ReadCRC(br); - - _compressedSize = br.ReadUInt32(); - UncompressedSize = br.ReadUInt32(); - - ushort fileNameLength = br.ReadUInt16(); - ushort extraFieldLength = br.ReadUInt16(); - ushort fileCommentLength = br.ReadUInt16(); - - br.ReadUInt16(); // diskNumberStart - br.ReadUInt16(); // internalFileAttributes - br.ReadUInt32(); // externalFileAttributes - - RelativeOffsetOfLocalHeader = br.ReadUInt32(); - - byte[] bFileName = br.ReadBytes(fileNameLength); - FileName = (GeneralPurposeBitFlag & (1 << 11)) == 0 - ? ZipUtils.GetString(bFileName) - : Encoding.UTF8.GetString(bFileName, 0, fileNameLength); - - if (extraFieldLength > 0) - { - byte[] extraField = br.ReadBytes(extraFieldLength); - - - ZipReturn zr = ZipExtraField.ReadLocalExtraField(extraField, bFileName, this, true); - if (zr != ZipReturn.ZipGood) - return zr; - } - - if (ZipUtils.IsCodePage437(FileName) != ((GeneralPurposeBitFlag & (1 << 11)) == 0)) - TrrntZip = false; - - if (fileCommentLength > 0) - { - byte[] fileComment = br.ReadBytes(fileCommentLength); - } - - return ZipReturn.ZipGood; } } catch @@ -151,111 +180,65 @@ namespace Compress.ZipFile } } - public void CenteralDirectoryWrite(Stream crcStream) + internal void CentralDirectoryWrite(Stream crcStream) { - using (BinaryWriter bw = new BinaryWriter(crcStream, Encoding.UTF8, true)) + using BinaryWriter bw = new(crcStream, Encoding.UTF8, true); + + ZipExtraFieldWrite zefw = new(); + SetStatus(LocalFileStatus.Zip64, + zefw.Zip64(UncompressedSize, _compressedSize, RelativeOffsetOfLocalHeader, true, + out uint headerUnCompressedSize, out uint headerCompressedSize, out uint headerRelativeOffsetOfLocalHeader) + ); + _extraField = zefw.ExtraField; + + ushort extraFieldLength = (ushort)_extraField.Length; + + byte[] bFileName; + if (CompressUtils.IsCodePage437(Filename)) { - const uint header = 0x2014B50; - - List extraField = new List(); - - uint cdUncompressedSize; - if (UncompressedSize >= 0xffffffff) - { - Zip64 = true; - cdUncompressedSize = 0xffffffff; - extraField.AddRange(BitConverter.GetBytes(UncompressedSize)); - } - else - { - cdUncompressedSize = (uint)UncompressedSize; - } - - uint cdCompressedSize; - if (_compressedSize >= 0xffffffff) - { - Zip64 = true; - cdCompressedSize = 0xffffffff; - extraField.AddRange(BitConverter.GetBytes(_compressedSize)); - } - else - { - cdCompressedSize = (uint)_compressedSize; - } - - uint cdRelativeOffsetOfLocalHeader; - if (RelativeOffsetOfLocalHeader >= 0xffffffff) - { - Zip64 = true; - cdRelativeOffsetOfLocalHeader = 0xffffffff; - extraField.AddRange(BitConverter.GetBytes(RelativeOffsetOfLocalHeader)); - } - else - { - cdRelativeOffsetOfLocalHeader = (uint)RelativeOffsetOfLocalHeader; - } - - - if (extraField.Count > 0) - { - ushort exfl = (ushort)extraField.Count; - extraField.InsertRange(0, BitConverter.GetBytes((ushort)0x0001)); - extraField.InsertRange(2, BitConverter.GetBytes(exfl)); - } - - ushort extraFieldLength = (ushort)extraField.Count; - - byte[] bFileName; - if (ZipUtils.IsCodePage437(FileName)) - { - bFileName = ZipUtils.GetBytes(FileName); - } - else - { - GeneralPurposeBitFlag |= 1 << 11; - bFileName = Encoding.UTF8.GetBytes(FileName); - } - - ushort fileNameLength = (ushort)bFileName.Length; - - ushort versionNeededToExtract = (ushort)(Zip64 ? 45 : 20); - - ZipUtils.SetDateTime(_lastModFileTimeDate, out ushort lastModFileDate, out ushort lastModFileTime); - - bw.Write(header); - bw.Write((ushort)0); - bw.Write(versionNeededToExtract); - bw.Write(GeneralPurposeBitFlag); - bw.Write(_compressionMethod); - bw.Write(lastModFileTime); - bw.Write(lastModFileDate); - bw.Write(CRC[3]); - bw.Write(CRC[2]); - bw.Write(CRC[1]); - bw.Write(CRC[0]); - bw.Write(cdCompressedSize); - bw.Write(cdUncompressedSize); - bw.Write(fileNameLength); - bw.Write(extraFieldLength); - bw.Write((ushort)0); // file comment length - bw.Write((ushort)0); // disk number start - bw.Write((ushort)0); // internal file attributes - bw.Write((uint)0); // external file attributes - bw.Write(cdRelativeOffsetOfLocalHeader); - - bw.Write(bFileName, 0, fileNameLength); - bw.Write(extraField.ToArray(), 0, extraFieldLength); - // No File Comment + bFileName = CompressUtils.GetBytes(Filename); } + else + { + GeneralPurposeBitFlag |= 1 << 11; + bFileName = Encoding.UTF8.GetBytes(Filename); + } + + ushort fileNameLength = (ushort)bFileName.Length; + + ushort versionNeededToExtract = (ushort)(GetStatus(LocalFileStatus.Zip64) ? 45 : 20); + + CompressUtils.UtcTicksToDosDateTime(HeaderLastModified, out ushort lastModFileDate, out ushort lastModFileTime); + + bw.Write(CentralDirectoryHeaderSignature); + bw.Write((ushort)0); + bw.Write(versionNeededToExtract); + bw.Write(GeneralPurposeBitFlag); + bw.Write(_compressionMethod); + bw.Write(lastModFileTime); + bw.Write(lastModFileDate); + WriteCRC(bw, CRC); + bw.Write(headerCompressedSize); + bw.Write(headerUnCompressedSize); + bw.Write(fileNameLength); + bw.Write(extraFieldLength); + bw.Write((ushort)0); // file comment length + bw.Write((ushort)0); // disk number start + bw.Write((ushort)0); // internal file attributes + bw.Write((uint)0); // external file attributes + bw.Write(headerRelativeOffsetOfLocalHeader); + bw.Write(bFileName, 0, fileNameLength); + bw.Write(_extraField, 0, extraFieldLength); + // No File Comment } - public ZipReturn LocalFileHeaderRead(Stream zipFs) + internal ZipReturn LocalFileHeaderRead(Stream zipFs) { try { - using (BinaryReader br = new BinaryReader(zipFs, Encoding.UTF8, true)) + using (BinaryReader br = new(zipFs, Encoding.UTF8, true)) { - TrrntZip = true; + SetStatus(LocalFileStatus.TrrntZip); zipFs.Position = (long)RelativeOffsetOfLocalHeader; uint thisSignature = br.ReadUInt32(); @@ -268,7 +251,7 @@ namespace Compress.ZipFile ushort generalPurposeBitFlagLocal = br.ReadUInt16(); if (generalPurposeBitFlagLocal != GeneralPurposeBitFlag) { - TrrntZip = false; + SetStatus(LocalFileStatus.TrrntZip, false); } ushort tshort = br.ReadUInt16(); @@ -280,42 +263,45 @@ namespace Compress.ZipFile ushort lastModFileTime = br.ReadUInt16(); ushort lastModFileDate = br.ReadUInt16(); - long tTime = ZipUtils.SetDateTime(lastModFileDate, lastModFileTime); - if (tTime != _lastModFileTimeDate) + long tTime = CompressUtils.UtcTicksFromDosDateTime(lastModFileDate, lastModFileTime); + + if (tTime != HeaderLastModified) { - return ZipReturn.ZipLocalFileHeaderError; + SetStatus(LocalFileStatus.DateTimeMisMatch); + SetStatus(LocalFileStatus.TrrntZip, false); } - byte[] tCRC = ReadCRC(br); - _localHeaderCompressedSize = br.ReadUInt32(); - _localHeaderUncompressedSize = br.ReadUInt32(); + LocalFile localHeader = new(); + localHeader.CRC = ReadCRC(br); + ulong localHeaderCompressedSize = br.ReadUInt32(); + localHeader.UncompressedSize = br.ReadUInt32(); + ulong localRelativeOffset = 0; ushort fileNameLength = br.ReadUInt16(); ushort extraFieldLength = br.ReadUInt16(); byte[] bFileName = br.ReadBytes(fileNameLength); - _localHeaderFilename = (generalPurposeBitFlagLocal & (1 << 11)) == 0 - ? ZipUtils.GetString(bFileName) + localHeader.Filename = (generalPurposeBitFlagLocal & (1 << 11)) == 0 + ? CompressUtils.GetString(bFileName) : Encoding.UTF8.GetString(bFileName, 0, fileNameLength); - Zip64 = false; if (extraFieldLength > 0) { byte[] extraField = br.ReadBytes(extraFieldLength); - ZipReturn zr = ZipExtraField.ReadLocalExtraField(extraField, bFileName, this, false); + ZipReturn zr = ZipExtraField.ReadExtraField(extraField, bFileName, localHeader, ref localHeaderCompressedSize, ref localRelativeOffset, false); if (zr != ZipReturn.ZipGood) return zr; } - if (!ZipUtils.CompareString(FileName, _localHeaderFilename)) + if (!CompressUtils.CompareString(Filename, localHeader.Filename)) { - TrrntZip = false; - if (!ZipUtils.CompareStringSlash(FileName, _localHeaderFilename)) + SetStatus(LocalFileStatus.TrrntZip, false); + if (!CompressUtils.CompareStringSlash(Filename.ToLower(), localHeader.Filename.ToLower())) { - return ZipReturn.ZipLocalFileHeaderError; + SetStatus(LocalFileStatus.FilenameMisMatch); } } @@ -323,31 +309,42 @@ namespace Compress.ZipFile if ((GeneralPurposeBitFlag & 8) == 8) { + SetStatus(LocalFileStatus.TrrntZip, false); zipFs.Position += (long)_compressedSize; - tCRC = ReadCRC(br); - if (!ZipUtils.ByteArrCompare(tCRC, new byte[] { 0x50, 0x4b, 0x07, 0x08 })) + localHeader.CRC = ReadCRC(br); + if (CompressUtils.ByteArrCompare(localHeader.CRC, new byte[] { 0x08, 0x07, 0x4b, 0x50 })) { - tCRC = ReadCRC(br); + localHeader.CRC = ReadCRC(br); + } + + if (GetStatus(LocalFileStatus.Zip64)) + { + localHeaderCompressedSize = br.ReadUInt64(); + localHeader.UncompressedSize = br.ReadUInt64(); + } + else + { + localHeaderCompressedSize = br.ReadUInt32(); + localHeader.UncompressedSize = br.ReadUInt32(); } - _localHeaderCompressedSize = br.ReadUInt32(); - _localHeaderUncompressedSize = br.ReadUInt32(); } - - if (ZipUtils.IsCodePage437(FileName) != ((GeneralPurposeBitFlag & (1 << 11)) == 0)) - TrrntZip = false; - - if (!ZipUtils.ByteArrCompare(tCRC, CRC)) + + if (CompressUtils.IsCodePage437(Filename) != ((GeneralPurposeBitFlag & (1 << 11)) == 0)) + SetStatus(LocalFileStatus.TrrntZip, false); + + + if (!CompressUtils.ByteArrCompare(localHeader.CRC, CRC)) { return ZipReturn.ZipLocalFileHeaderError; } - if (_localHeaderCompressedSize != _compressedSize) + if (localHeaderCompressedSize != _compressedSize) { return ZipReturn.ZipLocalFileHeaderError; } - if (_localHeaderUncompressedSize != UncompressedSize) + if (localHeader.UncompressedSize != UncompressedSize) { return ZipReturn.ZipLocalFileHeaderError; } @@ -361,69 +358,63 @@ namespace Compress.ZipFile } } - public ZipReturn LocalFileHeaderReadQuick(Stream zipFs) + internal ZipReturn LocalFileHeaderReadQuick(Stream zipFs) { try { - using (BinaryReader br = new BinaryReader(zipFs, Encoding.UTF8, true)) + using BinaryReader br = new(zipFs, Encoding.UTF8, true); + SetStatus(LocalFileStatus.TrrntZip); + + zipFs.Position = (long)RelativeOffsetOfLocalHeader; + uint thisSignature = br.ReadUInt32(); + if (thisSignature != LocalFileHeaderSignature) { - TrrntZip = true; - - zipFs.Position = (long)RelativeOffsetOfLocalHeader; - uint thisSignature = br.ReadUInt32(); - if (thisSignature != LocalFileHeaderSignature) - { - return ZipReturn.ZipLocalFileHeaderError; - } - - br.ReadUInt16(); // version needed to extract - GeneralPurposeBitFlag = br.ReadUInt16(); - if ((GeneralPurposeBitFlag & 8) == 8) - { - return ZipReturn.ZipCannotFastOpen; - } - - _compressionMethod = br.ReadUInt16(); - - ushort lastModFileTime = br.ReadUInt16(); - ushort lastModFileDate = br.ReadUInt16(); - _lastModFileTimeDate = ZipUtils.SetDateTime(lastModFileDate, lastModFileTime); - - CRC = ReadCRC(br); - _localHeaderCompressedSize = br.ReadUInt32(); - _localHeaderUncompressedSize = br.ReadUInt32(); - - ushort fileNameLength = br.ReadUInt16(); - ushort extraFieldLength = br.ReadUInt16(); - - byte[] bFileName = br.ReadBytes(fileNameLength); - - _localHeaderFilename = (GeneralPurposeBitFlag & (1 << 11)) == 0 - ? ZipUtils.GetString(bFileName) - : Encoding.UTF8.GetString(bFileName, 0, fileNameLength); - - Zip64 = false; - if (extraFieldLength > 0) - { - byte[] extraField = br.ReadBytes(extraFieldLength); - - ZipReturn zr = ZipExtraField.ReadLocalExtraField(extraField, bFileName, this, false); - if (zr != ZipReturn.ZipGood) - return zr; - } - - - _dataLocation = (ulong)zipFs.Position; - - FileName = _localHeaderFilename; - _compressedSize = _localHeaderCompressedSize; - UncompressedSize = _localHeaderUncompressedSize; - - if (ZipUtils.IsCodePage437(FileName) != ((GeneralPurposeBitFlag & (1 << 11)) == 0)) - TrrntZip = false; - - return ZipReturn.ZipGood; + return ZipReturn.ZipLocalFileHeaderError; } + + br.ReadUInt16(); // version needed to extract + GeneralPurposeBitFlag = br.ReadUInt16(); + if ((GeneralPurposeBitFlag & 8) == 8) + { + return ZipReturn.ZipCannotFastOpen; + } + + _compressionMethod = br.ReadUInt16(); + + ushort lastModFileTime = br.ReadUInt16(); + ushort lastModFileDate = br.ReadUInt16(); + HeaderLastModified = CompressUtils.UtcTicksFromDosDateTime(lastModFileDate, lastModFileTime); + + CRC = ReadCRC(br); + _compressedSize = br.ReadUInt32(); + UncompressedSize = br.ReadUInt32(); + + ushort fileNameLength = br.ReadUInt16(); + ushort extraFieldLength = br.ReadUInt16(); + + byte[] bFileName = br.ReadBytes(fileNameLength); + + Filename = (GeneralPurposeBitFlag & (1 << 11)) == 0 + ? CompressUtils.GetString(bFileName) + : Encoding.UTF8.GetString(bFileName, 0, fileNameLength); + + SetStatus(LocalFileStatus.Zip64, false); + if (extraFieldLength > 0) + { + byte[] extraField = br.ReadBytes(extraFieldLength); + + ulong LocalHeader = 0; + ZipReturn zr = ZipExtraField.ReadExtraField(extraField, bFileName, this, ref _compressedSize, ref LocalHeader, false); + if (zr != ZipReturn.ZipGood) + return zr; + } + + if (CompressUtils.IsCodePage437(Filename) != ((GeneralPurposeBitFlag & (1 << 11)) == 0)) + SetStatus(LocalFileStatus.TrrntZip, false); + + _dataLocation = (ulong)zipFs.Position; + + return ZipReturn.ZipGood; } catch { @@ -434,128 +425,150 @@ namespace Compress.ZipFile private void LocalFileHeaderWrite(Stream zipFs) { - using (BinaryWriter bw = new BinaryWriter(zipFs, Encoding.UTF8, true)) + using BinaryWriter bw = new(zipFs, Encoding.UTF8, true); + ZipExtraFieldWrite zefw = new(); + bool zip64 = zefw.Zip64(UncompressedSize, _compressedSize, RelativeOffsetOfLocalHeader, false, + out uint headerUnCompressedSize, out uint headerCompressedSize, + out uint headerRelativeOffsetOfLocalHeader); + _extraField = zefw.ExtraField; + + SetStatus(LocalFileStatus.Zip64, zip64); + + byte[] bFileName; + if (CompressUtils.IsCodePage437(Filename)) { - Zip64 = UncompressedSize >= 0xffffffff; - - byte[] bFileName; - if (ZipUtils.IsCodePage437(FileName)) - { - bFileName = ZipUtils.GetBytes(FileName); - } - else - { - GeneralPurposeBitFlag |= 1 << 11; - bFileName = Encoding.UTF8.GetBytes(FileName); - } - - ushort versionNeededToExtract = (ushort)(Zip64 ? 45 : 20); - - RelativeOffsetOfLocalHeader = (ulong)zipFs.Position; - const uint header = 0x4034B50; - bw.Write(header); - bw.Write(versionNeededToExtract); - bw.Write(GeneralPurposeBitFlag); - bw.Write(_compressionMethod); - - ZipUtils.SetDateTime(_lastModFileTimeDate, out ushort lastModFileDate, out ushort lastModFileTime); - bw.Write(lastModFileTime); - bw.Write(lastModFileDate); - - _crc32Location = (ulong)zipFs.Position; - - // these 3 values will be set correctly after the file data has been written - bw.Write(0xffffffff); - bw.Write(0xffffffff); - bw.Write(0xffffffff); - - ushort fileNameLength = (ushort)bFileName.Length; - bw.Write(fileNameLength); - - ushort extraFieldLength = (ushort)(Zip64 ? 20 : 0); - bw.Write(extraFieldLength); - - bw.Write(bFileName, 0, fileNameLength); - - _extraLocation = (ulong)zipFs.Position; - if (Zip64) - bw.Write(new byte[20], 0, extraFieldLength); + bFileName = CompressUtils.GetBytes(Filename); } + else + { + GeneralPurposeBitFlag |= 1 << 11; + bFileName = Encoding.UTF8.GetBytes(Filename); + } + + ushort versionNeededToExtract = (ushort)(GetStatus(LocalFileStatus.Zip64) ? 45 : 20); + + RelativeOffsetOfLocalHeader = (ulong)zipFs.Position; + bw.Write(LocalFileHeaderSignature); + bw.Write(versionNeededToExtract); + bw.Write(GeneralPurposeBitFlag); + bw.Write(_compressionMethod); + + CompressUtils.UtcTicksToDosDateTime(HeaderLastModified, out ushort lastModFileDate, out ushort lastModFileTime); + bw.Write(lastModFileTime); + bw.Write(lastModFileDate); + + // these 3 values will be set correctly after the file data has been written + bw.Write(0xffffffff); // crc + bw.Write(0xffffffff); // CompressedSize 32bit + bw.Write(0xffffffff); // UncompressedSie 32bit + + ushort fileNameLength = (ushort)bFileName.Length; + bw.Write(fileNameLength); + + ushort extraFieldLength = (ushort)_extraField.Length; + bw.Write(extraFieldLength); + + bw.Write(bFileName, 0, fileNameLength); + + _extraLocation = (ulong)zipFs.Position; + bw.Write(_extraField); } - public void LocalFileHeaderFake(ulong filePosition, ulong uncompressedSize, ulong compressedSize, byte[] crc32, MemoryStream ms) + private void LocalFileHeaderPostCompressWrite(Stream zipFs) { - using (BinaryWriter bw = new BinaryWriter(ms, Encoding.UTF8, true)) + // after data is written to the zip, go back and finish up the header. + + // there is a rare case where you can have a file that is very close up to the 32 bit size limit in it uncompressed size, + // and when it compresses it get just a little bigger, and bumps over the 32 bit size limit on its compressed size. + // so you get uncompressed size is < 0xffffffff so we did not think it needed a zip64 header. + // but compressed size is >= 0xffffffff so we do need to zip64 header, so we have to go back and move the compressed data + // down the file to make room in the file header for the zip64 extra data. + + ZipExtraFieldWrite zefw = new(); + bool postZip64 = zefw.Zip64(UncompressedSize, _compressedSize, RelativeOffsetOfLocalHeader, false, + out uint headerUnCompressedSize, out uint headerCompressedSize, out uint headerRelativeOffsetOfLocalHeader); + byte[] postExtraField = zefw.ExtraField; + + if (postZip64 != GetStatus(LocalFileStatus.Zip64)) { - RelativeOffsetOfLocalHeader = filePosition; - TrrntZip = true; - UncompressedSize = uncompressedSize; - _compressedSize = compressedSize; - CRC = crc32; + SetStatus(LocalFileStatus.Zip64); + FixFileForZip64(zipFs, _extraField.Length, postExtraField.Length); + } - Zip64 = UncompressedSize >= 0xffffffff || _compressedSize >= 0xffffffff; + _extraField = postExtraField; + long posNow = zipFs.Position; - byte[] bFileName; - if (ZipUtils.IsCodePage437(FileName)) + zipFs.Seek((long)RelativeOffsetOfLocalHeader + 14, SeekOrigin.Begin); + using (BinaryWriter bw = new(zipFs, Encoding.UTF8, true)) + { + WriteCRC(bw, CRC); + bw.Write(headerCompressedSize); + bw.Write(headerUnCompressedSize); + + if (_extraField.Length > 0) { - bFileName = ZipUtils.GetBytes(FileName); - } - else - { - GeneralPurposeBitFlag |= 1 << 11; - bFileName = Encoding.UTF8.GetBytes(FileName); - } - - ushort versionNeededToExtract = (ushort)(Zip64 ? 45 : 20); - - const uint header = 0x4034B50; - bw.Write(header); - bw.Write(versionNeededToExtract); - bw.Write(GeneralPurposeBitFlag); - bw.Write(_compressionMethod); - - ZipUtils.SetDateTime(_lastModFileTimeDate, out ushort lastModFileDate, out ushort lastModFileTime); - bw.Write(lastModFileTime); - bw.Write(lastModFileDate); - - uint tCompressedSize; - uint tUncompressedSize; - if (Zip64) - { - tCompressedSize = 0xffffffff; - tUncompressedSize = 0xffffffff; - } - else - { - tCompressedSize = (uint)_compressedSize; - tUncompressedSize = (uint)UncompressedSize; - } - - bw.Write(CRC[3]); - bw.Write(CRC[2]); - bw.Write(CRC[1]); - bw.Write(CRC[0]); - bw.Write(tCompressedSize); - bw.Write(tUncompressedSize); - - ushort fileNameLength = (ushort)bFileName.Length; - bw.Write(fileNameLength); - - ushort extraFieldLength = (ushort)(Zip64 ? 20 : 0); - bw.Write(extraFieldLength); - - bw.Write(bFileName, 0, fileNameLength); - - if (Zip64) - { - bw.Write((ushort)0x0001); // id - bw.Write((ushort)16); // data length - bw.Write(UncompressedSize); - bw.Write(_compressedSize); + zipFs.Seek((long)_extraLocation, SeekOrigin.Begin); + bw.Write(_extraField); } } + + zipFs.Seek(posNow, SeekOrigin.Begin); } - public ZipReturn LocalFileOpenReadStream(Stream zipFs, bool raw, out Stream readStream, out ulong streamSize, out ushort compressionMethod) + + + internal void LocalFileHeaderFake(ulong filePosition, ulong uncompressedSize, ulong compressedSize, byte[] crc32, MemoryStream ms) + { + using BinaryWriter bw = new(ms, Encoding.UTF8, true); + RelativeOffsetOfLocalHeader = filePosition; + SetStatus(LocalFileStatus.TrrntZip); + UncompressedSize = uncompressedSize; + _compressedSize = compressedSize; + CRC = crc32; + + ZipExtraFieldWrite zefw = new(); + SetStatus(LocalFileStatus.Zip64, + zefw.Zip64(UncompressedSize, _compressedSize, RelativeOffsetOfLocalHeader, false, + out uint headerUnCompressedSize, out uint headerCompressedSize, out uint headerRelativeOffsetOfLocalHeader) + ); + _extraField = zefw.ExtraField; + + byte[] bFileName; + if (CompressUtils.IsCodePage437(Filename)) + { + bFileName = CompressUtils.GetBytes(Filename); + } + else + { + GeneralPurposeBitFlag |= 1 << 11; + bFileName = Encoding.UTF8.GetBytes(Filename); + } + + ushort versionNeededToExtract = (ushort)(GetStatus(LocalFileStatus.Zip64) ? 45 : 20); + + bw.Write(LocalFileHeaderSignature); + bw.Write(versionNeededToExtract); + bw.Write(GeneralPurposeBitFlag); + bw.Write(_compressionMethod); + + CompressUtils.UtcTicksToDosDateTime(HeaderLastModified, out ushort lastModFileDate, out ushort lastModFileTime); + bw.Write(lastModFileTime); + bw.Write(lastModFileDate); + + WriteCRC(bw, CRC); + bw.Write(headerCompressedSize); + bw.Write(headerUnCompressedSize); + + ushort fileNameLength = (ushort)bFileName.Length; + bw.Write(fileNameLength); + + ushort extraFieldLength = (ushort)_extraField.Length; + bw.Write(extraFieldLength); + + bw.Write(bFileName, 0, fileNameLength); + + bw.Write(_extraField); + } + internal ZipReturn LocalFileOpenReadStream(Stream zipFs, bool raw, out Stream readStream, out ulong streamSize, out ushort compressionMethod) { streamSize = 0; compressionMethod = _compressionMethod; @@ -565,6 +578,11 @@ namespace Compress.ZipFile switch (_compressionMethod) { + case 0: + readStream = zipFs; + streamSize = _compressedSize; // same as UncompressedSize + break; + case 8: if (raw) { @@ -573,51 +591,94 @@ namespace Compress.ZipFile } else { - readStream=new System.IO.Compression.DeflateStream(zipFs,System.IO.Compression.CompressionMode.Decompress,true); //readStream = new ZlibBaseStream(zipFs, CompressionMode.Decompress, CompressionLevel.Default, ZlibStreamFlavor.DEFLATE, true); + readStream = new System.IO.Compression.DeflateStream(zipFs, System.IO.Compression.CompressionMode.Decompress, true); streamSize = UncompressedSize; } + break; - case 0: - readStream = zipFs; - streamSize = _compressedSize; // same as UncompressedSize + case 9: + readStream = new Deflate64Stream(zipFs, System.IO.Compression.CompressionMode.Decompress); + streamSize = UncompressedSize; break; + + //case 10: + // readStream = new BlastStream(zipFs); + // streamSize = UncompressedSize; + // break; + + case 12: + readStream = new CBZip2InputStream(zipFs, false); + streamSize = UncompressedSize; + break; + + case 14: + { + zipFs.ReadByte(); // Major version + zipFs.ReadByte(); // Minor version + int headerSize = zipFs.ReadByte() + (zipFs.ReadByte() << 8); + byte[] header = new byte[headerSize]; + zipFs.Read(header, 0, headerSize); + readStream = new LzmaStream(header, zipFs); + streamSize = UncompressedSize; + break; + } + + case 20: + case 93: + readStream = new ZstdSharp.DecompressionStream(zipFs); + streamSize = UncompressedSize; + break; + + case 98: + { + int headerSize = 2; + byte[] header = new byte[headerSize]; + zipFs.Read(header, 0, headerSize); + readStream = new PpmdStream(new PpmdProperties(header), zipFs, false); + streamSize = UncompressedSize; + break; + } } return readStream == null ? ZipReturn.ZipErrorGettingDataStream : ZipReturn.ZipGood; } - public ZipReturn LocalFileOpenWriteStream(Stream zipFs, bool raw, bool trrntZip, ulong uncompressedSize, ushort compressionMethod, out Stream writeStream) + internal ZipReturn LocalFileOpenWriteStream(Stream zipFs, bool raw, ulong uncompressedSize, ushort compressionMethod, out Stream writeStream) { UncompressedSize = uncompressedSize; + _compressedSize = 0; + RelativeOffsetOfLocalHeader = 0; _compressionMethod = compressionMethod; LocalFileHeaderWrite(zipFs); _dataLocation = (ulong)zipFs.Position; + writeStream = null; if (raw) { writeStream = zipFs; - TrrntZip = trrntZip; } else { if (compressionMethod == 0) { writeStream = zipFs; - TrrntZip = false; } - else + else if (compressionMethod == 93) + { + writeStream = new ZstdSharp.CompressionStream(zipFs, 19); + } + else if (compressionMethod == 8) { writeStream = new ZlibBaseStream(zipFs, CompressionMode.Compress, CompressionLevel.BestCompression, ZlibStreamFlavor.DEFLATE, true); - TrrntZip = true; } } return writeStream == null ? ZipReturn.ZipErrorGettingDataStream : ZipReturn.ZipGood; } - public ZipReturn LocalFileCloseWriteStream(Stream zipFs, byte[] crc32) + internal ZipReturn LocalFileCloseWriteStream(Stream zipFs, byte[] crc32) { _compressedSize = (ulong)zipFs.Position - _dataLocation; @@ -633,27 +694,27 @@ namespace Compress.ZipFile } CRC = crc32; - WriteCompressedSize(zipFs); + LocalFileHeaderPostCompressWrite(zipFs); return ZipReturn.ZipGood; } - private void FixFileForZip64(Stream zipFs) + private void FixFileForZip64(Stream zipFs, int oldExtraFieldLength, int newExtraFieldLength) { long posNow = zipFs.Position; - using (BinaryWriter bw = new BinaryWriter(zipFs, Encoding.UTF8, true)) + using (BinaryWriter bw = new(zipFs, Encoding.UTF8, true)) { - // _crc32Location - 10 needs set to 45 - zipFs.Seek((long)_crc32Location - 10, SeekOrigin.Begin); + zipFs.Seek((long)RelativeOffsetOfLocalHeader + 4, SeekOrigin.Begin); ushort versionNeededToExtract = 45; bw.Write(versionNeededToExtract); - zipFs.Seek((long)_crc32Location + 14, SeekOrigin.Begin); - ushort extraFieldLength = 20; - bw.Write(extraFieldLength); + zipFs.Seek((long)RelativeOffsetOfLocalHeader + 28, SeekOrigin.Begin); + bw.Write((ushort)newExtraFieldLength); } - ExpandFile(zipFs, (long)_extraLocation, posNow, 20); - zipFs.Position = posNow + 20; + + int expandBy = newExtraFieldLength - oldExtraFieldLength; + ExpandFile(zipFs, (long)_extraLocation, posNow, expandBy); + zipFs.Position = posNow + expandBy; } private static void ExpandFile(Stream stream, long offset, long length, int extraBytes) @@ -673,55 +734,7 @@ namespace Compress.ZipFile } } - private void WriteCompressedSize(Stream zipFs) - { - if (_compressedSize >= 0xffffffff && !Zip64) - { - Zip64 = true; - FixFileForZip64(zipFs); - } - - - long posNow = zipFs.Position; - zipFs.Seek((long)_crc32Location, SeekOrigin.Begin); - using (BinaryWriter bw = new BinaryWriter(zipFs, Encoding.UTF8, true)) - { - uint tCompressedSize; - uint tUncompressedSize; - if (Zip64) - { - tCompressedSize = 0xffffffff; - tUncompressedSize = 0xffffffff; - } - else - { - tCompressedSize = (uint)_compressedSize; - tUncompressedSize = (uint)UncompressedSize; - } - - bw.Write(CRC[3]); - bw.Write(CRC[2]); - bw.Write(CRC[1]); - bw.Write(CRC[0]); - bw.Write(tCompressedSize); - bw.Write(tUncompressedSize); - - - // also need to write extradata - if (Zip64) - { - zipFs.Seek((long)_extraLocation, SeekOrigin.Begin); - bw.Write((ushort)0x0001); // id - bw.Write((ushort)16); // data length - bw.Write(UncompressedSize); - bw.Write(_compressedSize); - } - } - - zipFs.Seek(posNow, SeekOrigin.Begin); - } - - public static void LocalFileAddZeroLengthFile(Stream zipFs) + internal static void LocalFileAddZeroLengthFile(Stream zipFs) { zipFs.WriteByte(03); zipFs.WriteByte(00); @@ -736,6 +749,16 @@ namespace Compress.ZipFile tCRC[0] = br.ReadByte(); return tCRC; } + + private static void WriteCRC(BinaryWriter bw, byte[] CRC) + { + bw.Write(CRC[3]); + bw.Write(CRC[2]); + bw.Write(CRC[1]); + bw.Write(CRC[0]); + + } + } } diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipRead.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipRead.cs index 5a07820a..faa15d7b 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipRead.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipRead.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Compress.Utils; +using Compress.Support.Utils; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; @@ -35,7 +35,7 @@ namespace Compress.ZipFile if (errorCode != 0) { ZipFileClose(); - if (errorCode == 32) + if (errorCode == 32 || errorCode==5) { return ZipReturn.ZipFileLocked; } @@ -109,7 +109,6 @@ namespace Compress.ZipFile return zRet; } - // check if ZIP64 header is required bool zip64Required = (_centralDirStart == 0xffffffff || _centralDirSize == 0xffffffff || _localFilesCount == 0xffff); @@ -118,12 +117,12 @@ namespace Compress.ZipFile zRet = Zip64EndOfCentralDirectoryLocatorRead(); if (zRet == ZipReturn.ZipGood) { - _zipFs.Position = (long)_endOfCenterDir64; + _zipFs.Position = (long)_endOfCentralDir64; zRet = Zip64EndOfCentralDirRead(); if (zRet == ZipReturn.ZipGood) { _zip64 = true; - endOfCentralDir = _endOfCenterDir64; + endOfCentralDir = _endOfCentralDir64; } } @@ -132,14 +131,18 @@ namespace Compress.ZipFile return ZipReturn.Zip64EndOfCentralDirError; } + offset = (endOfCentralDir - _centralDirSize) - _centralDirStart; + + _centralDirStart += offset; + bool trrntzip = false; // check if the ZIP has a valid TorrentZip file comment - if (_fileComment.Length == 22) + if (FileComment.Length == 22) { - if (ZipUtils.GetString(_fileComment).Substring(0, 14) == "TORRENTZIPPED-") + if (CompressUtils.GetString(FileComment).Substring(0, 14) == "TORRENTZIPPED-") { - CrcCalculatorStream crcCs = new CrcCalculatorStream(_zipFs, true); + CrcCalculatorStream crcCs = new(_zipFs, true); byte[] buffer = new byte[_centralDirSize]; _zipFs.Position = (long)_centralDirStart; crcCs.Read(buffer, 0, (int)_centralDirSize); @@ -149,7 +152,7 @@ namespace Compress.ZipFile uint r = (uint)crcCs.Crc; crcCs.Dispose(); - string tcrc = ZipUtils.GetString(_fileComment).Substring(14, 8); + string tcrc = CompressUtils.GetString(FileComment).Substring(14, 8); string zcrc = r.ToString("X8"); if (string.Compare(tcrc, zcrc, StringComparison.Ordinal) == 0) { @@ -157,7 +160,7 @@ namespace Compress.ZipFile } } } - + if (zip64Required != _zip64) trrntzip = false; @@ -168,14 +171,14 @@ namespace Compress.ZipFile _localFiles.Capacity = (int)_localFilesCount; for (int i = 0; i < _localFilesCount; i++) { - LocalFile lc = new LocalFile(); - zRet = lc.CenteralDirectoryRead(_zipFs); + ZipLocalFile lc = new(); + zRet = lc.CentralDirectoryRead(_zipFs, offset); if (zRet != ZipReturn.ZipGood) { ZipFileClose(); return zRet; } - _zip64 |= lc.Zip64; + _zip64 |= lc.GetStatus(LocalFileStatus.Zip64); _localFiles.Add(lc); } @@ -187,7 +190,7 @@ namespace Compress.ZipFile ZipFileClose(); return zRet; } - trrntzip &= _localFiles[i].TrrntZip; + trrntzip &= _localFiles[i].GetStatus(LocalFileStatus.TrrntZip); } // check trrntzip file order @@ -195,7 +198,7 @@ namespace Compress.ZipFile { for (int i = 0; i < _localFilesCount - 1; i++) { - if (ZipUtils.TrrntZipStringCompare(_localFiles[i].FileName, _localFiles[i + 1].FileName) < 0) + if (CompressUtils.TrrntZipStringCompare(_localFiles[i].Filename, _localFiles[i + 1].Filename) < 0) { continue; } @@ -210,19 +213,19 @@ namespace Compress.ZipFile for (int i = 0; i < _localFilesCount - 1; i++) { // see if we found a directory - string filename0 = _localFiles[i].FileName; + string filename0 = _localFiles[i].Filename; if (filename0.Substring(filename0.Length - 1, 1) != "/") { continue; } // see if the next file is in that directory - string filename1 = _localFiles[i + 1].FileName; + string filename1 = _localFiles[i + 1].Filename; if (filename1.Length <= filename0.Length) { continue; } - if (ZipUtils.TrrntZipStringCompare(filename0, filename1.Substring(0, filename0.Length)) != 0) + if (CompressUtils.TrrntZipStringCompare(filename0, filename1.Substring(0, filename0.Length)) != 0) { continue; } diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipReadStream.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipReadStream.cs index 4e80eacd..35627e96 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipReadStream.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipReadStream.cs @@ -1,5 +1,4 @@ using System.IO; -using Compress.ZipFile.ZLib; namespace Compress.ZipFile { @@ -38,7 +37,7 @@ namespace Compress.ZipFile { ZipFileCloseReadStream(); - LocalFile tmpFile = new LocalFile { LocalFilePos = pos }; + ZipLocalFile tmpFile = new ZipLocalFile { RelativeOffsetOfLocalHeader = pos }; _localFiles.Clear(); _localFiles.Add(tmpFile); ZipReturn zRet = tmpFile.LocalFileHeaderReadQuick(_zipFs); @@ -60,16 +59,13 @@ namespace Compress.ZipFile { if (_compressionStream == null) return ZipReturn.ZipGood; - if (_compressionStream is ZlibBaseStream dfStream) + + if (_compressionStream != _zipFs) { - dfStream.Close(); - dfStream.Dispose(); - } - else if (_compressionStream is System.IO.Compression.DeflateStream dfIOStream) - { - dfIOStream.Close(); - dfIOStream.Dispose(); + _compressionStream.Close(); + _compressionStream.Dispose(); } + _compressionStream = null; return ZipReturn.ZipGood; diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipWrite.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipWrite.cs index fbc67e2f..df1a611f 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipWrite.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipWrite.cs @@ -1,27 +1,32 @@ -using Compress.Utils; +using Compress.Support.Utils; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; -// UInt16 = ushort -// UInt32 = uint -// ULong = ulong namespace Compress.ZipFile { + public partial class Zip { - private ulong _centralDirStart; - private ulong _centralDirSize; - private ulong _endOfCenterDir64; public ZipReturn ZipFileCreate(string newFilename) + { + return ZipFileCreate(newFilename, OutputZipType.None); + } + + // OutType of Trrntzip forces that we must have a trrtnzip file made + // OutType of None will still make a trrntzip file if everything was supplied in trrntzip format. + + public ZipReturn ZipFileCreate(string newFilename, OutputZipType outType) { if (ZipOpen != ZipOpenType.Closed) { return ZipReturn.ZipFileAlreadyOpen; } - DirUtil.CreateDirForFile(newFilename); + writeZipType = outType; + + CompressUtils.CreateDirForFile(newFilename); _zipFileInfo = new FileInfo(newFilename); int errorCode = FileStream.OpenFileWrite(newFilename, out _zipFs); @@ -42,10 +47,10 @@ namespace Compress.ZipFile using (CrcCalculatorStream crcCs = new CrcCalculatorStream(_zipFs, true)) { - foreach (LocalFile t in _localFiles) + foreach (ZipLocalFile t in _localFiles) { - t.CenteralDirectoryWrite(crcCs); - lTrrntzip &= t.TrrntZip; + t.CentralDirectoryWrite(crcCs); + lTrrntzip &= t.GetStatus(LocalFileStatus.TrrntZip); } crcCs.Flush(); @@ -53,7 +58,7 @@ namespace Compress.ZipFile _centralDirSize = (ulong)_zipFs.Position - _centralDirStart; - _fileComment = lTrrntzip ? ZipUtils.GetBytes("TORRENTZIPPED-" + crcCs.Crc.ToString("X8")) : new byte[0]; + FileComment = lTrrntzip ? CompressUtils.GetBytes("TORRENTZIPPED-" + crcCs.Crc.ToString("X8")) : new byte[0]; ZipStatus = lTrrntzip ? ZipStatus.TrrntZip : ZipStatus.None; } @@ -64,7 +69,7 @@ namespace Compress.ZipFile if (_zip64) { - _endOfCenterDir64 = (ulong)_zipFs.Position; + _endOfCentralDir64 = (ulong)_zipFs.Position; Zip64EndOfCentralDirWrite(); Zip64EndOfCentralDirectoryLocatorWrite(); } diff --git a/SabreTools.FileTypes/Compress/ZipFile/ZipWriteStream.cs b/SabreTools.FileTypes/Compress/ZipFile/ZipWriteStream.cs index 7d2e911c..b0a1038e 100644 --- a/SabreTools.FileTypes/Compress/ZipFile/ZipWriteStream.cs +++ b/SabreTools.FileTypes/Compress/ZipFile/ZipWriteStream.cs @@ -1,6 +1,6 @@ using System.IO; -using Compress.Utils; -using Compress.ZipFile.ZLib; +using Compress.Support.Compression.Deflate; +using Compress.Support.Utils; // UInt16 = ushort // UInt32 = uint @@ -12,6 +12,14 @@ namespace Compress.ZipFile { private Stream _compressionStream; + /* + raw is true if we are just going to copy the raw data stream from the source to the destination zip file + trrntzip is thue if the source zip is a valid trrntzip file + compressionMethod must be set to 8 to make a valid trrntzip file. + + if raw is false then compressionMthod must be 0,8 or 93 (zstd) + */ + public ZipReturn ZipFileOpenWriteStream(bool raw, bool trrntzip, string filename, ulong uncompressedSize, ushort compressionMethod, out Stream stream, TimeStamps timeStamp = null) { stream = null; @@ -20,19 +28,56 @@ namespace Compress.ZipFile return ZipReturn.ZipWritingToInputFile; } - LocalFile lf = new LocalFile(filename, timeStamp); + ZipReturn validTrrntzip = ZipReturn.ZipGood; - ZipReturn retVal = lf.LocalFileOpenWriteStream(_zipFs, raw, trrntzip, uncompressedSize, compressionMethod, out stream); + //invalid torrentZip Input If: + if (compressionMethod != 8) validTrrntzip = ZipReturn.ZipTrrntzipIncorrectCompressionUsed; + if (raw && !trrntzip) validTrrntzip = ZipReturn.ZipTrrntZipIncorrectDataStream; + + int localFilesCount = _localFiles.Count; + if (localFilesCount > 0) + { + // check that filenames are in trrntzip order + string lastFilename = _localFiles[localFilesCount - 1].Filename; + if (CompressUtils.TrrntZipStringCompare(lastFilename, filename) > 0) + validTrrntzip = ZipReturn.ZipTrrntzipIncorrectFileOrder; + + // check that no un-needed directory entries are added + if (_localFiles[localFilesCount - 1].IsDirectory && filename.Length > lastFilename.Length) + { + if (CompressUtils.TrrntZipStringCompare(lastFilename, filename.Substring(0, lastFilename.Length)) == 0) + { + validTrrntzip = ZipReturn.ZipTrrntzipIncorrectDirectoryAddedToZip; + } + } + } + + // if we are requirering a trrrntzp file and it is not a trrntzip formated supplied stream then error out + if (writeZipType == OutputZipType.TrrntZip) + { + if (validTrrntzip != ZipReturn.ZipGood) + return validTrrntzip; + } + + ZipLocalFile lf = new ZipLocalFile(filename, timeStamp); + lf.SetStatus(LocalFileStatus.TrrntZip, validTrrntzip == ZipReturn.ZipGood); + + ZipReturn retVal = lf.LocalFileOpenWriteStream(_zipFs, raw, uncompressedSize, compressionMethod, out stream); + if (retVal != ZipReturn.ZipGood) + return retVal; + + if (filename.Length > 0) + lf.IsDirectory = (filename.Substring(filename.Length - 1, 1) == "/"); _compressionStream = stream; _localFiles.Add(lf); - return retVal; + return ZipReturn.ZipGood; } public void ZipFileAddZeroLengthFile() { - LocalFile.LocalFileAddZeroLengthFile(_zipFs); + ZipLocalFile.LocalFileAddZeroLengthFile(_zipFs); } @@ -62,10 +107,8 @@ namespace Compress.ZipFile return ZipReturn.ZipErrorRollBackFile; } - LocalFile lf = _localFiles[fileCount - 1]; - _localFiles.RemoveAt(fileCount - 1); - _zipFs.Position = (long)lf.LocalFilePos; + _zipFs.Position = (long)_localFiles[fileCount - 1].RelativeOffsetOfLocalHeader; return ZipReturn.ZipGood; } diff --git a/SabreTools.FileTypes/Compress/gZip/gZip.cs b/SabreTools.FileTypes/Compress/gZip/gZip.cs index 4c8049c0..fb4ca434 100644 --- a/SabreTools.FileTypes/Compress/gZip/gZip.cs +++ b/SabreTools.FileTypes/Compress/gZip/gZip.cs @@ -1,8 +1,8 @@ using System; using System.IO; using System.Text; -using Compress.Utils; -using Compress.ZipFile.ZLib; +using Compress.Support.Compression.Deflate; +using Compress.Support.Utils; using FileInfo = RVIO.FileInfo; using FileStream = RVIO.FileStream; using Path = RVIO.Path; @@ -16,9 +16,10 @@ namespace Compress.gZip private Stream _zipFs; private Stream _compressionStream; - public byte[] CRC { get; private set; } - public ulong UnCompressedSize { get; private set; } + private byte[] CRC; + private ulong UnCompressedSize; public ulong CompressedSize { get; private set; } + private uint MTime; private long headerStartPos; private long dataStartPos; @@ -28,41 +29,17 @@ namespace Compress.gZip return 1; } - public string Filename(int i) + public LocalFile GetLocalFile(int i) { - return Path.GetFileName(ZipFilename); - } - - public ulong? LocalHeader(int i) - { - return 0; - } - - public ulong UncompressedSize(int i) - { - return UnCompressedSize; - } - - public byte[] CRC32(int i) - { - return CRC; - } - - public bool IsDirectory(int i) - { - return false; - } - public long LastModified(int i) - { - return 0; // need to test if this is the same as Zip Date (Probably is) - } - public long? Created(int i) - { - return null; - } - public long? Accessed(int i) - { - return null; + LocalFile lf = new() + { + Filename = Path.GetFileName(ZipFilename), + UncompressedSize = UnCompressedSize, + CRC = this.CRC, + IsDirectory = false, + ModifiedTime = MTime == 0 ? null : (long?)CompressUtils.UtcTicksFromUnixDateTime((int)MTime) + }; + return lf; } @@ -96,6 +73,12 @@ namespace Compress.gZip } return ZipReturn.ZipErrorOpeningFile; } + ZipOpen = ZipOpenType.OpenRead; + if (!readHeaders) + { + return ZipReturn.ZipGood; + } + return ZipFileReadHeaders(); } catch (PathTooLongException) { @@ -107,13 +90,12 @@ namespace Compress.gZip ZipFileClose(); return ZipReturn.ZipErrorOpeningFile; } - ZipOpen = ZipOpenType.OpenRead; - - if (!readHeaders) + catch(Exception) { - return ZipReturn.ZipGood; + ZipFileClose(); + return ZipReturn.ZipErrorReadingFile; } - return ZipFileReadHeaders(); + } public ZipReturn ZipFileOpen(Stream inStream) @@ -129,143 +111,141 @@ namespace Compress.gZip private ZipReturn ZipFileReadHeaders() { - using (BinaryReader zipBr = new BinaryReader(_zipFs, Encoding.UTF8, true)) + using BinaryReader zipBr = new(_zipFs, Encoding.UTF8, true); + + byte ID1 = zipBr.ReadByte(); + byte ID2 = zipBr.ReadByte(); + + if ((ID1 != 0x1f) || (ID2 != 0x8b)) { - - byte ID1 = zipBr.ReadByte(); - byte ID2 = zipBr.ReadByte(); - - if ((ID1 != 0x1f) || (ID2 != 0x8b)) - { - _zipFs.Close(); - return ZipReturn.ZipSignatureError; - } - - byte CM = zipBr.ReadByte(); - if (CM != 8) - { - _zipFs.Close(); - return ZipReturn.ZipUnsupportedCompression; - } - - byte FLG = zipBr.ReadByte(); - - uint MTime = zipBr.ReadUInt32(); - byte XFL = zipBr.ReadByte(); - byte OS = zipBr.ReadByte(); - - ExtraData = null; - //if FLG.FEXTRA set - if ((FLG & 0x4) == 0x4) - { - int XLen = zipBr.ReadInt16(); - ExtraData = zipBr.ReadBytes(XLen); - - switch (XLen) - { - case 12: - // 0-3: byte[4] CRC - CRC = new byte[4]; - Array.Copy(ExtraData, 0, CRC, 0, 4); - // 4-11: ulong uncompressed - UnCompressedSize = BitConverter.ToUInt64(ExtraData, 4); - break; - case 28: - // 0-15: byte[16] md5 - // byte[] md5Hash = new byte[16]; - // Array.Copy(ExtraData, 0, md5Hash, 0, 16); - // 16-19: byte[4] CRC - CRC = new byte[4]; - Array.Copy(ExtraData, 16, CRC, 0, 4); - // 20-27: ulong uncompressed - UnCompressedSize = BitConverter.ToUInt64(ExtraData, 20); - break; - case 77: - // 0-15: byte[16] md5 - // md5Hash = new byte[16]; - // Array.Copy(ExtraData, 0, md5Hash, 0, 16); - // 16-19: byte[4] CRC - CRC = new byte[4]; - Array.Copy(ExtraData, 16, CRC, 0, 4); - // 20-27: ulong uncompressed - UnCompressedSize = BitConverter.ToUInt64(ExtraData, 20); - - // 28: altFileType - // byte altType = ExtraData[28]; - - // 29-44: byte[16] altmd5 - // byte[] altmd5Hash = new byte[16]; - // Array.Copy(ExtraData, 29, altmd5Hash, 0, 16); - // 45-64: byte[20] altsha1 - // byte[] altsha1Hash = new byte[20]; - // Array.Copy(ExtraData, 45, altsha1Hash, 0, 20); - // 65-68: byte[4] altcrc - // byte[] altcrc = new byte[4]; - // Array.Copy(ExtraData, 65, altcrc, 0, 4); - // 69-76: ulong altuncompressed - // ulong uncompressedAltSize = BitConverter.ToUInt64(ExtraData, 69); - - break; - } - } - - //if FLG.FNAME set - if ((FLG & 0x8) == 0x8) - { - int XLen = zipBr.ReadInt16(); - byte[] bytes = zipBr.ReadBytes(XLen); - } - - //if FLG.FComment set - if ((FLG & 0x10) == 0x10) - { - int XLen = zipBr.ReadInt16(); - byte[] bytes = zipBr.ReadBytes(XLen); - } - - //if FLG.FHCRC set - if ((FLG & 0x2) == 0x2) - { - uint crc16 = zipBr.ReadUInt16(); - } - - CompressedSize = (ulong)(_zipFs.Length - _zipFs.Position) - 8; - - dataStartPos = _zipFs.Position; - - _zipFs.Position = _zipFs.Length - 8; - byte[] gzcrc = zipBr.ReadBytes(4); - uint gzLength = zipBr.ReadUInt32(); - - if (CRC != null) - { - for (int i = 0; i < 4; i++) - { - if (gzcrc[3 - i] == CRC[i]) - { - continue; - } - - _zipFs.Close(); - return ZipReturn.ZipDecodeError; - } - } - else - { - CRC = new[] { gzcrc[3], gzcrc[2], gzcrc[1], gzcrc[0] }; - } - - if (UnCompressedSize != 0) - { - if (gzLength != (UnCompressedSize & 0xffffffff)) - { - _zipFs.Close(); - return ZipReturn.ZipDecodeError; - } - } - - return ZipReturn.ZipGood; + _zipFs.Close(); + return ZipReturn.ZipSignatureError; } + + byte CM = zipBr.ReadByte(); + if (CM != 8) + { + _zipFs.Close(); + return ZipReturn.ZipUnsupportedCompression; + } + + byte FLG = zipBr.ReadByte(); + + MTime = zipBr.ReadUInt32(); + byte XFL = zipBr.ReadByte(); + byte OS = zipBr.ReadByte(); + + ExtraData = null; + //if FLG.FEXTRA set + if ((FLG & 0x4) == 0x4) + { + int XLen = zipBr.ReadInt16(); + ExtraData = zipBr.ReadBytes(XLen); + + switch (XLen) + { + case 12: + // 0-3: byte[4] CRC + CRC = new byte[4]; + Array.Copy(ExtraData, 0, CRC, 0, 4); + // 4-11: ulong uncompressed + UnCompressedSize = BitConverter.ToUInt64(ExtraData, 4); + break; + case 28: + // 0-15: byte[16] md5 + // byte[] md5Hash = new byte[16]; + // Array.Copy(ExtraData, 0, md5Hash, 0, 16); + // 16-19: byte[4] CRC + CRC = new byte[4]; + Array.Copy(ExtraData, 16, CRC, 0, 4); + // 20-27: ulong uncompressed + UnCompressedSize = BitConverter.ToUInt64(ExtraData, 20); + break; + case 77: + // 0-15: byte[16] md5 + // md5Hash = new byte[16]; + // Array.Copy(ExtraData, 0, md5Hash, 0, 16); + // 16-19: byte[4] CRC + CRC = new byte[4]; + Array.Copy(ExtraData, 16, CRC, 0, 4); + // 20-27: ulong uncompressed + UnCompressedSize = BitConverter.ToUInt64(ExtraData, 20); + + // 28: altFileType + // byte altType = ExtraData[28]; + + // 29-44: byte[16] altmd5 + // byte[] altmd5Hash = new byte[16]; + // Array.Copy(ExtraData, 29, altmd5Hash, 0, 16); + // 45-64: byte[20] altsha1 + // byte[] altsha1Hash = new byte[20]; + // Array.Copy(ExtraData, 45, altsha1Hash, 0, 20); + // 65-68: byte[4] altcrc + // byte[] altcrc = new byte[4]; + // Array.Copy(ExtraData, 65, altcrc, 0, 4); + // 69-76: ulong altuncompressed + // ulong uncompressedAltSize = BitConverter.ToUInt64(ExtraData, 69); + + break; + } + } + + //if FLG.FNAME set + if ((FLG & 0x8) == 0x8) + { + int XLen = zipBr.ReadInt16(); + byte[] bytes = zipBr.ReadBytes(XLen); + } + + //if FLG.FComment set + if ((FLG & 0x10) == 0x10) + { + int XLen = zipBr.ReadInt16(); + byte[] bytes = zipBr.ReadBytes(XLen); + } + + //if FLG.FHCRC set + if ((FLG & 0x2) == 0x2) + { + uint crc16 = zipBr.ReadUInt16(); + } + + CompressedSize = (ulong)(_zipFs.Length - _zipFs.Position) - 8; + + dataStartPos = _zipFs.Position; + + _zipFs.Position = _zipFs.Length - 8; + byte[] gzcrc = zipBr.ReadBytes(4); + uint gzLength = zipBr.ReadUInt32(); + + if (CRC != null) + { + for (int i = 0; i < 4; i++) + { + if (gzcrc[3 - i] == CRC[i]) + { + continue; + } + + _zipFs.Close(); + return ZipReturn.ZipDecodeError; + } + } + else + { + CRC = new[] { gzcrc[3], gzcrc[2], gzcrc[1], gzcrc[0] }; + } + + if (UnCompressedSize != 0) + { + if (gzLength != (UnCompressedSize & 0xffffffff)) + { + _zipFs.Close(); + return ZipReturn.ZipDecodeError; + } + } + + return ZipReturn.ZipGood; } public void ZipFileClose() @@ -336,7 +316,7 @@ namespace Compress.gZip public ZipReturn ZipFileOpenWriteStream(bool raw, bool trrntzip, string filename, ulong unCompressedSize, ushort compressionMethod, out Stream stream, TimeStamps dateTime) { - using (BinaryWriter zipBw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) + using (BinaryWriter zipBw = new(_zipFs, Encoding.UTF8, true)) { UnCompressedSize = unCompressedSize; @@ -415,7 +395,7 @@ namespace Compress.gZip return ZipReturn.ZipFileAlreadyOpen; } - DirUtil.CreateDirForFile(newFilename); + CompressUtils.CreateDirForFile(newFilename); _zipFileInfo = new FileInfo(newFilename); int errorCode = FileStream.OpenFileWrite(newFilename, out _zipFs); @@ -442,7 +422,7 @@ namespace Compress.gZip CompressedSize = (ulong)(_zipFs.Position - dataStartPos); - using (BinaryWriter zipBw = new BinaryWriter(_zipFs, Encoding.UTF8, true)) + using (BinaryWriter zipBw = new(_zipFs, Encoding.UTF8, true)) { zipBw.Write(crc32[3]); diff --git a/SabreTools.FileTypes/RVIO/RVIO.cs b/SabreTools.FileTypes/RVIO/RVIO.cs index 1896b26f..68b3140f 100644 --- a/SabreTools.FileTypes/RVIO/RVIO.cs +++ b/SabreTools.FileTypes/RVIO/RVIO.cs @@ -34,7 +34,7 @@ namespace RVIO } } - public static class unix + public static class Unix { public static bool IsUnix { @@ -51,10 +51,15 @@ namespace RVIO public string Name; public string FullName; public long LastWriteTime; + public long LastAccessTime; + public long CreationTime; public long Length; - public FileInfo() - { } + public FileInfo(string name, string fullName) + { + Name = name; + FullName = fullName; + } public FileInfo(string path) { @@ -66,7 +71,9 @@ namespace RVIO if (!fi.Exists) return; Length = fi.Length; - LastWriteTime = fi.LastWriteTimeUtc.Ticks; + try { LastWriteTime = fi.LastWriteTimeUtc.Ticks; } catch { LastWriteTime = 0; } + try { LastAccessTime = fi.LastAccessTimeUtc.Ticks; } catch { LastAccessTime = 0; } + try { CreationTime = fi.CreationTimeUtc.Ticks; } catch { CreationTime = 0; } } } @@ -75,9 +82,14 @@ namespace RVIO public string Name; public string FullName; public long LastWriteTime; + public long LastAccessTime; + public long CreationTime; - public DirectoryInfo() - { } + public DirectoryInfo(string name, string fullName) + { + Name = name; + FullName = fullName; + } public DirectoryInfo(string path) { FullName = path; @@ -87,7 +99,9 @@ namespace RVIO if (!fi.Exists) return; - LastWriteTime = fi.LastWriteTimeUtc.Ticks; + try { LastWriteTime = fi.LastWriteTimeUtc.Ticks; } catch { LastWriteTime = 0; } + try { LastAccessTime = fi.LastAccessTimeUtc.Ticks; } catch { LastAccessTime = 0; } + try { CreationTime = fi.CreationTimeUtc.Ticks; } catch { CreationTime = 0; } } public DirectoryInfo[] GetDirectories() @@ -103,16 +117,19 @@ namespace RVIO System.IO.DirectoryInfo[] arrDi = di.GetDirectories(); foreach (System.IO.DirectoryInfo tDi in arrDi) { - DirectoryInfo lDi = new DirectoryInfo + try { - Name = tDi.Name, - FullName = Path.Combine(FullName, tDi.Name), - LastWriteTime = tDi.LastWriteTimeUtc.Ticks - }; - dirs.Add(lDi); + DirectoryInfo lDi = new DirectoryInfo(tDi.Name, Path.Combine(FullName, tDi.Name)); + try { lDi.LastWriteTime = tDi.LastWriteTimeUtc.Ticks; } catch { lDi.LastWriteTime = 0; } + try { lDi.LastAccessTime = tDi.LastAccessTimeUtc.Ticks; } catch { lDi.LastAccessTime = 0; } + try { lDi.CreationTime = tDi.CreationTimeUtc.Ticks; } catch { lDi.CreationTime = 0; } + + dirs.Add(lDi); + } + catch { } } } - catch (Exception e) + catch { } @@ -132,17 +149,19 @@ namespace RVIO System.IO.FileInfo[] arrDi = di.GetFiles(SearchPattern); foreach (System.IO.FileInfo tDi in arrDi) { - FileInfo lDi = new FileInfo + try { - Name = tDi.Name, - FullName = Path.Combine(FullName, tDi.Name), - Length = tDi.Length, - LastWriteTime = tDi.LastWriteTimeUtc.Ticks - }; - files.Add(lDi); + FileInfo lFi = new FileInfo(tDi.Name, Path.Combine(FullName, tDi.Name)) { Length = tDi.Length }; + try { lFi.LastWriteTime = tDi.LastWriteTimeUtc.Ticks; } catch { lFi.LastWriteTime = 0; } + try { lFi.LastAccessTime = tDi.LastAccessTimeUtc.Ticks; } catch { lFi.LastAccessTime = 0; } + try { lFi.CreationTime = tDi.CreationTimeUtc.Ticks; } catch { lFi.CreationTime = 0; } + + files.Add(lFi); + } + catch { } } } - catch (Exception e) + catch { } @@ -201,6 +220,16 @@ namespace RVIO } + public static void WriteAllBytes(string path, byte[] data) + { + System.IO.File.WriteAllBytes(NameFix.AddLongPathPrefix(path), data); + } + public static void WriteAllText(string path, string contents) + { + System.IO.File.WriteAllText(NameFix.AddLongPathPrefix(path), contents); + } + + public static bool SetAttributes(string path, FileAttributes fileAttributes) { try @@ -224,6 +253,15 @@ namespace RVIO int errorCode = FileStream.OpenFileRead(filename, out Stream fStream); return errorCode != 0 ? null : new StreamReader(fStream, Enc); } + + public static string ReadAllText(string filename) + { + return System.IO.File.ReadAllText(NameFix.AddLongPathPrefix(filename)); + } + public static byte[] ReadAllBytes(string filename) + { + return System.IO.File.ReadAllBytes(NameFix.AddLongPathPrefix(filename)); + } } public static class Path @@ -234,7 +272,12 @@ namespace RVIO public static char DirSeparatorChar { - get { return unix.IsUnix ? AltDirectorySeparatorChar : DirectorySeparatorChar; } + get { return Unix.IsUnix ? AltDirectorySeparatorChar : DirectorySeparatorChar; } + } + + public static string FixSlash(string path) + { + return !Unix.IsUnix ? path : path.Replace(DirectorySeparatorChar, AltDirectorySeparatorChar); } public static string GetExtension(string path) @@ -243,7 +286,7 @@ namespace RVIO } public static string Combine(string path1, string path2) { - if (unix.IsUnix) + if (Unix.IsUnix) return System.IO.Path.Combine(path1, path2); if (path1 == null || path2 == null) @@ -312,10 +355,10 @@ namespace RVIO stream = new System.IO.FileStream(NameFix.AddLongPathPrefix(path), FileMode.Open, FileAccess.Read); return 0; } - catch (Exception) + catch (Exception e) { stream = null; - return Marshal.GetLastWin32Error(); + return e.HResult; } } @@ -326,55 +369,19 @@ namespace RVIO stream = new System.IO.FileStream(NameFix.AddLongPathPrefix(path), FileMode.Create, FileAccess.ReadWrite); return 0; } - catch (Exception) + catch (Exception e) { stream = null; - return Marshal.GetLastWin32Error(); + return e.HResult; } } } public static class NameFix { - public static string GetShortPath(string path) + public static string AddLongPathPrefix(string path) { - if (unix.IsUnix) - return path; - - int remove = 0; - string retPath; - if (path.StartsWith(@"\\")) - { - retPath = @"\\?\UNC\" + path.Substring(2); - remove = 8; - } - else - { - retPath = path; - if (path.Substring(1, 1) != ":") - retPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), retPath); - - retPath = cleandots(retPath); - retPath = @"\\?\" + retPath; - remove = 4; - } - - const int MAX_PATH = 300; - StringBuilder shortPath = new StringBuilder(MAX_PATH); - Win32Native.GetShortPathName(retPath, shortPath, MAX_PATH); - retPath = shortPath.ToString(); - - retPath = retPath.Substring(remove); - if (remove == 8) retPath = "\\" + retPath; - - return retPath; - } - - - - internal static string AddLongPathPrefix(string path) - { - if (unix.IsUnix) + if (Unix.IsUnix) return path; if (string.IsNullOrEmpty(path) || path.StartsWith(@"\\?\")) @@ -387,12 +394,12 @@ namespace RVIO if (path.Substring(1, 1) != ":") retPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), retPath); - retPath = cleandots(retPath); + retPath = CleanDots(retPath); return @"\\?\" + retPath; } - private static string cleandots(string path) + private static string CleanDots(string path) { string retPath = path; while (retPath.Contains(@"\..\")) @@ -409,4 +416,4 @@ namespace RVIO } } -} +} \ No newline at end of file diff --git a/SabreTools.FileTypes/RVIO/Win32Native.cs b/SabreTools.FileTypes/RVIO/Win32Native.cs index acbcfa63..7d7dbcdb 100644 --- a/SabreTools.FileTypes/RVIO/Win32Native.cs +++ b/SabreTools.FileTypes/RVIO/Win32Native.cs @@ -1,7 +1,7 @@ /****************************************************** * ROMVault3 is written by Gordon J. * * Contact gordon@romvault.com * - * Copyright 2020 * + * Copyright 2022 * ******************************************************/ using System; diff --git a/SabreTools.FileTypes/SabreTools.FileTypes.csproj b/SabreTools.FileTypes/SabreTools.FileTypes.csproj index e073dec7..4305e2a2 100644 --- a/SabreTools.FileTypes/SabreTools.FileTypes.csproj +++ b/SabreTools.FileTypes/SabreTools.FileTypes.csproj @@ -14,6 +14,7 @@ +