// 2013 Hiroyuki Ogasawara (http://wlog.flatlib.jp) // vim:ts=4 sw=4 noet: #include #include #include #include #include #if _WINDOWS # include # include "external/zlib/zlib.h" #else # include # include # include "zlib.h" #endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- typedef unsigned short UI16; typedef unsigned int UI32; namespace le { inline UI32 Load2( const unsigned char *ptr ) { return (ptr[0]) | (ptr[1]<< 8); } inline UI32 Load4( const unsigned char *ptr ) { return (ptr[0]) | (ptr[1]<< 8) | (ptr[2]<<16) | (ptr[3]<<24); } inline void Store2( unsigned char *ptr, UI32 value ) { ptr[0]= static_cast( value ); ptr[1]= static_cast( value >> 8); } inline void Store4( unsigned char *ptr, UI32 value ) { ptr[0]= static_cast( value ); ptr[1]= static_cast( value >> 8 ); ptr[2]= static_cast( value >>16 ); ptr[3]= static_cast( value >>24 ); } } struct UI16LE { unsigned char Memory[2]; UI32 Get() const { return le::Load2( Memory ); } void Set( UI32 value ) { le::Store2( Memory, value ); } }; struct UI32LE { unsigned char Memory[4]; UI32 Get() const { return le::Load4( Memory ); } void Set( UI32 value ) { le::Store4( Memory, value ); } }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- enum { ZIP_SIGNATURE_LOCAL32 = 0x04034b50, ZIP_SIGNATURE_CENTRALDIR32 = 0x02014b50, ZIP_SIGNATURE_CENTRALDIR32_EOR = 0x06054b50, }; struct T_ZIP_LOCAL32 { UI32LE Signature; // PK0304 = 0x04034b50 UI16LE Extract; UI16LE BitFlag; UI16LE Method; UI16LE Time; UI16LE Date; UI32LE CRC32; UI32LE CompressedSize; UI32LE UncompressedSize; UI16LE NameLength; UI16LE ExtraLength; }; struct T_ZIP_CENTRALDIR32 { UI32LE Signature; // PK0102 = 0x02014b50 UI16LE Version; UI16LE Extract; UI16LE BitFlag; UI16LE Method; UI16LE Time; UI16LE Date; UI32LE CRC32; UI32LE CompressedSize; UI32LE UncompressedSize; UI16LE NameLength; UI16LE ExtraLength; UI16LE CommentLength; UI16LE DiskNumberStart; UI16LE InternalAttributes; UI32LE ExternalAttributes; UI32LE LocalHeaderOffset; public: const char* GetNamePointer() const { return reinterpret_cast( reinterpret_cast( this ) + sizeof(T_ZIP_CENTRALDIR32) ); } const bool IsDirectory() const { unsigned int name_length= NameLength.Get(); return name_length && GetNamePointer()[ name_length-1 ] == '/'; } const T_ZIP_CENTRALDIR32* Next() const { return reinterpret_cast( reinterpret_cast( this ) + sizeof(T_ZIP_CENTRALDIR32) + NameLength.Get() + ExtraLength.Get() + CommentLength.Get() ); } }; struct T_ZIP_CENTRALDIR32_EOR { UI32LE Signature; // PK0506 = 0x06054b50 UI16LE NumberOfThisDisk; UI16LE StartDisk; UI16LE TotalEntriesDisk; UI16LE TotalEntries; UI32LE CentralDirectorySize; UI32LE CentralDirectoryOffset; UI16LE CommentLength; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class Buffer { void* Memory; size_t MemorySize; Buffer( const Buffer& ){} Buffer& operator=( const Buffer& src ){ return *this; } public: Buffer() : Memory( NULL ), MemorySize( 0 ){} ~Buffer() { Release(); } void Release() { if( Memory ){ free( Memory ); Memory= NULL; } } void Alloc( size_t size ) { Release(); Memory= malloc( size ); MemorySize= size; } void Shrink( size_t size ) { assert( size <= MemorySize ); MemorySize= size; } size_t Size() const { return MemorySize; } unsigned long SizeLong() const { return static_cast( MemorySize ); } template T* Address() const { return reinterpret_cast( Memory ); } }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class File { FILE* Fp; public: File() : Fp( NULL ){} ~File() { Close(); } bool Open( const char* file_name ) { #if _WINDOWS fopen_s( &Fp, file_name, "rb" ); #else Fp= fopen( file_name, "rb" ); #endif return Fp != NULL; } bool Create( const char* file_name ) { #if _WINDOWS fopen_s( &Fp, file_name, "wb" ); #else Fp= fopen( file_name, "wb" ); #endif return Fp != NULL; } void Close() { if( Fp ){ fclose( Fp ); Fp= NULL; } } size_t Read( void* buffer, size_t size ) { return fread( buffer, 1, size, Fp ); } size_t Write( void* buffer, size_t size ) { return fwrite( buffer, 1, size, Fp ); } void Seek( long long offset, int origin ) { #if _WINDOWS _fseeki64( Fp, offset, origin ); #else fseek( Fp, offset, origin ); #endif } }; static bool MakeDirectory( const char* path ) { #if _WINDOWS return _mkdir( path ) == 0; #else return mkdir( path, 0755 ) == 0; #endif } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- struct PathCache { Buffer PrevPath; bool IsNewPath( const char* path ) { const char* last_path= NULL; for( const char* ptr= path ; *ptr ; ptr++ ){ if( *ptr == '/' ){ last_path= ptr + 1; } } if( last_path ){ size_t size= last_path - path; if( PrevPath.Size() == size + 1 ){ if( memcmp( PrevPath.Address(), path, size ) == 0 ){ return false; } } PrevPath.Alloc( size + 1 ); memcpy( PrevPath.Address(), path, size ); PrevPath.Address()[size]= '\0'; return true; } return false; } const char* GetPath() const { return PrevPath.Address(); } }; static void MakePath( const char* extract_file_name ) { size_t length= strlen( extract_file_name ); Buffer path_buffer; path_buffer.Alloc( length + 1 ); char* str= path_buffer.Address(); const char* ptr= extract_file_name; for(; *ptr ;){ if( *ptr == '/' ){ *str= '\0'; MakeDirectory( path_buffer.Address() ); } *str++= *ptr++; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- size_t zlib_uncompress_raw( Buffer& dest, const Buffer& source ) { z_stream stream; memset( &stream, 0, sizeof(z_stream) ); stream.next_in= source.Address(); stream.avail_in= source.SizeLong(); stream.next_out= dest.Address(); stream.avail_out= dest.SizeLong(); if( inflateInit2( &stream, -MAX_WBITS ) != Z_OK ){ return 0; } int err= inflate( &stream, Z_FINISH ); if( err != Z_STREAM_END && err != Z_OK ){ return 0; } err= inflateEnd( &stream ); assert( dest.Size() == stream.total_out ); return stream.total_out; } //----------------------------------------------------------------------------- class ZipDirectory { unsigned int DirectoryEntries; Buffer DirectoryImage; public: const T_ZIP_CENTRALDIR32* Begin() const { return DirectoryImage.Address(); } bool IsEnd( const T_ZIP_CENTRALDIR32* dir_ptr ) const { return reinterpret_cast(dir_ptr) >= reinterpret_cast(DirectoryImage.Address()) + DirectoryImage.Size(); } static const unsigned char* FindEOR( const unsigned char* ptr, size_t size ) { const unsigned char* end_ptr= ptr + size - sizeof(T_ZIP_CENTRALDIR32_EOR) + sizeof(UI32LE); for(; ptr < end_ptr ; ptr++ ){ if( *ptr == 'P' && le::Load4( ptr ) == ZIP_SIGNATURE_CENTRALDIR32_EOR ){ return ptr; } } return NULL; } bool Load( const char* zip_file_name ) { File file; if( !file.Open( zip_file_name ) ){ return false; } Buffer source_file; const int EOR_LOCATOR_SIZE= 512; unsigned char Locator[EOR_LOCATOR_SIZE]; file.Seek( -EOR_LOCATOR_SIZE, SEEK_END ); size_t read_size= file.Read( Locator, EOR_LOCATOR_SIZE ); const unsigned char* ptr= FindEOR( Locator, read_size ); if( !ptr ){ file.Close(); return false; } const T_ZIP_CENTRALDIR32_EOR* eor= reinterpret_cast( ptr ); unsigned int dir_size= eor->CentralDirectorySize.Get(); DirectoryEntries= eor->TotalEntries.Get(); DirectoryImage.Alloc( dir_size ); file.Seek( eor->CentralDirectoryOffset.Get(), SEEK_SET ); file.Read( DirectoryImage.Address(), dir_size ); file.Close(); return true; } }; //----------------------------------------------------------------------------- static bool UnzipFile( const T_ZIP_CENTRALDIR32* entry, const char* zip_file_name, const char* extract_file_name ) { File zip_file; if( !zip_file.Open( zip_file_name ) ){ return false; } T_ZIP_LOCAL32 LocalHeader; zip_file.Seek( entry->LocalHeaderOffset.Get(), SEEK_SET ); zip_file.Read( &LocalHeader, sizeof(T_ZIP_LOCAL32) ); assert( LocalHeader.Signature.Get() == ZIP_SIGNATURE_LOCAL32 ); unsigned int local_offset= sizeof(T_ZIP_LOCAL32) + LocalHeader.NameLength.Get() + LocalHeader.ExtraLength.Get(); unsigned int uncompressed_size= entry->UncompressedSize.Get(); unsigned int compressed_size= entry->CompressedSize.Get(); Buffer src_buffer; src_buffer.Alloc( compressed_size ); zip_file.Seek( entry->LocalHeaderOffset.Get() + local_offset, SEEK_SET ); zip_file.Read( src_buffer.Address(), compressed_size ); Buffer dest_buffer; dest_buffer.Alloc( uncompressed_size ); switch( entry->Method.Get() ){ case 0: assert( uncompressed_size == compressed_size ); memcpy( dest_buffer.Address(), src_buffer.Address(), uncompressed_size ); break; case 8: if( !zlib_uncompress_raw( dest_buffer, src_buffer ) ){ return false; } break; default: assert( 0 ); break; } if( crc32( 0, dest_buffer.Address(), uncompressed_size ) != entry->CRC32.Get() ){ return false; } File file; if( !file.Create( extract_file_name ) ){ return false; } file.Write( dest_buffer.Address(), dest_buffer.Size() ); file.Close(); return true; } //----------------------------------------------------------------------------- static void Unzip( const char* zip_file_name ) { ZipDirectory directory; directory.Load( zip_file_name ); const T_ZIP_CENTRALDIR32* dir_ptr= directory.Begin(); PathCache path_cache; for(; !directory.IsEnd( dir_ptr ) ; dir_ptr= dir_ptr->Next() ){ assert( dir_ptr->Signature.Get() == ZIP_SIGNATURE_CENTRALDIR32 ); unsigned int uncompressed_size= dir_ptr->UncompressedSize.Get(); unsigned int compressed_size= dir_ptr->CompressedSize.Get(); unsigned int name_length= dir_ptr->NameLength.Get(); const int NAME_BUFFER_SIZE= 512; char name_buffer[ NAME_BUFFER_SIZE ]; assert( name_length < NAME_BUFFER_SIZE ); memcpy( name_buffer, dir_ptr->GetNamePointer(), name_length ); name_buffer[name_length]= '\0'; printf( "%8d %8d %s\n", uncompressed_size, compressed_size, name_buffer ); if( !dir_ptr->IsDirectory() ){ if( path_cache.IsNewPath( name_buffer ) ){ MakePath( path_cache.GetPath() ); } UnzipFile( dir_ptr, zip_file_name, name_buffer ); } } } //----------------------------------------------------------------------------- int main( int argc, char** argv ) { for(; --argc ; Unzip( *++argv ) ); return 0; }