1580 lines
60 KiB
C#
1580 lines
60 KiB
C#
/******************************************************
|
|
* ROMVault2 is written by Gordon J. *
|
|
* Contact gordon@romvault.com *
|
|
* Copyright 2014 *
|
|
******************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using ROMVault2.SupportedFiles.Zip.ZLib;
|
|
|
|
// UInt16 = ushort
|
|
// UInt32 = uint
|
|
// ULong = ulong
|
|
|
|
namespace ROMVault2.SupportedFiles.Zip
|
|
{
|
|
|
|
public class ZipFile
|
|
{
|
|
const int Buffersize = 4096 * 1024;
|
|
private static byte[] _buffer0;
|
|
private static byte[] _buffer1;
|
|
|
|
private const uint LocalFileHeaderSignature = 0x04034b50;
|
|
private const uint CentralDirectoryHeaderSigniature = 0x02014b50;
|
|
private const uint EndOfCentralDirSignature = 0x06054b50;
|
|
private const uint Zip64EndOfCentralDirSignatue = 0x06064b50;
|
|
private const uint Zip64EndOfCentralDirectoryLocator = 0x07064b50;
|
|
|
|
class LocalFile
|
|
{
|
|
private readonly Stream _zipFs;
|
|
public string FileName { get; private set; }
|
|
public ushort _generalPurposeBitFlag { get; private set; }
|
|
private ushort _compressionMethod;
|
|
private ushort _lastModFileTime;
|
|
private ushort _lastModFileDate;
|
|
public byte[] CRC { get; private set; }
|
|
private ulong _compressedSize;
|
|
public ulong UncompressedSize { get; private set; }
|
|
public ulong RelativeOffsetOfLocalHeader; // only in centeral directory
|
|
|
|
private ulong _crc32Location;
|
|
private ulong _extraLocation;
|
|
private ulong _dataLocation;
|
|
|
|
public bool Zip64 { get; private set; }
|
|
public bool TrrntZip { get; private set; }
|
|
|
|
public byte[] sha1 { get; private set; }
|
|
public byte[] md5 { get; private set; }
|
|
|
|
public ZipReturn FileStatus = ZipReturn.ZipUntested;
|
|
public LocalFile(Stream zipFs)
|
|
{
|
|
_zipFs = zipFs;
|
|
}
|
|
public LocalFile(Stream zipFs, string filename)
|
|
{
|
|
Zip64 = false;
|
|
_zipFs = zipFs;
|
|
_generalPurposeBitFlag = 2; // Maximum Compression Deflating
|
|
_compressionMethod = 8; // Compression Method Deflate
|
|
_lastModFileTime = 48128;
|
|
_lastModFileDate = 8600;
|
|
|
|
FileName = filename;
|
|
}
|
|
|
|
|
|
public ZipReturn CenteralDirectoryRead()
|
|
{
|
|
try
|
|
{
|
|
BinaryReader br = new BinaryReader(_zipFs);
|
|
|
|
uint thisSignature = br.ReadUInt32();
|
|
if (thisSignature != CentralDirectoryHeaderSigniature)
|
|
return ZipReturn.ZipCenteralDirError;
|
|
|
|
br.ReadUInt16(); // Version Made By
|
|
|
|
br.ReadUInt16(); // Version Needed To Extract
|
|
|
|
|
|
_generalPurposeBitFlag = br.ReadUInt16();
|
|
_compressionMethod = br.ReadUInt16();
|
|
if (_compressionMethod != 8 && _compressionMethod != 0)
|
|
return ZipReturn.ZipUnsupportedCompression;
|
|
|
|
_lastModFileTime = br.ReadUInt16();
|
|
_lastModFileDate = br.ReadUInt16();
|
|
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 ?
|
|
GetString(bFileName) :
|
|
Encoding.UTF8.GetString(bFileName, 0, fileNameLength);
|
|
|
|
Byte[] extraField = br.ReadBytes(extraFieldLength);
|
|
br.ReadBytes(fileCommentLength); // File Comments
|
|
|
|
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 (RelativeOffsetOfLocalHeader == 0xffffffff)
|
|
{
|
|
RelativeOffsetOfLocalHeader = BitConverter.ToUInt64(extraField, pos);
|
|
pos += 8;
|
|
}
|
|
break;
|
|
case 0x7075:
|
|
//byte version = extraField[pos];
|
|
pos += 1;
|
|
uint nameCRC32 = BitConverter.ToUInt32(extraField, pos);
|
|
pos += 4;
|
|
|
|
CRC32 crcTest = new CRC32();
|
|
crcTest.SlurpBlock(bFileName, 0, fileNameLength);
|
|
uint fCRC = crcTest.Crc32ResultU;
|
|
|
|
if (nameCRC32 != fCRC) return ZipReturn.ZipCenteralDirError;
|
|
|
|
int charLen = blockLength - 5;
|
|
|
|
FileName = Encoding.UTF8.GetString(extraField, pos, charLen);
|
|
pos += charLen;
|
|
|
|
break;
|
|
default:
|
|
pos += blockLength;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
catch
|
|
{
|
|
return ZipReturn.ZipCenteralDirError;
|
|
}
|
|
|
|
}
|
|
public void CenteralDirectoryWrite(Stream crcStream)
|
|
{
|
|
BinaryWriter bw = new BinaryWriter(crcStream);
|
|
|
|
const uint header = 0x2014B50;
|
|
|
|
List<Byte> extraField = new List<byte>();
|
|
|
|
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 (IsUnicode(FileName))
|
|
{
|
|
_generalPurposeBitFlag |= 1 << 11;
|
|
bFileName = Encoding.UTF8.GetBytes(FileName);
|
|
}
|
|
else
|
|
bFileName = GetBytes(FileName);
|
|
ushort fileNameLength = (ushort)bFileName.Length;
|
|
|
|
ushort versionNeededToExtract = (ushort)(Zip64 ? 45 : 20);
|
|
|
|
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
|
|
}
|
|
|
|
|
|
public ZipReturn LocalFileHeaderRead()
|
|
{
|
|
try
|
|
{
|
|
TrrntZip = true;
|
|
|
|
BinaryReader br = new BinaryReader(_zipFs);
|
|
|
|
_zipFs.Position = (long)RelativeOffsetOfLocalHeader;
|
|
uint thisSignature = br.ReadUInt32();
|
|
if (thisSignature != LocalFileHeaderSignature)
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
br.ReadUInt16(); // version needed to extract
|
|
ushort generalPurposeBitFlagLocal = br.ReadUInt16();
|
|
if (generalPurposeBitFlagLocal != _generalPurposeBitFlag) TrrntZip = false;
|
|
|
|
ushort tshort = br.ReadUInt16();
|
|
if (tshort != _compressionMethod) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
tshort = br.ReadUInt16();
|
|
if (tshort != _lastModFileTime) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
tshort = br.ReadUInt16();
|
|
if (tshort != _lastModFileDate) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
byte[] tCRC = ReadCRC(br);
|
|
if (((_generalPurposeBitFlag & 8) == 0) && !ByteArrCompare(tCRC, CRC)) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
uint tCompressedSize = br.ReadUInt32();
|
|
if (Zip64 && tCompressedSize != 0xffffffff && tCompressedSize != _compressedSize) // if Zip64 File then the compressedSize should be 0xffffffff
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
if ((_generalPurposeBitFlag & 8) == 8 && tCompressedSize != 0) // if bit 4 set then no compressedSize is set yet
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
if (!Zip64 && (_generalPurposeBitFlag & 8) != 8 && tCompressedSize != _compressedSize) // check the compressedSize
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
|
|
|
|
uint tUnCompressedSize = br.ReadUInt32();
|
|
if (Zip64 && tUnCompressedSize != 0xffffffff && tUnCompressedSize != UncompressedSize) // if Zip64 File then the unCompressedSize should be 0xffffffff
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
if ((_generalPurposeBitFlag & 8) == 8 && tUnCompressedSize != 0) // if bit 4 set then no unCompressedSize is set yet
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
if (!Zip64 && (_generalPurposeBitFlag & 8) != 8 && tUnCompressedSize != UncompressedSize) // check the unCompressedSize
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
ushort fileNameLength = br.ReadUInt16();
|
|
ushort extraFieldLength = br.ReadUInt16();
|
|
|
|
|
|
byte[] bFileName = br.ReadBytes(fileNameLength);
|
|
string tFileName = (generalPurposeBitFlagLocal & (1 << 11)) == 0 ?
|
|
GetString(bFileName) :
|
|
Encoding.UTF8.GetString(bFileName, 0, fileNameLength);
|
|
|
|
byte[] extraField = br.ReadBytes(extraFieldLength);
|
|
|
|
|
|
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 (tUnCompressedSize == 0xffffffff)
|
|
{
|
|
ulong tLong = BitConverter.ToUInt64(extraField, pos);
|
|
if (tLong != UncompressedSize) return ZipReturn.ZipLocalFileHeaderError;
|
|
pos += 8;
|
|
}
|
|
if (tCompressedSize == 0xffffffff)
|
|
{
|
|
ulong tLong = BitConverter.ToUInt64(extraField, pos);
|
|
if (tLong != _compressedSize) return ZipReturn.ZipLocalFileHeaderError;
|
|
pos += 8;
|
|
}
|
|
break;
|
|
case 0x7075:
|
|
//byte version = extraField[pos];
|
|
pos += 1;
|
|
uint nameCRC32 = BitConverter.ToUInt32(extraField, pos);
|
|
pos += 4;
|
|
|
|
CRC32 crcTest = new CRC32();
|
|
crcTest.SlurpBlock(bFileName, 0, fileNameLength);
|
|
uint fCRC = crcTest.Crc32ResultU;
|
|
|
|
if (nameCRC32 != fCRC) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
int charLen = blockLength - 5;
|
|
|
|
tFileName = Encoding.UTF8.GetString(extraField, pos, charLen);
|
|
pos += charLen;
|
|
|
|
break;
|
|
default:
|
|
pos += blockLength;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!CompareString(FileName, tFileName)) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
_dataLocation = (ulong)_zipFs.Position;
|
|
|
|
if ((_generalPurposeBitFlag & 8) == 0) return ZipReturn.ZipGood;
|
|
|
|
_zipFs.Position += (long)_compressedSize;
|
|
|
|
tCRC = ReadCRC(br);
|
|
if (!ByteArrCompare(tCRC, new byte[] { 0x50, 0x4b, 0x07, 0x08 }))
|
|
tCRC = ReadCRC(br);
|
|
|
|
if (!ByteArrCompare(tCRC, CRC)) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
uint tint = br.ReadUInt32();
|
|
if (tint != _compressedSize) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
tint = br.ReadUInt32();
|
|
if (tint != UncompressedSize) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
catch
|
|
{
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
}
|
|
|
|
|
|
}
|
|
public ZipReturn LocalFileHeaderReadQuick()
|
|
{
|
|
try
|
|
{
|
|
TrrntZip = true;
|
|
|
|
BinaryReader br = new BinaryReader(_zipFs);
|
|
|
|
_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();
|
|
_lastModFileTime = br.ReadUInt16();
|
|
_lastModFileDate = br.ReadUInt16();
|
|
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 ?
|
|
GetString(bFileName) :
|
|
Encoding.UTF8.GetString(bFileName, 0, fileNameLength);
|
|
|
|
byte[] extraField = br.ReadBytes(extraFieldLength);
|
|
|
|
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 (UncompressedSize == 0xffffffff)
|
|
{
|
|
UncompressedSize = BitConverter.ToUInt64(extraField, pos);
|
|
pos += 8;
|
|
}
|
|
if (_compressedSize == 0xffffffff)
|
|
{
|
|
_compressedSize = BitConverter.ToUInt64(extraField, pos);
|
|
pos += 8;
|
|
}
|
|
break;
|
|
case 0x7075:
|
|
pos += 1;
|
|
uint nameCRC32 = BitConverter.ToUInt32(extraField, pos);
|
|
pos += 4;
|
|
|
|
CRC32 crcTest = new CRC32();
|
|
crcTest.SlurpBlock(bFileName, 0, fileNameLength);
|
|
uint fCRC = crcTest.Crc32ResultU;
|
|
|
|
if (nameCRC32 != fCRC) return ZipReturn.ZipLocalFileHeaderError;
|
|
|
|
int charLen = blockLength - 5;
|
|
|
|
FileName = Encoding.UTF8.GetString(extraField, pos, charLen);
|
|
|
|
pos += charLen;
|
|
|
|
break;
|
|
default:
|
|
pos += blockLength;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_dataLocation = (ulong)_zipFs.Position;
|
|
return ZipReturn.ZipGood;
|
|
|
|
}
|
|
catch
|
|
{
|
|
return ZipReturn.ZipLocalFileHeaderError;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private void LocalFileHeaderWrite()
|
|
{
|
|
BinaryWriter bw = new BinaryWriter(_zipFs);
|
|
|
|
List<Byte> extraField = new List<byte>();
|
|
Zip64 = UncompressedSize >= 0xffffffff;
|
|
|
|
byte[] bFileName;
|
|
if (IsUnicode(FileName))
|
|
{
|
|
_generalPurposeBitFlag |= 1 << 11;
|
|
bFileName = Encoding.UTF8.GetBytes(FileName);
|
|
}
|
|
else
|
|
bFileName = 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);
|
|
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);
|
|
|
|
|
|
|
|
if (Zip64)
|
|
{
|
|
for (int i = 0; i < 20; i++)
|
|
extraField.Add(0);
|
|
}
|
|
|
|
ushort fileNameLength = (ushort)bFileName.Length;
|
|
bw.Write(fileNameLength);
|
|
|
|
ushort extraFieldLength = (ushort)extraField.Count;
|
|
bw.Write(extraFieldLength);
|
|
|
|
bw.Write(bFileName, 0, fileNameLength);
|
|
|
|
_extraLocation = (ulong)_zipFs.Position;
|
|
bw.Write(extraField.ToArray(), 0, extraFieldLength);
|
|
|
|
}
|
|
|
|
|
|
private Stream _readStream;
|
|
public ZipReturn LocalFileOpenReadStream(bool raw, out Stream stream, out ulong streamSize, out ushort compressionMethod)
|
|
{
|
|
streamSize = 0;
|
|
compressionMethod = _compressionMethod;
|
|
|
|
_readStream = null;
|
|
_zipFs.Seek((long)_dataLocation, SeekOrigin.Begin);
|
|
|
|
switch (_compressionMethod)
|
|
{
|
|
case 8:
|
|
if (raw)
|
|
{
|
|
_readStream = _zipFs;
|
|
streamSize = _compressedSize;
|
|
}
|
|
else
|
|
{
|
|
_readStream = new DeflateStream(_zipFs, CompressionMode.Decompress, true);
|
|
streamSize = UncompressedSize;
|
|
|
|
}
|
|
break;
|
|
case 0:
|
|
_readStream = _zipFs;
|
|
streamSize = _compressedSize; // same as UncompressedSize
|
|
break;
|
|
}
|
|
stream = _readStream;
|
|
return stream == null ? ZipReturn.ZipErrorGettingDataStream : ZipReturn.ZipGood;
|
|
}
|
|
public ZipReturn LocalFileCloseReadStream()
|
|
{
|
|
DeflateStream dfStream = _readStream as DeflateStream;
|
|
if (dfStream != null)
|
|
{
|
|
dfStream.Close();
|
|
dfStream.Dispose();
|
|
}
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
|
|
private Stream _writeStream;
|
|
public ZipReturn LocalFileOpenWriteStream(bool raw, bool trrntZip, ulong uncompressedSize, ushort compressionMethod, out Stream stream)
|
|
{
|
|
UncompressedSize = uncompressedSize;
|
|
_compressionMethod = compressionMethod;
|
|
|
|
LocalFileHeaderWrite();
|
|
_dataLocation = (ulong)_zipFs.Position;
|
|
|
|
if (raw)
|
|
{
|
|
_writeStream = _zipFs;
|
|
TrrntZip = trrntZip;
|
|
}
|
|
else
|
|
{
|
|
if (compressionMethod == 0)
|
|
{
|
|
_writeStream = _zipFs;
|
|
TrrntZip = false;
|
|
}
|
|
else
|
|
{
|
|
_writeStream = new DeflateStream(_zipFs, CompressionMode.Compress, CompressionLevel.BestCompression, true);
|
|
TrrntZip = true;
|
|
}
|
|
}
|
|
|
|
stream = _writeStream;
|
|
return stream == null ? ZipReturn.ZipErrorGettingDataStream : ZipReturn.ZipGood;
|
|
}
|
|
public ZipReturn LocalFileCloseWriteStream(byte[] crc32)
|
|
{
|
|
DeflateStream dfStream = _writeStream as DeflateStream;
|
|
if (dfStream != null)
|
|
{
|
|
dfStream.Flush();
|
|
dfStream.Close();
|
|
dfStream.Dispose();
|
|
}
|
|
|
|
_compressedSize = (ulong)_zipFs.Position - _dataLocation;
|
|
|
|
if (_compressedSize == 0 && UncompressedSize == 0)
|
|
{
|
|
LocalFileAddDirectory();
|
|
_compressedSize = (ulong)_zipFs.Position - _dataLocation;
|
|
}
|
|
|
|
CRC = crc32;
|
|
WriteCompressedSize();
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
private void WriteCompressedSize()
|
|
{
|
|
long posNow = _zipFs.Position;
|
|
_zipFs.Seek((long)_crc32Location, SeekOrigin.Begin);
|
|
BinaryWriter bw = new BinaryWriter(_zipFs);
|
|
|
|
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 void LocalFileCheck()
|
|
{
|
|
if (FileStatus != ZipReturn.ZipUntested) return;
|
|
|
|
try
|
|
{
|
|
Stream sInput = null;
|
|
_zipFs.Seek((long)_dataLocation, SeekOrigin.Begin);
|
|
|
|
switch (_compressionMethod)
|
|
{
|
|
case 8:
|
|
sInput = new DeflateStream(_zipFs, CompressionMode.Decompress, true);
|
|
break;
|
|
case 0:
|
|
sInput = _zipFs;
|
|
break;
|
|
}
|
|
|
|
if (sInput == null)
|
|
{
|
|
FileStatus = ZipReturn.ZipErrorGettingDataStream;
|
|
return;
|
|
}
|
|
|
|
CRC32Hash crc32 = new CRC32Hash();
|
|
MD5 lmd5 = System.Security.Cryptography.MD5.Create();
|
|
SHA1 lsha1 = System.Security.Cryptography.SHA1.Create();
|
|
|
|
if (_buffer0 == null)
|
|
{
|
|
_buffer0 = new byte[Buffersize];
|
|
_buffer1 = new byte[Buffersize];
|
|
}
|
|
|
|
ulong sizetogo = UncompressedSize;
|
|
|
|
// Pre load the first buffer0
|
|
int sizeNext = sizetogo > Buffersize ? Buffersize : (int)sizetogo;
|
|
sInput.Read(_buffer0, 0, sizeNext);
|
|
int sizebuffer = sizeNext;
|
|
sizetogo -= (ulong)sizeNext;
|
|
bool whichBuffer = true;
|
|
|
|
while (sizebuffer > 0)
|
|
{
|
|
sizeNext = sizetogo > Buffersize ? Buffersize : (int)sizetogo;
|
|
|
|
Thread t0 = null;
|
|
if (sizeNext > 0)
|
|
{
|
|
t0 = new Thread(() => { sInput.Read(whichBuffer ? _buffer1 : _buffer0, 0, sizeNext); });
|
|
t0.Start();
|
|
}
|
|
|
|
byte[] buffer = whichBuffer ? _buffer0 : _buffer1;
|
|
Thread t1 = new Thread(() => { crc32.TransformBlock(buffer, 0, sizebuffer, null, 0); });
|
|
Thread t2 = new Thread(() => { lmd5.TransformBlock(buffer, 0, sizebuffer, null, 0); });
|
|
Thread t3 = new Thread(() => { lsha1.TransformBlock(buffer, 0, sizebuffer, null, 0); });
|
|
t1.Start();
|
|
t2.Start();
|
|
t3.Start();
|
|
if (t0 != null)
|
|
t0.Join();
|
|
t1.Join();
|
|
t2.Join();
|
|
t3.Join();
|
|
|
|
sizebuffer = sizeNext;
|
|
sizetogo -= (ulong)sizeNext;
|
|
whichBuffer = !whichBuffer;
|
|
}
|
|
|
|
crc32.TransformFinalBlock(_buffer0, 0, 0);
|
|
lmd5.TransformFinalBlock(_buffer0, 0, 0);
|
|
lsha1.TransformFinalBlock(_buffer0, 0, 0);
|
|
|
|
byte[] testcrc = crc32.Hash;
|
|
md5 = lmd5.Hash;
|
|
sha1 = lsha1.Hash;
|
|
|
|
if (_compressionMethod == 8)
|
|
{
|
|
sInput.Close();
|
|
sInput.Dispose();
|
|
}
|
|
|
|
FileStatus = ByteArrCompare(CRC, testcrc) ? ZipReturn.ZipGood : ZipReturn.ZipCRCDecodeError;
|
|
}
|
|
catch
|
|
{
|
|
FileStatus = ZipReturn.ZipDecodeError;
|
|
}
|
|
}
|
|
|
|
|
|
public void LocalFileAddDirectory()
|
|
{
|
|
Stream ds = _zipFs;
|
|
ds.WriteByte(03);
|
|
ds.WriteByte(00);
|
|
}
|
|
|
|
|
|
|
|
public ulong LocalFilePos
|
|
{
|
|
get { return RelativeOffsetOfLocalHeader; }
|
|
set { RelativeOffsetOfLocalHeader = value; }
|
|
}
|
|
private static byte[] ReadCRC(BinaryReader br)
|
|
{
|
|
byte[] tCRC = new byte[4];
|
|
tCRC[3] = br.ReadByte();
|
|
tCRC[2] = br.ReadByte();
|
|
tCRC[1] = br.ReadByte();
|
|
tCRC[0] = br.ReadByte();
|
|
return tCRC;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
private IO.FileInfo _zipFileInfo;
|
|
|
|
public string ZipFilename
|
|
{
|
|
get { return _zipFileInfo != null ? _zipFileInfo.FullName : ""; }
|
|
}
|
|
public long TimeStamp
|
|
{
|
|
get { return _zipFileInfo != null ? _zipFileInfo.LastWriteTime : 0; }
|
|
}
|
|
|
|
private ulong _centerDirStart;
|
|
private ulong _centerDirSize;
|
|
private ulong _endOfCenterDir64;
|
|
|
|
byte[] _fileComment;
|
|
private Stream _zipFs;
|
|
|
|
private uint _localFilesCount;
|
|
private readonly List<LocalFile> _localFiles = new List<LocalFile>();
|
|
|
|
private ZipStatus _pZipStatus;
|
|
private bool _zip64;
|
|
public ZipOpenType ZipOpen;
|
|
|
|
public ZipStatus ZipStatus { get { return _pZipStatus; } }
|
|
|
|
public int LocalFilesCount() { return _localFiles.Count; }
|
|
|
|
public string Filename(int i) { return _localFiles[i].FileName; }
|
|
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 ZipReturn FileStatus(int i) { return _localFiles[i].FileStatus; }
|
|
public byte[] CRC32(int i) { return _localFiles[i].CRC; }
|
|
public byte[] MD5(int i) { return _localFiles[i].md5; }
|
|
public byte[] SHA1(int i) { return _localFiles[i].sha1; }
|
|
|
|
|
|
~ZipFile()
|
|
{
|
|
if (_zipFs != null)
|
|
{
|
|
_zipFs.Close();
|
|
_zipFs.Dispose();
|
|
}
|
|
}
|
|
|
|
|
|
private ZipReturn FindEndOfCentralDirSignature()
|
|
{
|
|
long fileSize = _zipFs.Length;
|
|
long maxBackSearch = 0xffff;
|
|
|
|
if (_zipFs.Length < maxBackSearch)
|
|
maxBackSearch = fileSize;
|
|
|
|
const long buffSize = 0x400;
|
|
|
|
byte[] buffer = new byte[buffSize + 4];
|
|
|
|
long backPosition = 4;
|
|
while (backPosition < maxBackSearch)
|
|
{
|
|
backPosition += buffSize;
|
|
if (backPosition > maxBackSearch) backPosition = maxBackSearch;
|
|
|
|
long readSize = backPosition > (buffSize + 4) ? (buffSize + 4) : backPosition;
|
|
|
|
_zipFs.Position = fileSize - backPosition;
|
|
|
|
_zipFs.Read(buffer, 0, (int)readSize);
|
|
|
|
|
|
for (long i = readSize - 4; i >= 0; i--)
|
|
{
|
|
if ((buffer[i] != 0x50) || (buffer[i + 1] != 0x4b) || (buffer[i + 2] != 0x05) || (buffer[i + 3] != 0x06)) continue;
|
|
|
|
_zipFs.Position = (fileSize - backPosition) + i;
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
}
|
|
return ZipReturn.ZipCenteralDirError;
|
|
}
|
|
|
|
|
|
private ZipReturn EndOfCentralDirRead()
|
|
{
|
|
BinaryReader zipBr = new BinaryReader(_zipFs);
|
|
|
|
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(); // TotalNumberOfEnteriesDisk
|
|
|
|
tushort = zipBr.ReadUInt16(); // TotalNumber of enteries in the central directory
|
|
if (tushort != _localFilesCount) return ZipReturn.ZipEndOfCentralDirectoryError;
|
|
|
|
_centerDirSize = zipBr.ReadUInt32(); // SizeOfCenteralDir
|
|
_centerDirStart = zipBr.ReadUInt32(); // Offset
|
|
|
|
ushort zipFileCommentLength = zipBr.ReadUInt16();
|
|
|
|
_fileComment = zipBr.ReadBytes(zipFileCommentLength);
|
|
|
|
if (_zipFs.Position != _zipFs.Length) _pZipStatus |= ZipStatus.ExtraData;
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
private void EndOfCentralDirWrite()
|
|
{
|
|
|
|
BinaryWriter bw = new BinaryWriter(_zipFs);
|
|
bw.Write(EndOfCentralDirSignature);
|
|
bw.Write((ushort)0); // NumberOfThisDisk
|
|
bw.Write((ushort)0); // NumberOfThisDiskCenterDir
|
|
bw.Write((ushort)(_localFiles.Count >= 0xffff ? 0xffff : _localFiles.Count)); // TotalNumberOfEnteriesDisk
|
|
bw.Write((ushort)(_localFiles.Count >= 0xffff ? 0xffff : _localFiles.Count)); // TotalNumber of enteries in the central directory
|
|
bw.Write((uint)(_centerDirSize >= 0xffffffff ? 0xffffffff : _centerDirSize));
|
|
bw.Write((uint)(_centerDirStart >= 0xffffffff ? 0xffffffff : _centerDirStart));
|
|
bw.Write((ushort)_fileComment.Length);
|
|
bw.Write(_fileComment, 0, _fileComment.Length);
|
|
}
|
|
|
|
private ZipReturn Zip64EndOfCentralDirRead()
|
|
{
|
|
_zip64 = true;
|
|
BinaryReader zipBr = new BinaryReader(_zipFs);
|
|
|
|
uint thisSignature = zipBr.ReadUInt32();
|
|
if (thisSignature != Zip64EndOfCentralDirSignatue)
|
|
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;
|
|
|
|
_centerDirSize = zipBr.ReadUInt64(); // size of central directory
|
|
|
|
_centerDirStart = zipBr.ReadUInt64(); // offset of start of central directory with respect to the starting disk number
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
|
|
private void Zip64EndOfCentralDirWrite()
|
|
{
|
|
BinaryWriter bw = new BinaryWriter(_zipFs);
|
|
bw.Write(Zip64EndOfCentralDirSignatue);
|
|
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 directroy
|
|
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(_centerDirSize); // size of central directory
|
|
bw.Write(_centerDirStart); // offset of start of central directory with respect to the starting disk number
|
|
}
|
|
|
|
private ZipReturn Zip64EndOfCentralDirectoryLocatorRead()
|
|
{
|
|
_zip64 = true;
|
|
BinaryReader zipBr = new BinaryReader(_zipFs);
|
|
|
|
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 centeral directory
|
|
if (tuint != 0) return ZipReturn.Zip64EndOfCentralDirectoryLocatorError;
|
|
|
|
_endOfCenterDir64 = zipBr.ReadUInt64(); // relative offset of the zip64 end of central directroy record
|
|
|
|
tuint = zipBr.ReadUInt32(); // total number of disks
|
|
if (tuint != 1) return ZipReturn.Zip64EndOfCentralDirectoryLocatorError;
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
private void Zip64EndOfCentralDirectoryLocatorWrite()
|
|
{
|
|
BinaryWriter bw = new BinaryWriter(_zipFs);
|
|
bw.Write(Zip64EndOfCentralDirectoryLocator);
|
|
bw.Write((uint)0); // number of the disk with the start of the zip64 end of centeral directory
|
|
bw.Write(_endOfCenterDir64); // relative offset of the zip64 end of central directroy record
|
|
bw.Write((uint)1); // total number of disks
|
|
}
|
|
|
|
|
|
|
|
public ZipReturn ZipFileOpen(string newFilename, long timestamp, bool readHeaders)
|
|
{
|
|
ZipFileClose();
|
|
_pZipStatus = ZipStatus.None;
|
|
_zip64 = false;
|
|
_centerDirStart = 0;
|
|
_centerDirSize = 0;
|
|
_zipFileInfo = null;
|
|
|
|
try
|
|
{
|
|
if (!IO.File.Exists(newFilename))
|
|
{
|
|
ZipFileClose();
|
|
return ZipReturn.ZipErrorFileNotFound;
|
|
}
|
|
_zipFileInfo = new IO.FileInfo(newFilename);
|
|
if (_zipFileInfo.LastWriteTime != timestamp)
|
|
{
|
|
ZipFileClose();
|
|
return ZipReturn.ZipErrorTimeStamp;
|
|
}
|
|
int errorCode = IO.FileStream.OpenFileRead(newFilename, out _zipFs);
|
|
if (errorCode != 0)
|
|
{
|
|
ZipFileClose();
|
|
if (errorCode == 32)
|
|
return ZipReturn.ZipFileLocked;
|
|
return ZipReturn.ZipErrorOpeningFile;
|
|
}
|
|
}
|
|
catch (PathTooLongException)
|
|
{
|
|
ZipFileClose();
|
|
return ZipReturn.ZipFileNameToLong;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
ZipFileClose();
|
|
return ZipReturn.ZipErrorOpeningFile;
|
|
}
|
|
ZipOpen = ZipOpenType.OpenRead;
|
|
|
|
if (!readHeaders)
|
|
return ZipReturn.ZipGood;
|
|
|
|
|
|
try
|
|
{
|
|
ZipReturn zRet = FindEndOfCentralDirSignature();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
|
|
long endOfCentralDir = _zipFs.Position;
|
|
zRet = EndOfCentralDirRead();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
|
|
// check if this is a ZIP64 zip and if it is read the Zip64 End Of Central Dir Info
|
|
if (_centerDirStart == 0xffffffff || _centerDirSize == 0xffffffff || _localFilesCount == 0xffff)
|
|
{
|
|
_zip64 = true;
|
|
_zipFs.Position = endOfCentralDir - 20;
|
|
zRet = Zip64EndOfCentralDirectoryLocatorRead();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
_zipFs.Position = (long)_endOfCenterDir64;
|
|
zRet = Zip64EndOfCentralDirRead();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
}
|
|
|
|
bool trrntzip = false;
|
|
|
|
// check if the ZIP has a valid TorrentZip file comment
|
|
if (_fileComment.Length == 22)
|
|
{
|
|
if (GetString(_fileComment).Substring(0, 14) == "TORRENTZIPPED-")
|
|
{
|
|
CrcCalculatorStream crcCs = new CrcCalculatorStream(_zipFs, true);
|
|
byte[] buffer = new byte[_centerDirSize];
|
|
_zipFs.Position = (long)_centerDirStart;
|
|
crcCs.Read(buffer, 0, (int)_centerDirSize);
|
|
crcCs.Flush();
|
|
crcCs.Close();
|
|
|
|
uint r = (uint)crcCs.Crc;
|
|
crcCs.Dispose();
|
|
|
|
string tcrc = GetString(_fileComment).Substring(14, 8);
|
|
string zcrc = r.ToString("X8");
|
|
if (String.Compare(tcrc, zcrc, StringComparison.Ordinal) == 0)
|
|
trrntzip = true;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// now read the central directory
|
|
_zipFs.Position = (long)_centerDirStart;
|
|
|
|
_localFiles.Clear();
|
|
_localFiles.Capacity = (int)_localFilesCount;
|
|
for (int i = 0; i < _localFilesCount; i++)
|
|
{
|
|
LocalFile lc = new LocalFile(_zipFs);
|
|
zRet = lc.CenteralDirectoryRead();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
_zip64 |= lc.Zip64;
|
|
_localFiles.Add(lc);
|
|
}
|
|
|
|
for (int i = 0; i < _localFilesCount; i++)
|
|
{
|
|
zRet = _localFiles[i].LocalFileHeaderRead();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
trrntzip &= _localFiles[i].TrrntZip;
|
|
}
|
|
|
|
// check trrntzip file order
|
|
if (trrntzip)
|
|
for (int i = 0; i < _localFilesCount - 1; i++)
|
|
{
|
|
if (TrrntZipStringCompare(_localFiles[i].FileName, _localFiles[i + 1].FileName) < 0) continue;
|
|
trrntzip = false;
|
|
break;
|
|
}
|
|
|
|
// check trrntzip directories
|
|
if (trrntzip)
|
|
for (int i = 0; i < _localFilesCount - 1; i++)
|
|
{
|
|
// see if we found a directory
|
|
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;
|
|
if (filename1.Length <= filename0.Length) continue;
|
|
if (TrrntZipStringCompare(filename0, filename1.Substring(0, filename0.Length)) != 0) continue;
|
|
|
|
// if we found a file in the directory then we do not need the directory entry
|
|
trrntzip = false;
|
|
break;
|
|
}
|
|
|
|
if (trrntzip)
|
|
_pZipStatus |= ZipStatus.TrrntZip;
|
|
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
catch
|
|
{
|
|
ZipFileClose();
|
|
return ZipReturn.ZipErrorReadingFile;
|
|
}
|
|
|
|
}
|
|
|
|
public ZipReturn ZipFileCreate(string newFilename)
|
|
{
|
|
if (ZipOpen != ZipOpenType.Closed)
|
|
return ZipReturn.ZipFileAlreadyOpen;
|
|
|
|
CreateDirForFile(newFilename);
|
|
_zipFileInfo = new IO.FileInfo(newFilename);
|
|
|
|
int errorCode = IO.FileStream.OpenFileWrite(newFilename, out _zipFs);
|
|
if (errorCode != 0)
|
|
{
|
|
ZipFileClose();
|
|
return ZipReturn.ZipErrorOpeningFile;
|
|
}
|
|
ZipOpen = ZipOpenType.OpenWrite;
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
|
|
public void ZipFileClose()
|
|
{
|
|
if (ZipOpen == ZipOpenType.Closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ZipOpen == ZipOpenType.OpenRead)
|
|
{
|
|
if (_zipFs != null)
|
|
{
|
|
_zipFs.Close();
|
|
_zipFs.Dispose();
|
|
}
|
|
ZipOpen = ZipOpenType.Closed;
|
|
return;
|
|
}
|
|
|
|
_zip64 = false;
|
|
bool lTrrntzip = true;
|
|
|
|
_centerDirStart = (ulong)_zipFs.Position;
|
|
if (_centerDirStart >= 0xffffffff)
|
|
_zip64 = true;
|
|
|
|
CrcCalculatorStream crcCs = new CrcCalculatorStream(_zipFs, true);
|
|
|
|
foreach (LocalFile t in _localFiles)
|
|
{
|
|
t.CenteralDirectoryWrite(crcCs);
|
|
_zip64 |= t.Zip64;
|
|
lTrrntzip &= t.TrrntZip;
|
|
}
|
|
|
|
crcCs.Flush();
|
|
crcCs.Close();
|
|
|
|
_centerDirSize = (ulong)_zipFs.Position - _centerDirStart;
|
|
|
|
_fileComment = lTrrntzip ? GetBytes("TORRENTZIPPED-" + crcCs.Crc.ToString("X8")) : new byte[0];
|
|
_pZipStatus = lTrrntzip ? ZipStatus.TrrntZip : ZipStatus.None;
|
|
|
|
crcCs.Dispose();
|
|
|
|
if (_zip64)
|
|
{
|
|
_endOfCenterDir64 = (ulong)_zipFs.Position;
|
|
Zip64EndOfCentralDirWrite();
|
|
Zip64EndOfCentralDirectoryLocatorWrite();
|
|
}
|
|
EndOfCentralDirWrite();
|
|
|
|
_zipFs.SetLength(_zipFs.Position);
|
|
_zipFs.Flush();
|
|
_zipFs.Close();
|
|
_zipFs.Dispose();
|
|
_zipFileInfo = new IO.FileInfo(_zipFileInfo.FullName);
|
|
ZipOpen = ZipOpenType.Closed;
|
|
|
|
}
|
|
|
|
public void ZipFileCloseFailed()
|
|
{
|
|
if (ZipOpen == ZipOpenType.Closed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ZipOpen == ZipOpenType.OpenRead)
|
|
{
|
|
if (_zipFs != null)
|
|
{
|
|
_zipFs.Close();
|
|
_zipFs.Dispose();
|
|
}
|
|
ZipOpen = ZipOpenType.Closed;
|
|
return;
|
|
}
|
|
|
|
_zipFs.Flush();
|
|
_zipFs.Close();
|
|
_zipFs.Dispose();
|
|
IO.File.Delete(_zipFileInfo.FullName);
|
|
_zipFileInfo = null;
|
|
ZipOpen = ZipOpenType.Closed;
|
|
}
|
|
|
|
|
|
private int _readIndex;
|
|
public ZipReturn ZipFileOpenReadStream(int index, bool raw, out Stream stream, out ulong streamSize, out ushort compressionMethod)
|
|
{
|
|
streamSize = 0;
|
|
compressionMethod = 0;
|
|
_readIndex = index;
|
|
stream = null;
|
|
if (ZipOpen != ZipOpenType.OpenRead)
|
|
return ZipReturn.ZipReadingFromOutputFile;
|
|
|
|
ZipReturn zRet = _localFiles[index].LocalFileHeaderRead();
|
|
if (zRet != ZipReturn.ZipGood)
|
|
{
|
|
ZipFileClose();
|
|
return zRet;
|
|
}
|
|
|
|
return _localFiles[index].LocalFileOpenReadStream(raw, out stream, out streamSize, out compressionMethod);
|
|
}
|
|
|
|
public ZipReturn ZipFileOpenReadStreamQuick(ulong pos, bool raw, out Stream stream, out ulong streamSize, out ushort compressionMethod)
|
|
{
|
|
LocalFile tmpFile = new LocalFile(_zipFs) { LocalFilePos = pos };
|
|
_localFiles.Clear();
|
|
_localFiles.Add(tmpFile);
|
|
ZipReturn zr = tmpFile.LocalFileHeaderReadQuick();
|
|
if (zr != ZipReturn.ZipGood)
|
|
{
|
|
stream = null;
|
|
streamSize = 0;
|
|
compressionMethod = 0;
|
|
return zr;
|
|
}
|
|
_readIndex = 0;
|
|
|
|
return tmpFile.LocalFileOpenReadStream(raw, out stream, out streamSize, out compressionMethod);
|
|
}
|
|
|
|
public ZipReturn ZipFileCloseReadStream()
|
|
{
|
|
return _localFiles[_readIndex].LocalFileCloseReadStream();
|
|
}
|
|
|
|
public ZipReturn ZipFileOpenWriteStream(bool raw, bool trrntzip, string filename, ulong uncompressedSize, ushort compressionMethod, out Stream stream)
|
|
{
|
|
stream = null;
|
|
if (ZipOpen != ZipOpenType.OpenWrite)
|
|
return ZipReturn.ZipWritingToInputFile;
|
|
|
|
LocalFile lf = new LocalFile(_zipFs, filename);
|
|
|
|
ZipReturn retVal = lf.LocalFileOpenWriteStream(raw, trrntzip, uncompressedSize, compressionMethod, out stream);
|
|
|
|
_localFiles.Add(lf);
|
|
|
|
return retVal;
|
|
}
|
|
public ZipReturn ZipFileCloseWriteStream(byte[] crc32)
|
|
{
|
|
return _localFiles[_localFiles.Count - 1].LocalFileCloseWriteStream(crc32);
|
|
}
|
|
|
|
public ZipReturn ZipFileRollBack()
|
|
{
|
|
if (ZipOpen != ZipOpenType.OpenWrite)
|
|
return ZipReturn.ZipWritingToInputFile;
|
|
|
|
int fileCount = _localFiles.Count;
|
|
if (fileCount == 0)
|
|
return ZipReturn.ZipErrorRollBackFile;
|
|
|
|
LocalFile lf = _localFiles[fileCount - 1];
|
|
|
|
_localFiles.RemoveAt(fileCount - 1);
|
|
_zipFs.Position = (long)lf.LocalFilePos;
|
|
return ZipReturn.ZipGood;
|
|
}
|
|
|
|
public void ZipFileAddDirectory()
|
|
{
|
|
_localFiles[_localFiles.Count - 1].LocalFileAddDirectory();
|
|
}
|
|
|
|
|
|
/*
|
|
public void BreakTrrntZip(string filename)
|
|
{
|
|
_zipFs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);
|
|
BinaryReader zipBr = new BinaryReader(_zipFs);
|
|
_zipFs.Position = _zipFs.Length - 22;
|
|
byte[] fileComment = zipBr.ReadBytes(22);
|
|
if (GetString(fileComment).Substring(0, 14) == "TORRENTZIPPED-")
|
|
{
|
|
_zipFs.Position = _zipFs.Length - 8;
|
|
_zipFs.WriteByte(48); _zipFs.WriteByte(48); _zipFs.WriteByte(48); _zipFs.WriteByte(48);
|
|
_zipFs.WriteByte(48); _zipFs.WriteByte(48); _zipFs.WriteByte(48); _zipFs.WriteByte(48);
|
|
}
|
|
|
|
zipBr.Close();
|
|
_zipFs.Flush();
|
|
_zipFs.Close();
|
|
}
|
|
*/
|
|
|
|
public void DeepScan()
|
|
{
|
|
foreach (LocalFile lfile in _localFiles)
|
|
lfile.LocalFileCheck();
|
|
}
|
|
|
|
private static void CreateDirForFile(string sFilename)
|
|
{
|
|
string strTemp = IO.Path.GetDirectoryName(sFilename);
|
|
|
|
if (String.IsNullOrEmpty(strTemp)) return;
|
|
|
|
if (IO.Directory.Exists(strTemp)) return;
|
|
|
|
|
|
while (strTemp.Length > 0 && !IO.Directory.Exists(strTemp))
|
|
{
|
|
int pos = strTemp.LastIndexOf(IO.Path.DirectorySeparatorChar);
|
|
if (pos < 0) pos = 0;
|
|
strTemp = strTemp.Substring(0, pos);
|
|
}
|
|
|
|
while (sFilename.IndexOf(IO.Path.DirectorySeparatorChar, strTemp.Length + 1) > 0)
|
|
{
|
|
strTemp = sFilename.Substring(0, sFilename.IndexOf(IO.Path.DirectorySeparatorChar, strTemp.Length + 1));
|
|
IO.Directory.CreateDirectory(strTemp);
|
|
}
|
|
}
|
|
|
|
|
|
public static string ZipErrorMessageText(ZipReturn zS)
|
|
{
|
|
string ret = "Unknown";
|
|
switch (zS)
|
|
{
|
|
case ZipReturn.ZipGood:
|
|
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";
|
|
break;
|
|
case ZipReturn.ZipSignatureError:
|
|
ret = "An unknown Signature Block was found in the Zip";
|
|
break;
|
|
case ZipReturn.ZipExtraDataOnEndOfZip:
|
|
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";
|
|
break;
|
|
case ZipReturn.ZipLocalFileHeaderError:
|
|
ret = "Error reading a zipped file header information";
|
|
break;
|
|
case ZipReturn.ZipCenteralDirError:
|
|
ret = "There is an error in the Zip Centeral 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";
|
|
break;
|
|
case ZipReturn.ZipErrorGettingDataStream:
|
|
ret = "Error creating Data Stream";
|
|
break;
|
|
case ZipReturn.ZipCRCDecodeError:
|
|
ret = "CRC error";
|
|
break;
|
|
case ZipReturn.ZipDecodeError:
|
|
ret = "Error unzipping a file";
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static byte[] GetBytes(String s)
|
|
{
|
|
char[] c = s.ToCharArray();
|
|
byte[] b = new byte[c.Length];
|
|
for (int i = 0; i < c.Length; i++)
|
|
{
|
|
char t = c[i];
|
|
b[i] = t > 255 ? (byte)'?' : (byte)c[i];
|
|
}
|
|
return b;
|
|
}
|
|
private static bool IsUnicode(string s)
|
|
{
|
|
char[] c = s.ToCharArray();
|
|
for (int i = 0; i < c.Length; i++)
|
|
if (c[i] > 255) return true;
|
|
return false;
|
|
}
|
|
|
|
private static string GetString(byte[] b)
|
|
{
|
|
string s = "";
|
|
for (int i = 0; i < b.Length; i++)
|
|
s += (char)b[i];
|
|
return s;
|
|
}
|
|
private static bool CompareString(string s1, string s2)
|
|
{
|
|
char[] c1 = s1.ToCharArray();
|
|
char[] c2 = s2.ToCharArray();
|
|
|
|
if (c1.Length != c2.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < c1.Length; i++)
|
|
if (c1[i] != c2[i]) return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
private static bool ByteArrCompare(byte[] b0, byte[] b1)
|
|
{
|
|
if (b0 == null || b1 == null)
|
|
return false;
|
|
if (b0.Length != b1.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < b0.Length; i++)
|
|
{
|
|
if (b0[i] != b1[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static int TrrntZipStringCompare(string string1, string string2)
|
|
{
|
|
char[] bytes1 = string1.ToCharArray();
|
|
char[] bytes2 = string2.ToCharArray();
|
|
|
|
int pos1 = 0;
|
|
int pos2 = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (pos1 == bytes1.Length)
|
|
return ((pos2 == bytes2.Length) ? 0 : -1);
|
|
if (pos2 == bytes2.Length)
|
|
return 1;
|
|
|
|
int byte1 = bytes1[pos1++];
|
|
int byte2 = bytes2[pos2++];
|
|
|
|
if (byte1 >= 65 && byte1 <= 90) byte1 += 0x20;
|
|
if (byte2 >= 65 && byte2 <= 90) byte2 += 0x20;
|
|
|
|
if (byte1 < byte2)
|
|
return -1;
|
|
if (byte1 > byte2)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|