fci.c 57.9 KB
Newer Older
1 2 3 4
/*
 * File Compression Interface
 *
 * Copyright 2002 Patrik Stridvall
5
 * Copyright 2005 Gerold Jens Wucherpfennig
6
 * Copyright 2011 Alexandre Julliard
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22
 */

23 24 25 26
/*

There is still some work to be done:

27
- unknown behaviour if files>=2GB or cabinet >=4GB
28 29 30 31 32 33 34
- 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

*/



35 36
#include "config.h"

37
#include <assert.h>
38
#include <stdarg.h>
39 40
#include <stdio.h>
#include <string.h>
41 42 43
#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
44

45
#include "windef.h"
46 47
#include "winbase.h"
#include "winerror.h"
48
#include "winternl.h"
49
#include "fci.h"
50
#include "cabinet.h"
51
#include "wine/list.h"
52 53
#include "wine/debug.h"

54
WINE_DEFAULT_DEBUG_CHANNEL(cabinet);
55 56 57 58 59 60 61 62 63 64

#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


65 66 67 68 69 70 71 72 73 74 75
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*/
Austin English's avatar
Austin English committed
76
  cab_UWORD flags;        /* 1=prev cab, 2=next cabinet, 4=reserved sections*/
77 78 79 80 81 82 83 84 85 86
  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 */
87
} CFFOLDER; /* minimum 8 bytes */
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108

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;

109 110 111 112 113 114
struct temp_file
{
    INT_PTR   handle;
    char      name[CB_MAX_FILENAME];
};

115 116
struct folder
{
117 118 119 120 121 122
    struct list      entry;
    struct list      files_list;
    struct list      blocks_list;
    struct temp_file data;
    cab_ULONG        data_start;
    cab_UWORD        data_count;
123
    TCOMP            compression;
124
};
125

126 127 128 129 130 131 132 133 134 135 136 137
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];
};

138 139 140 141 142 143 144
struct data_block
{
    struct list entry;
    cab_UWORD   compressed;
    cab_UWORD   uncompressed;
};

145
typedef struct FCI_Int
146 147
{
  unsigned int       magic;
148
  PERF               perf;
149 150 151 152 153 154 155 156 157 158
  PFNFCIFILEPLACED   fileplaced;
  PFNFCIALLOC        alloc;
  PFNFCIFREE         free;
  PFNFCIOPEN         open;
  PFNFCIREAD         read;
  PFNFCIWRITE        write;
  PFNFCICLOSE        close;
  PFNFCISEEK         seek;
  PFNFCIDELETE       delete;
  PFNFCIGETTEMPFILE  gettemp;
159
  CCAB               ccab;
160 161 162 163 164 165 166 167
  PCCAB              pccab;
  BOOL               fPrevCab;
  BOOL               fNextCab;
  BOOL               fSplitFolder;
  cab_ULONG          statusFolderCopied;
  cab_ULONG          statusFolderTotal;
  BOOL               fGetNextCabInVain;
  void               *pv;
168 169 170 171
  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 */
172 173 174 175 176 177 178
  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 */
179
  struct temp_file   data;
180 181
  BOOL               fNewPrevious;
  cab_ULONG          estimatedCabinetSize;
182
  struct list        folders_list;
183
  struct list        files_list;
184
  struct list        blocks_list;
185
  cab_ULONG          folders_size;
186 187 188 189
  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 */
190 191
  TCOMP              compression;
  cab_UWORD        (*compress)(struct FCI_Int *);
192 193 194 195
} FCI_Int;

#define FCI_INT_MAGIC 0xfcfcfc05

196 197 198 199 200 201 202 203
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 );
}

204 205 206 207
static FCI_Int *get_fci_ptr( HFCI hfci )
{
    FCI_Int *fci= (FCI_Int *)hfci;

208
    if (!fci || fci->magic != FCI_INT_MAGIC)
209 210 211 212 213 214 215
    {
        SetLastError( ERROR_INVALID_HANDLE );
        return NULL;
    }
    return fci;
}

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
/* 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;
}

233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
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;
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
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 );
288
    fci->files_size += sizeof(CFFILE) + strlen(filename) + 1;
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    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 );
}

312 313
/* create a new data block for the data in fci->data_in */
static BOOL add_data_block( FCI_Int *fci, PFNFCISTATUS status_callback )
314
{
315 316
    int err;
    struct data_block *block;
317

318 319
    if (!fci->cdata_in) return TRUE;

320 321
    if (fci->data.handle == -1 && !create_temp_file( fci, &fci->data )) return FALSE;

322
    if (!(block = fci->alloc( sizeof(*block) )))
323 324
    {
        set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
325
        return FALSE;
326
    }
327 328 329 330 331 332 333 334 335 336 337 338
    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;
339
    fci->pending_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed;
340
    fci->cCompressedBytesInFolder += block->compressed;
341
    fci->cDataBlocks++;
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    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;
388 389 390 391 392 393 394 395
}

static void free_data_block( FCI_Int *fci, struct data_block *block )
{
    list_remove( &block->entry );
    fci->free( block );
}

396 397 398 399 400 401 402 403 404
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;
    }
405 406 407
    folder->data.handle = -1;
    folder->data_start  = fci->folders_data_size;
    folder->data_count  = 0;
408
    folder->compression = fci->compression;
409
    list_init( &folder->files_list );
410
    list_init( &folder->blocks_list );
411
    list_add_tail( &fci->folders_list, &folder->entry );
412
    fci->folders_size += sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder;
413 414 415 416
    fci->cFolders++;
    return folder;
}

417 418
static void free_folder( FCI_Int *fci, struct folder *folder )
{
419 420
    struct file *file, *file_next;
    struct data_block *block, *block_next;
421

422 423 424 425 426
    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 );
427 428 429 430
    list_remove( &folder->entry );
    fci->free( folder );
}

431 432 433 434 435 436 437 438 439 440 441 442
/* 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;
443
    fci->folders_data_size = 0;
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
}

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);
469
      /* fall through */
470 471
    case 2:
      ul |= (((ULONG)(*pb++)) <<  8);
472
      /* fall through */
473 474
    case 1:
      ul |= *pb;
475
      /* fall through */
476 477 478 479 480 481 482 483
    default:
      break;
  }
  csum ^= ul;

  return csum;
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
/* 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;
        }
514
        fci->pending_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed;
515 516 517 518 519 520 521 522 523 524 525 526 527
        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;
}

528
/* write all folders to disk and remove them from the list */
529
static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size, PFNFCISTATUS status_callback )
530
{
531
    struct folder *folder;
532
    int err;
533
    CFFOLDER *cffolder = (CFFOLDER *)fci->data_out;
534
    cab_ULONG folder_size = sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder;
535 536 537

    memset( cffolder, 0, folder_size );

538 539
    /* write the folders */
    LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
540 541 542 543 544 545 546
    {
        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 );
547
            return FALSE;
548
        }
549
    }
550
    return TRUE;
551 552 553 554 555 556 557 558 559
}

/* 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;
560
    CFFILE *cffile = (CFFILE *)fci->data_out;
561

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
    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 );
577
                return FALSE;
578 579 580 581 582
            }
            if (!fci->fSplitFolder)
            {
                fci->statusFolderCopied = 0;
                /* TODO TEST THIS further */
583
                fci->statusFolderTotal = fci->folders_data_size + fci->placed_files_size;
584 585 586 587 588 589 590
            }
            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 );
591
                return FALSE;
592 593 594
            }
        }
    }
595
    return TRUE;
596 597
}

598 599 600
/* write all data blocks to the cabinet file */
static BOOL write_data_blocks( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback )
{
601
    struct folder *folder;
602 603 604 605 606 607 608 609 610 611 612
    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;

613
    LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
614
    {
615
        if (fci->seek( folder->data.handle, 0, SEEK_SET, &err, fci->pv ) != 0)
616 617 618 619
        {
            set_error( fci, FCIERR_CAB_FILE, err );
            return FALSE;
        }
620
        LIST_FOR_EACH_ENTRY( block, &folder->blocks_list, struct data_block, entry )
621
        {
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
            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;
            }
643 644 645 646 647
        }
    }
    return TRUE;
}

648
/* write the cabinet file to disk */
649
static BOOL write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
650 651 652
{
    char filename[CB_MAX_CAB_PATH + CB_MAX_CABINET_NAME];
    int err;
653
    char *ptr;
654
    INT_PTR handle;
655
    CFHEADER *cfheader = (CFHEADER *)fci->data_out;
656 657
    cab_UWORD flags = 0;
    cab_ULONG header_size = get_header_size( fci );
658 659
    cab_ULONG total_size = header_size + fci->folders_size +
                           fci->placed_files_size + fci->folders_data_size;
660

661
    assert( header_size <= sizeof(fci->data_out) );
662 663 664 665 666 667 668 669
    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 );
670
    cfheader->cbCabinet    = fci_endian_ulong( total_size );
671 672 673 674 675 676 677
    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 );
678
    cfheader->iCabinet     = fci_endian_uword( fci->ccab.iCab );
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
    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 );
722
        return FALSE;
723 724 725 726 727 728 729 730 731 732 733
    }

    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;
734 735
    if (!write_files( fci, handle, status_callback )) goto failed;
    if (!write_data_blocks( fci, handle, status_callback )) goto failed;
736

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
    /* 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;
754 755 756 757

failed:
    fci->close( handle, &err, fci->pv );
    fci->delete( filename, &err, fci->pv );
758
    return FALSE;
759 760
}

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
/* 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;
776
    fci->pending_data_size = 0;
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

    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--;
    }

834
    if (list_empty( &fci->blocks_list )) return TRUE;
835 836 837
    return copy_data_blocks( fci, folder->data.handle, start_pos, &fci->data, status_callback );
}

838 839 840 841 842 843 844 845 846 847 848 849
/* 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 */
850 851
        fci->fileplaced( &fci->ccab, file->name, file->size,
                         (file->folder == cffileCONTINUED_FROM_PREV), fci->pv );
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905

        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;
}

906 907 908 909 910 911
static cab_UWORD compress_NONE( FCI_Int *fci )
{
    memcpy( fci->data_out, fci->data_in, fci->cdata_in );
    return fci->cdata_in;
}

912 913 914 915 916 917 918 919 920 921 922
#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;
923
    fci->free( ptr );
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
}

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;
941
    stream.avail_out = sizeof(fci->data_out) - 2;
942 943 944 945 946 947 948 949 950 951
    /* 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 */

952

953 954
/***********************************************************************
 *		FCICreate (CABINET.10)
955
 *
956 957
 * FCICreate is provided with several callbacks and
 * returns a handle which can be used to create cabinet files.
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
 *
 * 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
973
 *                    as _read.
974 975 976 977 978 979 980 981 982
 *   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
983 984
 *                    temporary file.
 *   pccab       [I]  A pointer to an initialized CCAB structure.
985 986 987 988 989 990 991 992 993 994 995 996
 *   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
 *
997 998 999
 */
HFCI __cdecl FCICreate(
	PERF perf,
1000 1001 1002
	PFNFCIFILEPLACED   pfnfiledest,
	PFNFCIALLOC        pfnalloc,
	PFNFCIFREE         pfnfree,
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
	PFNFCIOPEN         pfnopen,
	PFNFCIREAD         pfnread,
	PFNFCIWRITE        pfnwrite,
	PFNFCICLOSE        pfnclose,
	PFNFCISEEK         pfnseek,
	PFNFCIDELETE       pfndelete,
	PFNFCIGETTEMPFILE  pfnfcigtf,
	PCCAB              pccab,
	void *pv)
{
1013
  FCI_Int *p_fci_internal;
1014

1015 1016 1017 1018 1019
  if (!perf) {
    SetLastError(ERROR_BAD_ARGUMENTS);
    return NULL;
  }
  if ((!pfnalloc) || (!pfnfree) || (!pfnopen) || (!pfnread) ||
1020 1021
      (!pfnwrite) || (!pfnclose) || (!pfnseek) || (!pfndelete) ||
      (!pfnfcigtf) || (!pccab)) {
1022
    perf->erfOper = FCIERR_NONE;
1023
    perf->erfType = ERROR_BAD_ARGUMENTS;
1024 1025
    perf->fError = TRUE;

1026 1027 1028 1029
    SetLastError(ERROR_BAD_ARGUMENTS);
    return NULL;
  }

1030
  if (!((p_fci_internal = pfnalloc(sizeof(FCI_Int))))) {
1031 1032 1033
    perf->erfOper = FCIERR_ALLOC_FAIL;
    perf->erfType = ERROR_NOT_ENOUGH_MEMORY;
    perf->fError = TRUE;
1034

1035
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1036
    return NULL;
1037 1038
  }

1039
  p_fci_internal->magic = FCI_INT_MAGIC;
1040
  p_fci_internal->perf = perf;
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
  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;
1051
  p_fci_internal->ccab = *pccab;
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
  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;
1063
  p_fci_internal->data.handle = -1;
1064
  p_fci_internal->fNewPrevious = FALSE;
1065 1066
  p_fci_internal->estimatedCabinetSize = 0;
  p_fci_internal->statusFolderTotal = 0;
1067
  p_fci_internal->folders_size = 0;
1068 1069
  p_fci_internal->files_size = 0;
  p_fci_internal->placed_files_size = 0;
1070
  p_fci_internal->pending_data_size = 0;
1071
  p_fci_internal->folders_data_size = 0;
1072 1073
  p_fci_internal->compression = tcompTYPE_NONE;
  p_fci_internal->compress = compress_NONE;
1074 1075

  list_init( &p_fci_internal->folders_list );
1076
  list_init( &p_fci_internal->files_list );
1077
  list_init( &p_fci_internal->blocks_list );
1078

1079 1080
  memcpy(p_fci_internal->szPrevCab, pccab->szCab, CB_MAX_CABINET_NAME);
  memcpy(p_fci_internal->szPrevDisk, pccab->szDisk, CB_MAX_DISK_NAME);
1081

1082 1083
  return (HFCI)p_fci_internal;
}
1084 1085 1086 1087




1088
static BOOL fci_flush_folder( FCI_Int *p_fci_internal,
1089 1090 1091 1092
	BOOL                  fGetNextCab,
	PFNFCIGETNEXTCABINET  pfnfcignc,
	PFNFCISTATUS          pfnfcis)
{
1093 1094
  cab_ULONG payload;
  cab_ULONG read_result;
1095
  struct folder *folder;
1096

1097
  if ((!pfnfcignc) || (!pfnfcis)) {
1098
    set_error( p_fci_internal, FCIERR_NONE, ERROR_BAD_ARGUMENTS );
1099
    return FALSE;
1100
  }
1101

1102 1103
  if( p_fci_internal->fGetNextCabInVain &&
      p_fci_internal->fNextCab ){
1104
    /* internal error */
1105
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1106 1107 1108 1109 1110
    return FALSE;
  }

  /* If there was no FCIAddFile or FCIFlushFolder has already been called */
  /* this function will return TRUE */
1111
  if( p_fci_internal->files_size == 0 ) {
1112
    if ( p_fci_internal->pending_data_size != 0 ) {
1113
      /* error handling */
1114
      set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1115 1116 1117 1118
      return FALSE;
    }
    return TRUE;
  }
1119

1120
  /* FCIFlushFolder has already been called... */
1121
  if (p_fci_internal->fSplitFolder && p_fci_internal->placed_files_size!=0) {
1122 1123 1124
    return TRUE;
  }

1125 1126
  /* This can be set already, because it makes only a difference */
  /* when the current function exits with return FALSE */
1127 1128 1129
  p_fci_internal->fSplitFolder=FALSE;

  /* START of COPY */
1130
  if (!add_data_block( p_fci_internal, pfnfcis )) return FALSE;
1131 1132 1133 1134 1135

  /* 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;

1136 1137 1138
  p_fci_internal->statusFolderTotal = get_header_size( p_fci_internal ) +
      sizeof(CFFOLDER) + p_fci_internal->ccab.cbReserveCFFolder +
      p_fci_internal->placed_files_size+
1139
      p_fci_internal->folders_data_size + p_fci_internal->files_size+
1140
      p_fci_internal->pending_data_size + p_fci_internal->folders_size;
1141 1142 1143 1144 1145 1146
  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) {
1147
    set_error( p_fci_internal, FCIERR_USER_ABORT, 0 );
1148
    return FALSE;
1149
  }
1150

1151
  /* USE the variable read_result */
1152
  read_result = get_header_size( p_fci_internal ) + p_fci_internal->folders_data_size +
1153
      p_fci_internal->placed_files_size + p_fci_internal->folders_size;
1154

1155
  if(p_fci_internal->files_size!=0) {
1156
    read_result+= sizeof(CFFOLDER)+p_fci_internal->ccab.cbReserveCFFolder;
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
  }

  /* 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 &&
      (
        (
1171
          p_fci_internal->ccab.cb < read_result +
1172
          p_fci_internal->pending_data_size +
1173
          p_fci_internal->files_size +
1174 1175 1176 1177 1178 1179 1180 1181
          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 */
1182 1183 1184
    p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
    if (!(*pfnfcignc)(p_fci_internal->pccab,
        p_fci_internal->estimatedCabinetSize, /* estimated size of cab */
1185
        p_fci_internal->pv)) {
1186
      set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1187 1188 1189
      return FALSE;
    }

1190
    /* Skip a few lines of code. This is caught by the next if. */
1191 1192 1193 1194 1195 1196 1197 1198
    p_fci_internal->fGetNextCabInVain=TRUE;
  }

  /* too much data for cabinet */
  if( (p_fci_internal->fGetNextCabInVain ||
        p_fci_internal->fNextCab ) &&
      (
        (
1199
          p_fci_internal->ccab.cb < read_result +
1200
          p_fci_internal->pending_data_size +
1201
          p_fci_internal->files_size +
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
          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 */
1212
    if (p_fci_internal->ccab.cb <=
1213
        p_fci_internal->files_size +
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227
        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) {
1228
      /* internal error */
1229
      set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1230 1231 1232 1233
      return FALSE;
    }
  }

1234
  if (!(folder = add_folder( p_fci_internal ))) return FALSE;
1235 1236
  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;
1237 1238 1239 1240 1241 1242

  /* reset CFFolder specific information */
  p_fci_internal->cDataBlocks=0;
  p_fci_internal->cCompressedBytesInFolder=0;

  return TRUE;
1243
}
1244 1245 1246 1247




1248
static BOOL fci_flush_cabinet( FCI_Int *p_fci_internal,
1249 1250 1251 1252
	BOOL                  fGetNextCab,
	PFNFCIGETNEXTCABINET  pfnfcignc,
	PFNFCISTATUS          pfnfcis)
{
1253
  cab_ULONG read_result=0;
1254 1255 1256 1257
  BOOL returntrue=FALSE;

  /* TODO test if fci_flush_cabinet really aborts if there was no FCIAddFile */

1258
  /* when FCIFlushCabinet was or FCIAddFile hasn't been called */
1259
  if( p_fci_internal->files_size==0 && fGetNextCab ) {
1260 1261 1262
    returntrue=TRUE;
  }

1263
  if (!fci_flush_folder(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)){
1264 1265 1266 1267 1268 1269
    /* TODO set error */
    return FALSE;
  }

  if(returntrue) return TRUE;

1270
  if ( (p_fci_internal->fSplitFolder && p_fci_internal->fNextCab==FALSE)||
1271
       (p_fci_internal->folders_size==0 &&
1272 1273
         (p_fci_internal->files_size!=0 ||
          p_fci_internal->placed_files_size!=0 )
1274 1275 1276
     ) )
  {
      /* error */
1277
      set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1278 1279 1280
      return FALSE;
  }

1281
  /* create the cabinet */
1282
  if (!write_cabinet( p_fci_internal, pfnfcis )) return FALSE;
1283 1284 1285 1286 1287 1288 1289 1290

  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;

1291
    if (p_fci_internal->files_size==0 && p_fci_internal->pending_data_size!=0) {
1292
      /* THIS CAN NEVER HAPPEN */
1293
      /* set error code */
1294
      set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1295 1296 1297
      return FALSE;
    }

1298 1299 1300 1301 1302 1303 1304 1305
    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;
1306 1307

    /* REUSE the variable read_result */
1308 1309 1310
    read_result=get_header_size( p_fci_internal );
    if(p_fci_internal->files_size!=0) {
        read_result+=p_fci_internal->ccab.cbReserveCFFolder;
1311
    }
1312
    read_result+= p_fci_internal->pending_data_size +
1313
      p_fci_internal->files_size + p_fci_internal->folders_data_size +
1314
      p_fci_internal->placed_files_size + p_fci_internal->folders_size +
1315 1316 1317 1318
      sizeof(CFFOLDER); /* set size of new CFFolder entry */

    /* too much data for the maximum size of a cabinet */
    if( p_fci_internal->fGetNextCabInVain==FALSE &&
1319
        p_fci_internal->ccab.cb < read_result ) {
1320
      return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1321 1322 1323 1324 1325 1326 1327 1328 1329
    }

    /* 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 && (
1330
      p_fci_internal->ccab.cb < read_result +
1331 1332 1333 1334 1335
      CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME
    )) {
      /* increment cabinet index */
      ++(p_fci_internal->pccab->iCab);
      /* get name of next cabinet */
1336 1337 1338
      p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
      if (!(*pfnfcignc)(p_fci_internal->pccab,
          p_fci_internal->estimatedCabinetSize, /* estimated size of cab */
1339
          p_fci_internal->pv)) {
1340
        /* error handling */
1341
        set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1342 1343
        return FALSE;
      }
1344
      /* Skip a few lines of code. This is caught by the next if. */
1345 1346 1347 1348 1349
      p_fci_internal->fGetNextCabInVain=TRUE;
    }

    /* too much data for cabinet */
    if (p_fci_internal->fGetNextCabInVain && (
1350 1351 1352
        p_fci_internal->ccab.cb < read_result +
        strlen(p_fci_internal->ccab.szCab)+1+
        strlen(p_fci_internal->ccab.szDisk)+1
1353 1354 1355
    )) {
      p_fci_internal->fGetNextCabInVain=FALSE;
      p_fci_internal->fNextCab=TRUE;
1356
      return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1357 1358 1359
    }

    /* if the FolderThreshold has been reached flush the folder automatically */
1360
    if (p_fci_internal->cCompressedBytesInFolder >= p_fci_internal->ccab.cbFolderThresh)
1361
        return fci_flush_folder(p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1362

1363
    if( p_fci_internal->files_size>0 ) {
1364
      if( !fci_flush_folder(p_fci_internal, FALSE, pfnfcignc, pfnfcis) ) return FALSE;
1365 1366 1367 1368
      p_fci_internal->fNewPrevious=TRUE;
    }
  } else {
    p_fci_internal->fNewPrevious=FALSE;
1369
    if( p_fci_internal->files_size>0 || p_fci_internal->pending_data_size) {
1370
      /* THIS MAY NEVER HAPPEN */
1371
      /* set error structures */
1372
      set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
      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;
1425
  FCI_Int *p_fci_internal = get_fci_ptr( hfci );
1426

1427
  if (!p_fci_internal) return FALSE;
1428 1429 1430

  if ((!pszSourceFile) || (!pszFileName) || (!pfnfcignc) || (!pfnfcis) ||
      (!pfnfcigoi) || strlen(pszFileName)>=CB_MAX_FILENAME) {
1431
    set_error( p_fci_internal, FCIERR_NONE, ERROR_BAD_ARGUMENTS );
1432 1433 1434
    return FALSE;
  }

1435 1436 1437 1438 1439
  if (typeCompress != p_fci_internal->compression)
  {
      if (!FCIFlushFolder( hfci, pfnfcignc, pfnfcis )) return FALSE;
      switch (typeCompress)
      {
1440 1441 1442 1443 1444 1445
      case tcompTYPE_MSZIP:
#ifdef HAVE_ZLIB
          p_fci_internal->compression = tcompTYPE_MSZIP;
          p_fci_internal->compress    = compress_MSZIP;
          break;
#endif
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
      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;
      }
  }

1456 1457 1458
  /* TODO check if pszSourceFile??? */

  if(p_fci_internal->fGetNextCabInVain && p_fci_internal->fNextCab) {
1459
    /* internal error */
1460
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1461 1462 1463 1464
    return FALSE;
  }

  if(p_fci_internal->fNextCab) {
1465
    /* internal error */
1466
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1467 1468 1469 1470
    return FALSE;
  }

  /* REUSE the variable read_result */
1471
  read_result=get_header_size( p_fci_internal ) + p_fci_internal->ccab.cbReserveCFFolder;
1472 1473

  read_result+= sizeof(CFFILE) + strlen(pszFileName)+1 +
1474
    p_fci_internal->files_size + p_fci_internal->folders_data_size +
1475
    p_fci_internal->placed_files_size + p_fci_internal->folders_size +
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
    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 &&
1486
      ( p_fci_internal->ccab.cb < read_result +
1487 1488 1489 1490 1491 1492
        CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME
      )
  ) {
    /* increment cabinet index */
    ++(p_fci_internal->pccab->iCab);
    /* get name of next cabinet */
1493 1494 1495
    p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
    if (!(*pfnfcignc)(p_fci_internal->pccab,
        p_fci_internal->estimatedCabinetSize, /* estimated size of cab */
1496
        p_fci_internal->pv)) {
1497
      /* error handling */
1498
      set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1499 1500
      return FALSE;
    }
1501
    /* Skip a few lines of code. This is caught by the next if. */
1502 1503 1504 1505 1506 1507 1508
    p_fci_internal->fGetNextCabInVain=TRUE;
  }

  if( p_fci_internal->fGetNextCabInVain &&
      p_fci_internal->fNextCab
  ) {
    /* THIS CAN NEVER HAPPEN */
1509
    /* set error code */
1510
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1511 1512 1513 1514 1515 1516
    return FALSE;
  }

  /* too much data for cabinet */
  if( p_fci_internal->fGetNextCabInVain &&
     (
1517
      p_fci_internal->ccab.cb < read_result +
1518 1519 1520 1521 1522
      strlen(p_fci_internal->pccab->szCab)+1+
      strlen(p_fci_internal->pccab->szDisk)+1
  )) {
    p_fci_internal->fGetNextCabInVain=FALSE;
    p_fci_internal->fNextCab=TRUE;
1523
    if(!fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis)) return FALSE;
1524 1525 1526 1527
  }

  if( p_fci_internal->fNextCab ) {
    /* THIS MAY NEVER HAPPEN */
1528
    /* set error code */
1529
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1530 1531 1532
    return FALSE;
  }

1533
  if (!add_file_data( p_fci_internal, pszSourceFile, pszFileName, fExecute, pfnfcigoi, pfnfcis ))
1534 1535 1536
      return FALSE;

  /* REUSE the variable read_result */
1537
  read_result = get_header_size( p_fci_internal ) + p_fci_internal->ccab.cbReserveCFFolder;
1538
  read_result+= p_fci_internal->pending_data_size +
1539
    p_fci_internal->files_size + p_fci_internal->folders_data_size +
1540
    p_fci_internal->placed_files_size + p_fci_internal->folders_size +
1541 1542 1543 1544 1545 1546
    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 */
1547
      p_fci_internal->ccab.cb < read_result ) {
1548
    return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
  }

  /* 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 &&
1560
      ( p_fci_internal->ccab.cb < read_result +
1561 1562 1563 1564 1565 1566
        CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME
      )
  ) {
    /* increment cabinet index */
    ++(p_fci_internal->pccab->iCab);
    /* get name of next cabinet */
1567 1568 1569
    p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
    if (!(*pfnfcignc)(p_fci_internal->pccab,
        p_fci_internal->estimatedCabinetSize,/* estimated size of cab */
1570
        p_fci_internal->pv)) {
1571
      /* error handling */
1572
      set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1573 1574
      return FALSE;
    }
1575
    /* Skip a few lines of code. This is caught by the next if. */
1576 1577 1578 1579 1580 1581 1582
    p_fci_internal->fGetNextCabInVain=TRUE;
  }

  if( p_fci_internal->fGetNextCabInVain &&
      p_fci_internal->fNextCab
  ) {
    /* THIS CAN NEVER HAPPEN */
1583
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1584 1585 1586 1587 1588 1589
    return FALSE;
  }

  /* too much data for cabinet */
  if( (p_fci_internal->fGetNextCabInVain ||
      p_fci_internal->fNextCab) && (
1590
      p_fci_internal->ccab.cb < read_result +
1591 1592 1593 1594 1595 1596
      strlen(p_fci_internal->pccab->szCab)+1+
      strlen(p_fci_internal->pccab->szDisk)+1
  )) {

    p_fci_internal->fGetNextCabInVain=FALSE;
    p_fci_internal->fNextCab=TRUE;
1597
    return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1598 1599 1600 1601
  }

  if( p_fci_internal->fNextCab ) {
    /* THIS MAY NEVER HAPPEN */
1602
    /* set error code */
1603
    set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1604 1605 1606 1607
    return FALSE;
  }

  /* if the FolderThreshold has been reached flush the folder automatically */
1608
  if (p_fci_internal->cCompressedBytesInFolder >= p_fci_internal->ccab.cbFolderThresh)
1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
      return FCIFlushFolder(hfci, pfnfcignc, pfnfcis);

  return TRUE;
} /* end of FCIAddFile */





/***********************************************************************
 *		FCIFlushFolder (CABINET.12)
 *
 * FCIFlushFolder completes the CFFolder structure under construction.
 *
Austin English's avatar
Austin English committed
1623
 * All further data which is added by FCIAddFile will be associated to
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652
 * 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)
{
1653 1654 1655 1656 1657
    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);
}
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697



/***********************************************************************
 *		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)
{
1698
  FCI_Int *p_fci_internal = get_fci_ptr( hfci );
1699

1700 1701 1702
  if (!p_fci_internal) return FALSE;

  if(!fci_flush_cabinet(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)) return FALSE;
1703

1704 1705
  while( p_fci_internal->files_size>0 ||
         p_fci_internal->placed_files_size>0 ) {
1706
    if(!fci_flush_cabinet(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)) return FALSE;
1707 1708 1709
  }

  return TRUE;
1710
}
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727


/***********************************************************************
 *		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)
{
1728
    struct folder *folder, *folder_next;
1729
    struct file *file, *file_next;
1730
    struct data_block *block, *block_next;
1731 1732 1733
    FCI_Int *p_fci_internal = get_fci_ptr( hfci );

    if (!p_fci_internal) return FALSE;
1734 1735 1736

    /* before hfci can be removed all temporary files must be closed */
    /* and deleted */
1737
    p_fci_internal->magic = 0;
1738

1739 1740
    LIST_FOR_EACH_ENTRY_SAFE( folder, folder_next, &p_fci_internal->folders_list, struct folder, entry )
    {
1741 1742 1743 1744 1745
        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 );
1746
    }
1747 1748 1749 1750
    LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &p_fci_internal->blocks_list, struct data_block, entry )
    {
        free_data_block( p_fci_internal, block );
    }
1751

1752
    close_temp_file( p_fci_internal, &p_fci_internal->data );
1753 1754

    /* hfci can now be removed */
1755
    p_fci_internal->free(hfci);
1756
    return TRUE;
1757
}