Improvements to ZIP data format

This commit is contained in:
Michael Becker 2014-06-05 15:13:15 -04:00
parent 5ced936963
commit 5029b626f1

View File

@ -8,15 +8,16 @@ using UniversalEditor.ObjectModels.FileSystem;
using UniversalEditor.Common;
using UniversalEditor.Compression;
using UniversalEditor.IO;
using UniversalEditor.UserInterface;
namespace UniversalEditor.DataFormats.FileSystem.ZIP
{
// TODO: Fix the ZIP data format's saving!!!
// TODO: Fix the ZIP data format's saving!!!
public class ZIPDataFormat : DataFormat
{
private ZIPSettings mvarSettings = new ZIPSettings();
public class ZIPDataFormat : DataFormat
{
private ZIPSettings mvarSettings = new ZIPSettings();
private string mvarComment = String.Empty;
public string Comment { get { return mvarComment; } set { mvarComment = value; } }
@ -33,19 +34,19 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP
protected override void LoadInternal(ref ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null) return;
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null) return;
IO.Reader br = base.Accessor.Reader;
int firstFile = this.zip_FindFirstFileOffset(br);
if (firstFile == -1)
{
int firstFile = this.zip_FindFirstFileOffset(br);
if (firstFile == -1)
{
throw new InvalidDataFormatException();
}
br.Accessor.Seek((long) firstFile, SeekOrigin.Begin);
}
br.Accessor.Seek((long) firstFile, SeekOrigin.Begin);
while (!br.EndOfStream)
{
while (!br.EndOfStream)
{
File file = null;
try
{
@ -111,158 +112,158 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP
{
// file is encrypted, what do we do?
}
}
}
}
}
private bool readCentralDirectory(IO.Reader br)
{
int ctdir = this.seekCentralDirectory(br);
if (ctdir == -1)
{
return false;
}
br.Accessor.Seek((long) ctdir, SeekOrigin.Begin);
byte[] siggy = br.ReadBytes(4);
short s1 = br.ReadInt16();
short s2 = br.ReadInt16();
short s3 = br.ReadInt16();
short s4 = br.ReadInt16();
short s5 = br.ReadInt16();
short fileSiggy = br.ReadInt16();
short fileSiggy2 = br.ReadInt16();
short fileSiggy3 = br.ReadInt16();
short fileSiggy4 = br.ReadInt16();
int packedFileLength = br.ReadInt32();
int unpackedFileLength = br.ReadInt32();
int dumy3 = br.ReadInt32();
int dummy4 = br.ReadInt32();
int dummy5 = br.ReadInt32();
int dummy6 = br.ReadInt32();
short dummy7 = br.ReadInt16();
return true;
}
private bool readCentralDirectory(IO.Reader br)
{
int ctdir = this.seekCentralDirectory(br);
if (ctdir == -1)
{
return false;
}
br.Accessor.Seek((long) ctdir, SeekOrigin.Begin);
byte[] siggy = br.ReadBytes(4);
short s1 = br.ReadInt16();
short s2 = br.ReadInt16();
short s3 = br.ReadInt16();
short s4 = br.ReadInt16();
short s5 = br.ReadInt16();
short fileSiggy = br.ReadInt16();
short fileSiggy2 = br.ReadInt16();
short fileSiggy3 = br.ReadInt16();
short fileSiggy4 = br.ReadInt16();
int packedFileLength = br.ReadInt32();
int unpackedFileLength = br.ReadInt32();
int dumy3 = br.ReadInt32();
int dummy4 = br.ReadInt32();
int dummy5 = br.ReadInt32();
int dummy6 = br.ReadInt32();
short dummy7 = br.ReadInt16();
return true;
}
protected override void SaveInternal(ObjectModel objectModel)
{
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null) return;
FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel);
if (fsom == null) return;
Dictionary<File, int> relativeOffsetsOfLocalHeaders = new Dictionary<File, int>();
File[] files = fsom.GetAllFiles();
IO.Writer bw = base.Accessor.Writer;
foreach (File file in files)
{
IO.Writer bw = base.Accessor.Writer;
foreach (File file in files)
{
relativeOffsetsOfLocalHeaders.Add(file, (int)bw.Accessor.Position);
// signature first
bw.WriteBytes(new byte[] { 80, 0x4b, 3, 4 });
// signature first
bw.WriteBytes(new byte[] { 80, 0x4b, 3, 4 });
short iMinimumVersionNeededToExtract = 0x14;
bw.WriteInt16(iMinimumVersionNeededToExtract);
short iMinimumVersionNeededToExtract = 0x14;
bw.WriteInt16(iMinimumVersionNeededToExtract);
ZIPGeneralPurposeFlags iGeneralPurposeBitFlag = ZIPGeneralPurposeFlags.None;
bw.WriteInt16((short)iGeneralPurposeBitFlag);
ZIPGeneralPurposeFlags iGeneralPurposeBitFlag = ZIPGeneralPurposeFlags.None;
bw.WriteInt16((short)iGeneralPurposeBitFlag);
// If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written.
// The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded
// by a 4-byte signature) immediately after the compressed data:
// If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written.
// The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded
// by a 4-byte signature) immediately after the compressed data:
short compressionMethod = 0;
CompressionMethod _compressionMethod = CompressionMethod.Deflate;
switch (_compressionMethod)
{
case CompressionMethod.Deflate: compressionMethod = 8; break;
case CompressionMethod.Deflate64: compressionMethod = 9; break;
case CompressionMethod.Bzip2: compressionMethod = 12; break;
case CompressionMethod.LZMA: compressionMethod = 14; break;
}
bw.WriteInt16(compressionMethod);
short compressionMethod = 0;
CompressionMethod _compressionMethod = CompressionMethod.Deflate;
switch (_compressionMethod)
{
case CompressionMethod.Deflate: compressionMethod = 8; break;
case CompressionMethod.Deflate64: compressionMethod = 9; break;
case CompressionMethod.Bzip2: compressionMethod = 12; break;
case CompressionMethod.LZMA: compressionMethod = 14; break;
}
bw.WriteInt16(compressionMethod);
short iFileLastModificationTime = (short)(DateTime.Now.ToFileTime());
bw.WriteInt16(iFileLastModificationTime);
short iFileLastModificationTime = (short)(DateTime.Now.ToFileTime());
bw.WriteInt16(iFileLastModificationTime);
short iFileLastModificationDate = (short)(DateTime.Now.ToFileTime() >> 2);
bw.WriteInt16(iFileLastModificationDate);
short iFileLastModificationDate = (short)(DateTime.Now.ToFileTime() >> 2);
bw.WriteInt16(iFileLastModificationDate);
byte[] uncompressedData = file.GetDataAsByteArray();
bool isEncrypted = false;
int iCRC32 = (int)(new UniversalEditor.Checksum.Modules.CRC32.CRC32ChecksumModule()).Calculate(uncompressedData);
bw.WriteInt32(iCRC32);
bool isEncrypted = false;
int iCRC32 = (int)(new UniversalEditor.Checksum.Modules.CRC32.CRC32ChecksumModule()).Calculate(uncompressedData);
bw.WriteInt32(iCRC32);
byte[] compressedData = CompressionModule.FromKnownCompressionMethod(_compressionMethod).Compress(file.GetDataAsByteArray());
byte[] compressedData = CompressionModule.FromKnownCompressionMethod(_compressionMethod).Compress(file.GetDataAsByteArray());
bw.WriteInt32((int)compressedData.Length);
bw.WriteInt32((int)uncompressedData.Length);
short fileNameLength = (short)file.Name.Length;
short extraFieldLength = 0;
bw.WriteInt16(fileNameLength);
bw.WriteInt16(extraFieldLength);
short fileNameLength = (short)file.Name.Length;
short extraFieldLength = 0;
bw.WriteInt16(fileNameLength);
bw.WriteInt16(extraFieldLength);
bw.WriteFixedLengthString(file.Name, fileNameLength);
/*
long pos = br.Accessor.Position;
while (br.Accessor.Position < (pos + extraFieldLength))
{
short chunkIDCode = br.ReadInt16();
short chunkLength = br.ReadInt16();
byte[] data = br.ReadBytes(chunkLength);
}
*/
bw.WriteBytes(compressedData);
if (isEncrypted)
{
throw new System.Security.SecurityException("File is encrypted");
}
}
/*
long pos = br.Accessor.Position;
while (br.Accessor.Position < (pos + extraFieldLength))
{
short chunkIDCode = br.ReadInt16();
short chunkLength = br.ReadInt16();
byte[] data = br.ReadBytes(chunkLength);
}
*/
bw.WriteBytes(compressedData);
if (isEncrypted)
{
throw new System.Security.SecurityException("File is encrypted");
}
}
long ofs = bw.Accessor.Position;
// write the central directory
foreach (File file in files)
{
bw.WriteBytes(new byte[] { (byte)'P', (byte)'K', 0x01, 0x02 });
// write the central directory
foreach (File file in files)
{
bw.WriteBytes(new byte[] { (byte)'P', (byte)'K', 0x01, 0x02 });
ZIPCreationPlatform creationPlatform = ZIPCreationPlatform.WindowsNTFS; // Windows NTFS
byte formatVersion = 0x3F;
short u = BitConverter.ToInt16(new byte[] { (byte)creationPlatform, formatVersion }, 0);
bw.WriteInt16(u);
short iMinimumVersionNeededToExtract = 0x14;
bw.WriteInt16(iMinimumVersionNeededToExtract);
ZIPGeneralPurposeFlags iGeneralPurposeBitFlag = ZIPGeneralPurposeFlags.None;
bw.WriteInt16((short)iGeneralPurposeBitFlag);
short iMinimumVersionNeededToExtract = 0x14;
bw.WriteInt16(iMinimumVersionNeededToExtract);
ZIPGeneralPurposeFlags iGeneralPurposeBitFlag = ZIPGeneralPurposeFlags.None;
bw.WriteInt16((short)iGeneralPurposeBitFlag);
short compressionMethod = 0;
CompressionMethod _compressionMethod = CompressionMethod.Deflate;
switch (_compressionMethod)
{
case CompressionMethod.Deflate: compressionMethod = 8; break;
case CompressionMethod.Deflate64: compressionMethod = 9; break;
case CompressionMethod.Bzip2: compressionMethod = 12; break;
case CompressionMethod.LZMA: compressionMethod = 14; break;
}
bw.WriteInt16(compressionMethod);
short compressionMethod = 0;
CompressionMethod _compressionMethod = CompressionMethod.Deflate;
switch (_compressionMethod)
{
case CompressionMethod.Deflate: compressionMethod = 8; break;
case CompressionMethod.Deflate64: compressionMethod = 9; break;
case CompressionMethod.Bzip2: compressionMethod = 12; break;
case CompressionMethod.LZMA: compressionMethod = 14; break;
}
bw.WriteInt16(compressionMethod);
short iFileLastModificationTime = (short)(DateTime.Now.ToFileTime());
bw.WriteInt16(iFileLastModificationTime);
short iFileLastModificationTime = (short)(DateTime.Now.ToFileTime());
bw.WriteInt16(iFileLastModificationTime);
short iFileLastModificationDate = (short)(DateTime.Now.ToFileTime() >> 2);
bw.WriteInt16(iFileLastModificationDate);
short iFileLastModificationDate = (short)(DateTime.Now.ToFileTime() >> 2);
bw.WriteInt16(iFileLastModificationDate);
bool isEncrypted = false;
bool isEncrypted = false;
byte[] uncompressedData = file.GetDataAsByteArray();
int iCRC32 = (int)(new UniversalEditor.Checksum.Modules.CRC32.CRC32ChecksumModule()).Calculate(uncompressedData);
bw.WriteInt32(iCRC32);
bw.WriteInt32(iCRC32);
byte[] compressedData = CompressionModule.FromKnownCompressionMethod(_compressionMethod).Compress(file.GetDataAsByteArray());
bw.WriteInt32((int)compressedData.Length);
bw.WriteInt32((int)uncompressedData.Length);
byte[] compressedData = CompressionModule.FromKnownCompressionMethod(_compressionMethod).Compress(file.GetDataAsByteArray());
bw.WriteInt32((int)compressedData.Length);
bw.WriteInt32((int)uncompressedData.Length);
short fileNameLength = (short)file.Name.Length;
short fileNameLength = (short)file.Name.Length;
byte[] extraField = new byte[0];
bw.WriteInt16(fileNameLength);
bw.WriteInt16((short)extraField.Length);
bw.WriteInt16(fileNameLength);
bw.WriteInt16((short)extraField.Length);
string fileComment = String.Empty;
@ -277,7 +278,7 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP
bw.WriteInt32(relativeOffsetsOfLocalHeaders[file]);
bw.WriteFixedLengthString(file.Name.Replace("\\", "/"), fileNameLength);
bw.WriteFixedLengthString(file.Name.Replace("\\", "/"), fileNameLength);
bw.WriteBytes(extraField);
@ -318,76 +319,76 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP
#endregion
}
private void RecursiveLoadFolder(Folder folder, ref Dictionary<string, File> files, string parentFolderName)
{
foreach (Folder folder1 in folder.Folders)
{
if (String.IsNullOrEmpty(parentFolderName))
{
RecursiveLoadFolder(folder1, ref files, folder.Name);
}
else
{
RecursiveLoadFolder(folder1, ref files, parentFolderName + "/" + folder.Name);
}
}
foreach (File file in folder.Files)
{
if (String.IsNullOrEmpty(parentFolderName))
{
files.Add(folder.Name + "/" + file.Name, file);
}
else
{
files.Add(parentFolderName + "/" + folder.Name + "/" + file.Name, file);
}
}
}
private void RecursiveLoadFolder(Folder folder, ref Dictionary<string, File> files, string parentFolderName)
{
foreach (Folder folder1 in folder.Folders)
{
if (String.IsNullOrEmpty(parentFolderName))
{
RecursiveLoadFolder(folder1, ref files, folder.Name);
}
else
{
RecursiveLoadFolder(folder1, ref files, parentFolderName + "/" + folder.Name);
}
}
foreach (File file in folder.Files)
{
if (String.IsNullOrEmpty(parentFolderName))
{
files.Add(folder.Name + "/" + file.Name, file);
}
else
{
files.Add(parentFolderName + "/" + folder.Name + "/" + file.Name, file);
}
}
}
private int seekCentralDirectory(IO.Reader br)
{
byte[] signature = new byte[] { 0x3f, 0x7c, 80, 0x4b };
byte[] siggy = new byte[4];
private int seekCentralDirectory(IO.Reader br)
{
byte[] signature = new byte[] { 0x3f, 0x7c, 80, 0x4b };
byte[] siggy = new byte[4];
for (int l = 0; l < (br.Accessor.Length - 4L); l++)
{
br.Seek((long) l, SeekOrigin.Begin);
{
br.Seek((long) l, SeekOrigin.Begin);
br.Read(siggy, 0, 4);
if (signature.Match(siggy))
{
return l;
}
}
return -1;
}
if (signature.Match(siggy))
{
return l;
}
}
return -1;
}
private int zip_FindFirstFileOffset(IO.Reader br)
{
byte[] vFileEntrySignal = new byte[] { 80, 0x4b, 3, 4 };
private int zip_FindFirstFileOffset(IO.Reader br)
{
byte[] vFileEntrySignal = new byte[] { 80, 0x4b, 3, 4 };
while (br.Accessor.Length != br.Accessor.Position)
{
{
byte[] vFileEntrySignal1 = br.ReadBytes(vFileEntrySignal.Length);
if (vFileEntrySignal1.Match(vFileEntrySignal))
{
return (((int)br.Accessor.Position) - vFileEntrySignal.Length);
}
}
return -1;
}
if (vFileEntrySignal1.Match(vFileEntrySignal))
{
return (((int)br.Accessor.Position) - vFileEntrySignal.Length);
}
}
return -1;
}
private File zip_ReadFile(IO.Reader br)
{
byte[] unpackedData;
CompressionMethod method = CompressionMethod.Deflate;
byte[] vaLocalFileHeaderSignatureCmp = new byte[] { 80, 0x4b, 3, 4 };
if (br.Remaining < 4) return null;
private File zip_ReadFile(IO.Reader br)
{
byte[] unpackedData;
CompressionMethod method = CompressionMethod.Deflate;
byte[] vaLocalFileHeaderSignatureCmp = new byte[] { 80, 0x4b, 3, 4 };
if (br.Remaining < 4) return null;
byte[] vaLocalFileHeaderSignatureCmp1 = br.ReadBytes(4);
if (!vaLocalFileHeaderSignatureCmp1.Match(vaLocalFileHeaderSignatureCmp))
{
return null;
}
short iMinimumVersionNeededToExtract = br.ReadInt16();
short iGeneralPurposeBitFlag = br.ReadInt16();
{
return null;
}
short iMinimumVersionNeededToExtract = br.ReadInt16();
short iGeneralPurposeBitFlag = br.ReadInt16();
switch (br.ReadInt16())
{
case 8:
@ -411,43 +412,48 @@ namespace UniversalEditor.DataFormats.FileSystem.ZIP
break;
}
}
short iFileLastModificationTime = br.ReadInt16();
short iFileLastModificationDate = br.ReadInt16();
bool isEncrypted = false;
int iCRC32 = br.ReadInt32();
int packedFileLength = br.ReadInt32();
int unpackedFileLength = br.ReadInt32();
short fileNameLength = br.ReadInt16();
short extraFieldLength = br.ReadInt16();
string fileName = br.ReadFixedLengthString(fileNameLength);
short iFileLastModificationTime = br.ReadInt16();
short iFileLastModificationDate = br.ReadInt16();
bool isEncrypted = false;
int iCRC32 = br.ReadInt32();
int packedFileLength = br.ReadInt32();
int unpackedFileLength = br.ReadInt32();
short fileNameLength = br.ReadInt16();
short extraFieldLength = br.ReadInt16();
string fileName = br.ReadFixedLengthString(fileNameLength);
long pos = br.Accessor.Position;
while (br.Accessor.Position < (pos + extraFieldLength))
{
short chunkIDCode = br.ReadInt16();
short chunkLength = br.ReadInt16();
byte[] data = br.ReadBytes(chunkLength);
}
byte[] packedData = br.ReadBytes(packedFileLength);
if (isEncrypted)
{
{
short chunkIDCode = br.ReadInt16();
short chunkLength = br.ReadInt16();
byte[] data = br.ReadBytes(chunkLength);
}
byte[] packedData = br.ReadBytes(packedFileLength);
if (isEncrypted)
{
throw new System.Security.SecurityException("File is encrypted");
}
if (packedFileLength == unpackedFileLength)
{
unpackedData = packedData;
}
else
{
}
if (packedFileLength == unpackedFileLength)
{
unpackedData = packedData;
}
else
{
unpackedData = UniversalEditor.Compression.CompressionModule.FromKnownCompressionMethod(method).Decompress(packedData);
}
}
File f = new File();
f.SetDataAsByteArray(unpackedData);
f.Name = fileName;
if (unpackedData.Length != unpackedFileLength)
{
HostApplication.Messages.Add(HostApplicationMessageSeverity.Error, "File size mismatch, source archive may be corrupted", fileName);
}
File f = new File();
f.SetDataAsByteArray(unpackedData);
f.Name = fileName;
return f;
}
}
public ZIPSettings Settings { get { return mvarSettings; } }
}
public ZIPSettings Settings { get { return mvarSettings; } }
}
}