/* Copyright (c) MediaArea.net SARL. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license that can
* be found in the License.html file in the root of the source tree.
*/
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_ZIP_YES)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Archive/File_Zip.h"
#include "ZenLib/Utils.h"
//---------------------------------------------------------------------------
namespace MediaInfoLib
{
//***************************************************************************
// Infos
//***************************************************************************
//---------------------------------------------------------------------------
const char* Zip_made_by[20]=
{
"MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)",
"Amiga",
"OpenVMS",
"UNIX",
"VM/CMS",
"Atari ST",
"OS/2 H.P.F.S.",
"Macintosh",
"Z-System",
"CP/M",
"Windows NTFS",
"MVS (OS/390 - Z/OS)",
"VSE",
"Acorn Risc",
"VFAT",
"alternate MVS",
"BeOS",
"Tandem",
"OS/400",
"OS/X (Darwin)"
};
//---------------------------------------------------------------------------
const char* Zip_compression_method[22]=
{
"stored (no compression)",
"Shrunk",
"Reduced with compression factor 1",
"Reduced with compression factor 2",
"Reduced with compression factor 3",
"Reduced with compression factor 4",
"Imploded",
"Tokenizing compression algorithm",
"Deflated",
"Enhanced Deflating using Deflate64(tm)",
"PKWARE Data Compression Library Imploding (old IBM TERSE)",
"Reserved by PKWARE",
"compressed using BZIP2 algorithm",
"Reserved by PKWARE",
"LZMA (EFS)",
"Reserved by PKWARE",
"Reserved by PKWARE",
"Reserved by PKWARE",
"File is compressed using IBM TERSE (new)",
"IBM LZ77 z Architecture (PFS)",
"WavPack compressed data", // 97
"PPMd version I, Rev 1" // 98
};
//***************************************************************************
// Static stuff
//***************************************************************************
//---------------------------------------------------------------------------
bool File_Zip::FileHeader_Begin()
{
//Element_Size
if (Buffer_Size<4)
return false; //Must wait for more data
if (Buffer[0]!=0x50 //"PK.."
|| Buffer[1]!=0x4B
|| Buffer[2]!=0x03
|| Buffer[3]!=0x04)
{
Reject("ZIP");
return false;
}
//This is OK, ZIP detected
Accept();
Fill(Stream_General, 0, General_Format, "ZIP");
//Init
signature=0x00000000;
local_file_Step=0;
end_of_central_directory_IsParsed=false;
//Jumping to the end of the file minus end_of_central_directory size (we hope there is no comment)
GoTo(File_Size-22);
return true;
}
//***************************************************************************
// Buffer - Global
//***************************************************************************
//---------------------------------------------------------------------------
void File_Zip::Read_Buffer_Continue()
{
for (;;)
{
//Probing
if (signature==0x00000000) //If not already tested (else an element is being parsed)
{
if (Element_Offset+4>Element_Size) //signature size
return; //Not enough data
signature=LittleEndian2int32u(Buffer+(size_t)Element_Offset);
}
//Parsing
switch (signature)
{
case 0x04034b50 : if (!local_file())
return; //Not enough data
break;
case 0x02014b50 : if (!central_directory())
return; //Not enough data
break;
case 0x05054b50 : if (!digital_signature())
return; //Not enough data
break;
case 0x06054b50 : if (!end_of_central_directory())
return; //Not enough data
break;
case 0x08064b50 : if(!archive_extra_data_record())
return;
break;
case 0x06064b50 : if(!Zip64_end_of_central_directory_record())
return;
break;
case 0x07064b50 : if(!Zip64_end_of_central_directory_locator())
return;
break;
default: Finish(); return; //Reject(); return; //Unknown value //Décommenter quand toutes les signatures sont implémentées
}
//Cleanup
signature=0x00000000; //Reset, must probe again the signature (next element)
}
}
//***************************************************************************
// Files
//***************************************************************************
bool File_Zip::local_file()
{
switch(local_file_Step)
{
case 0 :
if (!local_file_header())
return false;
local_file_Step=1; //local_file_header parsed
break;
case 1 :
local_file_Step=2; //file_data is always parsed
if (!file_data())
return false;
break;
case 2:
if (!data_descriptor())
return false;
local_file_Step=0; //data_descriptor is parsed, back to begin
break;
default: ; //Should never happen
}
return true;
}
bool File_Zip::archive_extra_data_record()
{
if (Element_Offset+8>Element_Size) //archive_extra_data_record
return false; //Not enough data
//Retrieving complete archive_extra_data_record size
int32u extra_field_length=LittleEndian2int32u(Buffer+(size_t)Element_Offset+4);
//Parsing
Element_Begin1("archive_extra_data_record");
Skip_C4("Archive extra data signature");
Skip_L4("extra field length");
Skip_XX(extra_field_length,"extra_field_data");
Element_End0();
return true;
}
bool File_Zip::digital_signature()
{
if (Element_Offset+6>Element_Size) //digital_signature
return false; //Not enough data
//Retrieving complete archive_extra_data_record size
int16u size_of_data=LittleEndian2int16u(Buffer+(size_t)Element_Offset+4);
//Parsing
Element_Begin1("digital_signature");
Skip_C4("Header signature");
Skip_L2("size of data");
Skip_XX(size_of_data,"signature data");
Element_End0();
return true;
}
bool File_Zip::local_file_header()
{
if (Element_Offset+30>Element_Size) //local_file_header up to extra_field_length included
return false; //Not enough data
//Retrieving complete local_file_header size
int16u file_name_length=LittleEndian2int16u(Buffer+(size_t)Element_Offset+26);
int16u extra_field_length=LittleEndian2int16u(Buffer+(size_t)Element_Offset+28);
if (Element_Offset+30+file_name_length+extra_field_length>Element_Size) //local_file_header all included
return false; //Not enough data
//Parsing
Element_Begin1("local_file_header");
int16u general_purpose_bit_flag,compression_method;
bool efs;
Skip_C4("Local file header signature");
Skip_L2("Version needed to extract");
Get_L2 (general_purpose_bit_flag,"general purpose bit flag");
Skip_Flags(general_purpose_bit_flag, 0, "encrypted file");
Skip_Flags(general_purpose_bit_flag, 1, "8K sliding dictionary");
Skip_Flags(general_purpose_bit_flag, 2, "3 Shannon-Fano trees");
Get_Flags (general_purpose_bit_flag, 3, data_descriptor_set, "data descriptor");
Skip_Flags(general_purpose_bit_flag, 4, "Reserved for use with method 8");
Skip_Flags(general_purpose_bit_flag, 4, "file is compressed patched data");
Skip_Flags(general_purpose_bit_flag, 4, "Strong encryption");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
Get_Flags (general_purpose_bit_flag, 11, efs, "Language encoding flag (EFS)");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Reserved by PKWARE for enhanced compression");
//~ Skip_Flags(general_purpose_bit_flag, 4, "");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Reserved by PKWARE");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Reserved by PKWARE");
Get_L2 (compression_method,"compression method");
Param_Info1C((compression_method<20), Zip_compression_method[compression_method]);
Param_Info1C((compression_method==97||compression_method==98), Zip_compression_method[compression_method-97+20]);
Skip_L2("last mod file time");
Skip_L2("last mod file date");
Skip_L4("crc-32");
Get_L4(compressed_size,"compressed size");
Skip_L4("uncompressed size");
Get_L2(file_name_length,"file name lenth");
Get_L2(extra_field_length,"extra field length");
if(efs) {
Skip_UTF8(file_name_length,"file name");
Skip_UTF8(extra_field_length,"extra field");
} else {
Skip_Local(file_name_length,"file name");
Skip_Local(extra_field_length,"extra field");
}
Element_End0();
FILLING_BEGIN();
Accept("Zip");
Fill(Stream_General, 0, General_Format, "ZIP");
FILLING_END();
return true;
}
bool File_Zip::file_data()
{
Element_Begin1("file_data");
Skip_XX(compressed_size,"File_data");
Element_End0();
if (Element_Offset>Element_Size)
{
GoTo(File_Offset+Element_Offset);
return false;
}
return true;
}
bool File_Zip::data_descriptor()
{
if(data_descriptor_set)
{
if (Element_Offset+12>Element_Size)
return false; //Not enough data
Element_Begin1("data_descriptor");
Skip_L4("crc-32");
Skip_L4("compressed size");
Skip_L4("uncompressed size");
Element_End0();
}
return true;
}
bool File_Zip::central_directory()
{
if (Element_Offset+46>Element_Size) //central_directory up to relative offset of local header included
return false; //Not enough data
//Retrieving complete local_file_header size
int16u file_name_length=LittleEndian2int16u(Buffer+(size_t)Element_Offset+28);
int16u extra_field_length=LittleEndian2int16u(Buffer+(size_t)Element_Offset+30);
int16u file_comment_length=LittleEndian2int16u(Buffer+(size_t)Element_Offset+32);
if (Element_Offset+46+file_name_length+extra_field_length+file_comment_length>Element_Size) //central_directory_structure all included
return false; //Not enough data
int16u general_purpose_bit_flag;
bool efs;
int16u version_made_by,compression_method;
//Parsing
Element_Begin1("Central directory");
Skip_C4("central file header signature");
Get_L2 (version_made_by,"version made by");Param_Info1((version_made_by>>8)>20?"unused":Zip_made_by[version_made_by>>8]);
Skip_L2("version needed to extract");
Get_L2 (general_purpose_bit_flag,"general purpose bit flag");
Skip_Flags(general_purpose_bit_flag, 0, "encrypted file");
Skip_Flags(general_purpose_bit_flag, 1, "8K sliding dictionary");
Skip_Flags(general_purpose_bit_flag, 2, "3 Shannon-Fano trees");
Skip_Flags(general_purpose_bit_flag, 3, "data descriptor");
Skip_Flags(general_purpose_bit_flag, 4, "Reserved for use with method 8");
Skip_Flags(general_purpose_bit_flag, 4, "file is compressed patched data");
Skip_Flags(general_purpose_bit_flag, 4, "Strong encryption");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Currently unused");
Get_Flags (general_purpose_bit_flag, 11, efs, "Language encoding flag (EFS)");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Reserved by PKWARE for enhanced compression");
//~ Skip_Flags(general_purpose_bit_flag, 4, "");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Reserved by PKWARE");
//~ Skip_Flags(general_purpose_bit_flag, 4, "Reserved by PKWARE");
Get_L2 (compression_method,"compression method");
Param_Info1C((compression_method<20), Zip_compression_method[compression_method]);
Param_Info1C((compression_method==97||compression_method==98), Zip_compression_method[compression_method-97+20]);
Skip_L2("last mod file time");
Skip_L2("last mod file date");
Skip_L4("crc-32");
Skip_L4("compressed size");
Skip_L4("uncompressed size");
Skip_L2("file name length");
Skip_L2("extra field length");
Skip_L2("file comment length");
Skip_L2("disk number start");
Skip_L2("internal file attributes");
Skip_L4("external file attributes");
Skip_L4("relative offset of local header");
if(efs) {
Skip_UTF8(file_name_length,"file name");
Skip_UTF8(extra_field_length,"extra field");
Skip_UTF8(file_comment_length,"file comment");
} else {
Skip_Local(file_name_length,"file name");
Skip_Local(extra_field_length,"extra field");
Skip_Local(file_comment_length,"file comment");
}
Element_End0();
return true;
}
bool File_Zip::end_of_central_directory()
{
if (Element_Offset+22>Element_Size) //end_of_central_directory up to relative offset of .ZIP file comment length included
return false; //Not enough data
//Retrieving complete local_file_header size
int16u zip_comment_length=LittleEndian2int16u(Buffer+(size_t)Element_Offset+20);
if (Element_Offset+22+zip_comment_length>Element_Size) //end_of_central_directory all included
return false; //Not enough data
//Parsing
int32u offset;
Element_Begin1("End of central directory");
Skip_C4( "end of central dir signature");
Skip_L2( "number of this disk");
Skip_L2( "number of the disk");// with the start of the central directory
Skip_L2( "total number of entries on this disk");// in the central directory
Skip_L2( "total number of entries");// in the central directory
Skip_L4( "size of the central directory");
Get_L4 (offset, "offset of start of central directory");// with respect to the starting disk number
Skip_L2( "zip file comment length");
Skip_XX(zip_comment_length, "zip file comment");
Element_End0();
//Going to first central directory (once)
if (!end_of_central_directory_IsParsed)
{
end_of_central_directory_IsParsed=true;
GoTo(offset);
}
return true;
}
bool File_Zip::Zip64_end_of_central_directory_record()
{
if (Element_Offset+12>Element_Size) //Zip64_end_of_central_directory_record
return false; //Not enough data
//Retrieving complete Zip64_end_of_central_directory_record size
int64u size_of_Zip64_end_of_central_directory_record=LittleEndian2int64u(Buffer+(size_t)Element_Offset+4);
if (Element_Offset+12+size_of_Zip64_end_of_central_directory_record>Element_Size) //end_of_central_directory all included
return false; //Not enough data
//Parsing
//~ int32u offset;
int16u version_made_by;
Element_Begin1("Zip64 End of central directory record");
Skip_C4( "Zip64 end of central dir signature");
Skip_L8( "size of zip64 end of central directory record");
Get_L2 (version_made_by, "version made by");
Param_Info1((version_made_by>>8)>20?"unused":Zip_made_by[version_made_by>>8]);
Skip_L2( "version needed to extract");
Skip_L4( "number of this disk");
Skip_L4( "number of the disk");// with the start of the central directory
Skip_L8( "total number of entries on this disk");// in the central directory
Skip_L8( "total number of entries");// in the central directory
Skip_L8( "size of the central directory");
Skip_L8( "offset of start of central directory"); // with respect to the starting disk number
Skip_XX(size_of_Zip64_end_of_central_directory_record-44, "zip64 extensible data sector");
Element_End0();
return true;
}
bool File_Zip::Zip64_end_of_central_directory_locator()
{
if (Element_Offset+20>Element_Size) //Zip64_end_of_central_directory_locator
return false; //Not enough data
//Parsing
Element_Begin1("Zip64 end of central directory locator");
Skip_C4("zip64 end of central dir locator signature");
Skip_L4("number of the disk");// with the start of the zip64 end of central directory
Skip_L8("relative offset of the zip64 end of central directory record");
Skip_L4("total number of disks");
Element_End0();
return true;
}
} //NameSpace
#endif //MEDIAINFO_ZIP_YES
↑ V525 The code contains the collection of similar blocks. Check items '2', '2', '4' in lines 274, 275, 276.
↑ V525 The code contains the collection of similar blocks. Check items '4', '8', '4' in lines 467, 468, 469.