/* Zip File Writing Prototype Code haleyjd 08/28/09 */ #include #include #include typedef unsigned char byte; typedef enum { false, true } boolean; // // zipfile - a single file to be added to the zip // typedef struct zipfile_s { struct zipfile_s *next; // next file const char *name; // file name const byte *data; // file data unsigned int len; // length of data long offset; // offset in physical file after written unsigned int crc; // cached CRC value unsigned int extattr; // external attributes unsigned short intattr; // internal attributes } zipfile_t; // // ziparchive - represents the entire zip file // typedef struct ziparchive_s { zipfile_t *files; // list of files zipfile_t *last; // last file const char *filename; // physical file name long diroffset; // offset of central directory unsigned short fcount; // count of files unsigned int dirlen; // length of central directory } ziparchive_t; // // File output buffer // typedef struct outbuffer_s { FILE *f; // destination file byte *buffer; // buffer unsigned int len; // total buffer length unsigned int idx; // current index } outbuffer_t; //============================================================================= // // Buffer Functions // void Buffer_CreateFile(outbuffer_t *ob, const char *filename, unsigned int len) { ob->f = fopen(filename, "wb"); ob->buffer = calloc(len, sizeof(byte)); ob->len = len; ob->idx = 0; } void Buffer_Flush(outbuffer_t *ob) { if(ob->idx) { fwrite(ob->buffer, ob->idx, sizeof(byte), ob->f); ob->idx = 0; } } void Buffer_Close(outbuffer_t *ob) { Buffer_Flush(ob); fclose(ob->f); free(ob->buffer); } long Buffer_Tell(outbuffer_t *ob) { return ftell(ob->f); } void Buffer_Write(outbuffer_t *ob, const void *data, unsigned int size) { const byte *src = data; unsigned int writeAmt; unsigned int bytesToWrite = size; while(bytesToWrite) { writeAmt = ob->len - ob->idx; if(!writeAmt) { Buffer_Flush(ob); writeAmt = ob->len; } if(bytesToWrite < writeAmt) writeAmt = bytesToWrite; memcpy(&(ob->buffer[ob->idx]), src, writeAmt); ob->idx += writeAmt; src += writeAmt; bytesToWrite -= writeAmt; } } void Buffer_WriteUint32(outbuffer_t *ob, unsigned int num) { Buffer_Write(ob, &num, sizeof(unsigned int)); } void Buffer_WriteUint16(outbuffer_t *ob, unsigned short num) { Buffer_Write(ob, &num, sizeof(unsigned short)); } //============================================================================= // // Zip Functions // // // Zip_Create // void Zip_Create(ziparchive_t *zip, const char *filename) { memset(zip, 0, sizeof(ziparchive_t)); zip->files = zip->last = NULL; zip->filename = filename; } // // Zip_AddFile // zipfile_t *Zip_AddFile(ziparchive_t *zip, const char *name, const byte *data, unsigned int len) { zipfile_t *file = calloc(1, sizeof(zipfile_t)); file->name = name; file->data = data; file->len = len; if(zip->last) { zip->last->next = file; zip->last = file; } else zip->files = zip->last = file; zip->fcount++; return file; } //============================================================================= // // CRC routine // #define CRC32_IEEE_POLY 0xEDB88320 static unsigned int crc32_table[256]; // // M_CRC32BuildTable // // Builds the polynomial table for CRC32. // static void CRC32BuildTable(void) { unsigned int i, j; for(i = 0; i < 256; ++i) { unsigned int val = i; for(j = 0; j < 8; ++j) { if(val & 1) val = (val >> 1) ^ CRC32_IEEE_POLY; else val >>= 1; } crc32_table[i] = val; } } // // M_CRC32Initialize // // Builds the table if it hasn't been built yet. Doesn't do anything // else, since CRC32 starting value has already been set properly by // the main hash init routine. // static void CRC32Initialize(void) { static boolean tablebuilt = false; // build the CRC32 table if it hasn't been built yet if(!tablebuilt) { CRC32BuildTable(); tablebuilt = true; } // zero is the appropriate starting value for CRC32, so we need do nothing // special here } // // M_CRC32HashData // // Calculates a running CRC32 for the provided block of data. // static unsigned int CRC32HashData(byte *data, unsigned int len) { unsigned int crc = 0xFFFFFFFF; while(len) { byte idx = (byte)(((int)crc ^ *data++) & 0xff); crc = crc32_table[idx] ^ (crc >> 8); --len; } return crc ^ 0xFFFFFFFF; } // // End CRC routine // //============================================================================= // // Zip_WriteFile // void Zip_WriteFile(zipfile_t *file, outbuffer_t *ob) { unsigned short date, time; unsigned short namelen; Buffer_Flush(ob); file->offset = Buffer_Tell(ob); Buffer_WriteUint32(ob, 0x04034b50); // local file header signature Buffer_WriteUint16(ob, 0x0A); // version needed to extract (1.0) Buffer_WriteUint16(ob, 0); // general purpose bit flag Buffer_WriteUint16(ob, 0); // compression method == store // make up a date/time time = (35 << 5) | (3 << 11); date = 3 | (5 << 5) | (113 << 9); Buffer_WriteUint16(ob, time); // file time (3:35) Buffer_WriteUint16(ob, date); // file date (3/5/2093) if(file->len) { CRC32Initialize(); file->crc = CRC32HashData(file->data, file->len); } else file->crc = 0; Buffer_WriteUint32(ob, file->crc); // CRC-32 Buffer_WriteUint32(ob, file->len); // compressed size Buffer_WriteUint32(ob, file->len); // uncompressed size namelen = (unsigned short)strlen(file->name); Buffer_WriteUint16(ob, namelen); // filename length Buffer_WriteUint16(ob, 0); // extra field length // write file name Buffer_Write(ob, file->name, namelen); // write file contents if(file->len) Buffer_Write(ob, file->data, file->len); } // // Zip_WriteDirEntry // void Zip_WriteDirEntry(ziparchive_t *zip, zipfile_t *file, outbuffer_t *ob) { unsigned short time, date; unsigned short namelen; Buffer_WriteUint32(ob, 0x02014b50); // central file header signature Buffer_WriteUint16(ob, 0x0B14); // version made by (Info-Zip NTFS) Buffer_WriteUint16(ob, 0x0A); // version needed to extract (1.0) Buffer_WriteUint16(ob, 0); // general purpose bit flag Buffer_WriteUint16(ob, 0); // compression method == store // make up a date/time time = (35 << 5) | (3 << 11); date = 3 | (5 << 5) | (113 << 9); Buffer_WriteUint16(ob, time); // file time (3:35) Buffer_WriteUint16(ob, date); // file date (3/5/2093) Buffer_WriteUint32(ob, file->crc); // CRC-32 (already calculated) Buffer_WriteUint32(ob, file->len); // compressed size Buffer_WriteUint32(ob, file->len); // uncompressed size namelen = (unsigned short)strlen(file->name); Buffer_WriteUint16(ob, namelen); // filename length Buffer_WriteUint16(ob, 0); // extra field length Buffer_WriteUint16(ob, 0); // file comment length Buffer_WriteUint16(ob, 0); // disk number start Buffer_WriteUint16(ob, file->intattr); // internal attributes Buffer_WriteUint32(ob, file->extattr); // external attributes Buffer_WriteUint32(ob, file->offset); // local header offset // write the file name Buffer_Write(ob, file->name, namelen); zip->dirlen += (46 + namelen); } // // Zip_WriteEndOfDir // void Zip_WriteEndOfDir(ziparchive_t *zip, outbuffer_t *ob) { Buffer_WriteUint32(ob, 0x06054b50); // end of central dir signature Buffer_WriteUint16(ob, 0); // number of disk Buffer_WriteUint16(ob, 0); // no. of disk with central dir Buffer_WriteUint16(ob, zip->fcount); // no. of central dir entries on disk Buffer_WriteUint16(ob, zip->fcount); // no. of central dir entries total Buffer_WriteUint32(ob, zip->dirlen); // size of central directory Buffer_WriteUint32(ob, zip->diroffset); // offset of central dir Buffer_WriteUint16(ob, 0); // length of zip comment } // // Zip_Write // void Zip_Write(ziparchive_t *zip) { outbuffer_t ob; zipfile_t *curfile; Buffer_CreateFile(&ob, zip->filename, 16384); // write files curfile = zip->files; while(curfile) { Zip_WriteFile(curfile, &ob); curfile = curfile->next; } Buffer_Flush(&ob); zip->diroffset = Buffer_Tell(&ob); // write central directory curfile = zip->files; while(curfile) { Zip_WriteDirEntry(zip, curfile, &ob); curfile = curfile->next; } // write end of central directory Zip_WriteEndOfDir(zip, &ob); // close the file Buffer_Close(&ob); } // // main // int main(void) { ziparchive_t zip; zipfile_t *foo; zipfile_t *bar_folder; zipfile_t *baz; const char *foodata = "Hello Zip World!\n"; unsigned int bazdata = 0xDEADBEEF; Zip_Create(&zip, "test.zip"); // add some files foo = Zip_AddFile(&zip, "foo.txt", foodata, strlen(foodata)); bar_folder = Zip_AddFile(&zip, "bar/", NULL, 0); baz = Zip_AddFile(&zip, "bar/baz", (byte *)&bazdata, sizeof(unsigned int)); // set attributes foo->intattr = 0x01; // is a text file foo->extattr = 0x20; // set archive flag bar_folder->extattr = 0x10; // set directory flag baz->extattr = 0x20; // set archive flag Zip_Write(&zip); return 0; } // EOF