//////////////////////////////////////////
//                                      //
// Using zlib                           //
//                                      //
// You found this at bobobobo's weblog, //
// http://bobobobo.wordpress.com        //
//                                      //
// Creation date:  Feb 22/08            //
// Last modified:  Feb 23/08            //
//                                      //
//////////////////////////////////////////

//////////////////////////////////////////
//////////////////////////////////////////
///////////  BEFORE YOU START  ///////////
//////////////////////////////////////////
//////////////////////////////////////////

// Visit zlib.net, bookmark it,
// and grab the dll version of zlib.

// Once you get zlib, as the docs say:
// "Copy ZLIB1.DLL to the SYSTEM or the
//  SYSTEM32 directory."

// C:\WINDOWS\System32 may not work for you
// if you're on 64-bit.  Instead, use
// the C:\WINDOWS\System directory.

// If you don't do this, you get this error
// when you try to launch any program
// that uses "zlib1.dll":

/*
     "THIS APPLICATION has failed to start
      because zlib1.dll was not found.
      Re-installing the application
      may fix this problem."
*/

// This is because Windows can't find
// the dll.

// The other option is to place the
// .dll file in the same directory
// as the EXECUTABLE that uses the
// dll.  So, you'd copy out zlib1.dll
// into the Using_zlib\debug folder,
// since that's where visual studio
// dumps the final executable after building.

/////////////////////
// QUICK NOTES ABOUT .LIB AND .DLL FILES:
//
// .LIB FILES:
//
// .lib files are library files and they
// are used for static linking.  That is,
// they contain precompiled code that other
// applications can link to.  When you link
// your application with a .lib library, then
// the .lib library's code is intermixed
// with your own code in the final executable.

// .DLL FILES:
//
// .dll files are dynamically linked library files
// and applications link to them at runtime.

/////////////
// WHAT'S A DLL?

// What's a DLL anyway?

// DLL stands for DYNAMICALLY
// LINKED LIBARY.

// Why "LIBRARY"?  Well, clearly because
// the .DLL is just a LIBRARY of
// FUNCTIONS that you can CALL.

// ZLIB is really just a bunch of
// subroutines that you use to zip
// and unzip data.

// "DYNAMICALLY LINKED":  Because
// the actual code of ZLIB (that performs
// the zipping and unzipping operations),
// is going to REMAIN STORED inside of
// zlib.dll.

// Even though your program that you write
// will make calls to zlib's functions,
// THERE WILL BE NO _ACTUAL_ ZLIB CODE
// IN __YOUR__ FINAL EXECUTABLE.

// In other words, the actual ZIP /UNZIP
// code routines WILL NOT be packed into
// YOUR final executable that you build
// with your code when you use zlib.dll.

// Instead, your code will contain REFERENCES TO
// the actual executable code pieces inside
// of zlib.dll, and WINDOWS will resolve
// ("LINK") your application's calls with
// the .dll file's subroutines DYNAMICALLY
// (AT RUN TIME).

// Hence the term "dynamically linked library".
// Because the LINKING of the code happens
// DYNAMICALLY ("dynamically" just means
// "at run time", just like "dynamically allocated
// memory" is memory that you allocate at
// run time using malloc()).

// So at the end of the day, a .dll file
// just contains a bunch of CODE.

// The difference comes in in HOW the
// Windows operating system manages
// that code.

///////////////////////////
// WHAT'S THIS FILE?
//
// This project shows you how to use zlib.
// Its a console project, because there's
// no need to create a window to demonstrate
// use of zlib.

// "words.txt" was generated from the 5 letter
// words listed @ http://www.math.toronto.edu/~jjchew/scrabble/lists/common-5.html

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <windows.h>

// the zlib.h header file contains
// function prototypes and a lot
// of information about zlib!
#include "zlibdll/include/zlib.h"

// link the static lib
#pragma comment (lib, "zlibdll/lib/zdll.lib" )

int main()
{
    srand(time(0));      // seed random number generator

    bool showOutput;     // make false if you don't want text output
    printf( "Show textual output?  [y/n]\n");
    if ( tolower(getchar()) == 'y' )
        showOutput = true;
    else
        showOutput = false;

    printf("*********************\n");
    printf("* zlib test file\n");
    printf("* Using version %s of zlib\n", zlibVersion() ) ;

    #pragma region _make some data to compress_
    const int NUM_WORDS = 8938;     // pre-counted constant

    char * randomWords[ NUM_WORDS ] = {
#include "words.txt"  // get in project files
    };

    const int NUM_WORDS_TO_USE = 80;

    // Fill dataOriginal with a random jumbling of
    // 80 * (5 letter words + 1 space)
    // (each word followed by a space).
    int sizeDataOriginal = 6 * NUM_WORDS_TO_USE + 1 ;
    printf("* Before compression:  your data is %d bytes\n", sizeDataOriginal );
    BYTE * dataOriginal = (BYTE*)malloc( sizeDataOriginal );

    for( int i = 0; i < 6 * NUM_WORDS_TO_USE; i += 6 )
    {
        int randomIndex = rand() % NUM_WORDS ;
        static char buf[6];
        strncpy( buf, randomWords[ randomIndex ], 5 );  // don't copy the NULL character.
        buf[5] = ' ';
        strncpy( (char*)(dataOriginal+i), buf, 6 );
    }

    dataOriginal[ sizeDataOriginal - 1 ] = NULL; // null terminator.
    // test it.
    printf("* \n* Here's the data we generated for you:\n\n--\n");
    if( showOutput )
    {
        for( int i = 0 ; i < sizeDataOriginal ; i++ )
        {
            putchar( dataOriginal[i] ); // coulda puts'd this
            // but wanted to be consistent.
        }
        printf("\n--\n\n");
    }
    else
        printf(" (suppressed)\n--\n\n");
    #pragma endregion

    #pragma region compress the data
    //////////////
    // compress it.
    // To compress some data, we'll use the compress()
    // function.

    // To use the compress function, we must
    // create a destination buffer to
    // hold the compressed data.

    // So how big should the compressed
    // data buffer be?

    // This may seem a bit weird at first,
    // but the array that is to hold the compressed
    // data must start out being AT LEAST 0.1% larger than
    // the original size of the data, + 12 extra bytes.

    // So, we'll just play it safe and alloated 1.1x
    // as much memory + 12 bytes (110% original + 12 bytes)

    ULONG sizeDataCompressed  = (sizeDataOriginal * 1.1) + 12;

    BYTE * dataCompressed = (BYTE*)malloc( sizeDataCompressed );

    // Now hold on, you ask.  WHY is the array
    // that's supposed to hold the COMPRESSED
    // data ALREADY BIGGER than the original
    // data array?  This isn't compression!
    // This is meaningless expansion!

    // Well, you'll see that this extra space
    // in the compressed array is only TEMPORARY.
    // Just suffice it to say that zlib
    // "needs room to breathe".

    // When zlib performs compression, it will
    // need a bit of extra room to do its work.

    // When the compress() routine returns,
    // the compressedData array will have
    // been AUTOMATICALLY RESIZED by ZLIB
    // to being a smaller, compressed size.

    // We will also know the EXACT size of
    // that compressed data by looking at
    // the 'sizeDataCompressed' variable
    // AFTER the compress() routine runs.
    // That variable 'sizeDataCompressed'
    // will updated by the compress()
    // function when we call it!

    // Don't worry, the "compressed" data
    // will be smaller than the original
    // data was in the end!
    int z_result = compress(

        dataCompressed,         // destination buffer,
                                // must be at least
                                // (1.01X + 12) bytes as large
                                // as source.. we made it 1.1X + 12bytes

        &sizeDataCompressed,    // pointer to var containing
                                // the current size of the
                                // destination buffer.
                                // WHEN this function completes,
                                // this var will be updated to
                                // contain the NEW size of the
                                // compressed data in bytes.

        dataOriginal,           // source data for compression

        sizeDataOriginal ) ;    // size of source data in bytes

    switch( z_result )
    {
    case Z_OK:
        printf("***** SUCCESS! *****\n");
        break;

    case Z_MEM_ERROR:
        printf("out of memory\n");
        exit(1);    // quit.
        break;

    case Z_BUF_ERROR:
        printf("output buffer wasn't large enough!\n");
        exit(1);    // quit.
        break;
    }

    printf("*******************************\n");
    printf("* DATA COMPRESSION COMPLETE!! :\n");
    printf("*\n");
    printf("* Compressed size is %d bytes\n", sizeDataCompressed );
    printf("* This is what it looks like:\n\n--\n");

    // Now we want to print the compressed data out.
    // Can't just printf() it because
    // the nulls will be all over the place, and there
    // isn't necessarily a null at the end.
    if( showOutput )
    {
        for( int i = 0; i < sizeDataCompressed; i++ )
        {
            putchar( dataCompressed[i] );
        }
        printf("\n--\n\n");
    }
    else
        printf(" (suppressed)\n--\n\n");
    #pragma endregion

    #pragma region save compressed data to disk
    //////////////////
    // Save that compressed data to disk.
    // Maybe its save game information or
    // something.
    printf("********************************\n");
    printf("* Saving compressed data to disk\n\n");

    ///////////////////////////////////////////////
    //////////////!!!!!!!!!!!!!!!!!!!!/////////////
    //////////////!!!IMPORTANT NOTE!!!/////////////
    // You see how I'm opening the file in "wb" mode,
    // NOT just "w" mode?????
    FILE * out = fopen( "savedData.dat", "wb" );
    // Well, "wb" means BINARY MODE for writing.
    // THIS IS EXTREMELY IMPORTANT.  IF YOU DON'T
    // USE "wb" TO WRITE YOUR FILES, AND YOU DON'T
    // USE "rb" TO READ YOUR FILES, YOU FILE
    // WILL BE VERY SLIGHTLY CORRUPTED,
    // WINDOWS PROMISES YOU THAT!!

    // One of the reasons for the corruption is
    // under Windows, if you write out a
    // NEWLINE (ASCII code 10, escape sequence "\n",
    // aka the "LINEFEED" (LF)) to the output stream,
    // then Windows puts out a 13 FIRST, then a 10.

    // 13 is the ascii code for CARRIAGE RETURN (CR),
    // and 10 is ascii code for the Linefeed (LF).

    // Now that seems awfully stupid.  Why would Windows
    // output hex 0D 0A when I clearly asked it to
    // output just 0A?

    // The answer is that Windows uses TWO CHARACTERS
    // to designate the new line:  (CRLF), NOT just LF.

    // Unix uses just LF.  Macs use just CR.  (Windows is
    // the only weirdo that uses 2 characters for a newline.
    // I suppose you could say that this is more true to the
    // original typewriter (or the dot matrix printer),
    // where you have to push the "carriage" (thing that types)
    // back to the left side (carriage return), then
    // you have to feed the paper up a line (linefeed)).

    // SO, this is significant because everytime you write
    // the integer value 10 to the output stream, IF you're
    // NOT writing in binary mode, Windows thinks you're
    // trying to write text.  So NATURALLY, if you write
    // the integer value 10 out in one of the bytes,
    // Windows recognizes this is a linefeed and
    // write out 13 10 instead.

    // This introduces extra data into your output
    // and is enough to significantly corrupt the result.

    if( out == NULL )
    {
        printf("Couldn't open output file for writing\n");
        exit(1);    //quit
    }
    fwrite( dataCompressed, sizeDataCompressed, 1, out );
    fclose( out );
    out = NULL;
    #pragma endregion

    #pragma region read in data from disk
    ///////////////
    // Next, we'll READ the compressed data
    // from the file, then DECOMPRESS it, to
    // prove that it'll be the same as
    // the original data.
    printf("********************************\n");
    printf("* Reading in data from save file\n");

    //////////////!!!!!!!!!!IMPORTANT:  note "rb"
    // NOT just "r".
    FILE * readFile = fopen("savedData.dat", "rb");
    if( readFile == NULL )
    {
        printf("Couldn't open input file for reading\n");
        exit(1);    //quit
    }

    // get size of file
    fseek( readFile, 0, SEEK_END );
    ULONG fileLength = ftell( readFile );
    rewind( readFile );

    // allocate enough mems to hold entire file
    // alternatively, we could "memory map" the
    // file contents using the CreateFileMapping and
    // MapViewOfFile funcs.
    BYTE * dataReadInCompressed = (BYTE*)malloc( fileLength );

    // read in entire file
    fread( dataReadInCompressed, fileLength, 1, readFile );

    // close file
    fclose( readFile );
    readFile = NULL;

    printf("*\n* This is what I read from the saved file:\n");
    printf("\n--\n");
    if( showOutput )
    {
        for( int i = 0 ; i < fileLength ; i++ )
        {
            putchar( dataReadInCompressed[i] );
        }
        printf("\n--\n\n");
    }
    else
        printf(" (suppressed)\n--\n\n");
    #pragma endregion

    #pragma region decompress the read-in data
    ///////////////
    // Next, we'll decompress that
    // data we just read in from disk.

    // How large should we make the array
    // into which the UNZIPPED/UNCOMPRESSED
    // data will go?

    // WELL, there's the catch with ZLIB.
    // You never know how big compressed data
    // will blow out to be.  It can blow up
    // to being anywhere from 2 times as big,
    // or it can be (exactly the same size),
    // or it can be up to 10 times as big
    // or even bigger!

    // So, you can tell its a really bad idea
    // to try to GUESS the proper size that the
    // uncompressed data will end up being.

    // You're SUPPOSED TO HAVE SAVED THE INFORMATION
    // about the original size of the data at
    // the time you compress it.

    // There's a note on how to do that easily
    // at the bottom of this file, in the end notes.

    // FOR NOW, we're just going to
    // use the dataSizeOriginal variable.
    printf("*******************************\n");
    printf("* Decompressing your data . . .\n");
    ULONG sizeDataUncompressed = sizeDataOriginal;
    BYTE * dataUncompressed = (BYTE*)malloc( sizeDataUncompressed );

    //////////////
    // now uncompress
    z_result = uncompress(

        dataUncompressed,       // destination for the uncompressed
                                // data.  This should be the size of
                                // the original data, which you should
                                // already know.

        &sizeDataUncompressed,  // length of destination (uncompressed)
                                // buffer

        dataReadInCompressed,   // source buffer - the compressed data

        sizeDataCompressed );   // length of compressed data in bytes

    switch( z_result )
    {
    case Z_OK:
        printf("***** SUCCESS! *****\n");
        break;

    case Z_MEM_ERROR:
        printf("out of memory\n");
        exit(1);    // quit.
        break;

    case Z_BUF_ERROR:
        printf("output buffer wasn't large enough!\n");
        exit(1);    // quit.
        break;
    }

    printf("************************\n");
    printf("* Uncompressed size is %d bytes\n", sizeDataUncompressed );
    printf("* Your UNCOMPRESSED data looks like this:\n");

    printf("\n--\n");
    if( showOutput )
    {
        for( int i = 0 ; i < sizeDataUncompressed ; i++ )
        {
            putchar( dataUncompressed[i] );
        }
        printf("\n--\n\n");
    }
    else
        printf(" (suppressed)\n--\n\n");
    #pragma endregion

    #pragma region compare decompressed data with original data
    if( memcmp( dataOriginal, dataUncompressed, sizeDataOriginal ) == 0 )
    {
        printf("* SEE?  It was EXACTLY the same.\n");
    }
    else
    {
        printf( "\n\n=====================================\n"
                "Oh. . . dear.  There is a problem.  The uncompressed data "
                "isn't exactly the same as the original data.  Your data "
                "may be corrupted.  WHOOPS!!\n"
                "Please make sure if that you are reading and writing "
                "any file i/o in BINARY MODE." );
    }
    #pragma endregion

    free( dataOriginal );
    free( dataCompressed );
    free( dataReadInCompressed );
    free( dataUncompressed );

}

/////////
// END NOTES:

// So in this tutorial, when I saved the
// data out to a file, I just used fwrite()
// and dumped the array of bytes with no
// information ABOUT what the file is
// whatsoever.

// in real life, that's probably not a good idea.
// You want to always output at the beginning
// of your compressed data AT LEAST the size
// the data will be when it becomes uncompressed.
// This is the only way you can know how big
// to make the "receiving array" for the uncompressed data.

// ZLIB has no "tell_me_the_size_of_this_thing_
// when_it_gets_uncompressed() function.

// You MUST keep that data in the file itself.

// So, you might create a structure like thus
// and like so:

/*

struct saveFile
{
    ULONG compressedSize;   // size of the data array, compressed
    ULONG uncompressedSize; // size when data gets uncompressed
    char what[16];          // what is this file?  may want to
                            // indicate what kind of data is saved here.

    // add whatever other info you need
    // here.

    BYTE * data;            // the actual compressed data

};

*/

// Then, just fwrite out the data,
// with a couple of fwrites().

// When you fread it, you fread the
// size fields first . . . you know
// what to do.

/*
     ____   __   __      __   __  ___
    / _  \ /  / /  /    /  /  \ \/  /
   / _/ / /  / /  /    /  /    \   /
  / _/ \ /  / /  /__  /  /__   /  /
 /_____//__/ /______//______/ /__/

*/

Download the Visual Studio 2005 project files.

4 Comments

    • sens-
    • Posted March 12, 2008 at 9:40 am
    • Permalink

    this tutorial just saved my day, as my compress functions in c++ always cause buffer errs

    big thanks for your effort
    appreciated!

    • sens-
    • Posted March 12, 2008 at 12:38 pm
    • Permalink

    ok…since u cant append files to archives using zlib my day isnt saved at all ;)

    • Jojo Girl
    • Posted September 4, 2008 at 2:13 pm
    • Permalink

    can you please write a program in c++ to take a folder with many files [c:\testFolder.zip] and extract it to c:\testFolderUnzipped].

    PLease… if you can write and mail me on ninja_power86@yahoo.com or posting on your blog… i will be heartily thankful. I am digging minizip project right now..

    plz guide.

    • goun
    • Posted September 10, 2009 at 2:20 pm
    • Permalink

    Man, you are simply great!

    I am trying two days now to hanlde this zlib until I discovered your forum.

    The Force may be with you…


One Trackback/Pingback

  1. By How to use libpng « Bobobobo’s Weblog on 02 Mar 2009 at 3:01 am

    [...] already had zlib, so if you don’t have it, install it as zlib is required for libpng to [...]

Post a Comment