/*
 * NuFX archive manipulation library
 * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved.
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the BSD License, see the file COPYING-LIB.
 *
 * Global internal declarations and definitions.
 */
#ifndef NUFXLIB_NUFXLIBPRIV_H
#define NUFXLIB_NUFXLIBPRIV_H

/* include files that everybody needs */
#include "SysDefs.h"
#include "NufxLib.h"
#include "MiscStuff.h"

#ifdef USE_DMALLOC
/* enable with something like "dmalloc -l logfile -i 100 medium" */
# include "dmalloc.h"
#endif


/*
 * ===========================================================================
 *      NuArchive definition
 * ===========================================================================
 */

/*
 * Archives can be opened in streaming read-only, non-streaming read-only,
 * and non-streaming read-write mode.
 */
typedef enum NuOpenMode {
    kNuOpenUnknown,
    kNuOpenStreamingRO,
    kNuOpenRO,
    kNuOpenRW
} NuOpenMode;
#define Nu_IsStreaming(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO)
#define Nu_IsReadOnly(pArchive)  ((pArchive)->openMode == kNuOpenStreamingRO ||\
                                  (pArchive)->openMode == kNuOpenRO)

#ifdef FOPEN_WANTS_B
# define kNuFileOpenReadOnly        "rb"
# define kNuFileOpenReadWrite       "r+b"
# define kNuFileOpenWriteTrunc      "wb"
# define kNuFileOpenReadWriteCreat  "w+b"
#else
# define kNuFileOpenReadOnly        "r"
# define kNuFileOpenReadWrite       "r+"
# define kNuFileOpenWriteTrunc      "w"
# define kNuFileOpenReadWriteCreat  "w+"
#endif


/*
 * Some NuFX and Binary II definitions.
 */
#define kNuMasterHeaderSize     48  /* size of fixed-length master header */
#define kNuRecordHeaderBaseSize 58  /* size of rec hdr up to variable stuff */
#define kNuThreadHeaderSize     16  /* size of fixed-length thread header */
#define kNuDefaultFilenameThreadSize    32  /* default size of filename thred */
#define kNuDefaultCommentSize   200 /* size of GSHK-mimic comments */
#define kNuBinary2BlockSize     128 /* size of bxy header and padding */
#define kNuSEAOffset            0x2ee5  /* fixed(??) offset to data in SEA */

#define kNuInitialChunkCRC      0x0000  /* start for CRC in LZW/1 chunk */
#define kNuInitialThreadCRC     0xffff  /* start for CRC in v3 thread header */

/* size of general-purpose compression buffer */
#define kNuGenCompBufSize       32768

#define kNuCharLF   0x0a
#define kNuCharCR   0x0d


/*
 * A list of records.  Generally we use one of these for read-only
 * archives, and two for read-write.
 *
 * The "loaded" flag is set when we've made some use of the record set.
 * Relying on "numRecords" won't always work; for example, if the "copy"
 * record set was initialized from "orig", and then had all of its records
 * deleted, you couldn't look at "numRecords" and decide whether it was
 * appropriate to use "orig" or not.
 */
typedef struct NuRecordSet {
    Boolean         loaded;
    uint32_t        numRecords;
    NuRecord*       nuRecordHead;
    NuRecord*       nuRecordTail;
} NuRecordSet;

/*
 * Archive state.
 */
struct NuArchive {
    uint32_t        structMagic;
    Boolean         busy;

    NuOpenMode      openMode;
    Boolean         newlyCreated;
    UNICHAR*        archivePathnameUNI;     /* pathname or "(stream)" */
    FILE*           archiveFp;
    NuArchiveType   archiveType;

    /* stuff before NuFX; both offsets are from 0, i.e. hdrOff includes junk */
    long            junkOffset;             /* skip past leading junk */
    long            headerOffset;           /* adjustment for BXY/SEA/BSE */

    UNICHAR*        tmpPathnameUNI;         /* temp file, for writes */
    FILE*           tmpFp;

    /* used during initial processing; helps avoid ftell() calls */
    long            currentOffset;

    /* setting this changes Extract into Test */
    Boolean         testMode;

    /* clumsy way of remembering name used for other fork in forked file */
    const UNICHAR*  lastFileCreatedUNI;
    /* clumsy way to avoid trying to create the same subdir several times */
    const UNICHAR*  lastDirCreatedUNI;

    /* master header from the archive */
    NuMasterHeader  masterHeader;           /* original */
    NuMasterHeader  newMasterHeader;        /* working copy during update */

    /* list of records from archive, plus some extra state */
    NuRecordIdx     recordIdxSeed;          /* where the NuRecordIdxs start */
    NuRecordIdx     nextRecordIdx;          /* next record gets this value */
    Boolean         haveToc;                /* set if we have full TOC */
    NuRecordSet     origRecordSet;          /* records from archive */
    NuRecordSet     copyRecordSet;          /* copy of orig, for write ops */
    NuRecordSet     newRecordSet;           /* newly-added records */

    /* state for compression functions */
    uint8_t*        compBuf;                /* large general-purpose buffer */
    void*           lzwCompressState;       /* state for LZW/1 and LZW/2 */
    void*           lzwExpandState;         /* state for LZW/1 and LZW/2 */

    /* options and attributes that the user can set */
    /* (these can be changed by a callback, so don't cache them internally) */
    void*           extraData;              /* application-defined pointer */

    NuValue         valAllowDuplicates;     /* allow dups when adding? */
    NuValue         valConvertExtractedEOL; /* convert EOL during extract? */
    NuValue         valDataCompression;     /* how to compress when adding? */
    NuValue         valDiscardWrapper;      /* remove BNY or SEA header? */
    NuValue         valEOL;                 /* EOL value to convert to */
    NuValue         valHandleExisting;      /* how to deal with existing files*/
    NuValue         valIgnoreCRC;           /* don't compute or test CRCs */
    NuValue         valMaskDataless;        /* alter Records w/o data threads */
    NuValue         valMimicSHK;            /* mimic some ShrinkIt quirks */
    NuValue         valModifyOrig;          /* modify original arc in place? */
    NuValue         valOnlyUpdateOlder;     /* modify original arc in place? */
    NuValue         valStripHighASCII;      /* during EOL conv, strip hi bit? */
    NuValue         valJunkSkipMax;         /* scan this far for header */
    NuValue         valIgnoreLZW2Len;       /* don't verify LZW/II len field */
    NuValue         valHandleBadMac;        /* handle "bad Mac" archives */

    /* callback functions */
    NuCallback      selectionFilterFunc;
    NuCallback      outputPathnameFunc;
    NuCallback      progressUpdaterFunc;
    NuCallback      errorHandlerFunc;
    NuCallback      messageHandlerFunc;
};

#define kNuArchiveStructMagic   0xc0edbabe

#define kNuDefaultRecordName    "UNKNOWN"   /* use ASCII charset */


/*
 * ===========================================================================
 *      ThreadMod definition
 * ===========================================================================
 */

/* operations we can perform on threads in a record */
typedef enum ThreadModKind {
    kNuThreadModUnknown = 0,

    kNuThreadModAdd,
    kNuThreadModUpdate,
    kNuThreadModDelete
} ThreadModKind;

/*
 * We attach a list of these to records we plan to modify.  Care is taken
 * to ensure that they don't conflict, e.g. you can't update a thread
 * right after you delete it, nor delete one you have modified.
 */
struct NuThreadMod {
    union {
        ThreadModKind       kind;

        struct {
            ThreadModKind   kind;
            Boolean         used;
        } generic;

        struct {
            ThreadModKind   kind;
            Boolean         used;
            Boolean         isPresized;
            NuThreadIdx     threadIdx;
            NuThreadID      threadID;
            NuThreadFormat  threadFormat;
            NuDataSource*   pDataSource;
        } add;

        struct {
            ThreadModKind   kind;
            Boolean         used;
            NuThreadIdx     threadIdx;
            NuDataSource*   pDataSource;
        } update;

        struct {
            ThreadModKind   kind;
            Boolean         used;
            NuThreadIdx     threadIdx;
            NuThreadID      threadID;   /* used for watching filename threads */
        } delete;
    } entry;

    struct NuThreadMod*     pNext;
};


/*
 * ===========================================================================
 *      NuFunnel/NuStraw definition
 * ===========================================================================
 */

#define kNuFunnelBufSize    16384

/*
 * File funnel definition.  This is used for writing output to files
 * (so we can do things like pipe compressed output through an LF->CR
 * converter) and archive files (so we can halt compression when the
 * output size exceeds the uncompressed original).  [ for various reasons,
 * I'm not using this on the archive anymore. ]
 *
 * Funnels are unidirectional.  You write data into them with a
 * function call; the top-level action (which is usually compressing or
 * expanding data) reads from the input and crams things into the pipe.
 * We could fully abstract the concept, and write the compression
 * functions so that they operate as a Funnel filter, but it's much
 * easier to write block-oriented compression than stream-oriented (and
 * more to the point, the ShrinkIt LZW functions are very much
 * block-oriented).
 */
typedef struct NuFunnel {
    /* data storage */
    uint8_t*        buffer;         /* kNuFunnelBufSize worth of storage */
    long            bufCount;       /* #of bytes in buffer */

    /* text conversion; if "auto", on first flush we convert to "on" or "off" */
    NuValue         convertEOL;     /* on/off/auto */
    NuValue         convertEOLTo;   /* EOL to switch to */
    NuValue         convertEOLFrom; /* EOL terminator we think we found */
    Boolean         checkStripHighASCII;    /* do we want to check for it? */
    Boolean         doStripHighASCII;   /* strip high ASCII during EOL conv */
    Boolean         lastCR;         /* was last char a CR? */

    Boolean         isFirstWrite;   /* cleared on first write */

#if 0
    uint32_t        inCount;        /* total #of bytes in the top */
    uint32_t        outCount;       /* total #of bytes out the bottom */

    uint32_t        outMax;         /* flag an err when outCount exceeds this */
    Boolean         outMaxExceeded; /* in fact, it's this flag */
#endif

    /* update this when stuff happens */
    NuProgressData* pProgress;

    /* data goeth out here */
    NuDataSink*     pDataSink;
} NuFunnel;


/*
 * File straw definition.  This is used for slurping up input data.
 *
 * Mostly this is an encapsulation of an input source and a progress
 * updater, useful for reading uncompressed data and feeding it to a
 * compressor.  It doesn't make sense to show a thermometer based on
 * compressed output, since we don't know how big the eventual result
 * will be, so we want to do it for the input.
 */
typedef struct NuStraw {
    /* update this when stuff happens */
    NuProgressData* pProgress;

    /* data cometh in here */
    NuDataSource*   pDataSource;

    /* progress update fields */
    uint32_t        lastProgress;
    uint32_t        lastDisplayed;
} NuStraw;

/*NuError Nu_CopyStreamToStream(FILE* outfp, FILE* infp, uint32_t count);*/


/*
 * ===========================================================================
 *      Data source and sink abstractions
 * ===========================================================================
 */

/*
 * DataSource is used when adding data to an archive.
 */

typedef enum NuDataSourceType {
    kNuDataSourceUnknown = 0,
    kNuDataSourceFromFile,
    kNuDataSourceFromFP,
    kNuDataSourceFromBuffer
} NuDataSourceType;

typedef struct NuDataSourceCommon {
    NuDataSourceType    sourceType;
    NuThreadFormat      threadFormat;       /* is it already compressed? */
    uint16_t            rawCrc;             /* crc for already-compressed data*/
    /*Boolean             doClose;            \* close on completion? */
    uint32_t            dataLen;            /* length of data (var for buf) */
    uint32_t            otherLen;           /* uncomp len or preset buf size */
    int                 refCount;           /* so we can copy structs */
} NuDataSourceCommon;

union NuDataSource {
    NuDataSourceType    sourceType;

    NuDataSourceCommon  common;

    struct {
        NuDataSourceCommon  common;
        UNICHAR*            pathnameUNI;
        Boolean             fromRsrcFork;

        /* temp storage; only valid when processing in library */
        FILE*               fp;
    } fromFile;

    struct {
        NuDataSourceCommon  common;
        FILE*               fp;
        long                offset;         /* starting offset */

        NuCallback          fcloseFunc;     /* how to fclose the file */
    } fromFP;

    struct {
        NuDataSourceCommon  common;
        const uint8_t*      buffer;         /* non-const if doClose=true */
        long                offset;         /* starting offset */

        long                curOffset;      /* current offset */
        long                curDataLen;     /* remaining data */

        NuCallback          freeFunc;       /* how to free data */
    } fromBuffer;
};


/*
 * DataSink is used when extracting data from an archive.
 */

typedef enum NuDataSinkType {
    kNuDataSinkUnknown = 0,
    kNuDataSinkToFile,
    kNuDataSinkToFP,
    kNuDataSinkToBuffer,
    kNuDataSinkToVoid
} NuDataSinkType;

typedef struct NuDataSinkCommon {
    NuDataSinkType      sinkType;
    Boolean             doExpand;       /* expand file? */
    NuValue             convertEOL;     /* convert EOL?  (req "expand") */
    uint32_t            outCount;
} NuDataSinkCommon;

union NuDataSink {
    NuDataSinkType      sinkType;

    NuDataSinkCommon    common;

    struct {
        NuDataSinkCommon    common;
        UNICHAR*            pathnameUNI;   /* file to open */
        UNICHAR             fssep;

        /* temp storage; must be NULL except when processing in library */
        FILE*               fp;
    } toFile;

    struct {
        NuDataSinkCommon    common;
        FILE*               fp;
    } toFP;

    struct {
        NuDataSinkCommon    common;
        uint8_t*            buffer;
        uint32_t            bufLen;     /* max amount of data "buffer" holds */
        NuError             stickyErr;
    } toBuffer;
};


/*
 * ===========================================================================
 *      Function prototypes
 * ===========================================================================
 */

/*
 * This is a little unpleasant.  This blob of stuff gets stuffed in as
 * the first arguments to Nu_ReportError, so we don't have to type them
 * in every time we use the function.  It would've been much easier to
 * use a gcc-style varargs macro, but not everybody uses gcc.
 *
 * TODO: Visual C++ has vararg macros now; time to replace this.
 */
#ifdef HAS__FUNCTION__
# define _FUNCTION_    __FUNCTION__
#else
# define _FUNCTION_    ""
#endif

#define NU_BLOB        pArchive, __FILE__, __LINE__, _FUNCTION_, false
#define NU_BLOB_DEBUG  pArchive, __FILE__, __LINE__, _FUNCTION_, true
#define NU_NILBLOB     NULL, __FILE__, __LINE__, _FUNCTION_, false

#ifdef DEBUG_MSGS
# define DebugShowError(err)                                        \
        Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_,    \
            true, err, "(DEBUG)");
#else
# define DebugShowError(err)    ((void)0)
#endif

/*
 * The BailError macro serves two purposes.  First, it's a convenient
 * way to avoid typing, "if (err != kNuErrNone) goto bail;".  Second,
 * when the library is built with debugging enabled, it vitually gives
 * us a stack trace of exiting functions.  This makes it easier to debug
 * problems sent in as screen dumps via e-mail.
 */
#define BailError(err)  {                                           \
            if ((err) != kNuErrNone) {                              \
                /* [should this be debug-only, or all the time?] */ \
                DebugShowError(err);                                \
                goto bail;                                          \
            }                                                       \
        }
#define BailErrorQuiet(err) {                                       \
            if ((err) != kNuErrNone)                                \
                goto bail;                                          \
        }
#define BailNil(val)    {                                           \
            if ((val) == NULL) {                                    \
                err = kNuErrUnexpectedNil;                          \
                BailError(err);                                     \
            }                                                       \
        }
#define BailAlloc(val)  {                                           \
            if ((val) == NULL) {                                    \
                err = kNuErrMalloc;                                 \
                BailError(err);                                     \
            }                                                       \
        }


/*
 * Internal function prototypes and inline functions.
 */

/* Archive.c */
void Nu_MasterHeaderCopy(NuArchive* pArchive, NuMasterHeader* pDstHeader,
    const NuMasterHeader* pSrcHeader);
NuError Nu_GetMasterHeader(NuArchive* pArchive,
    const NuMasterHeader** ppMasterHeader);
NuRecordIdx Nu_GetNextRecordIdx(NuArchive* pArchive);
NuThreadIdx Nu_GetNextThreadIdx(NuArchive* pArchive);
NuError Nu_CopyWrapperToTemp(NuArchive* pArchive);
NuError Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp);
NuError Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp);
NuError Nu_AllocCompressionBufferIFN(NuArchive* pArchive);
NuError Nu_StreamOpenRO(FILE* infp, NuArchive** ppArchive);
NuError Nu_OpenRO(const UNICHAR* archivePathnameUNI, NuArchive** ppArchive);
NuError Nu_OpenRW(const UNICHAR* archivePathnameUNI,
    const UNICHAR* tempPathnameUNI, uint32_t flags, NuArchive** ppArchive);
NuError Nu_WriteMasterHeader(NuArchive* pArchive, FILE* fp,
    NuMasterHeader* pMasterHeader);
NuError Nu_Close(NuArchive* pArchive);
NuError Nu_Abort(NuArchive* pArchive);
NuError Nu_RenameTempToArchive(NuArchive* pArchive);
NuError Nu_DeleteArchiveFile(NuArchive* pArchive);

/* ArchiveIO.c */
uint8_t Nu_ReadOneC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc);
uint8_t Nu_ReadOne(NuArchive* pArchive, FILE* fp);
void Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uint8_t val, uint16_t* pCrc);
void Nu_WriteOne(NuArchive* pArchive, FILE* fp, uint8_t val);
uint16_t Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc);
uint16_t Nu_ReadTwo(NuArchive* pArchive, FILE* fp);
void Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, uint16_t val, uint16_t* pCrc);
void Nu_WriteTwo(NuArchive* pArchive, FILE* fp, uint16_t val);
uint32_t Nu_ReadFourC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc);
uint32_t Nu_ReadFour(NuArchive* pArchive, FILE* fp);
void Nu_WriteFourC(NuArchive* pArchive, FILE* fp, uint32_t val, uint16_t* pCrc);
void Nu_WriteFour(NuArchive* pArchive, FILE* fp, uint32_t val);
NuDateTime Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc);
NuDateTime Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, uint16_t* pCrc);
void Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime,
    uint16_t* pCrc);
void Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime);
void Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count,
    uint16_t* pCrc);
void Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count);
void Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer,
    long count, uint16_t* pCrc);
void Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer,
    long count);
NuError Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp);
NuError Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset,
    int ptrname);
NuError Nu_RewindArchive(NuArchive* pArchive);

/* Bzip2.c */
NuError Nu_CompressBzip2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_ExpandBzip2(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc);

/* Charset.c */
size_t Nu_ConvertMORToUNI(const char* stringMOR, UNICHAR* bufUNI,
    size_t bufSize);
UNICHAR* Nu_CopyMORToUNI(const char* stringMOR);
size_t Nu_ConvertUNIToMOR(const UNICHAR* stringUNI, char* bufMOR,
    size_t bufSize);

/* Compress.c */
NuError Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource,
    NuThreadID threadID, NuThreadFormat sourceFormat,
    NuThreadFormat targetFormat, NuProgressData* progressData, FILE* dstFp,
    NuThread* pThread);
NuError Nu_CopyPresizedToArchive(NuArchive* pArchive,
    NuDataSource* pDataSource, NuThreadID threadID, FILE* dstFp,
    NuThread* pThread, char** ppSavedCopy);

/* Crc16.c */
extern const uint16_t gNuCrc16Table[256];
uint16_t Nu_CalcCRC16(uint16_t seed, const uint8_t* ptr, int count);
/*
 * Update the CRC-16.
 *
 * _val (uint8_t) is the byte to add to the CRC.  It's evaluated once.
 * _crc (uint16_t) is the previous CRC.  It's evaluated twice.
 * Returns the updated CRC as a uint16_t.
 */
#define Nu_UpdateCRC16(_val, _crc) \
    (gNuCrc16Table[(((_crc) >> 8) & 0xff) ^ (_val)] ^ ((_crc) << 8))

/* Debug.c */
#if defined(DEBUG_MSGS) || !defined(NDEBUG)
void Nu_DebugDumpAll(NuArchive* pArchive);
void Nu_DebugDumpThread(const NuThread* pThread);
#endif

/* Deferred.c */
NuError Nu_ThreadModAdd_New(NuArchive* pArchive, NuThreadID threadID,
    NuThreadFormat threadFormat, NuDataSource* pDataSource,
    NuThreadMod** ppThreadMod);
NuError Nu_ThreadModUpdate_New(NuArchive* pArchive, NuThreadIdx threadIdx,
    NuDataSource* pDataSource, NuThreadMod** ppThreadMod);
NuError Nu_ThreadModDelete_New(NuArchive* pArchive, NuThreadIdx threadIdx,
    NuThreadID threadID, NuThreadMod** ppThreadMod);
void Nu_ThreadModFree(NuArchive* pArchive, NuThreadMod* pThreadMod);
NuError Nu_ThreadModAdd_FindByThreadID(const NuRecord* pRecord,
    NuThreadID threadID, NuThreadMod** ppThreadMod);
void Nu_FreeThreadMods(NuArchive* pArchive, NuRecord* pRecord);
NuThreadMod* Nu_ThreadMod_FindByThreadIdx(const NuRecord* pRecord,
    NuThreadIdx threadIdx);
NuError Nu_Flush(NuArchive* pArchive, uint32_t* pStatusFlags);

/* Deflate.c */
NuError Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc);

/* Expand.c */
NuError Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel);

/* FileIO.c */
void Nu_SetCurrentDateTime(NuDateTime* pDateTime);
Boolean Nu_IsOlder(const NuDateTime* pWhen1, const NuDateTime* pWhen2);
NuError Nu_OpenOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, const UNICHAR* newPathnameUNI, UNICHAR newFssep,
    FILE** pFp);
NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
    FILE* fp, const UNICHAR* pathnameUNI);
NuError Nu_OpenInputFile(NuArchive* pArchive, const UNICHAR* pathnameUNI,
    Boolean openRsrc, FILE** pFp);
NuError Nu_DeleteFile(const UNICHAR* pathnameUNI);
NuError Nu_RenameFile(const UNICHAR* fromPathUNI, const UNICHAR* toPathUNI);
NuError Nu_FTell(FILE* fp, long* pOffset);
NuError Nu_FSeek(FILE* fp, long offset, int ptrname);
NuError Nu_FRead(FILE* fp, void* buf, size_t nbyte);
NuError Nu_FWrite(FILE* fp, const void* buf, size_t nbyte);
NuError Nu_CopyFileSection(NuArchive* pArchive, FILE* dstFp, FILE* srcFp,
    long length);
NuError Nu_GetFileLength(NuArchive* pArchive, FILE* fp, long* pLength);
NuError Nu_TruncateOpenFile(FILE* fp, long length);

/* Funnel.c */
NuError Nu_ProgressDataInit_Compress(NuArchive* pArchive,
    NuProgressData* pProgressData, const NuRecord* pRecord,
    const UNICHAR* origPathnameUNI, const UNICHAR* pathnameUNI);
NuError Nu_ProgressDataInit_Expand(NuArchive* pArchive,
    NuProgressData* pProgressData, const NuRecord* pRecord,
    const UNICHAR* newPathnameUNI, UNICHAR newFssep,
    const UNICHAR* origPathnameUNI, NuValue convertEOL);
NuError Nu_SendInitialProgress(NuArchive* pArchive, NuProgressData* pProgress);

NuError Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink,
    NuValue convertEOL, NuValue convertEOLTo, NuProgressData* pProgress,
    NuFunnel** ppFunnel);
NuError Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel);
/*void Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, uint32_t maxBytes);*/
NuError Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel,
    const uint8_t* buffer, uint32_t count);
NuError Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel);
NuError Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw,
    NuThreadFormat threadFormat, uint32_t sourceLen);
NuError Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel,
    const NuThread* pThread);
NuError Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state);
NuError Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel);
Boolean Nu_FunnelGetDoExpand(NuFunnel* pFunnel);

NuError Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource,
    NuProgressData* pProgress, NuStraw** ppStraw);
NuError Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw);
NuError Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state);
NuError Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw);
NuError Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uint8_t* buffer,
    long len);
NuError Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw);

/* Lzc.c */
NuError Nu_CompressLZC12(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_CompressLZC16(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_ExpandLZC(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc);

/* Lzw.c */
NuError Nu_CompressLZW1(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_CompressLZW2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_ExpandLZW(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel,
    uint16_t* pThreadCrc);

/* MiscUtils.c */
/*extern const char* kNufxLibName;*/
extern NuCallback gNuGlobalErrorMessageHandler;
const char* Nu_StrError(NuError err);
void Nu_ReportError(NuArchive* pArchive, const char* file, int line,
    const char* function, Boolean isDebug, NuError err,
    const UNICHAR* format, ...)
    #if defined(__GNUC__)
        __attribute__ ((format(printf, 7, 8)))
    #endif
    ;
#ifdef USE_DMALLOC  /* want file and line numbers for calls */
# define Nu_Malloc(archive, size) malloc(size)
# define Nu_Calloc(archive, size) calloc(1, size)
# define Nu_Realloc(archive, ptr, size) realloc(ptr, size)
# define Nu_Free(archive, ptr) (ptr != NULL ? free(ptr) : (void)0)
#else
void* Nu_Malloc(NuArchive* pArchive, size_t size);
void* Nu_Calloc(NuArchive* pArchive, size_t size);
void* Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size);
void Nu_Free(NuArchive* pArchive, void* ptr);
#endif
NuResult Nu_InternalFreeCallback(NuArchive* pArchive, void* args);

/* Record.c */
void Nu_RecordAddThreadMod(NuRecord* pRecord, NuThreadMod* pThreadMod);
Boolean Nu_RecordIsEmpty(NuArchive* pArchive, const NuRecord* pRecord);
Boolean Nu_RecordSet_GetLoaded(const NuRecordSet* pRecordSet);
uint32_t Nu_RecordSet_GetNumRecords(const NuRecordSet* pRecordSet);
void Nu_RecordSet_SetNumRecords(NuRecordSet* pRecordSet, uint32_t val);
void Nu_RecordSet_IncNumRecords(NuRecordSet* pRecordSet);
NuRecord* Nu_RecordSet_GetListHead(const NuRecordSet* pRecordSet);
NuRecord** Nu_RecordSet_GetListHeadPtr(NuRecordSet* pRecordSet);
NuRecord* Nu_RecordSet_GetListTail(const NuRecordSet* pRecordSet);
Boolean Nu_RecordSet_IsEmpty(const NuRecordSet* pRecordSet);
NuError Nu_RecordSet_FreeAllRecords(NuArchive* pArchive,
    NuRecordSet* pRecordSet);
NuError Nu_RecordSet_DeleteRecordPtr(NuArchive* pArchive,
    NuRecordSet* pRecordSet, NuRecord** ppRecord);
NuError Nu_RecordSet_DeleteRecord(NuArchive* pArchive, NuRecordSet* pRecordSet,
    NuRecord* pRecord);
NuError Nu_RecordSet_Clone(NuArchive* pArchive, NuRecordSet* pDstSet,
    const NuRecordSet* pSrcSet);
NuError Nu_RecordSet_MoveAllRecords(NuArchive* pArchive, NuRecordSet* pDstSet,
    NuRecordSet* pSrcSet);
NuError Nu_RecordSet_FindByIdx(const NuRecordSet* pRecordSet, NuRecordIdx rec,
    NuRecord** ppRecord);
NuError Nu_RecordSet_FindByThreadIdx(NuRecordSet* pRecordSet,
    NuThreadIdx threadIdx, NuRecord** ppRecord, NuThread** ppThread);
NuError Nu_RecordSet_ReplaceRecord(NuArchive* pArchive, NuRecordSet* pBadSet,
    NuRecord* pBadRecord, NuRecordSet* pGoodSet, NuRecord** ppGoodRecord);
Boolean Nu_ShouldIgnoreBadCRC(NuArchive* pArchive, const NuRecord* pRecord,
    NuError err);
NuError Nu_WriteRecordHeader(NuArchive* pArchive, NuRecord* pRecord, FILE* fp);
NuError Nu_GetTOCIfNeeded(NuArchive* pArchive);
NuError Nu_StreamContents(NuArchive* pArchive, NuCallback contentFunc);
NuError Nu_StreamExtract(NuArchive* pArchive);
NuError Nu_StreamTest(NuArchive* pArchive);
NuError Nu_Contents(NuArchive* pArchive, NuCallback contentFunc);
NuError Nu_Extract(NuArchive* pArchive);
NuError Nu_ExtractRecord(NuArchive* pArchive, NuRecordIdx recIdx);
NuError Nu_Test(NuArchive* pArchive);
NuError Nu_TestRecord(NuArchive* pArchive, NuRecordIdx recIdx);
NuError Nu_GetRecord(NuArchive* pArchive, NuRecordIdx recordIdx,
    const NuRecord** ppRecord);
NuError Nu_GetRecordIdxByName(NuArchive* pArchive, const char* nameMOR,
    NuRecordIdx* pRecordIdx);
NuError Nu_GetRecordIdxByPosition(NuArchive* pArchive, uint32_t position,
    NuRecordIdx* pRecordIdx);
NuError Nu_FindRecordForWriteByIdx(NuArchive* pArchive, NuRecordIdx recIdx,
    NuRecord** ppFoundRecord);
NuError Nu_AddFile(NuArchive* pArchive, const UNICHAR* pathnameUNI,
    const NuFileDetails* pFileDetails, Boolean fromRsrcFork,
    NuRecordIdx* pRecordIdx);
NuError Nu_AddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails,
    NuRecordIdx* pRecordIdx, NuRecord** ppRecord);
NuError Nu_Rename(NuArchive* pArchive, NuRecordIdx recIdx,
    const char* pathnameMOR, char fssepMOR);
NuError Nu_SetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx,
    const NuRecordAttr* pRecordAttr);
NuError Nu_Delete(NuArchive* pArchive);
NuError Nu_DeleteRecord(NuArchive* pArchive, NuRecordIdx rec);

/* SourceSink.c */
NuError Nu_DataSourceFile_New(NuThreadFormat threadFormat,
    uint32_t otherLen, const UNICHAR* pathnameUNI, Boolean isFromRsrcFork,
    NuDataSource** ppDataSource);
NuError Nu_DataSourceFP_New(NuThreadFormat threadFormat,
    uint32_t otherLen, FILE* fp, long offset, long length,
    NuCallback fcloseFunc, NuDataSource** ppDataSource);
NuError Nu_DataSourceBuffer_New(NuThreadFormat threadFormat,
    uint32_t otherLen, const uint8_t* buffer, long offset, long length,
    NuCallback freeFunc, NuDataSource** ppDataSource);
NuDataSource* Nu_DataSourceCopy(NuDataSource* pDataSource);
NuError Nu_DataSourceFree(NuDataSource* pDataSource);
NuDataSourceType Nu_DataSourceGetType(const NuDataSource* pDataSource);
NuThreadFormat Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource);
uint32_t Nu_DataSourceGetDataLen(const NuDataSource* pDataSource);
uint32_t Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource);
void Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen);
uint16_t Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource);
void Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, uint16_t crc);
NuError Nu_DataSourcePrepareInput(NuArchive* pArchive,
    NuDataSource* pDataSource);
void Nu_DataSourceUnPrepareInput(NuArchive* pArchive,
    NuDataSource* pDataSource);
const char* Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource);
NuError Nu_DataSourceGetBlock(NuDataSource* pDataSource, uint8_t* buf,
    uint32_t len);
NuError Nu_DataSourceRewind(NuDataSource* pDataSource);
NuError Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL,
    const UNICHAR* pathnameUNI, UNICHAR fssep, NuDataSink** ppDataSink);
NuError Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp,
    NuDataSink** ppDataSink);
NuError Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL,
    uint8_t* buffer, uint32_t bufLen, NuDataSink** ppDataSink);
NuError Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL,
    NuDataSink** ppDataSink);
NuError Nu_DataSinkFree(NuDataSink* pDataSink);
NuDataSinkType Nu_DataSinkGetType(const NuDataSink* pDataSink);
Boolean Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink);
NuValue Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink);
uint32_t Nu_DataSinkGetOutCount(const NuDataSink* pDataSink);
const char* Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink);
UNICHAR Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink);
FILE* Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink);
void Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp);
void Nu_DataSinkFile_Close(NuDataSink* pDataSink);
NuError Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uint8_t* buf,
    uint32_t len);
NuError Nu_DataSinkGetError(NuDataSink* pDataSink);

/* Squeeze.c */
NuError Nu_CompressHuffmanSQ(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc);
NuError Nu_ExpandHuffmanSQ(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc);

/* Thread.c */
NuThread* Nu_GetThread(const NuRecord* pRecord, int idx);
void Nu_StripHiIfAllSet(char* str);
Boolean Nu_IsPresizedThreadID(NuThreadID threadID);
Boolean Nu_IsCompressibleThreadID(NuThreadID threadID);
Boolean Nu_ThreadHasCRC(uint16_t recordVersion, NuThreadID threadID);
NuError Nu_FindThreadByIdx(const NuRecord* pRecord, NuThreadIdx thread,
    NuThread** ppThread);
NuError Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID,
    NuThread** ppThread);
void Nu_CopyThreadContents(NuThread* pDstThread, const NuThread* pSrcThread);
NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
    uint16_t* pCrc);
NuError Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp,
    uint16_t* pCrc);
NuError Nu_ComputeThreadData(NuArchive* pArchive, NuRecord* pRecord);
NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord,long numThreads);
NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread);
NuError Nu_SkipThread(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread);
NuError Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx,
    NuDataSink* pDataSink);
NuError Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord,
    NuThreadID threadID);
NuError Nu_AddThread(NuArchive* pArchive, NuRecordIdx rec, NuThreadID threadID,
    NuDataSource* pDataSource, NuThreadIdx* pThreadIdx);
NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx,
    NuDataSource* pDataSource, int32_t* pMaxLen);
NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx);

/* Value.c */
NuError Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue);
NuError Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value);
NuError Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr);
NuThreadFormat Nu_ConvertCompressValToFormat(NuArchive* pArchive,
    NuValue compValue);

/* Version.c */
NuError Nu_GetVersion(int32_t* pMajorVersion, int32_t* pMinorVersion,
    int32_t* pBugVersion, const char** ppBuildDate, const char** ppBuildFlags);

#endif /*NUFXLIB_NUFXLIBPRIV_H*/
