/* * File Compression Interface * * Copyright 2002 Patrik Stridvall * Copyright 2005 Gerold Jens Wucherpfennig * Copyright 2011 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ /* There is still some work to be done: - unknown behaviour if files>=2GB or cabinet >=4GB - check if the maximum size for a cabinet is too small to store any data - call pfnfcignc on exactly the same position as MS FCIAddFile in every case */ #include "config.h" #include <assert.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #ifdef HAVE_ZLIB # include <zlib.h> #endif #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winternl.h" #include "fci.h" #include "cabinet.h" #include "wine/list.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(cabinet); #ifdef WORDS_BIGENDIAN #define fci_endian_ulong(x) RtlUlongByteSwap(x) #define fci_endian_uword(x) RtlUshortByteSwap(x) #else #define fci_endian_ulong(x) (x) #define fci_endian_uword(x) (x) #endif typedef struct { cab_UBYTE signature[4]; /* !CAB for unfinished cabinets else MSCF */ cab_ULONG reserved1; cab_ULONG cbCabinet; /* size of the cabinet file in bytes*/ cab_ULONG reserved2; cab_ULONG coffFiles; /* offset to first CFFILE section */ cab_ULONG reserved3; cab_UBYTE versionMinor; /* 3 */ cab_UBYTE versionMajor; /* 1 */ cab_UWORD cFolders; /* number of CFFOLDER entries in the cabinet*/ cab_UWORD cFiles; /* number of CFFILE entries in the cabinet*/ cab_UWORD flags; /* 1=prev cab, 2=next cabinet, 4=reserved sections*/ cab_UWORD setID; /* identification number of all cabinets in a set*/ cab_UWORD iCabinet; /* number of the cabinet in a set */ /* additional area if "flags" were set*/ } CFHEADER; /* minimum 36 bytes */ typedef struct { cab_ULONG coffCabStart; /* offset to the folder's first CFDATA section */ cab_UWORD cCFData; /* number of this folder's CFDATA sections */ cab_UWORD typeCompress; /* compression type of data in CFDATA section*/ /* additional area if reserve flag was set */ } CFFOLDER; /* minimum 8 bytes */ typedef struct { cab_ULONG cbFile; /* size of the uncompressed file in bytes */ cab_ULONG uoffFolderStart; /* offset of the uncompressed file in the folder */ cab_UWORD iFolder; /* number of folder in the cabinet 0=first */ /* for special values see below this structure*/ cab_UWORD date; /* last modification date*/ cab_UWORD time; /* last modification time*/ cab_UWORD attribs; /* DOS fat attributes and UTF indicator */ /* ... and a C string with the name of the file */ } CFFILE; /* 16 bytes + name of file */ typedef struct { cab_ULONG csum; /* checksum of this entry*/ cab_UWORD cbData; /* number of compressed bytes */ cab_UWORD cbUncomp; /* number of bytes when data is uncompressed */ /* optional reserved area */ /* compressed data */ } CFDATA; struct temp_file { INT_PTR handle; char name[CB_MAX_FILENAME]; }; struct folder { struct list entry; struct list files_list; struct list blocks_list; struct temp_file data; cab_ULONG data_start; cab_UWORD data_count; TCOMP compression; }; struct file { struct list entry; cab_ULONG size; /* uncompressed size */ cab_ULONG offset; /* offset in folder */ cab_UWORD folder; /* index of folder */ cab_UWORD date; cab_UWORD time; cab_UWORD attribs; char name[1]; }; struct data_block { struct list entry; cab_UWORD compressed; cab_UWORD uncompressed; }; typedef struct FCI_Int { unsigned int magic; PERF perf; PFNFCIFILEPLACED fileplaced; PFNFCIALLOC alloc; PFNFCIFREE free; PFNFCIOPEN open; PFNFCIREAD read; PFNFCIWRITE write; PFNFCICLOSE close; PFNFCISEEK seek; PFNFCIDELETE delete; PFNFCIGETTEMPFILE gettemp; CCAB ccab; PCCAB pccab; BOOL fPrevCab; BOOL fNextCab; BOOL fSplitFolder; cab_ULONG statusFolderCopied; cab_ULONG statusFolderTotal; BOOL fGetNextCabInVain; void *pv; char szPrevCab[CB_MAX_CABINET_NAME]; /* previous cabinet name */ char szPrevDisk[CB_MAX_DISK_NAME]; /* disk name of previous cabinet */ unsigned char data_in[CAB_BLOCKMAX]; /* uncompressed data blocks */ unsigned char data_out[2 * CAB_BLOCKMAX]; /* compressed data blocks */ cab_UWORD cdata_in; ULONG cCompressedBytesInFolder; cab_UWORD cFolders; cab_UWORD cFiles; cab_ULONG cDataBlocks; cab_ULONG cbFileRemainer; /* uncompressed, yet to be written data */ /* of spanned file of a spanning folder of a spanning cabinet */ struct temp_file data; BOOL fNewPrevious; cab_ULONG estimatedCabinetSize; struct list folders_list; struct list files_list; struct list blocks_list; cab_ULONG folders_size; cab_ULONG files_size; /* size of files not yet assigned to a folder */ cab_ULONG placed_files_size; /* size of files already placed into a folder */ cab_ULONG pending_data_size; /* size of data not yet assigned to a folder */ cab_ULONG folders_data_size; /* total size of data contained in the current folders */ TCOMP compression; cab_UWORD (*compress)(struct FCI_Int *); } FCI_Int; #define FCI_INT_MAGIC 0xfcfcfc05 static void set_error( FCI_Int *fci, int oper, int err ) { fci->perf->erfOper = oper; fci->perf->erfType = err; fci->perf->fError = TRUE; if (err) SetLastError( err ); } static FCI_Int *get_fci_ptr( HFCI hfci ) { FCI_Int *fci= (FCI_Int *)hfci; if (!fci || fci->magic != FCI_INT_MAGIC) { SetLastError( ERROR_INVALID_HANDLE ); return NULL; } return fci; } /* compute the cabinet header size */ static cab_ULONG get_header_size( FCI_Int *fci ) { cab_ULONG ret = sizeof(CFHEADER) + fci->ccab.cbReserveCFHeader; if (fci->ccab.cbReserveCFHeader || fci->ccab.cbReserveCFFolder || fci->ccab.cbReserveCFData) ret += 4; if (fci->fPrevCab) ret += strlen( fci->szPrevCab ) + 1 + strlen( fci->szPrevDisk ) + 1; if (fci->fNextCab) ret += strlen( fci->pccab->szCab ) + 1 + strlen( fci->pccab->szDisk ) + 1; return ret; } static BOOL create_temp_file( FCI_Int *fci, struct temp_file *file ) { int err; if (!fci->gettemp( file->name, CB_MAX_FILENAME, fci->pv )) { set_error( fci, FCIERR_TEMP_FILE, ERROR_FUNCTION_FAILED ); return FALSE; } if ((file->handle = fci->open( file->name, _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY, _S_IREAD | _S_IWRITE, &err, fci->pv )) == -1) { set_error( fci, FCIERR_TEMP_FILE, err ); return FALSE; } return TRUE; } static BOOL close_temp_file( FCI_Int *fci, struct temp_file *file ) { int err; if (file->handle == -1) return TRUE; if (fci->close( file->handle, &err, fci->pv ) == -1) { set_error( fci, FCIERR_TEMP_FILE, err ); return FALSE; } file->handle = -1; if (fci->delete( file->name, &err, fci->pv ) == -1) { set_error( fci, FCIERR_TEMP_FILE, err ); return FALSE; } return TRUE; } static struct file *add_file( FCI_Int *fci, const char *filename ) { unsigned int size = FIELD_OFFSET( struct file, name[strlen(filename) + 1] ); struct file *file = fci->alloc( size ); if (!file) { set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY ); return NULL; } file->size = 0; file->offset = fci->cDataBlocks * CAB_BLOCKMAX + fci->cdata_in; file->folder = fci->cFolders; file->date = 0; file->time = 0; file->attribs = 0; strcpy( file->name, filename ); list_add_tail( &fci->files_list, &file->entry ); fci->files_size += sizeof(CFFILE) + strlen(filename) + 1; return file; } static struct file *copy_file( FCI_Int *fci, const struct file *orig ) { unsigned int size = FIELD_OFFSET( struct file, name[strlen(orig->name) + 1] ); struct file *file = fci->alloc( size ); if (!file) { set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY ); return NULL; } memcpy( file, orig, size ); return file; } static void free_file( FCI_Int *fci, struct file *file ) { list_remove( &file->entry ); fci->free( file ); } /* create a new data block for the data in fci->data_in */ static BOOL add_data_block( FCI_Int *fci, PFNFCISTATUS status_callback ) { int err; struct data_block *block; if (!fci->cdata_in) return TRUE; if (fci->data.handle == -1 && !create_temp_file( fci, &fci->data )) return FALSE; if (!(block = fci->alloc( sizeof(*block) ))) { set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY ); return FALSE; } block->uncompressed = fci->cdata_in; block->compressed = fci->compress( fci ); if (fci->write( fci->data.handle, fci->data_out, block->compressed, &err, fci->pv ) != block->compressed) { set_error( fci, FCIERR_TEMP_FILE, err ); fci->free( block ); return FALSE; } fci->cdata_in = 0; fci->pending_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed; fci->cCompressedBytesInFolder += block->compressed; fci->cDataBlocks++; list_add_tail( &fci->blocks_list, &block->entry ); if (status_callback( statusFile, block->compressed, block->uncompressed, fci->pv ) == -1) { set_error( fci, FCIERR_USER_ABORT, 0 ); return FALSE; } return TRUE; } /* add compressed blocks for all the data that can be read from the file */ static BOOL add_file_data( FCI_Int *fci, char *sourcefile, char *filename, BOOL execute, PFNFCIGETOPENINFO get_open_info, PFNFCISTATUS status_callback ) { int err, len; INT_PTR handle; struct file *file; if (!(file = add_file( fci, filename ))) return FALSE; handle = get_open_info( sourcefile, &file->date, &file->time, &file->attribs, &err, fci->pv ); if (handle == -1) { free_file( fci, file ); set_error( fci, FCIERR_OPEN_SRC, err ); return FALSE; } if (execute) file->attribs |= _A_EXEC; for (;;) { len = fci->read( handle, fci->data_in + fci->cdata_in, CAB_BLOCKMAX - fci->cdata_in, &err, fci->pv ); if (!len) break; if (len == -1) { set_error( fci, FCIERR_READ_SRC, err ); return FALSE; } file->size += len; fci->cdata_in += len; if (fci->cdata_in == CAB_BLOCKMAX && !add_data_block( fci, status_callback )) return FALSE; } fci->close( handle, &err, fci->pv ); return TRUE; } static void free_data_block( FCI_Int *fci, struct data_block *block ) { list_remove( &block->entry ); fci->free( block ); } static struct folder *add_folder( FCI_Int *fci ) { struct folder *folder = fci->alloc( sizeof(*folder) ); if (!folder) { set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY ); return NULL; } folder->data.handle = -1; folder->data_start = fci->folders_data_size; folder->data_count = 0; folder->compression = fci->compression; list_init( &folder->files_list ); list_init( &folder->blocks_list ); list_add_tail( &fci->folders_list, &folder->entry ); fci->folders_size += sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder; fci->cFolders++; return folder; } static void free_folder( FCI_Int *fci, struct folder *folder ) { struct file *file, *file_next; struct data_block *block, *block_next; LIST_FOR_EACH_ENTRY_SAFE( file, file_next, &folder->files_list, struct file, entry ) free_file( fci, file ); LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &folder->blocks_list, struct data_block, entry ) free_data_block( fci, block ); close_temp_file( fci, &folder->data ); list_remove( &folder->entry ); fci->free( folder ); } /* reset state for the next cabinet file once the current one has been flushed */ static void reset_cabinet( FCI_Int *fci ) { struct folder *folder, *folder_next; LIST_FOR_EACH_ENTRY_SAFE( folder, folder_next, &fci->folders_list, struct folder, entry ) free_folder( fci, folder ); fci->cFolders = 0; fci->cFiles = 0; fci->folders_size = 0; fci->placed_files_size = 0; fci->folders_data_size = 0; } static cab_ULONG fci_get_checksum( const void *pv, UINT cb, cab_ULONG seed ) { cab_ULONG csum; cab_ULONG ul; int cUlong; const BYTE *pb; csum = seed; cUlong = cb / 4; pb = pv; while (cUlong-- > 0) { ul = *pb++; ul |= (((cab_ULONG)(*pb++)) << 8); ul |= (((cab_ULONG)(*pb++)) << 16); ul |= (((cab_ULONG)(*pb++)) << 24); csum ^= ul; } ul = 0; switch (cb % 4) { case 3: ul |= (((ULONG)(*pb++)) << 16); /* fall through */ case 2: ul |= (((ULONG)(*pb++)) << 8); /* fall through */ case 1: ul |= *pb; /* fall through */ default: break; } csum ^= ul; return csum; } /* copy all remaining data block to a new temp file */ static BOOL copy_data_blocks( FCI_Int *fci, INT_PTR handle, cab_ULONG start_pos, struct temp_file *temp, PFNFCISTATUS status_callback ) { struct data_block *block; int err; if (fci->seek( handle, start_pos, SEEK_SET, &err, fci->pv ) != start_pos) { set_error( fci, FCIERR_TEMP_FILE, err ); return FALSE; } if (!create_temp_file( fci, temp )) return FALSE; LIST_FOR_EACH_ENTRY( block, &fci->blocks_list, struct data_block, entry ) { if (fci->read( handle, fci->data_out, block->compressed, &err, fci->pv ) != block->compressed) { close_temp_file( fci, temp ); set_error( fci, FCIERR_TEMP_FILE, err ); return FALSE; } if (fci->write( temp->handle, fci->data_out, block->compressed, &err, fci->pv ) != block->compressed) { close_temp_file( fci, temp ); set_error( fci, FCIERR_TEMP_FILE, err ); return FALSE; } fci->pending_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed; fci->statusFolderCopied += block->compressed; if (status_callback( statusFolder, fci->statusFolderCopied, fci->statusFolderTotal, fci->pv) == -1) { close_temp_file( fci, temp ); set_error( fci, FCIERR_USER_ABORT, 0 ); return FALSE; } } return TRUE; } /* write all folders to disk and remove them from the list */ static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size, PFNFCISTATUS status_callback ) { struct folder *folder; int err; CFFOLDER *cffolder = (CFFOLDER *)fci->data_out; cab_ULONG folder_size = sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder; memset( cffolder, 0, folder_size ); /* write the folders */ LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry ) { cffolder->coffCabStart = fci_endian_ulong( folder->data_start + header_size ); cffolder->cCFData = fci_endian_uword( folder->data_count ); cffolder->typeCompress = fci_endian_uword( folder->compression ); if (fci->write( handle, cffolder, folder_size, &err, fci->pv ) != folder_size) { set_error( fci, FCIERR_CAB_FILE, err ); return FALSE; } } return TRUE; } /* write all the files to the cabinet file */ static BOOL write_files( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback ) { cab_ULONG file_size; struct folder *folder; struct file *file; int err; CFFILE *cffile = (CFFILE *)fci->data_out; LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry ) { LIST_FOR_EACH_ENTRY( file, &folder->files_list, struct file, entry ) { cffile->cbFile = fci_endian_ulong( file->size ); cffile->uoffFolderStart = fci_endian_ulong( file->offset ); cffile->iFolder = fci_endian_uword( file->folder ); cffile->date = fci_endian_uword( file->date ); cffile->time = fci_endian_uword( file->time ); cffile->attribs = fci_endian_uword( file->attribs ); lstrcpynA( (char *)(cffile + 1), file->name, CB_MAX_FILENAME ); file_size = sizeof(CFFILE) + strlen( (char *)(cffile + 1) ) + 1; if (fci->write( handle, cffile, file_size, &err, fci->pv ) != file_size) { set_error( fci, FCIERR_CAB_FILE, err ); return FALSE; } if (!fci->fSplitFolder) { fci->statusFolderCopied = 0; /* TODO TEST THIS further */ fci->statusFolderTotal = fci->folders_data_size + fci->placed_files_size; } fci->statusFolderCopied += file_size; /* report status about copied size of folder */ if (status_callback( statusFolder, fci->statusFolderCopied, fci->statusFolderTotal, fci->pv ) == -1) { set_error( fci, FCIERR_USER_ABORT, 0 ); return FALSE; } } } return TRUE; } /* write all data blocks to the cabinet file */ static BOOL write_data_blocks( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback ) { struct folder *folder; struct data_block *block; int err, len; CFDATA *cfdata; void *data; cab_UWORD header_size; header_size = sizeof(CFDATA) + fci->ccab.cbReserveCFData; cfdata = (CFDATA *)fci->data_out; memset( cfdata, 0, header_size ); data = (char *)cfdata + header_size; LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry ) { if (fci->seek( folder->data.handle, 0, SEEK_SET, &err, fci->pv ) != 0) { set_error( fci, FCIERR_CAB_FILE, err ); return FALSE; } LIST_FOR_EACH_ENTRY( block, &folder->blocks_list, struct data_block, entry ) { len = fci->read( folder->data.handle, data, block->compressed, &err, fci->pv ); if (len != block->compressed) return FALSE; cfdata->cbData = fci_endian_uword( block->compressed ); cfdata->cbUncomp = fci_endian_uword( block->uncompressed ); cfdata->csum = fci_endian_ulong( fci_get_checksum( &cfdata->cbData, header_size - FIELD_OFFSET(CFDATA, cbData), fci_get_checksum( data, len, 0 ))); fci->statusFolderCopied += len; len += header_size; if (fci->write( handle, fci->data_out, len, &err, fci->pv ) != len) { set_error( fci, FCIERR_CAB_FILE, err ); return FALSE; } if (status_callback( statusFolder, fci->statusFolderCopied, fci->statusFolderTotal, fci->pv) == -1) { set_error( fci, FCIERR_USER_ABORT, 0 ); return FALSE; } } } return TRUE; } /* write the cabinet file to disk */ static BOOL write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback ) { char filename[CB_MAX_CAB_PATH + CB_MAX_CABINET_NAME]; int err; char *ptr; INT_PTR handle; CFHEADER *cfheader = (CFHEADER *)fci->data_out; cab_UWORD flags = 0; cab_ULONG header_size = get_header_size( fci ); cab_ULONG total_size = header_size + fci->folders_size + fci->placed_files_size + fci->folders_data_size; assert( header_size <= sizeof(fci->data_out) ); memset( cfheader, 0, header_size ); if (fci->fPrevCab) flags |= cfheadPREV_CABINET; if (fci->fNextCab) flags |= cfheadNEXT_CABINET; if (fci->ccab.cbReserveCFHeader || fci->ccab.cbReserveCFFolder || fci->ccab.cbReserveCFData) flags |= cfheadRESERVE_PRESENT; memcpy( cfheader->signature, "!CAB", 4 ); cfheader->cbCabinet = fci_endian_ulong( total_size ); cfheader->coffFiles = fci_endian_ulong( header_size + fci->folders_size ); cfheader->versionMinor = 3; cfheader->versionMajor = 1; cfheader->cFolders = fci_endian_uword( fci->cFolders ); cfheader->cFiles = fci_endian_uword( fci->cFiles ); cfheader->flags = fci_endian_uword( flags ); cfheader->setID = fci_endian_uword( fci->ccab.setID ); cfheader->iCabinet = fci_endian_uword( fci->ccab.iCab ); ptr = (char *)(cfheader + 1); if (flags & cfheadRESERVE_PRESENT) { struct { cab_UWORD cbCFHeader; cab_UBYTE cbCFFolder; cab_UBYTE cbCFData; } *reserve = (void *)ptr; reserve->cbCFHeader = fci_endian_uword( fci->ccab.cbReserveCFHeader ); reserve->cbCFFolder = fci->ccab.cbReserveCFFolder; reserve->cbCFData = fci->ccab.cbReserveCFData; ptr = (char *)(reserve + 1); } ptr += fci->ccab.cbReserveCFHeader; if (flags & cfheadPREV_CABINET) { strcpy( ptr, fci->szPrevCab ); ptr += strlen( ptr ) + 1; strcpy( ptr, fci->szPrevDisk ); ptr += strlen( ptr ) + 1; } if (flags & cfheadNEXT_CABINET) { strcpy( ptr, fci->pccab->szCab ); ptr += strlen( ptr ) + 1; strcpy( ptr, fci->pccab->szDisk ); ptr += strlen( ptr ) + 1; } assert( ptr - (char *)cfheader == header_size ); strcpy( filename, fci->ccab.szCabPath ); strcat( filename, fci->ccab.szCab ); if ((handle = fci->open( filename, _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY, _S_IREAD | _S_IWRITE, &err, fci->pv )) == -1) { set_error( fci, FCIERR_CAB_FILE, err ); return FALSE; } if (fci->write( handle, cfheader, header_size, &err, fci->pv ) != header_size) { set_error( fci, FCIERR_CAB_FILE, err ); goto failed; } /* add size of header size of all CFFOLDERs and size of all CFFILEs */ header_size += fci->placed_files_size + fci->folders_size; if (!write_folders( fci, handle, header_size, status_callback )) goto failed; if (!write_files( fci, handle, status_callback )) goto failed; if (!write_data_blocks( fci, handle, status_callback )) goto failed; /* update the signature */ if (fci->seek( handle, 0, SEEK_SET, &err, fci->pv ) != 0 ) { set_error( fci, FCIERR_CAB_FILE, err ); goto failed; } memcpy( cfheader->signature, "MSCF", 4 ); if (fci->write( handle, cfheader->signature, 4, &err, fci->pv ) != 4) { set_error( fci, FCIERR_CAB_FILE, err ); goto failed; } fci->close( handle, &err, fci->pv ); reset_cabinet( fci ); status_callback( statusCabinet, fci->estimatedCabinetSize, total_size, fci->pv ); return TRUE; failed: fci->close( handle, &err, fci->pv ); fci->delete( filename, &err, fci->pv ); return FALSE; } /* add all pending data blocks folder */ static BOOL add_data_to_folder( FCI_Int *fci, struct folder *folder, cab_ULONG *payload, PFNFCISTATUS status_callback ) { struct data_block *block, *new, *next; BOOL split_block = FALSE; cab_ULONG current_size, start_pos = 0; *payload = 0; current_size = get_header_size( fci ) + fci->folders_size + fci->files_size + fci->placed_files_size + fci->folders_data_size; /* move the temp file into the folder structure */ folder->data = fci->data; fci->data.handle = -1; fci->pending_data_size = 0; LIST_FOR_EACH_ENTRY_SAFE( block, next, &fci->blocks_list, struct data_block, entry ) { /* No more CFDATA fits into the cabinet under construction */ /* So don't try to store more data into it */ if (fci->fNextCab && (fci->ccab.cb <= sizeof(CFDATA) + fci->ccab.cbReserveCFData + current_size + sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder)) break; if (!(new = fci->alloc( sizeof(*new) ))) { set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY ); return FALSE; } /* Is cabinet with new CFDATA too large? Then data block has to be split */ if( fci->fNextCab && (fci->ccab.cb < sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed + current_size + sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder)) { /* Modify the size of the compressed data to store only a part of the */ /* data block into the current cabinet. This is done to prevent */ /* that the maximum cabinet size will be exceeded. The remainder */ /* will be stored into the next following cabinet. */ new->compressed = fci->ccab.cb - (sizeof(CFDATA) + fci->ccab.cbReserveCFData + current_size + sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder ); new->uncompressed = 0; /* on split blocks of data this is zero */ block->compressed -= new->compressed; split_block = TRUE; } else { new->compressed = block->compressed; new->uncompressed = block->uncompressed; } start_pos += new->compressed; current_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + new->compressed; fci->folders_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + new->compressed; fci->statusFolderCopied += new->compressed; (*payload) += new->uncompressed; list_add_tail( &folder->blocks_list, &new->entry ); folder->data_count++; /* report status with pfnfcis about copied size of folder */ if (status_callback( statusFolder, fci->statusFolderCopied, fci->statusFolderTotal, fci->pv ) == -1) { set_error( fci, FCIERR_USER_ABORT, 0 ); return FALSE; } if (split_block) break; free_data_block( fci, block ); fci->cDataBlocks--; } if (list_empty( &fci->blocks_list )) return TRUE; return copy_data_blocks( fci, folder->data.handle, start_pos, &fci->data, status_callback ); } /* add all pending files to folder */ static BOOL add_files_to_folder( FCI_Int *fci, struct folder *folder, cab_ULONG payload ) { cab_ULONG sizeOfFiles = 0, sizeOfFilesPrev; cab_ULONG cbFileRemainer = 0; struct file *file, *next; LIST_FOR_EACH_ENTRY_SAFE( file, next, &fci->files_list, struct file, entry ) { cab_ULONG size = sizeof(CFFILE) + strlen(file->name) + 1; /* fnfilfnfildest: placed file on cabinet */ fci->fileplaced( &fci->ccab, file->name, file->size, (file->folder == cffileCONTINUED_FROM_PREV), fci->pv ); sizeOfFilesPrev = sizeOfFiles; /* set complete size of all processed files */ if (file->folder == cffileCONTINUED_FROM_PREV && fci->cbFileRemainer != 0) { sizeOfFiles += fci->cbFileRemainer; fci->cbFileRemainer = 0; } else sizeOfFiles += file->size; /* check if spanned file fits into this cabinet folder */ if (sizeOfFiles > payload) { if (file->folder == cffileCONTINUED_FROM_PREV) file->folder = cffileCONTINUED_PREV_AND_NEXT; else file->folder = cffileCONTINUED_TO_NEXT; } list_remove( &file->entry ); list_add_tail( &folder->files_list, &file->entry ); fci->placed_files_size += size; fci->cFiles++; /* This is only true for files which will be written into the */ /* next cabinet of the spanning folder */ if (sizeOfFiles > payload) { /* add a copy back onto the list */ if (!(file = copy_file( fci, file ))) return FALSE; list_add_before( &next->entry, &file->entry ); /* Files which data will be partially written into the current cabinet */ if (file->folder == cffileCONTINUED_PREV_AND_NEXT || file->folder == cffileCONTINUED_TO_NEXT) { if (sizeOfFilesPrev <= payload) { /* The size of the uncompressed, data of a spanning file in a */ /* spanning data */ cbFileRemainer = sizeOfFiles - payload; } file->folder = cffileCONTINUED_FROM_PREV; } else file->folder = 0; } else { fci->files_size -= size; } } fci->cbFileRemainer = cbFileRemainer; return TRUE; } static cab_UWORD compress_NONE( FCI_Int *fci ) { memcpy( fci->data_out, fci->data_in, fci->cdata_in ); return fci->cdata_in; } #ifdef HAVE_ZLIB static void *zalloc( void *opaque, unsigned int items, unsigned int size ) { FCI_Int *fci = opaque; return fci->alloc( items * size ); } static void zfree( void *opaque, void *ptr ) { FCI_Int *fci = opaque; fci->free( ptr ); } static cab_UWORD compress_MSZIP( FCI_Int *fci ) { z_stream stream; stream.zalloc = zalloc; stream.zfree = zfree; stream.opaque = fci; if (deflateInit2( &stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY ) != Z_OK) { set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY ); return 0; } stream.next_in = fci->data_in; stream.avail_in = fci->cdata_in; stream.next_out = fci->data_out + 2; stream.avail_out = sizeof(fci->data_out) - 2; /* insert the signature */ fci->data_out[0] = 'C'; fci->data_out[1] = 'K'; deflate( &stream, Z_FINISH ); deflateEnd( &stream ); return stream.total_out + 2; } #endif /* HAVE_ZLIB */ /*********************************************************************** * FCICreate (CABINET.10) * * FCICreate is provided with several callbacks and * returns a handle which can be used to create cabinet files. * * PARAMS * perf [IO] A pointer to an ERF structure. When FCICreate * returns an error condition, error information may * be found here as well as from GetLastError. * pfnfiledest [I] A pointer to a function which is called when a file * is placed. Only useful for subsequent cabinet files. * pfnalloc [I] A pointer to a function which allocates ram. Uses * the same interface as malloc. * pfnfree [I] A pointer to a function which frees ram. Uses the * same interface as free. * pfnopen [I] A pointer to a function which opens a file. Uses * the same interface as _open. * pfnread [I] A pointer to a function which reads from a file into * a caller-provided buffer. Uses the same interface * as _read. * pfnwrite [I] A pointer to a function which writes to a file from * a caller-provided buffer. Uses the same interface * as _write. * pfnclose [I] A pointer to a function which closes a file handle. * Uses the same interface as _close. * pfnseek [I] A pointer to a function which seeks in a file. * Uses the same interface as _lseek. * pfndelete [I] A pointer to a function which deletes a file. * pfnfcigtf [I] A pointer to a function which gets the name of a * temporary file. * pccab [I] A pointer to an initialized CCAB structure. * pv [I] A pointer to an application-defined notification * function which will be passed to other FCI functions * as a parameter. * * RETURNS * On success, returns an FCI handle of type HFCI. * On failure, the NULL file handle is returned. Error * info can be retrieved from perf. * * INCLUDES * fci.h * */ HFCI __cdecl FCICreate( PERF perf, PFNFCIFILEPLACED pfnfiledest, PFNFCIALLOC pfnalloc, PFNFCIFREE pfnfree, PFNFCIOPEN pfnopen, PFNFCIREAD pfnread, PFNFCIWRITE pfnwrite, PFNFCICLOSE pfnclose, PFNFCISEEK pfnseek, PFNFCIDELETE pfndelete, PFNFCIGETTEMPFILE pfnfcigtf, PCCAB pccab, void *pv) { FCI_Int *p_fci_internal; if (!perf) { SetLastError(ERROR_BAD_ARGUMENTS); return NULL; } if ((!pfnalloc) || (!pfnfree) || (!pfnopen) || (!pfnread) || (!pfnwrite) || (!pfnclose) || (!pfnseek) || (!pfndelete) || (!pfnfcigtf) || (!pccab)) { perf->erfOper = FCIERR_NONE; perf->erfType = ERROR_BAD_ARGUMENTS; perf->fError = TRUE; SetLastError(ERROR_BAD_ARGUMENTS); return NULL; } if (!((p_fci_internal = pfnalloc(sizeof(FCI_Int))))) { perf->erfOper = FCIERR_ALLOC_FAIL; perf->erfType = ERROR_NOT_ENOUGH_MEMORY; perf->fError = TRUE; SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } p_fci_internal->magic = FCI_INT_MAGIC; p_fci_internal->perf = perf; p_fci_internal->fileplaced = pfnfiledest; p_fci_internal->alloc = pfnalloc; p_fci_internal->free = pfnfree; p_fci_internal->open = pfnopen; p_fci_internal->read = pfnread; p_fci_internal->write = pfnwrite; p_fci_internal->close = pfnclose; p_fci_internal->seek = pfnseek; p_fci_internal->delete = pfndelete; p_fci_internal->gettemp = pfnfcigtf; p_fci_internal->ccab = *pccab; p_fci_internal->pccab = pccab; p_fci_internal->fPrevCab = FALSE; p_fci_internal->fNextCab = FALSE; p_fci_internal->fSplitFolder = FALSE; p_fci_internal->fGetNextCabInVain = FALSE; p_fci_internal->pv = pv; p_fci_internal->cdata_in = 0; p_fci_internal->cCompressedBytesInFolder = 0; p_fci_internal->cFolders = 0; p_fci_internal->cFiles = 0; p_fci_internal->cDataBlocks = 0; p_fci_internal->data.handle = -1; p_fci_internal->fNewPrevious = FALSE; p_fci_internal->estimatedCabinetSize = 0; p_fci_internal->statusFolderTotal = 0; p_fci_internal->folders_size = 0; p_fci_internal->files_size = 0; p_fci_internal->placed_files_size = 0; p_fci_internal->pending_data_size = 0; p_fci_internal->folders_data_size = 0; p_fci_internal->compression = tcompTYPE_NONE; p_fci_internal->compress = compress_NONE; list_init( &p_fci_internal->folders_list ); list_init( &p_fci_internal->files_list ); list_init( &p_fci_internal->blocks_list ); memcpy(p_fci_internal->szPrevCab, pccab->szCab, CB_MAX_CABINET_NAME); memcpy(p_fci_internal->szPrevDisk, pccab->szDisk, CB_MAX_DISK_NAME); return (HFCI)p_fci_internal; } static BOOL fci_flush_folder( FCI_Int *p_fci_internal, BOOL fGetNextCab, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis) { cab_ULONG payload; cab_ULONG read_result; struct folder *folder; if ((!pfnfcignc) || (!pfnfcis)) { set_error( p_fci_internal, FCIERR_NONE, ERROR_BAD_ARGUMENTS ); return FALSE; } if( p_fci_internal->fGetNextCabInVain && p_fci_internal->fNextCab ){ /* internal error */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } /* If there was no FCIAddFile or FCIFlushFolder has already been called */ /* this function will return TRUE */ if( p_fci_internal->files_size == 0 ) { if ( p_fci_internal->pending_data_size != 0 ) { /* error handling */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } return TRUE; } /* FCIFlushFolder has already been called... */ if (p_fci_internal->fSplitFolder && p_fci_internal->placed_files_size!=0) { return TRUE; } /* This can be set already, because it makes only a difference */ /* when the current function exits with return FALSE */ p_fci_internal->fSplitFolder=FALSE; /* START of COPY */ if (!add_data_block( p_fci_internal, pfnfcis )) return FALSE; /* reset to get the number of data blocks of this folder which are */ /* actually in this cabinet ( at least partially ) */ p_fci_internal->cDataBlocks=0; p_fci_internal->statusFolderTotal = get_header_size( p_fci_internal ) + sizeof(CFFOLDER) + p_fci_internal->ccab.cbReserveCFFolder + p_fci_internal->placed_files_size+ p_fci_internal->folders_data_size + p_fci_internal->files_size+ p_fci_internal->pending_data_size + p_fci_internal->folders_size; p_fci_internal->statusFolderCopied = 0; /* report status with pfnfcis about copied size of folder */ if( (*pfnfcis)(statusFolder, p_fci_internal->statusFolderCopied, p_fci_internal->statusFolderTotal, /* TODO total folder size */ p_fci_internal->pv) == -1) { set_error( p_fci_internal, FCIERR_USER_ABORT, 0 ); return FALSE; } /* USE the variable read_result */ read_result = get_header_size( p_fci_internal ) + p_fci_internal->folders_data_size + p_fci_internal->placed_files_size + p_fci_internal->folders_size; if(p_fci_internal->files_size!=0) { read_result+= sizeof(CFFOLDER)+p_fci_internal->ccab.cbReserveCFFolder; } /* Check if multiple cabinets have to be created. */ /* Might be too much data for the maximum allowed cabinet size.*/ /* When any further data will be added later, it might not */ /* be possible to flush the cabinet, because there might */ /* not be enough space to store the name of the following */ /* cabinet and name of the corresponding disk. */ /* So take care of this and get the name of the next cabinet */ if( p_fci_internal->fGetNextCabInVain==FALSE && p_fci_internal->fNextCab==FALSE && ( ( p_fci_internal->ccab.cb < read_result + p_fci_internal->pending_data_size + p_fci_internal->files_size + CB_MAX_CABINET_NAME + /* next cabinet name */ CB_MAX_DISK_NAME /* next disk name */ ) || fGetNextCab ) ) { /* increment cabinet index */ ++(p_fci_internal->pccab->iCab); /* get name of next cabinet */ p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal; if (!(*pfnfcignc)(p_fci_internal->pccab, p_fci_internal->estimatedCabinetSize, /* estimated size of cab */ p_fci_internal->pv)) { set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED ); return FALSE; } /* Skip a few lines of code. This is caught by the next if. */ p_fci_internal->fGetNextCabInVain=TRUE; } /* too much data for cabinet */ if( (p_fci_internal->fGetNextCabInVain || p_fci_internal->fNextCab ) && ( ( p_fci_internal->ccab.cb < read_result + p_fci_internal->pending_data_size + p_fci_internal->files_size + strlen(p_fci_internal->pccab->szCab)+1 + /* next cabinet name */ strlen(p_fci_internal->pccab->szDisk)+1 /* next disk name */ ) || fGetNextCab ) ) { p_fci_internal->fGetNextCabInVain=FALSE; p_fci_internal->fNextCab=TRUE; /* return FALSE if there is not enough space left*/ /* this should never happen */ if (p_fci_internal->ccab.cb <= p_fci_internal->files_size + read_result + strlen(p_fci_internal->pccab->szCab)+1 + /* next cabinet name */ strlen(p_fci_internal->pccab->szDisk)+1 /* next disk name */ ) { return FALSE; } /* the folder will be split across cabinets */ p_fci_internal->fSplitFolder=TRUE; } else { /* this should never happen */ if (p_fci_internal->fNextCab) { /* internal error */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } } if (!(folder = add_folder( p_fci_internal ))) return FALSE; if (!add_data_to_folder( p_fci_internal, folder, &payload, pfnfcis )) return FALSE; if (!add_files_to_folder( p_fci_internal, folder, payload )) return FALSE; /* reset CFFolder specific information */ p_fci_internal->cDataBlocks=0; p_fci_internal->cCompressedBytesInFolder=0; return TRUE; } static BOOL fci_flush_cabinet( FCI_Int *p_fci_internal, BOOL fGetNextCab, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis) { cab_ULONG read_result=0; BOOL returntrue=FALSE; /* TODO test if fci_flush_cabinet really aborts if there was no FCIAddFile */ /* when FCIFlushCabinet was or FCIAddFile hasn't been called */ if( p_fci_internal->files_size==0 && fGetNextCab ) { returntrue=TRUE; } if (!fci_flush_folder(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)){ /* TODO set error */ return FALSE; } if(returntrue) return TRUE; if ( (p_fci_internal->fSplitFolder && p_fci_internal->fNextCab==FALSE)|| (p_fci_internal->folders_size==0 && (p_fci_internal->files_size!=0 || p_fci_internal->placed_files_size!=0 ) ) ) { /* error */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } /* create the cabinet */ if (!write_cabinet( p_fci_internal, pfnfcis )) return FALSE; p_fci_internal->fPrevCab=TRUE; /* The sections szPrevCab and szPrevDisk are not being updated, because */ /* MS CABINET.DLL always puts the first cabinet name and disk into them */ if (p_fci_internal->fNextCab) { p_fci_internal->fNextCab=FALSE; if (p_fci_internal->files_size==0 && p_fci_internal->pending_data_size!=0) { /* THIS CAN NEVER HAPPEN */ /* set error code */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } if( p_fci_internal->fNewPrevious ) { memcpy(p_fci_internal->szPrevCab, p_fci_internal->ccab.szCab, CB_MAX_CABINET_NAME); memcpy(p_fci_internal->szPrevDisk, p_fci_internal->ccab.szDisk, CB_MAX_DISK_NAME); p_fci_internal->fNewPrevious=FALSE; } p_fci_internal->ccab = *p_fci_internal->pccab; /* REUSE the variable read_result */ read_result=get_header_size( p_fci_internal ); if(p_fci_internal->files_size!=0) { read_result+=p_fci_internal->ccab.cbReserveCFFolder; } read_result+= p_fci_internal->pending_data_size + p_fci_internal->files_size + p_fci_internal->folders_data_size + p_fci_internal->placed_files_size + p_fci_internal->folders_size + sizeof(CFFOLDER); /* set size of new CFFolder entry */ /* too much data for the maximum size of a cabinet */ if( p_fci_internal->fGetNextCabInVain==FALSE && p_fci_internal->ccab.cb < read_result ) { return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis); } /* Might be too much data for the maximum size of a cabinet.*/ /* When any further data will be added later, it might not */ /* be possible to flush the cabinet, because there might */ /* not be enough space to store the name of the following */ /* cabinet and name of the corresponding disk. */ /* So take care of this and get the name of the next cabinet */ if (p_fci_internal->fGetNextCabInVain==FALSE && ( p_fci_internal->ccab.cb < read_result + CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME )) { /* increment cabinet index */ ++(p_fci_internal->pccab->iCab); /* get name of next cabinet */ p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal; if (!(*pfnfcignc)(p_fci_internal->pccab, p_fci_internal->estimatedCabinetSize, /* estimated size of cab */ p_fci_internal->pv)) { /* error handling */ set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED ); return FALSE; } /* Skip a few lines of code. This is caught by the next if. */ p_fci_internal->fGetNextCabInVain=TRUE; } /* too much data for cabinet */ if (p_fci_internal->fGetNextCabInVain && ( p_fci_internal->ccab.cb < read_result + strlen(p_fci_internal->ccab.szCab)+1+ strlen(p_fci_internal->ccab.szDisk)+1 )) { p_fci_internal->fGetNextCabInVain=FALSE; p_fci_internal->fNextCab=TRUE; return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis); } /* if the FolderThreshold has been reached flush the folder automatically */ if (p_fci_internal->cCompressedBytesInFolder >= p_fci_internal->ccab.cbFolderThresh) return fci_flush_folder(p_fci_internal, FALSE, pfnfcignc, pfnfcis); if( p_fci_internal->files_size>0 ) { if( !fci_flush_folder(p_fci_internal, FALSE, pfnfcignc, pfnfcis) ) return FALSE; p_fci_internal->fNewPrevious=TRUE; } } else { p_fci_internal->fNewPrevious=FALSE; if( p_fci_internal->files_size>0 || p_fci_internal->pending_data_size) { /* THIS MAY NEVER HAPPEN */ /* set error structures */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } } return TRUE; } /* end of fci_flush_cabinet */ /*********************************************************************** * FCIAddFile (CABINET.11) * * FCIAddFile adds a file to the to be created cabinet file * * PARAMS * hfci [I] An HFCI from FCICreate * pszSourceFile [I] A pointer to a C string which contains the name and * location of the file which will be added to the cabinet * pszFileName [I] A pointer to a C string which contains the name under * which the file will be stored in the cabinet * fExecute [I] A boolean value which indicates if the file should be * executed after extraction of self extracting * executables * pfnfcignc [I] A pointer to a function which gets information about * the next cabinet * pfnfcis [IO] A pointer to a function which will report status * information about the compression process * pfnfcioi [I] A pointer to a function which reports file attributes * and time and date information * typeCompress [I] Compression type * * RETURNS * On success, returns TRUE * On failure, returns FALSE * * INCLUDES * fci.h * */ BOOL __cdecl FCIAddFile( HFCI hfci, char *pszSourceFile, char *pszFileName, BOOL fExecute, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis, PFNFCIGETOPENINFO pfnfcigoi, TCOMP typeCompress) { cab_ULONG read_result; FCI_Int *p_fci_internal = get_fci_ptr( hfci ); if (!p_fci_internal) return FALSE; if ((!pszSourceFile) || (!pszFileName) || (!pfnfcignc) || (!pfnfcis) || (!pfnfcigoi) || strlen(pszFileName)>=CB_MAX_FILENAME) { set_error( p_fci_internal, FCIERR_NONE, ERROR_BAD_ARGUMENTS ); return FALSE; } if (typeCompress != p_fci_internal->compression) { if (!FCIFlushFolder( hfci, pfnfcignc, pfnfcis )) return FALSE; switch (typeCompress) { case tcompTYPE_MSZIP: #ifdef HAVE_ZLIB p_fci_internal->compression = tcompTYPE_MSZIP; p_fci_internal->compress = compress_MSZIP; break; #endif default: FIXME( "compression %x not supported, defaulting to none\n", typeCompress ); /* fall through */ case tcompTYPE_NONE: p_fci_internal->compression = tcompTYPE_NONE; p_fci_internal->compress = compress_NONE; break; } } /* TODO check if pszSourceFile??? */ if(p_fci_internal->fGetNextCabInVain && p_fci_internal->fNextCab) { /* internal error */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } if(p_fci_internal->fNextCab) { /* internal error */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } /* REUSE the variable read_result */ read_result=get_header_size( p_fci_internal ) + p_fci_internal->ccab.cbReserveCFFolder; read_result+= sizeof(CFFILE) + strlen(pszFileName)+1 + p_fci_internal->files_size + p_fci_internal->folders_data_size + p_fci_internal->placed_files_size + p_fci_internal->folders_size + sizeof(CFFOLDER); /* size of new CFFolder entry */ /* Might be too much data for the maximum size of a cabinet.*/ /* When any further data will be added later, it might not */ /* be possible to flush the cabinet, because there might */ /* not be enough space to store the name of the following */ /* cabinet and name of the corresponding disk. */ /* So take care of this and get the name of the next cabinet */ if( p_fci_internal->fGetNextCabInVain==FALSE && p_fci_internal->fNextCab==FALSE && ( p_fci_internal->ccab.cb < read_result + CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME ) ) { /* increment cabinet index */ ++(p_fci_internal->pccab->iCab); /* get name of next cabinet */ p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal; if (!(*pfnfcignc)(p_fci_internal->pccab, p_fci_internal->estimatedCabinetSize, /* estimated size of cab */ p_fci_internal->pv)) { /* error handling */ set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED ); return FALSE; } /* Skip a few lines of code. This is caught by the next if. */ p_fci_internal->fGetNextCabInVain=TRUE; } if( p_fci_internal->fGetNextCabInVain && p_fci_internal->fNextCab ) { /* THIS CAN NEVER HAPPEN */ /* set error code */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } /* too much data for cabinet */ if( p_fci_internal->fGetNextCabInVain && ( p_fci_internal->ccab.cb < read_result + strlen(p_fci_internal->pccab->szCab)+1+ strlen(p_fci_internal->pccab->szDisk)+1 )) { p_fci_internal->fGetNextCabInVain=FALSE; p_fci_internal->fNextCab=TRUE; if(!fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis)) return FALSE; } if( p_fci_internal->fNextCab ) { /* THIS MAY NEVER HAPPEN */ /* set error code */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } if (!add_file_data( p_fci_internal, pszSourceFile, pszFileName, fExecute, pfnfcigoi, pfnfcis )) return FALSE; /* REUSE the variable read_result */ read_result = get_header_size( p_fci_internal ) + p_fci_internal->ccab.cbReserveCFFolder; read_result+= p_fci_internal->pending_data_size + p_fci_internal->files_size + p_fci_internal->folders_data_size + p_fci_internal->placed_files_size + p_fci_internal->folders_size + sizeof(CFFOLDER); /* set size of new CFFolder entry */ /* too much data for the maximum size of a cabinet */ /* (ignoring the unflushed data block) */ if( p_fci_internal->fGetNextCabInVain==FALSE && p_fci_internal->fNextCab==FALSE && /* this is always the case */ p_fci_internal->ccab.cb < read_result ) { return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis); } /* Might be too much data for the maximum size of a cabinet.*/ /* When any further data will be added later, it might not */ /* be possible to flush the cabinet, because there might */ /* not be enough space to store the name of the following */ /* cabinet and name of the corresponding disk. */ /* So take care of this and get the name of the next cabinet */ /* (ignoring the unflushed data block) */ if( p_fci_internal->fGetNextCabInVain==FALSE && p_fci_internal->fNextCab==FALSE && ( p_fci_internal->ccab.cb < read_result + CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME ) ) { /* increment cabinet index */ ++(p_fci_internal->pccab->iCab); /* get name of next cabinet */ p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal; if (!(*pfnfcignc)(p_fci_internal->pccab, p_fci_internal->estimatedCabinetSize,/* estimated size of cab */ p_fci_internal->pv)) { /* error handling */ set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED ); return FALSE; } /* Skip a few lines of code. This is caught by the next if. */ p_fci_internal->fGetNextCabInVain=TRUE; } if( p_fci_internal->fGetNextCabInVain && p_fci_internal->fNextCab ) { /* THIS CAN NEVER HAPPEN */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } /* too much data for cabinet */ if( (p_fci_internal->fGetNextCabInVain || p_fci_internal->fNextCab) && ( p_fci_internal->ccab.cb < read_result + strlen(p_fci_internal->pccab->szCab)+1+ strlen(p_fci_internal->pccab->szDisk)+1 )) { p_fci_internal->fGetNextCabInVain=FALSE; p_fci_internal->fNextCab=TRUE; return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis); } if( p_fci_internal->fNextCab ) { /* THIS MAY NEVER HAPPEN */ /* set error code */ set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE ); return FALSE; } /* if the FolderThreshold has been reached flush the folder automatically */ if (p_fci_internal->cCompressedBytesInFolder >= p_fci_internal->ccab.cbFolderThresh) return FCIFlushFolder(hfci, pfnfcignc, pfnfcis); return TRUE; } /* end of FCIAddFile */ /*********************************************************************** * FCIFlushFolder (CABINET.12) * * FCIFlushFolder completes the CFFolder structure under construction. * * All further data which is added by FCIAddFile will be associated to * the next CFFolder structure. * * FCIFlushFolder will be called by FCIAddFile automatically if the * threshold (stored in the member cbFolderThresh of the CCAB structure * pccab passed to FCICreate) is exceeded. * * FCIFlushFolder will be called by FCIFlushFolder automatically before * any data will be written into the cabinet file. * * PARAMS * hfci [I] An HFCI from FCICreate * pfnfcignc [I] A pointer to a function which gets information about * the next cabinet * pfnfcis [IO] A pointer to a function which will report status * information about the compression process * * RETURNS * On success, returns TRUE * On failure, returns FALSE * * INCLUDES * fci.h * */ BOOL __cdecl FCIFlushFolder( HFCI hfci, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis) { FCI_Int *p_fci_internal = get_fci_ptr( hfci ); if (!p_fci_internal) return FALSE; return fci_flush_folder(p_fci_internal,FALSE,pfnfcignc,pfnfcis); } /*********************************************************************** * FCIFlushCabinet (CABINET.13) * * FCIFlushCabinet stores the data which has been added by FCIAddFile * into the cabinet file. If the maximum cabinet size (stored in the * member cb of the CCAB structure pccab passed to FCICreate) has been * exceeded FCIFlushCabinet will be called automatic by FCIAddFile. * The remaining data still has to be flushed manually by calling * FCIFlushCabinet. * * After FCIFlushCabinet has been called (manually) FCIAddFile must * NOT be called again. Then hfci has to be released by FCIDestroy. * * PARAMS * hfci [I] An HFCI from FCICreate * fGetNextCab [I] Whether you want to add additional files to a * cabinet set (TRUE) or whether you want to * finalize it (FALSE) * pfnfcignc [I] A pointer to a function which gets information about * the next cabinet * pfnfcis [IO] A pointer to a function which will report status * information about the compression process * * RETURNS * On success, returns TRUE * On failure, returns FALSE * * INCLUDES * fci.h * */ BOOL __cdecl FCIFlushCabinet( HFCI hfci, BOOL fGetNextCab, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis) { FCI_Int *p_fci_internal = get_fci_ptr( hfci ); if (!p_fci_internal) return FALSE; if(!fci_flush_cabinet(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)) return FALSE; while( p_fci_internal->files_size>0 || p_fci_internal->placed_files_size>0 ) { if(!fci_flush_cabinet(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)) return FALSE; } return TRUE; } /*********************************************************************** * FCIDestroy (CABINET.14) * * Frees a handle created by FCICreate. * Only reason for failure would be an invalid handle. * * PARAMS * hfci [I] The HFCI to free * * RETURNS * TRUE for success * FALSE for failure */ BOOL __cdecl FCIDestroy(HFCI hfci) { struct folder *folder, *folder_next; struct file *file, *file_next; struct data_block *block, *block_next; FCI_Int *p_fci_internal = get_fci_ptr( hfci ); if (!p_fci_internal) return FALSE; /* before hfci can be removed all temporary files must be closed */ /* and deleted */ p_fci_internal->magic = 0; LIST_FOR_EACH_ENTRY_SAFE( folder, folder_next, &p_fci_internal->folders_list, struct folder, entry ) { free_folder( p_fci_internal, folder ); } LIST_FOR_EACH_ENTRY_SAFE( file, file_next, &p_fci_internal->files_list, struct file, entry ) { free_file( p_fci_internal, file ); } LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &p_fci_internal->blocks_list, struct data_block, entry ) { free_data_block( p_fci_internal, block ); } close_temp_file( p_fci_internal, &p_fci_internal->data ); /* hfci can now be removed */ p_fci_internal->free(hfci); return TRUE; }