/***    cabinet.h - Definitions for Cabinet File structure
 *
 *  Author:
 *      Benjamin W. Slivka
 *
 *  History:
 *      15-Aug-1993 bens    Initial version
 *      05-Sep-1993 bens    Added Overview section
 *      29-Nov-1993 chuckst Added disk names to folder first & next
 *                          Used "CF" consistently
 *                          Eliminated redundant cch fields
 *      09-Feb-1994 chuckst merged in some related global constants
 *      09-Mar-1994 bens    Add RESERVE defintions (for encryption)
 *      17-Mar-1994 bens    Improve comments about split CFDATA structures
 *      25-Mar-1994 bens    Add cabinet set ID
 *      13-May-1994 bens    Define bad value for iCabinet
 *
 *  Overview:
 *      This file contains definitions for the Diamond Cabinet File format.
 *      A Cabinet File exists to store one or more files.  Usually these
 *      files have been compressed, but that is not required.  It is also
 *      possible for a cabinet file to contain only a portion of a larger
 *      file.
 *
 *      In designing this format, the following goals where achieved:
 *      1) Minimize overhead in the CF format
 *         ==> Where ever possible BYTEs or USHORTs were used, rather
 *             than using LONGs, even though the latter would be easier
 *             to manipulate on certain RISC platforms.
 *      2) Support little-endian and big-endian byte ordering.
 *         ==> For simplicity on x86 systems, multi-byte numbers are
 *             stored in a little-endian form, but the code to read and
 *             write these numbers operates correctly on either type of
 *             computer.
 *
 *      A cabinet file contains the following structures in the following
 *      order:
 *          Name            Description
 *          -----------     -------------------
 *          CFHEADER        Cabinet description
 *            [CFRESERVE]   Optional RESERVED control information in CFHEADER
 *          CFFOLDER(s)     Folder descriptions
 *            [reserved]    Optional RESERVED data per folder
 *          CFFILE(s)       File descriptions
 *          CFDATA(s)       Data blocks
 *            [reserved]    Optional RESERVED data per data block
 *
 *  Data Integrity Strategy:
 *      The Cabinet File has built-in data integrity checks, since it is
 *      possible for customers to have damaged diskettes, or for accidental
 *      or malicious damage to occur.  Rather than doing an individual
 *      checksum for the entire cabinet file (which would have a dramatic
 *      impact on the speed of installation from floppy disk, since the
 *      entire file would need to be read), we have per-component
 *      checksums, and compute and check them as we read the various
 *      components of the file.
 *
 *      1)  Checksum CFHEADER
 *      2)  Store cabinet file length in CFHEADER (to detect file truncation)
 *      3)  Checksum entire set of CFFOLDER structures
 *      4)  Checksum entire set of CFFILE structures
 *      5)  Checksum each (compressed) data block independantly
 *
 *      This approach allows us to avoid reading unnecessary parts of the
 *      file cabinet (though reading all of CFFOLDER and CFFILE structures
 *      would otherwise not be required in all cases), while still providing
 *      adequate integrity checking.
 */

#ifndef INCLUDED_CABINET
#define INCLUDED_CABINET 1

//** Pack structures tightly in cabinet files!
#pragma pack(1)


/***    verCF - Cabinet File format version
 *
 *      The low-order byte is interpreted as a decimal number for the minor
 *      (1/100ths) portion of the version number.
 *      The high-order byte is interpreted as a decimal number for the major
 *      portion of the version number.
 *
 *      Examples:
 *          0x0000  0.00
 *          0x010A  1.10
 *          0x0410  4.16
 *
 *      History:
 *          1.01    Original
 *          1.02    Added flags field, changed signature
 *          1.03    Added setId,iCabinet so FDI can ensure correct cabinet
 *                      on continuation.
 */
#define verCF           0x0103      // CF version 1.03


/***    Various cabinet file limits
 *
 */
#define cMAX_FOLDERS_PER_CABINET    (ifoldMASK-1)
#define cMAX_FILES_PER_CABINET      65535


/***    cbRESERVE_XXX_MAX - Maximum size of RESERVE sections
 *
 *  NOTE: cbRESERVE_HEADER_MAX is a fair bit less than 64K because in
 *        the 16-bit version of this code, we want to have a USHORT
 *        variable that holds the size of the CFHEADER structure +
 *        the size of the CFRESERVE structure + the size of the per-header
 *        reserved data.
 */
//BUGBUG 16-Mar-1994 bens Define better bound for cbRESERVE_HEADER_MAX
#define cbRESERVE_HEADER_MAX        60000   // Fits in a USHORT
#define cbRESERVE_FOLDER_MAX          255   // Fits in a BYTE
#define cbRESERVE_DATA_MAX            255   // Fits in a BYTE


/***    ifoldXXXX - Special values for CFFILE.iFolder
 *
 */
#define ifoldMASK                    0xFFFC  // Low two bits zero
#define ifoldCONTINUED_FROM_PREV     0xFFFD
#define ifoldCONTINUED_TO_NEXT       0xFFFE
#define ifoldCONTINUED_PREV_AND_NEXT 0xFFFF

#define IS_CONTD_FORWARD(ifold) ((ifold & 0xfffe) == ifoldCONTINUED_TO_NEXT)
#define IS_CONTD_BACK(ifold) ((ifold & 0xfffd) == ifoldCONTINUED_FROM_PREV)


#ifndef MAKESIG
/***    MAKESIG - Construct a 4 byte signature
 *
 *  Entry:
 *      ch1,ch2,ch3,ch4 - four characters
 *
 *  Exit:
 *      returns unsigned long
 */
#define MAKESIG(ch1,ch2,ch3,ch4)          \
          (  ((unsigned long)ch1)      +  \
            (((unsigned long)ch2)<< 8) +  \
            (((unsigned long)ch3)<<16) +  \
            (((unsigned long)ch4)<<24) )
#endif // !MAKESIG


#define sigCFHEADER MAKESIG('M','S','C','F')  // CFHEADER signature


/***    cfhdrXXX - bit flags for cfheader.flags field
 *
 */
#define cfhdrPREV_CABINET       0x0001  // Set if previous cab/disk present
#define cfhdrNEXT_CABINET       0x0002  // Set if next cab/disk present
#define cfhdrRESERVE_PRESENT    0x0004  // Set if RESERVE_CONTROL is present

typedef unsigned long  CHECKSUM; /* checksum */
typedef unsigned long  UOFF;     /* uoff - uncompressed offset */
typedef unsigned long  COFF;     /* coff - cabinet file offset */

/***    CFHEADER - Cabinet File Header
 *
 */
typedef struct {
//**    LONGs are first, to ensure alignment
    long        sig;            // Cabinet File identification string
    CHECKSUM    csumHeader;     // Structure checksum (excluding csumHeader!)
    long        cbCabinet;      // Total length of file (consistency check)
    CHECKSUM    csumFolders;    // Checksum of CFFOLDER list
    COFF        coffFiles;      // Location in cabinet file of CFFILE list
    CHECKSUM    csumFiles;      // Checksum of CFFILE list

//**    SHORTs are next, to ensure alignment
    USHORT      version;        // Cabinet File version (verCF)
    USHORT      cFolders;       // Count of folders (CFIFOLDERs) in cabinet
    USHORT      cFiles;         // Count of files (CFIFILEs) in cabinet
    USHORT      flags;          // Flags to indicate optional data presence
    USHORT      setID;          // Cabinet set ID (identifies set of cabinets)
    USHORT      iCabinet;       // Cabinet number in set (0 based)
#define iCABINET_BAD    0xFFFF  // Illegal number for iCabinet

//**    If flags has the cfhdrRESERVE_PRESENT bit set, then a CFRESERVE
//      structure appears here, followed possibly by some CFHEADER reserved
//      space. The CFRESERVE structure has fields to define how much reserved
//      space is present in the CFHEADER, CFFOLDER, and CFDATA structures.
//      If CFRESERVE.cbCFHeader is non-zero, then abReserve[] immediately
//      follows the CFRESERVE structure.  Note that all of these sizes are
//      multiples of 4 bytes, to ensure structure alignment!
//
//  CFRESERVE   cfres;          // Reserve information
//  BYTE        abReserve[];    // Reserved data space
//

//**    The following fields presence depends upon the settings in the flags
//      field above.  If cfhdrPREV_CABINET is set, then there are two ASCIIZ
//      strings to describe the previous disk and cabinet.
//
//      NOTE: This "previous" cabinet is not necessarily the immediately
//            preceding cabinet!  While it usually will be, if a file is
//            continued into the current cabinet, then the "previous"
//            cabinet identifies the cabinet where the folder that contains
//            this file *starts*!  For example, if EXCEL.EXE starts in
//            cabinet excel.1 and is continued through excel.2 to excel.3,
//            then cabinet excel.3 will point back to *cabinet.1*, since
//            that is where you have to start in order to extract EXCEL.EXE.
//
//  char    szCabinetPrev[];    // Prev Cabinet filespec
//  char    szDiskPrev[];       // Prev descriptive disk name
//
//      Similarly, If cfhdrNEXT_CABINET is set, then there are two ASCIIZ
//      strings to describe the next disk and cabinet:
//
//  char    szCabinetNext[];    // Next Cabinet filespec
//  char    szDiskNext[];       // Next descriptive disk name
//
} CFHEADER; /* cfheader */


/***    CFRESERVE - Cabinet File Reserved data information
 *
 *  This structure is present in the middle of the CFHEADER structure if
 *  CFHEADER.flags has the cfhdrRESERVE_PRESENT bit set.  This structure
 *  defines the sizes of all the reserved data sections in the CFHEADER,
 *  CFFOLDER, and CFDATA structures.
 *
 *  These reserved sizes can be zero (although it would be silly to have
 *  all of them be zero), but otherwise must be a multiple of 4, to ensure
 *  structure alignment for RISC machines.
 */
typedef struct {
    USHORT  cbCFHeader;         // Size of abReserve in CFHEADER structure
    BYTE    cbCFFolder;         // Size of abReserve in CFFOLDER structure
    BYTE    cbCFData;           // Size of abReserve in CFDATA   structure
} CFRESERVE; /* cfreserve */

#define cbCF_HEADER_BAD     0xFFFF      // Bad value for CFRESERVE.cbCFHeader


/***    CFFOLDER - Cabinet Folder
 *
 *  This structure describes a partial or complete "compression unit".
 *  A folder is by definition a stream of compressed data.  To retrieve
 *  an uncompressed data from a folder, you *must* start decompressing
 *  the data at the start of the folder, regardless of how far into the
 *  folder the data you want actually starts.
 *
 *  Folders may start in one cabinet, and continue on to one or more
 *  succeeding cabinets.  In general, if a folder has been continued over
 *  a cabinet boundary, Diamond/FCI will terminate that folder as soon as
 *  the current file has been completely compressed.  Generally this means
 *  that a folder would span at most two cabinets, but if the file is really
 *  large, it could span more than two cabinets.
 *
 *  Note: CFFOLDERs actually refer to folder *fragments*, not necessarily
 *        complete folders.  You know that a CFFOLDER is the beginning of a
 *        folder (as opposed to a continuation in a subsequent cabinet file)
 *        if a file starts in it (i.e., the CFFILE.uoffFolderStart field is
 *        0).
 */
typedef struct {
    COFF    coffCabStart;       // Offset in cabinet file of first CFDATA
                                // block for this folder.

    USHORT  cCFData;            // Count of CFDATAs for this folder that
                                //  are actually in this cabinet.  Note that
                                //  a folder can continue into another cabinet
                                //  and have many more CFDATA blocks in that
                                //  cabinet, *and* a folder may have started
                                //  in a previous cabinet.  This count is
                                //  only of CFDATAs for this folder that are
                                //  (at least partially) in this cabinet.

    short   typeCompress;       // Indicates compression type for all CFDATA
                                //   blocks for this folder.  The valid values
                                //   are defined in the types.h built into
                                //   fci.h/fdi.h.

//**    If CFHEADER.flags has the cfhdrRESERVE_PRESENT bit set, and
//      CFRESERVE.cbCFFolder is non-zero, then abReserve[] appears here.
//
//  BYTE    abReserve[];        // Reserved data space
//
} CFFOLDER; /* cffolder */



/***    CFFILE - Cabinet File structure describing a single file in the cabinet
 *
 *  NOTE: iFolder is used to indicatate continuation cases, so we have to
 *        calculate the real iFolder by examining the cabinet files:
 *
 *        ifoldCONTINUED_FROM_PREV
 *            This file ends in this cabinet, but is continued from a
 *            previous cabinet.  Therefore, the portion of the file contained
 *            in *this* cabinet *must* start in the first folder.
 *
 *            NOTE: szCabinetPrev is the name of the cabinet where this file
 *                  *starts*, which is not necessarily the immediately
 *                  preceeding cabinet!  Since it only makes sense to
 *                  decompress a file from its start, the starting cabinet
 *                  is what is important!
 *
 *        ifoldCONTINUED_TO_NEXT
 *            This file starts in this cabinet, but is continued to the next
 *            cabinet.  Therfore, this file must start in the *last* folder
 *            in this cabinet.
 *
 *        ifoldCONTINUED_PREV_AND_NEXT
 *            This file is the *middle* portion of a file that started in a
 *            previous cabinet and is continued in the next cabinet.  Since
 *            this cabinet only contain this piece of a single file, there
 *            is only a single folder fragment in this cabinet.
 */
typedef struct {
    long    cbFile;             // Uncompressed size of file

    UOFF    uoffFolderStart;    // Offset in folder IN UNCOMPRESSED BYTES
                                //  of the start of this file

    USHORT  iFolder;            // Index of folder containing this file;
                                //  0 is first folder in this cabinet.
                                //  See ifoldCONTINUED_XXXX values above
                                //  for treatment of continuation files.

    USHORT  date;               // Date stamp in FAT file system format

    USHORT  time;               // Time stamp in FAT file system format

    USHORT  attribs;            // Attribute in FAT file system format

//  char    szName[];           // File name (may include path characters)
} CFFILE; /* cffile */


/***    CFDATA - Cabinet File structure describing a data block
 *
 */
typedef struct {
    CHECKSUM    csum;           // Checksum (excluding this field itself!)
                                //  of this structure and the data that
                                //  follows.  If this CFDATA structure is
                                //  continued to the next cabinet, then
                                //  the value of this field is ignored
                                //  (and set to zero).

    USHORT      cbData;         // Size of ab[] data that resides in the
                                //  current cabinet.  A CFDATA may be split
                                //  across a cabinet boundary, so this
                                //  value indicates only the amount of data
                                //  store in this cabinet.

    USHORT      cbUncomp;       // Uncompressed size of ab[] data; if this
                                //  CFDATA block is continued to the next
                                //  cabinet, then this value is zero!
                                //  If this CFDATA block the remainder of
                                //  of a CFDATA block that started in the
                                //  previous cabinet, then this value is
                                //  the total size of the uncompressed data
                                //  represented by the two CFDATA blocks!

//**    If CFHEADER.flags has the cfhdrRESERVE_PRESENT bit set, and
//      CFRESERVE.cbCFData is non-zero, then abReserve[] appears here.
//
//  BYTE        abReserve[];    // Reserved data space
//

//**    The actual data follows here, cbData bytes in length.
//
//  BYTE        ab[];           // Data
//
} CFDATA; /* cfdata */



//** Attribute Bit to use for Run after extract
#define  RUNATTRIB  0x40


//** Revert to default structure packing!
#pragma pack()

#endif // !INCLUDED_CABINET
