private List<FileEntry> ParseCentralDirectory()
{
BinaryReader reader = new BinaryReader(this.stream);
List<FileEntry> entries = new List<FileEntry>();
reader.BaseStream.Seek(-4, SeekOrigin.End);
while (reader.ReadInt32() != 101010256)
{
reader.BaseStream.Seek(-5, SeekOrigin.Current);
}
reader.BaseStream.Seek(6, SeekOrigin.Current);
short entryCount = reader.ReadInt16();
int directorySize = reader.ReadInt32();
int directoryStart = reader.ReadInt32();
reader.BaseStream.Seek(directoryStart, SeekOrigin.Begin);
bool needsFixing = false;
for (int i = 0; i < entryCount; i++)
{
int headerSignature = reader.ReadInt32();
if (headerSignature == 33639248) //Central directory file header signature
{
reader.BaseStream.Seek(4, SeekOrigin.Current);
short flag = reader.ReadInt16();
if (flag == 8) //Silverlight doesn't like this format
{
needsFixing = true;
}
reader.BaseStream.Seek(6, SeekOrigin.Current);
int crc32 = reader.ReadInt32();
int compressedSize = reader.ReadInt32();
int unCompressedSize = reader.ReadInt32();
short fileNameLenght = reader.ReadInt16();
short extraFieldLength = reader.ReadInt16();
short fileCommentLength = reader.ReadInt16();
reader.BaseStream.Seek(8, SeekOrigin.Current);
int fileStart = reader.ReadInt32();
string filename = new string(reader.ReadChars(fileNameLenght));
entries.Add(new FileEntry()
{
Filename = filename, FileStart = fileStart, CRC32 = crc32,
CompressedSize = compressedSize, UncompressedSize = unCompressedSize
});
reader.BaseStream.Seek(extraFieldLength + fileCommentLength, SeekOrigin.Current);
}
}
if (needsFixing)
{
//We are using a zipformat that Silverlight doesn't like.
//Zipfiles where the file size is reported after the compressed data
//is a no-go, so we rebuild the header and report the information there.
MemoryStream newZip = new MemoryStream();
BinaryWriter writer = new BinaryWriter(newZip);
//Rebuild file entries
foreach (FileEntry entry in entries)
{
FileEntry e = entry;
reader.BaseStream.Seek(entry.FileStart, SeekOrigin.Begin);
e.FileStart = (int)writer.BaseStream.Position;
CopyBytes(reader, writer, 6);
writer.Write((short)2); //This flag needs to be replaced to indicate the new format
reader.BaseStream.Seek(2, SeekOrigin.Current);
CopyBytes(reader, writer, 6);
writer.Write(entry.CRC32); //Update CRC
writer.Write(entry.CompressedSize); //Update Compressed size
writer.Write(entry.UncompressedSize); //Update Uncompressed size
writer.Write((short)entry.Filename.Length);
reader.BaseStream.Seek(14, SeekOrigin.Current);
short fieldLength = reader.ReadInt16();
writer.Write(fieldLength);
CopyBytes(reader, writer, entry.Filename.Length + fieldLength + entry.CompressedSize);
}
//Rebuild directory
reader.BaseStream.Seek(directoryStart, SeekOrigin.Begin);
for (int i = 0; i < entryCount; i++)
{
CopyBytes(reader, writer, 8);
writer.Write((short)2); //This flag needs to be replaced to indicate the new format
reader.BaseStream.Seek(2, SeekOrigin.Current);
CopyBytes(reader, writer, 18);
short filenamelength = reader.ReadInt16();
writer.Write(filenamelength);
short extrafieldlength = reader.ReadInt16();
writer.Write(extrafieldlength);
short filecommentlength = reader.ReadInt16();
writer.Write(filecommentlength);
CopyBytes(reader, writer, 8);
writer.Write(entries[i].FileStart);
reader.BaseStream.Seek(4, SeekOrigin.Current);
CopyBytes(reader, writer, filenamelength + extrafieldlength + filecommentlength);
}
CopyBytes(reader, writer, (int)(reader.BaseStream.Length - reader.BaseStream.Position));
this.stream = newZip;
}
return entries;
}