Search code examples
zipcompressionzlibunzip

Uncompressing a zip file using zlib


Studying the zip file format and zlib, I wrote a simple program that displays the contents of a zip file named test.zip and tries to use zlib to unzip the test.txt file in the archive:

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <zlib.h>

using namespace std;

#pragma pack(1)
struct LocalFileHeader {
    uint32_t signature;
    uint16_t version;
    uint16_t bit_flag;
    uint16_t compression;
    uint16_t last_mod_time;
    uint16_t last_mod_date;
    uint32_t crc32;
    uint32_t compressed_size;
    uint32_t uncompressed_size;
    uint16_t filename_length;
    uint16_t extra_length;
};

int readfile(FILE* f)
{
    LocalFileHeader lfh= {0};
    while(true)
    {
        if(!fread(&lfh,sizeof(LocalFileHeader),1,f)) return 1;
        if(lfh.signature!=0x04034b50) return 0;
        string filename(lfh.filename_length,0);
        if(!fread(filename.data(),1,lfh.filename_length,f)) return 1;
        cout<<filename<<" "<<lfh.compression<<" "<<lfh.compressed_size
            <<" "<<lfh.uncompressed_size<<endl;
        if(lfh.extra_length>0) fseek(f,lfh.extra_length,SEEK_CUR);
        if(filename=="test.txt"s)
        {
            cout<<endl<<"---------------test.txt------------------"<<endl;
            vector<unsigned char> srcbuf(lfh.compressed_size,0);
            vector<unsigned char> dstbuf(lfh.uncompressed_size+1,0);
            unsigned long dstlen= lfh.uncompressed_size;
            if(!fread(srcbuf.data(),1,lfh.compressed_size,f)) return 1;
            int res= uncompress(dstbuf.data(),&dstlen,srcbuf.data(), lfh.compressed_size);
            if(res !=Z_OK)
            {
                if(res==Z_DATA_ERROR) cout<<"Z_DATA_ERROR"<<endl;
                if(res==Z_BUF_ERROR) cout<<"Z_BUF_ERROR"<<endl;
                if(res==Z_MEM_ERROR) cout<<"Z_MEM_ERROR"<<endl;
                return 1;
            }
            cout<<dstbuf.data();
            cout<<endl<<"--------------------------------------"<<endl;
        }
        else if(fseek(f,lfh.compressed_size,SEEK_CUR)) return 1;
    }
    return 0;
}

int main()
{
    FILE* f= fopen("test.zip","rb");
    if(!f)
    {
        cout << "Error opening file" << endl;
        return 1;
    }
    if(readfile(f)) cout<<"Bad file"<<endl;
    fclose(f);
    return 0;
}

My program successfully outputs the contents of a zip file, but when it tries to unpack test.txt, I get Z_DATA_ERROR in the uncompress function. Obviously, I missed something, but I do not understand what exactly.


Solution

  • uncompress() is expecting a zlib stream, but you are giving it the raw deflate data of a zip entry. You need to use zlib's inflateInit2(), inflate(), and inflateEnd() functions to decompress raw deflate data.