1755 lines
58 KiB
C
1755 lines
58 KiB
C
/*
|
|
* 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);
|
|
case 2:
|
|
ul |= (((ULONG)(*pb++)) << 8);
|
|
case 1:
|
|
ul |= *pb;
|
|
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;
|
|
return 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;
|
|
}
|