mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
[ZipFileEntry] Get slightly modified version of LocalFile into the project, based on the work in RomVault at https://github.com/gjefferyes/RomVault/blob/master/ROMVault2/SupportedFiles/Zip/zipFile.cs
This commit is contained in:
@@ -83,34 +83,34 @@ namespace SabreTools.Helper
|
|||||||
#region Byte (1024-based) size comparisons
|
#region Byte (1024-based) size comparisons
|
||||||
|
|
||||||
public const long KibiByte = 1024;
|
public const long KibiByte = 1024;
|
||||||
public const long MibiByte = (long)Math.Pow(KibiByte, 2);
|
public static long MibiByte = (long)Math.Pow(KibiByte, 2);
|
||||||
public const long GibiByte = (long)Math.Pow(KibiByte, 3);
|
public static long GibiByte = (long)Math.Pow(KibiByte, 3);
|
||||||
public const long TibiByte = (long)Math.Pow(KibiByte, 4);
|
public static long TibiByte = (long)Math.Pow(KibiByte, 4);
|
||||||
public const long PibiByte = (long)Math.Pow(KibiByte, 5);
|
public static long PibiByte = (long)Math.Pow(KibiByte, 5);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Byte (1000-based) size comparisons
|
#region Byte (1000-based) size comparisons
|
||||||
|
|
||||||
public const long KiloByte = 1000;
|
public const long KiloByte = 1000;
|
||||||
public const long MegaByte = (long)Math.Pow(KiloByte, 2);
|
public static long MegaByte = (long)Math.Pow(KiloByte, 2);
|
||||||
public const long GigaByte = (long)Math.Pow(KiloByte, 2);
|
public static long GigaByte = (long)Math.Pow(KiloByte, 2);
|
||||||
public const long TeraByte = (long)Math.Pow(KiloByte, 2);
|
public static long TeraByte = (long)Math.Pow(KiloByte, 2);
|
||||||
public const long PetaByte = (long)Math.Pow(KiloByte, 2);
|
public static long PetaByte = (long)Math.Pow(KiloByte, 2);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Magic numbers as byte arrays
|
#region Magic numbers as byte arrays
|
||||||
|
|
||||||
public const byte[] SevenZipSigBytes = new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c };
|
public static byte[] SevenZipSigBytes = new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c };
|
||||||
public const byte[] GzSigBytes = new byte[] { 0x1f, 0x8b };
|
public static byte[] GzSigBytes = new byte[] { 0x1f, 0x8b };
|
||||||
public const byte[] RarSigBytes = new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
|
public static byte[] RarSigBytes = new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
|
||||||
public const byte[] RarFiveSigBytes = new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 };
|
public static byte[] RarFiveSigBytes = new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 };
|
||||||
public const byte[] TarSigBytes = new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 };
|
public static byte[] TarSigBytes = new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 };
|
||||||
public const byte[] TarZeroSigBytes = new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 };
|
public static byte[] TarZeroSigBytes = new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 };
|
||||||
public const byte[] ZipSigBytes = new byte[] { 0x50, 0x4b, 0x03, 0x04 };
|
public static byte[] ZipSigBytes = new byte[] { 0x50, 0x4b, 0x03, 0x04 };
|
||||||
public const byte[] ZipSigEmptyBytes = new byte[] { 0x50, 0x4b, 0x05, 0x06 };
|
public static byte[] ZipSigEmptyBytes = new byte[] { 0x50, 0x4b, 0x05, 0x06 };
|
||||||
public const byte[] ZipSigSpannedBytes = new byte[] { 0x50, 0x4b, 0x07, 0x08 };
|
public static byte[] ZipSigSpannedBytes = new byte[] { 0x50, 0x4b, 0x07, 0x08 };
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ namespace SabreTools.Helper
|
|||||||
0A-0B Last mod file time (0x00, 0xBC)
|
0A-0B Last mod file time (0x00, 0xBC)
|
||||||
0C-0D Last mod file date (0x98, 0x21)
|
0C-0D Last mod file date (0x98, 0x21)
|
||||||
*/
|
*/
|
||||||
public const byte[] TorrentZipHeader = new byte[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0xbc, 0x98, 0x21 };
|
public static byte[] TorrentZipHeader = new byte[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0xbc, 0x98, 0x21 };
|
||||||
|
|
||||||
/* Torrent7z Header Format
|
/* Torrent7z Header Format
|
||||||
http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-1.0/7zip/DOC/7zFormat.txt
|
http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-1.0/7zip/DOC/7zFormat.txt
|
||||||
@@ -150,7 +150,7 @@ namespace SabreTools.Helper
|
|||||||
06-07 ArchiveVersion (0x00, 0x03)
|
06-07 ArchiveVersion (0x00, 0x03)
|
||||||
The rest is unknown
|
The rest is unknown
|
||||||
*/
|
*/
|
||||||
public const byte[] Torrent7ZipHeader = new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, 0x00, 0x03 };
|
public static byte[] Torrent7ZipHeader = new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, 0x00, 0x03 };
|
||||||
|
|
||||||
/* (Torrent)GZ Header Format
|
/* (Torrent)GZ Header Format
|
||||||
https://tools.ietf.org/html/rfc1952
|
https://tools.ietf.org/html/rfc1952
|
||||||
@@ -168,7 +168,7 @@ namespace SabreTools.Helper
|
|||||||
1C-1F CRC hash
|
1C-1F CRC hash
|
||||||
20-27 Int64 size (mirrored)
|
20-27 Int64 size (mirrored)
|
||||||
*/
|
*/
|
||||||
public const byte[] TorrentGZHeader = new byte[] { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00 };
|
public static byte[] TorrentGZHeader = new byte[] { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00 };
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -227,5 +227,38 @@
|
|||||||
Type17 = 17,
|
Type17 = 17,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zip testing type
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>https://raw.githubusercontent.com/gjefferyes/RomVault/5a93500001f0d068f32cf77a048950717507f733/ROMVault2/SupportedFiles/ZipEnums.cs</remarks>
|
||||||
|
public enum ZipReturn
|
||||||
|
{
|
||||||
|
ZipGood,
|
||||||
|
ZipFileLocked,
|
||||||
|
ZipFileCountError,
|
||||||
|
ZipSignatureError,
|
||||||
|
ZipExtraDataOnEndOfZip,
|
||||||
|
ZipUnsupportedCompression,
|
||||||
|
ZipLocalFileHeaderError,
|
||||||
|
ZipCentralDirError,
|
||||||
|
ZipEndOfCentralDirectoryError,
|
||||||
|
Zip64EndOfCentralDirError,
|
||||||
|
Zip64EndOfCentralDirectoryLocatorError,
|
||||||
|
ZipReadingFromOutputFile,
|
||||||
|
ZipWritingToInputFile,
|
||||||
|
ZipErrorGettingDataStream,
|
||||||
|
ZipCRCDecodeError,
|
||||||
|
ZipDecodeError,
|
||||||
|
ZipFileNameToLong,
|
||||||
|
ZipFileAlreadyOpen,
|
||||||
|
ZipCannotFastOpen,
|
||||||
|
ZipErrorOpeningFile,
|
||||||
|
ZipErrorFileNotFound,
|
||||||
|
ZipErrorReadingFile,
|
||||||
|
ZipErrorTimeStamp,
|
||||||
|
ZipErrorRollBackFile,
|
||||||
|
ZipUntested
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
2
SabreTools.Helper/External/OptimizedCRC.cs
vendored
2
SabreTools.Helper/External/OptimizedCRC.cs
vendored
@@ -26,7 +26,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace CRC32
|
namespace OCRC
|
||||||
{
|
{
|
||||||
public class OptimizedCRC : IDisposable
|
public class OptimizedCRC : IDisposable
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,605 +0,0 @@
|
|||||||
using Ionic.Crc;
|
|
||||||
using Ionic.Zlib;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace SabreTools.Helper
|
|
||||||
{
|
|
||||||
/// <remarks>
|
|
||||||
/// Based on work by GordonJ for RomVault
|
|
||||||
/// https://github.com/gjefferyes/RomVault/blob/master/ROMVault2/SupportedFiles/Zip/zipFile.cs
|
|
||||||
/// </remarks>
|
|
||||||
public class ZipFileEntry
|
|
||||||
{
|
|
||||||
#region Private instance variables
|
|
||||||
|
|
||||||
private readonly Stream _zipstream;
|
|
||||||
private string _fileName;
|
|
||||||
private CompressionMethod _compressionMethod;
|
|
||||||
private ArchiveVersion _versionMadeBy;
|
|
||||||
private ArchiveVersion _versionNeeded;
|
|
||||||
private GeneralPurposeBitFlag _generalPurposeBitFlag;
|
|
||||||
private ushort _lastModFileTime;
|
|
||||||
private ushort _lastModFileDate;
|
|
||||||
private uint _crc;
|
|
||||||
private ulong _compressedSize;
|
|
||||||
private ulong _uncompressedSize;
|
|
||||||
private byte[] _extraField;
|
|
||||||
private byte[] _comment;
|
|
||||||
private InternalFileAttributes _internalFileAttributes;
|
|
||||||
private uint _externalFileAttributes;
|
|
||||||
private ulong _relativeOffset;
|
|
||||||
private ulong _crc32Location;
|
|
||||||
private ulong _extraLocation;
|
|
||||||
private ulong _dataLocation;
|
|
||||||
private bool _zip64;
|
|
||||||
private bool _torrentZip;
|
|
||||||
private byte[] _md5;
|
|
||||||
private byte[] _sha1;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public facing variables
|
|
||||||
|
|
||||||
public string FileName
|
|
||||||
{
|
|
||||||
get { return _fileName; }
|
|
||||||
private set { _fileName = value; }
|
|
||||||
}
|
|
||||||
public GeneralPurposeBitFlag GeneralPurposeBitFlag
|
|
||||||
{
|
|
||||||
get { return _generalPurposeBitFlag; }
|
|
||||||
private set { _generalPurposeBitFlag = value; }
|
|
||||||
}
|
|
||||||
public ushort LastModFileTime
|
|
||||||
{
|
|
||||||
get { return _lastModFileTime; }
|
|
||||||
set { _lastModFileTime = value; }
|
|
||||||
}
|
|
||||||
public ushort LastModFileDate
|
|
||||||
{
|
|
||||||
get { return _lastModFileDate; }
|
|
||||||
set { _lastModFileDate = value; }
|
|
||||||
}
|
|
||||||
public byte[] CRC
|
|
||||||
{
|
|
||||||
get { return BitConverter.GetBytes(_crc); }
|
|
||||||
private set { _crc = BitConverter.ToUInt32(value, 0); }
|
|
||||||
}
|
|
||||||
public ulong UncompressedSize
|
|
||||||
{
|
|
||||||
get { return _uncompressedSize; }
|
|
||||||
private set { _uncompressedSize = value; }
|
|
||||||
}
|
|
||||||
public string ExtraField
|
|
||||||
{
|
|
||||||
get { return Encoding.ASCII.GetString(_extraField); }
|
|
||||||
set { _extraField = Style.StringToByteArray(Style.ConvertAsciiToHex(value)); }
|
|
||||||
}
|
|
||||||
public string Comment
|
|
||||||
{
|
|
||||||
get { return Encoding.ASCII.GetString(_comment); }
|
|
||||||
set { _comment = Style.StringToByteArray(Style.ConvertAsciiToHex(value)); }
|
|
||||||
}
|
|
||||||
public ulong RelativeOffset
|
|
||||||
{
|
|
||||||
get { return _relativeOffset; }
|
|
||||||
set { _relativeOffset = value; }
|
|
||||||
}
|
|
||||||
public bool Zip64
|
|
||||||
{
|
|
||||||
get { return _zip64; }
|
|
||||||
private set { _zip64 = value; }
|
|
||||||
}
|
|
||||||
public bool TorrentZip
|
|
||||||
{
|
|
||||||
get { return _torrentZip; }
|
|
||||||
private set { _torrentZip = value; }
|
|
||||||
}
|
|
||||||
public byte[] MD5
|
|
||||||
{
|
|
||||||
get { return _md5; }
|
|
||||||
private set { _md5 = value; }
|
|
||||||
}
|
|
||||||
public byte[] SHA1
|
|
||||||
{
|
|
||||||
get { return _sha1; }
|
|
||||||
private set { _sha1 = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new ZipFileEntry using just a stream
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="zipstream">Stream representing the entry</param>
|
|
||||||
public ZipFileEntry(Stream zipstream)
|
|
||||||
{
|
|
||||||
_zipstream = zipstream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new ZipFileEntry from a stream and a filename
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="zipstream">Stream representing the entry</param>
|
|
||||||
/// <param name="filename">Internal filename to use</param>
|
|
||||||
/// <param name="torrentZip">True if the file should be set with TorrentZip defaults, false otherwise (default)</param>
|
|
||||||
public ZipFileEntry(Stream zipstream, string filename, bool torrentZip = false)
|
|
||||||
{
|
|
||||||
_zip64 = false;
|
|
||||||
_zipstream = zipstream;
|
|
||||||
_generalPurposeBitFlag = GeneralPurposeBitFlag.DeflatingMaximumCompression;
|
|
||||||
_compressionMethod = CompressionMethod.Deflated;
|
|
||||||
FileName = filename;
|
|
||||||
|
|
||||||
if (torrentZip)
|
|
||||||
{
|
|
||||||
_lastModFileTime = 48128;
|
|
||||||
_lastModFileDate = 8600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the central directory entry from the input stream
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the central directory was read correctly, false otherwise</returns>
|
|
||||||
public bool ReadCentralDirectory()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Open the stream for reading
|
|
||||||
using (BinaryReader br = new BinaryReader(_zipstream))
|
|
||||||
{
|
|
||||||
// If the first bytes aren't a central directory header, log and return
|
|
||||||
if (br.ReadUInt32() != Constants.CentralDirectoryHeaderSignature)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Central directory entry malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now read in available information, skipping the unnecessary
|
|
||||||
_versionMadeBy = (ArchiveVersion)br.ReadUInt16();
|
|
||||||
_versionNeeded = (ArchiveVersion)br.ReadUInt16();
|
|
||||||
_generalPurposeBitFlag = (GeneralPurposeBitFlag)br.ReadUInt16();
|
|
||||||
_compressionMethod = (CompressionMethod)br.ReadUInt16();
|
|
||||||
|
|
||||||
// If we have an unsupported compression method, log and return
|
|
||||||
if (_compressionMethod != CompressionMethod.Stored && _compressionMethod != CompressionMethod.Deflated)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Unsupported compression method; requires store or deflate");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep reading available information, skipping the unnecessary
|
|
||||||
_lastModFileTime = br.ReadUInt16();
|
|
||||||
_lastModFileDate = br.ReadUInt16();
|
|
||||||
_crc = br.ReadUInt32();
|
|
||||||
_compressedSize = br.ReadUInt32();
|
|
||||||
_uncompressedSize = br.ReadUInt32();
|
|
||||||
|
|
||||||
// Now store some temp vars to find the filename, extra field, and comment
|
|
||||||
ushort fileNameLength = br.ReadUInt16();
|
|
||||||
ushort extraFieldLength = br.ReadUInt16();
|
|
||||||
ushort fileCommentLength = br.ReadUInt16();
|
|
||||||
|
|
||||||
// Even more reading available information, skipping the unnecessary
|
|
||||||
br.ReadUInt16(); // Disk number start
|
|
||||||
_internalFileAttributes = (InternalFileAttributes)br.ReadUInt16();
|
|
||||||
_externalFileAttributes = br.ReadUInt16();
|
|
||||||
_relativeOffset = br.ReadUInt32();
|
|
||||||
byte[] fileNameBytes = br.ReadBytes(fileNameLength);
|
|
||||||
_fileName = ((_generalPurposeBitFlag & GeneralPurposeBitFlag.LanguageEncodingFlag) == 0
|
|
||||||
? Encoding.ASCII.GetString(fileNameBytes)
|
|
||||||
: Encoding.UTF8.GetString(fileNameBytes, 0, fileNameLength));
|
|
||||||
_extraField = br.ReadBytes(extraFieldLength);
|
|
||||||
_comment = br.ReadBytes(fileCommentLength);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Full disclosure: this next section is in GordonJ's work but I honestly
|
|
||||||
have no idea everything that it does. It seems to do something to figure
|
|
||||||
out if it's Zip64, or possibly check for random things but it uses the
|
|
||||||
extra field for this, which I do not fully understand. It's copied in
|
|
||||||
its entirety below in the hope that it makes things better...
|
|
||||||
*/
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
while (extraFieldLength > pos)
|
|
||||||
{
|
|
||||||
ushort type = BitConverter.ToUInt16(_extraField, pos);
|
|
||||||
pos += 2;
|
|
||||||
ushort blockLength = BitConverter.ToUInt16(_extraField, pos);
|
|
||||||
pos += 2;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case 0x0001:
|
|
||||||
Zip64 = true;
|
|
||||||
if (UncompressedSize == 0xffffffff)
|
|
||||||
{
|
|
||||||
UncompressedSize = BitConverter.ToUInt64(_extraField, pos);
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
if (_compressedSize == 0xffffffff)
|
|
||||||
{
|
|
||||||
_compressedSize = BitConverter.ToUInt64(_extraField, pos);
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
if (_relativeOffset == 0xffffffff)
|
|
||||||
{
|
|
||||||
_relativeOffset = BitConverter.ToUInt64(_extraField, pos);
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x7075:
|
|
||||||
//byte version = extraField[pos];
|
|
||||||
pos += 1;
|
|
||||||
uint nameCRC32 = BitConverter.ToUInt32(_extraField, pos);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
Ionic.Crc.CRC32 crcTest = new Ionic.Crc.CRC32();
|
|
||||||
crcTest.SlurpBlock(fileNameBytes, 0, fileNameLength);
|
|
||||||
uint fCRC = (uint)crcTest.Crc32Result;
|
|
||||||
|
|
||||||
if (nameCRC32 != fCRC)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Central directory entry malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int charLen = blockLength - 5;
|
|
||||||
|
|
||||||
_fileName = Encoding.UTF8.GetString(_extraField, pos, charLen);
|
|
||||||
pos += charLen;
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pos += blockLength;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.Write("Error: Central directory entry malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write the central directory entry from the included stream
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">Write out the data from the internal stream to the output stream</param>
|
|
||||||
public void WriteCentralDirectory(Stream output)
|
|
||||||
{
|
|
||||||
// Open the crcStream for writing
|
|
||||||
using (BinaryWriter bw = new BinaryWriter(output))
|
|
||||||
{
|
|
||||||
// Create an empty extra field to start out with
|
|
||||||
List<byte> extraField = new List<byte>();
|
|
||||||
|
|
||||||
// Now get the uncompressed size (for Zip64 compatibility)
|
|
||||||
uint uncompressedSize32;
|
|
||||||
if (_uncompressedSize >= 0xffffffff)
|
|
||||||
{
|
|
||||||
_zip64 = true;
|
|
||||||
uncompressedSize32 = 0xffffffff;
|
|
||||||
extraField.AddRange(BitConverter.GetBytes(_uncompressedSize));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uncompressedSize32 = (uint)_uncompressedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now get the compressed size (for Zip64 compatibility)
|
|
||||||
uint compressedSize32;
|
|
||||||
if (_compressedSize >= 0xffffffff)
|
|
||||||
{
|
|
||||||
_zip64 = true;
|
|
||||||
compressedSize32 = 0xffffffff;
|
|
||||||
extraField.AddRange(BitConverter.GetBytes(_compressedSize));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
compressedSize32 = (uint)_compressedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now get the relative offset (for Zip64 compatibility)
|
|
||||||
uint relativeOffset32;
|
|
||||||
if (_relativeOffset >= 0xffffffff)
|
|
||||||
{
|
|
||||||
_zip64 = true;
|
|
||||||
relativeOffset32 = 0xffffffff;
|
|
||||||
extraField.AddRange(BitConverter.GetBytes(_relativeOffset));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
relativeOffset32 = (uint)_relativeOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we wrote anything to the extra field, set the flag and size
|
|
||||||
if (extraField.Count > 0)
|
|
||||||
{
|
|
||||||
ushort extraFieldLengthInternal = (ushort)extraField.Count;
|
|
||||||
extraField.InsertRange(0, BitConverter.GetBytes((ushort)0x0001));
|
|
||||||
extraField.InsertRange(2, BitConverter.GetBytes(extraFieldLengthInternal));
|
|
||||||
}
|
|
||||||
ushort extraFieldLength = (ushort)extraField.Count;
|
|
||||||
|
|
||||||
// Now check for a unicode filename and set the flag accordingly
|
|
||||||
byte[] fileNameBytes;
|
|
||||||
if (Style.IsUnicode(_fileName))
|
|
||||||
{
|
|
||||||
_generalPurposeBitFlag |= GeneralPurposeBitFlag.LanguageEncodingFlag;
|
|
||||||
fileNameBytes = Encoding.UTF8.GetBytes(_fileName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fileNameBytes = Encoding.ASCII.GetBytes(_fileName);
|
|
||||||
}
|
|
||||||
ushort fileNameLength = (ushort)fileNameBytes.Length;
|
|
||||||
|
|
||||||
// Set the version needed to extract according to if it's Zip64
|
|
||||||
ushort versionNeededToExtract = (ushort)(_zip64 ? 45 : 20);
|
|
||||||
|
|
||||||
// Now, write all of the data to the stream
|
|
||||||
bw.Write(Constants.CentralDirectoryHeaderSignature);
|
|
||||||
bw.Write((ushort)ArchiveVersion.MSDOSandOS2);
|
|
||||||
bw.Write(versionNeededToExtract);
|
|
||||||
bw.Write((ushort)_generalPurposeBitFlag);
|
|
||||||
bw.Write((ushort)_compressionMethod);
|
|
||||||
bw.Write(_lastModFileTime);
|
|
||||||
bw.Write(_lastModFileDate);
|
|
||||||
bw.Write(_crc);
|
|
||||||
bw.Write(compressedSize32);
|
|
||||||
bw.Write(uncompressedSize32);
|
|
||||||
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(relativeOffset32);
|
|
||||||
bw.Write(fileNameBytes, 0, fileNameLength); // Only write first bytes if longer than allowed
|
|
||||||
bw.Write(extraField.ToArray(), 0, extraFieldLength); // Only write the first bytes if longer than allowed
|
|
||||||
// We have no file comment, so we don't have to write more
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the local file header from the input stream
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the local file header was read correctly, false otherwise</returns>
|
|
||||||
public bool ReadLocalFileHeader()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// We assume that the file is torrentzip until proven otherwise
|
|
||||||
_torrentZip = true;
|
|
||||||
|
|
||||||
// Open the stream for reading
|
|
||||||
using (BinaryReader br = new BinaryReader(_zipstream))
|
|
||||||
{
|
|
||||||
// Set the position of the writer based on the entry information
|
|
||||||
br.BaseStream.Seek((long)_relativeOffset, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
// If the first bytes aren't a local file header, log and return
|
|
||||||
if (br.ReadUInt32() != Constants.LocalFileHeaderSignature)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now read in available information, comparing to the known data
|
|
||||||
if (br.ReadUInt16() != (ushort)_versionNeeded)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (br.ReadUInt16() != (ushort)_generalPurposeBitFlag)
|
|
||||||
{
|
|
||||||
_torrentZip = false;
|
|
||||||
}
|
|
||||||
if (br.ReadUInt16() != (ushort)_compressionMethod)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (br.ReadUInt16() != _lastModFileTime)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (br.ReadUInt16() != _lastModFileDate)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ((_generalPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) == 0 && br.ReadUInt32() != _crc)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint readCompressedSize = br.ReadUInt32();
|
|
||||||
// If we have Zip64, the compressed size should be 0xffffffff
|
|
||||||
if (_zip64 && readCompressedSize != 0xffffffff && readCompressedSize != _compressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we have the zeroed flag set, then no size should be included
|
|
||||||
if ((_generalPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) == GeneralPurposeBitFlag.ZeroedCRCAndSize && readCompressedSize != 0)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we don't have the zeroed flag set, then the size should match
|
|
||||||
if ((_generalPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) == 0 && readCompressedSize != _compressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint readUncompressedSize = br.ReadUInt32();
|
|
||||||
// If we have Zip64, the uncompressed size should be 0xffffffff
|
|
||||||
if (_zip64 && readUncompressedSize != 0xffffffff && readUncompressedSize != _compressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we have the zeroed flag set, then no size should be included
|
|
||||||
if ((_generalPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) == GeneralPurposeBitFlag.ZeroedCRCAndSize && readUncompressedSize != 0)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we don't have the zeroed flag set, then the size should match
|
|
||||||
if ((_generalPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) == 0 && readUncompressedSize != _uncompressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort fileNameLength = br.ReadUInt16();
|
|
||||||
ushort extraFieldLength = br.ReadUInt16();
|
|
||||||
|
|
||||||
byte[] fileNameBytes = br.ReadBytes(fileNameLength);
|
|
||||||
string tempFileName = ((_generalPurposeBitFlag & GeneralPurposeBitFlag.LanguageEncodingFlag) == 0
|
|
||||||
? Encoding.ASCII.GetString(fileNameBytes)
|
|
||||||
: Encoding.UTF8.GetString(fileNameBytes, 0, fileNameLength));
|
|
||||||
|
|
||||||
byte[] extraField = br.ReadBytes(extraFieldLength);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Full disclosure: this next section is in GordonJ's work but I honestly
|
|
||||||
have no idea everything that it does. It seems to do something to figure
|
|
||||||
out if it's Zip64, or possibly check for random things but it uses the
|
|
||||||
extra field for this, which I do not fully understand. It's copied in
|
|
||||||
its entirety below in the hope that it makes things better...
|
|
||||||
*/
|
|
||||||
|
|
||||||
_zip64 = false;
|
|
||||||
int pos = 0;
|
|
||||||
while (extraFieldLength > pos)
|
|
||||||
{
|
|
||||||
ushort type = BitConverter.ToUInt16(extraField, pos);
|
|
||||||
pos += 2;
|
|
||||||
ushort blockLength = BitConverter.ToUInt16(extraField, pos);
|
|
||||||
pos += 2;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case 0x0001:
|
|
||||||
Zip64 = true;
|
|
||||||
if (readUncompressedSize == 0xffffffff)
|
|
||||||
{
|
|
||||||
ulong tLong = BitConverter.ToUInt64(extraField, pos);
|
|
||||||
if (tLong != UncompressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
if (readCompressedSize == 0xffffffff)
|
|
||||||
{
|
|
||||||
ulong tLong = BitConverter.ToUInt64(extraField, pos);
|
|
||||||
if (tLong != _compressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x7075:
|
|
||||||
//byte version = extraField[pos];
|
|
||||||
pos += 1;
|
|
||||||
uint nameCRC32 = BitConverter.ToUInt32(extraField, pos);
|
|
||||||
pos += 4;
|
|
||||||
|
|
||||||
Ionic.Crc.CRC32 crcTest = new Ionic.Crc.CRC32();
|
|
||||||
crcTest.SlurpBlock(fileNameBytes, 0, fileNameLength);
|
|
||||||
uint fCRC = (uint)crcTest.Crc32Result;
|
|
||||||
|
|
||||||
if (nameCRC32 != fCRC)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int charLen = blockLength - 5;
|
|
||||||
|
|
||||||
tempFileName = Encoding.UTF8.GetString(extraField, pos, charLen);
|
|
||||||
pos += charLen;
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pos += blockLength;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back to code I understand
|
|
||||||
if (String.Equals(_fileName, tempFileName, StringComparison.InvariantCulture))
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the position of the data
|
|
||||||
_dataLocation = (ulong)_zipstream.Position;
|
|
||||||
|
|
||||||
// Now if no other data should be after the data, return
|
|
||||||
if((_generalPurposeBitFlag & GeneralPurposeBitFlag.ZeroedCRCAndSize) == GeneralPurposeBitFlag.ZeroedCRCAndSize)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, compare the data after the file too
|
|
||||||
_zipstream.Seek((long)_compressedSize, SeekOrigin.Current);
|
|
||||||
|
|
||||||
// If there's no subheader, read the next thing as crc
|
|
||||||
uint tempCrc = br.ReadUInt32();
|
|
||||||
if (tempCrc != Constants.EndOfLocalFileHeaderSignature)
|
|
||||||
{
|
|
||||||
tempCrc = br.ReadUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempCrc != _crc)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (br.ReadUInt32() != _compressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (br.ReadUInt32() != _uncompressedSize)
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.Write("Error: Local file header malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1022
SabreTools.Helper/Objects/ZipFileEntry.cs
Normal file
1022
SabreTools.Helper/Objects/ZipFileEntry.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,7 @@
|
|||||||
<Compile Include="Objects\Import.cs" />
|
<Compile Include="Objects\Import.cs" />
|
||||||
<Compile Include="Objects\ImportTwo.cs" />
|
<Compile Include="Objects\ImportTwo.cs" />
|
||||||
<Compile Include="Objects\SimpleSort.cs" />
|
<Compile Include="Objects\SimpleSort.cs" />
|
||||||
<Compile Include="Objects\ZipArchiveEntry.cs" />
|
<Compile Include="Objects\ZipFileEntry.cs" />
|
||||||
<Compile Include="Objects\ZipFIle.cs" />
|
<Compile Include="Objects\ZipFIle.cs" />
|
||||||
<Compile Include="Resources\Resources.de-DE.Designer.cs">
|
<Compile Include="Resources\Resources.de-DE.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using CRC32;
|
using OCRC;
|
||||||
using SharpCompress.Archive;
|
using SharpCompress.Archive;
|
||||||
using SharpCompress.Archive.SevenZip;
|
using SharpCompress.Archive.SevenZip;
|
||||||
using SharpCompress.Common;
|
using SharpCompress.Common;
|
||||||
|
|||||||
Reference in New Issue
Block a user