File: Base\MS\Internal\IO\Zip\ZipIOLocalFileDataDescriptor.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//-------------   *** WARNING ***
//-------------    This file is part of a legally monitored development project.  
//-------------    Do not check in changes to this project.  Do not raid bugs on this
//-------------    code in the main PS database.  Do not contact the owner of this
//-------------    code directly.  Contact the legal team at ‘ZSLegal’ for assistance.
//-------------   *** WARNING ***
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
//
// <copyright file="ZipIOLocalFileDataDescriptor.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//  This is an internal class that enables interactions with Zip archives
//  for OPC scenarios 
//
// History:
//  11/19/2004: IgorBel: Initial creation.
//
//-----------------------------------------------------------------------------
 
using System;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Collections;
using System.Windows;
using MS.Internal.WindowsBase;
    
namespace MS.Internal.IO.Zip
{
    internal class ZipIOLocalFileDataDescriptor
    {
        // used in streaming case
        internal static ZipIOLocalFileDataDescriptor CreateNew()
        {
            ZipIOLocalFileDataDescriptor descriptor = new ZipIOLocalFileDataDescriptor();
            descriptor._size = _fixedMinimalRecordSizeWithSignatureZip64;
 
            return descriptor;
        }
 
        internal static ZipIOLocalFileDataDescriptor ParseRecord(BinaryReader reader,  
                                    long compressedSizeFromCentralDir, 
                                    long uncompressedSizeFromCentralDir,
                                    UInt32 crc32FromCentralDir,
                                    UInt16 versionNeededToExtract)
        {
            ZipIOLocalFileDataDescriptor descriptor = new ZipIOLocalFileDataDescriptor();
 
            // There are 4 distinct scenario we would like to support 
            //   1.based on the appnote it seems that the structure of this record is following:
            //                  crc-32                          4 bytes
            //                  compressed size                 4 bytes (scenario 1.a has 8 bytes)
            //                  uncompressed size               4 bytes (scenario 1.a has 8 bytes)
            //
            //   2.based on files that we have been able to examine 
            //                  data descriptor signature        4 bytes  (0x08074b50)
            //                  crc-32                                  4 bytes
            //                  compressed size                   4 bytes (scenario 2.a has 8 bytes)
            //                  uncompressed size               4 bytes (scenario 2.a has 8 bytes)
            //
            // we can safely assume that this record is not the last one in the file, so let's just 
            // read the max Bytes required to store the largest structure , and compare results 
 
            // at most we are looking at 6 * 4  = 24 bytes      
            UInt32[] buffer = new UInt32[6];
 
            // let's try to match the smallest possible structure (3 x 4) 32 bit without signature 
            buffer[0] = reader.ReadUInt32(); 
            buffer[1] = reader.ReadUInt32(); 
            buffer[2] = reader.ReadUInt32(); 
 
            if (descriptor.TestMatch(_signatureConstant, 
                                    crc32FromCentralDir,
                                    compressedSizeFromCentralDir, 
                                    uncompressedSizeFromCentralDir,
                               _signatureConstant,      
                                    buffer[0],
                                    buffer[1],
                                    buffer[2]))
            {
                descriptor._size = _fixedMinimalRecordSizeWithoutSignature;
                return descriptor;
            }
 
            // let's try to match the next record size (4 x 4) 32 bit with signature 
            buffer[3] = reader.ReadUInt32(); 
            if (descriptor.TestMatch(_signatureConstant, 
                                    crc32FromCentralDir,
                                    compressedSizeFromCentralDir, 
                                    uncompressedSizeFromCentralDir,
                                 buffer[0],      
                                    buffer[1],
                                    buffer[2],
                                    buffer[3]))
            {
                descriptor._size = _fixedMinimalRecordSizeWithSignature;
                return descriptor;
            }
 
            // At this point prior to trying to match 64 bit structures we need to make sure that version is high enough 
            if (versionNeededToExtract <  (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat)
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            //let's try to match the 64 bit structures 64 bit without signature 
            buffer[4] = reader.ReadUInt32();             
            if (descriptor.TestMatch(_signatureConstant, 
                                    crc32FromCentralDir,
                                    compressedSizeFromCentralDir, 
                                    uncompressedSizeFromCentralDir,
                                 _signatureConstant,      
                                    buffer[0],
                                    ZipIOBlockManager.ConvertToUInt64(buffer[1], buffer[2]),
                                    ZipIOBlockManager.ConvertToUInt64(buffer[3], buffer[4])))
            {
                descriptor._size = _fixedMinimalRecordSizeWithoutSignatureZip64;
                return descriptor;
            }            
 
            //let's try to match the 64 bit structures 64 bit with signature 
            buffer[5] = reader.ReadUInt32();             
            if (descriptor.TestMatch(_signatureConstant, 
                                    crc32FromCentralDir,
                                    compressedSizeFromCentralDir, 
                                    uncompressedSizeFromCentralDir,
                                 buffer[0],      
                                    buffer[1],
                                    ZipIOBlockManager.ConvertToUInt64(buffer[2], buffer[3]),
                                    ZipIOBlockManager.ConvertToUInt64(buffer[4], buffer[5])))
            {
                descriptor._size = _fixedMinimalRecordSizeWithSignatureZip64;
                return descriptor;
            }   
 
            // we couldn't match anything at this point we need to fail 
            throw new FileFormatException(SR.Get(SRID.CorruptedData));
        }
 
        /// <summary>
        /// Save - persist to given binary writer
        /// </summary>
        /// <param name="writer"></param>
        internal void Save(BinaryWriter writer)
        {
            // we only supposed to be saving a brand new record which is created with signature in Zip64 Mode 
            // this only supported for streaming publishing cases. 
            // For editing scenarios we never save them (not even preserve them), as any editing (even trivial moving of record around)
            // will result in switching File Item to a non-streaming mode without data desciptor 
            Invariant.Assert(_size == _fixedMinimalRecordSizeWithSignatureZip64);
 
            // always persist Zip64 version signature
            writer.Write(_signatureConstant);               // this is apparently optional, but omitting it causes wzzip to complain
            writer.Write(_crc32);
            writer.Write((UInt64)_compressedSize);          // 8 bytes
            writer.Write((UInt64)_uncompressedSize);        // 8 bytes
        }
 
        internal long Size
        {
            get
            {
                return _size;
            }
        }
 
        internal UInt32 Crc32 
        {
            get
            {
                return _crc32;
            }
            set
            {
                _crc32 = value;
            }
        }
 
        internal long CompressedSize
        {
            get
            {
                return _compressedSize;
            }
            set
            {
                Invariant.Assert((value >= 0), "CompressedSize must be non-negative");
 
                _compressedSize = value;
            }
        }
 
        internal long UncompressedSize
        {
            get
            {
                return _uncompressedSize;
            }
            set
            {
                Invariant.Assert((value >= 0), "UncompressedSize must be non-negative");
 
                _uncompressedSize = value;
            }
        }
 
 
        private bool TestMatch(UInt32 signature,
                                                UInt32 crc32FromCentralDir,
                                                long compressedSizeFromCentralDir, 
                                                long uncompressedSizeFromCentralDir,
                                                UInt32 suspectSignature,
                                                UInt32 suspectCrc32,
                                                UInt64 suspectCompressedSize, 
                                                UInt64 suspectUncompressedSize)
        {
            checked
            {
                // Don't compare compressedSize and uncompressedSize as long since the sniffed
                //  bytes might not be the actual match and can be bigger than Int64.MaxValue
                //  Convert them as long only if it is a match
                if  ((signature == suspectSignature) &&
                     ((UInt64) compressedSizeFromCentralDir == suspectCompressedSize) &&
                     ((UInt64) uncompressedSizeFromCentralDir == suspectUncompressedSize) &&
                     (crc32FromCentralDir == suspectCrc32))
                {
                    _signature = suspectSignature;
                    _compressedSize = (long) suspectCompressedSize;
                    _uncompressedSize = (long) suspectUncompressedSize;
                    _crc32 = suspectCrc32;
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
            
        private  const UInt32 _signatureConstant  = 0x08074b50;
        private const int _fixedMinimalRecordSizeWithoutSignature = 12;
        private const int _fixedMinimalRecordSizeWithSignature = 16;
        private const int _fixedMinimalRecordSizeWithoutSignatureZip64 = 20;
        private const int _fixedMinimalRecordSizeWithSignatureZip64 = 24;
        
        private int _size;
 
        private UInt32 _signature = _signatureConstant;
        private UInt32 _crc32;
        private long _compressedSize = 0;
        private long _uncompressedSize = 0;
            
    }
}