/* $Id: avc_binwr.c,v 1.18 2008/07/23 20:51:38 dmorissette Exp $
 *
 * Name:     avc_binwr.c
 * Project:  Arc/Info vector coverage (AVC)  E00->BIN conversion library
 * Language: ANSI C
 * Purpose:  Binary files access functions (write mode).
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
 *
 **********************************************************************
 * Copyright (c) 1999-2001, Daniel Morissette
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************
 *
 * $Log: avc_binwr.c,v $
 * Revision 1.18  2008/07/23 20:51:38  dmorissette
 * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char
 * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495)
 *
 * Revision 1.17  2006/06/14 16:31:28  daniel
 * Added support for AVCCoverPC2 type (bug 1491)
 *
 * Revision 1.16  2005/06/03 03:49:58  daniel
 * Update email address, website url, and copyright dates
 *
 * Revision 1.15  2001/07/06 05:09:33  daniel
 * Removed #ifdef around fseek to fix NT4 drive problem since ANSI-C requires
 * an fseek() between read and write operations so this applies to Unix too.
 *
 * Revision 1.14  2001/07/06 04:25:00  daniel
 * Fixed a problem writing arc.dir on NT4 networked drives in an empty dir.
 *
 * Revision 1.13  2000/10/16 16:13:29  daniel
 * Fixed sHeader.nPrecision when writing PC TXT files
 *
 * Revision 1.12  2000/09/26 21:40:18  daniel
 * Fixed writing of PC Coverage TXT... they're different from V7 TXT
 *
 * Revision 1.11  2000/09/26 20:21:04  daniel
 * Added AVCCoverPC write
 *
 * Revision 1.10  2000/09/22 19:45:20  daniel
 * Switch to MIT-style license
 *
 * Revision 1.9  2000/05/29 15:31:30  daniel
 * Added Japanese DBCS support
 *
 * Revision 1.8  2000/01/10 02:55:12  daniel
 * Added call to AVCAdjustCaseSensitiveFilename() when creating tables
 *
 * Revision 1.7  1999/12/24 07:18:34  daniel
 * Added PC Arc/Info coverages support
 *
 * Revision 1.6  1999/08/26 17:26:09  daniel
 * Removed printf() messages used in Windows tests
 *
 * Revision 1.5  1999/08/23 18:18:51  daniel
 * Fixed memory leak and some VC++ warnings
 *
 * Revision 1.4  1999/06/08 22:08:14  daniel
 * Modified CreateTable() to overwrite existing arc.dir entries if necessary
 *
 * Revision 1.3  1999/06/08 18:24:32  daniel
 * Fixed some problems with '/' vs '\\' on Windows
 *
 * Revision 1.2  1999/05/17 16:17:36  daniel
 * Added RXP + TXT/TX6/TX7 write support
 *
 * Revision 1.1  1999/05/11 02:34:46  daniel
 * Initial revision
 *
 **********************************************************************/

#include "avc.h"

#include <ctype.h>  /* tolower() */

/*=====================================================================
 * Stuff related to writing the binary coverage files
 *====================================================================*/

static void    _AVCBinWriteCloseTable(AVCBinFile *psFile);

static AVCBinFile *_AVCBinWriteCreateDBFTable(const char *pszPath, 
                                              const char *pszCoverName,
                                              AVCTableDef *psSrcTableDef,
                                              AVCCoverType eCoverType,
                                              int nPrecision, 
                                              AVCDBCSInfo *psDBCSInfo);

/**********************************************************************
 *                          AVCBinWriteCreate()
 *
 * Open a coverage file for writing, write a header if applicable, and
 * initialize the handle to be ready to write objects to the file.
 *
 * pszPath is the coverage (or info directory) path, terminated by
 *         a '/' or a '\\'
 * pszName is the name of the file to create relative to this directory.
 *
 * Note: For most file types except tables, passing pszPath="" and 
 * including the coverage path as part of pszName instead would work.
 *
 * Returns a valid AVCBinFile handle, or NULL if the file could
 * not be created.
 *
 * AVCBinClose() will eventually have to be called to release the 
 * resources used by the AVCBinFile structure.
 **********************************************************************/
AVCBinFile *AVCBinWriteCreate(const char *pszPath, const char *pszName, 
                              AVCCoverType eCoverType,
                              AVCFileType eType, int nPrecision,
                              AVCDBCSInfo *psDBCSInfo)
{
    AVCBinFile   *psFile;
    char         *pszFname = NULL, *pszExt;
    GBool        bCreateIndex = FALSE;
    int          nLen;

    /*-----------------------------------------------------------------
     * Make sure precision value is valid (AVC_DEFAULT_PREC is NOT valid)
     *----------------------------------------------------------------*/
    if (nPrecision!=AVC_SINGLE_PREC && nPrecision!=AVC_DOUBLE_PREC)
    {
        CPLError(CE_Failure, CPLE_IllegalArg,
                 "AVCBinWriteCreate(): Invalid precision parameter "
                 "(value must be AVC_SINGLE_PREC or AVC_DOUBLE_PREC)");
        return NULL;
    }

    /*-----------------------------------------------------------------
     * The case of INFO tables is a bit different...
     * tables have to be created through a separate function.
     *----------------------------------------------------------------*/
    if (eType == AVCFileTABLE)
    {
        CPLError(CE_Failure, CPLE_AssertionFailed,
                 "AVCBinWriteCreate(): TABLEs must be created using "
                 "AVCBinWriteCreateTable()");
        return NULL;
    }

    /*-----------------------------------------------------------------
     * Alloc and init the AVCBinFile struct.
     *----------------------------------------------------------------*/
    psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));

    psFile->eFileType = eType;
    psFile->nPrecision = nPrecision;

    psFile->pszFilename = (char*)CPLMalloc((strlen(pszPath)+strlen(pszName)+1)*
                                           sizeof(char));
    sprintf(psFile->pszFilename, "%s%s", pszPath, pszName);

    psFile->eCoverType = eCoverType;

    /*-----------------------------------------------------------------
     * PRJ files are text files... we won't use the AVCRawBin*()
     * functions for them... the file will be created and closed
     * inside AVCBinWritePrj().
     *----------------------------------------------------------------*/
    if (eType == AVCFilePRJ)
    {
        return psFile;
    }

    /*-----------------------------------------------------------------
     * All other file types share a very similar creation method.
     *----------------------------------------------------------------*/
    psFile->psRawBinFile = AVCRawBinOpen(psFile->pszFilename, "w",
                                 AVC_COVER_BYTE_ORDER(psFile->eCoverType),
                                         psDBCSInfo);

    if (psFile->psRawBinFile == NULL)
    {
        /* Failed to open file... just return NULL since an error message
         * has already been issued by AVCRawBinOpen()
         */
        CPLFree(psFile->pszFilename);
        CPLFree(psFile);
        return NULL;
    }

    /*-----------------------------------------------------------------
     * Create an Index file if applicable for current file type.
     * Yep, we'll have a problem if filenames come in as uppercase, but
     * this should not happen in a normal situation.
     * For each type there is 3 possibilities, e.g. "pal", "pal.adf", "ttt.pal"
     *----------------------------------------------------------------*/
    pszFname = CPLStrdup(psFile->pszFilename);
    nLen = strlen(pszFname);
    if (eType == AVCFileARC &&
        ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "arc", 3)) ||
          (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "arc.adf", 7)) ) )
    {
        strncpy(pszExt, "arx", 3);
        bCreateIndex = TRUE;
    }
    else if ((eType == AVCFilePAL || eType == AVCFileRPL) &&
             ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "pal", 3)) ||
               (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "pal.adf", 7)) ) )
    {
        strncpy(pszExt, "pax", 3);
        bCreateIndex = TRUE;
    }
    else if (eType == AVCFileCNT &&
             ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "cnt", 3)) ||
               (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "cnt.adf", 7)) ) )
    {
        strncpy(pszExt, "cnx", 3);
        bCreateIndex = TRUE;
    }
    else if ((eType == AVCFileTXT || eType == AVCFileTX6) &&
             ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "txt", 3)) ||
               (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "txt.adf", 7)) ) )
    {
        strncpy(pszExt, "txx", 3);
        bCreateIndex = TRUE;
    }

    if (bCreateIndex)
    {
        psFile->psIndexFile = AVCRawBinOpen(pszFname, "w",
                                    AVC_COVER_BYTE_ORDER(psFile->eCoverType),
                                            psDBCSInfo);
    }

    CPLFree(pszFname);

    /*-----------------------------------------------------------------
     * Generate the appropriate headers for the main file and its index
     * if one was created.
     *----------------------------------------------------------------*/
    if (AVCBinWriteHeader(psFile) == -1)
    {
        /* Failed!  Return NULL */
        AVCBinWriteClose(psFile);
        psFile = NULL;
    }

    return psFile;
}


/**********************************************************************
 *                          _AVCBinWriteHeader()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteHeader() instead)
 *
 * Generate a 100 bytes header using the info in psHeader.
 *
 * Note: PC Coverage files have an initial 256 bytes header followed by the
 * regular 100 bytes header.
 *
 * This function assumes that the file pointer is currently located at
 * the beginning of the file.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteHeader(AVCRawBinFile *psFile, AVCBinHeader *psHeader,
                       AVCCoverType eCoverType)
{
    int nStatus = 0;

    if (eCoverType == AVCCoverPC)
    {
        /* PC Coverage header starts with an initial 256 bytes header
         */
        AVCRawBinWriteInt16(psFile, 0x0400);  /* Signature??? */
        AVCRawBinWriteInt32(psFile, psHeader->nLength);

        AVCRawBinWriteZeros(psFile, 250);
    }

    AVCRawBinWriteInt32(psFile, psHeader->nSignature);
    AVCRawBinWriteInt32(psFile, psHeader->nPrecision);
    AVCRawBinWriteInt32(psFile, psHeader->nRecordSize);
    AVCRawBinWriteZeros(psFile, 12);
    AVCRawBinWriteInt32(psFile, psHeader->nLength);

    /* Pad the rest of the header with zeros
     */
    AVCRawBinWriteZeros(psFile, 72);

    if (CPLGetLastErrorNo() != 0)
        nStatus = -1;

    return nStatus;
}


/**********************************************************************
 *                          AVCBinWriteHeader()
 *
 * Write a header to the specified file using the values that apply to
 * this file's type.  The function simply does nothing if it is called
 * for a file type that requires no header.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int AVCBinWriteHeader(AVCBinFile *psFile)
{
    AVCBinHeader sHeader;
    int          nStatus=0;
    GBool        bHeader = TRUE;

    /*-----------------------------------------------------------------
     * Set the appropriate header information for this file type.
     *----------------------------------------------------------------*/
    sHeader.nPrecision = sHeader.nRecordSize = sHeader.nLength = 0;
    sHeader.nSignature = 9994;
    switch(psFile->eFileType)
    {
      case AVCFileARC:
        sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -1 : 1;
        break;
      case AVCFilePAL:
      case AVCFileRPL:
        sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -11 : 11;
        break;
      case AVCFileLAB:
        sHeader.nSignature = 9993;
        sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -2 : 2;
        sHeader.nRecordSize = (psFile->nPrecision == AVC_DOUBLE_PREC)? 28 : 16;
        break;
      case AVCFileCNT:
        sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -14 : 14;
        break;
      case AVCFileTOL:
        /* single prec: tol.adf has no header
         * double prec: par.adf has a header
         */
        if (psFile->nPrecision == AVC_DOUBLE_PREC)
        {
            sHeader.nSignature = 9993;
            sHeader.nPrecision = 40;
            sHeader.nRecordSize = 8;
        }
        else
        {
            bHeader = FALSE;
        }
        break;
      case AVCFileTXT:
      case AVCFileTX6:
        if (psFile->eCoverType == AVCCoverPC)
            sHeader.nPrecision = 1;
        else
            sHeader.nPrecision = (psFile->nPrecision==AVC_DOUBLE_PREC)? -67:67;
        break;
      default:
        /* Other file types don't need a header */
        bHeader = FALSE;
    }

    /*-----------------------------------------------------------------
     * Write a header only if applicable.
     *----------------------------------------------------------------*/
    if (bHeader)
        nStatus = _AVCBinWriteHeader(psFile->psRawBinFile, &sHeader,
                                     psFile->eCoverType);

    /*-----------------------------------------------------------------
     * Write a header for the index file... it is identical to the main
     * file's header.
     *----------------------------------------------------------------*/
    if (nStatus == 0 && bHeader && psFile->psIndexFile)
        nStatus = _AVCBinWriteHeader(psFile->psIndexFile, &sHeader,
                                     psFile->eCoverType);

    return nStatus;
}

/**********************************************************************
 *                          AVCBinWriteClose()
 *
 * Close a coverage file opened for wirting, and release all memory 
 * (object strcut., buffers, etc.) associated with this file.
 **********************************************************************/

void    AVCBinWriteClose(AVCBinFile *psFile)
{
    if (psFile->eFileType == AVCFileTABLE)
    {
        _AVCBinWriteCloseTable(psFile);
        return;
    }

    /*-----------------------------------------------------------------
     * Write the file size (nbr of 2 byte words) in the header at 
     * byte 24 in the 100 byte header (only if applicable)
     * (And write the same value at byte 2-5 in the first header of PC Cover)
     *----------------------------------------------------------------*/
    if (psFile->psRawBinFile &&
        (psFile->eFileType == AVCFileARC ||
         psFile->eFileType == AVCFilePAL ||
         psFile->eFileType == AVCFileRPL ||
         psFile->eFileType == AVCFileLAB ||
         psFile->eFileType == AVCFileCNT ||
         psFile->eFileType == AVCFileTXT ||
         psFile->eFileType == AVCFileTX6 ||
         (psFile->eFileType == AVCFileTOL && 
          psFile->nPrecision == AVC_DOUBLE_PREC) ) )
    {
        GInt32 n32Size;
        n32Size = psFile->psRawBinFile->nCurPos/2;

        if (psFile->eCoverType == AVCCoverPC)
        {
            /* PC Cover... Pad to multiple of 512 bytes and write 2 headers
             * extra bytes at EOF are not included in the size written in
             * header.
             * The first 256 bytes header is not counted in the file size 
             * written in both headers
             */
            n32Size -= 128;  /* minus 256 bytes */

            if (psFile->psRawBinFile->nCurPos%512 != 0)
                AVCRawBinWriteZeros(psFile->psRawBinFile, 
                                    512 - psFile->psRawBinFile->nCurPos%512);
                
            VSIFSeek(psFile->psRawBinFile->fp, 2, SEEK_SET);
            AVCRawBinWriteInt32(psFile->psRawBinFile, n32Size);

            VSIFSeek(psFile->psRawBinFile->fp, 256+24, SEEK_SET);
            AVCRawBinWriteInt32(psFile->psRawBinFile, n32Size);
        }
        else
        {
            /* V7 Cover ... only 1 header */
            VSIFSeek(psFile->psRawBinFile->fp, 24, SEEK_SET);
            AVCRawBinWriteInt32(psFile->psRawBinFile, n32Size);
        }
    }
        
    AVCRawBinClose(psFile->psRawBinFile);
    psFile->psRawBinFile = NULL;

    /*-----------------------------------------------------------------
     * Same for the index file if it exists.
     *----------------------------------------------------------------*/
    if (psFile->psIndexFile)
    {
        GInt32 n32Size;
        n32Size = psFile->psIndexFile->nCurPos/2;

        if (psFile->eCoverType == AVCCoverPC)
        {
            /* PC Cover... Pad to multiple of 512 bytes and write 2 headers
             * extra bytes at EOF are not included in the size written in
             * header
             */
            n32Size -= 128;  /* minus 256 bytes */

            if (psFile->psIndexFile->nCurPos%512 != 0)
                AVCRawBinWriteZeros(psFile->psIndexFile, 
                                    512 - psFile->psIndexFile->nCurPos%512);
                
            VSIFSeek(psFile->psIndexFile->fp, 2, SEEK_SET);
            AVCRawBinWriteInt32(psFile->psIndexFile, n32Size);

            VSIFSeek(psFile->psIndexFile->fp, 256+24, SEEK_SET);
            AVCRawBinWriteInt32(psFile->psIndexFile, n32Size);
        }
        else
        {
            /* V7 Cover ... only 1 header */
            VSIFSeek(psFile->psIndexFile->fp, 24, SEEK_SET);
            AVCRawBinWriteInt32(psFile->psIndexFile, n32Size);
        }

        AVCRawBinClose(psFile->psIndexFile);
        psFile->psIndexFile = NULL;
    }

    CPLFree(psFile->pszFilename);

    CPLFree(psFile);
}



/**********************************************************************
 *                          _AVCBinWriteIndexEntry()
 *
 * (This function is for internal library use... the index entries
 * are automatically handled by the AVCBinWrite*() functions)
 *
 * Write an Index Entry at the current position in the file.
 *
 * Position is relative to the beginning of the file, including the header.
 * Both position and size are specified in number of 2 byte words.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteIndexEntry(AVCRawBinFile *psFile, 
                           int nPosition, int nSize)
{
    AVCRawBinWriteInt32(psFile, nPosition);
    AVCRawBinWriteInt32(psFile, nSize);

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          AVCBinWriteObject()
 *
 * Write a CNT (Polygon Centroid) structure to the fin object to a 
 * coverage file.
 *
 * Simply redirects the call to the right function based on the value
 * of psFile->eFileType.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteObject(AVCBinFile *psFile, void *psObj)
{
    int nStatus = 0;
    switch(psFile->eFileType)
    {
      case AVCFileARC:
        nStatus = AVCBinWriteArc(psFile, (AVCArc *)psObj);
        break;
      case AVCFilePAL:
      case AVCFileRPL:
        nStatus = AVCBinWritePal(psFile, (AVCPal *)psObj);
        break;
      case AVCFileCNT:
        nStatus = AVCBinWriteCnt(psFile, (AVCCnt *)psObj);
        break;
      case AVCFileLAB:
        nStatus = AVCBinWriteLab(psFile, (AVCLab *)psObj);
        break;
      case AVCFileTOL:
        nStatus = AVCBinWriteTol(psFile, (AVCTol *)psObj);
        break;
      case AVCFilePRJ:
        nStatus = AVCBinWritePrj(psFile, (char **)psObj);
        break;
      case AVCFileTXT:
      case AVCFileTX6:
        nStatus = AVCBinWriteTxt(psFile, (AVCTxt *)psObj);
        break;
      case AVCFileRXP:
        nStatus = AVCBinWriteRxp(psFile, (AVCRxp *)psObj);
        break;
      case AVCFileTABLE:
        nStatus = AVCBinWriteTableRec(psFile, (AVCField *)psObj);
        break;
      default:
        CPLError(CE_Failure, CPLE_IllegalArg,
                 "AVCBinWriteObject(): Unsupported file type!");
        nStatus = -1;
    }

    return nStatus;
}


/*=====================================================================
 *                              ARC
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteArc()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteNextArc() instead)
 *
 * Write an Arc structure to the file.
 *
 * The contents of the psArc structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteArc(AVCRawBinFile *psFile, AVCArc *psArc,
                    int nPrecision, AVCRawBinFile *psIndexFile)
{
    int         i, nRecSize, nCurPos;

    nCurPos = psFile->nCurPos/2;  /* Value in 2 byte words */

    AVCRawBinWriteInt32(psFile, psArc->nArcId);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    /*-----------------------------------------------------------------
     * Record size is expressed in 2 byte words, and does not count the
     * first 8 bytes of the ARC entry.
     *----------------------------------------------------------------*/
    nRecSize = (6 * 4 + psArc->numVertices*2 * 
                ((nPrecision == AVC_SINGLE_PREC)? 4 : 8)) / 2;
    AVCRawBinWriteInt32(psFile, nRecSize);
    AVCRawBinWriteInt32(psFile, psArc->nUserId);
    AVCRawBinWriteInt32(psFile, psArc->nFNode);
    AVCRawBinWriteInt32(psFile, psArc->nTNode);
    AVCRawBinWriteInt32(psFile, psArc->nLPoly);
    AVCRawBinWriteInt32(psFile, psArc->nRPoly);
    AVCRawBinWriteInt32(psFile, psArc->numVertices);

    if (nPrecision == AVC_SINGLE_PREC)
    {
        for(i=0; i<psArc->numVertices; i++)
        {
            AVCRawBinWriteFloat(psFile, (float)psArc->pasVertices[i].x);
            AVCRawBinWriteFloat(psFile, (float)psArc->pasVertices[i].y);
        }
    }
    else
    {
        for(i=0; i<psArc->numVertices; i++)
        {
            AVCRawBinWriteDouble(psFile, psArc->pasVertices[i].x);
            AVCRawBinWriteDouble(psFile, psArc->pasVertices[i].y);
        }

    }

    /*-----------------------------------------------------------------
     * Write index entry (arx.adf)
     *----------------------------------------------------------------*/
    if (psIndexFile)
    {
        _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          AVCBinWriteArc()
 *
 * Write the next Arc structure to the file.
 *
 * The contents of the psArc structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteArc(AVCBinFile *psFile, AVCArc *psArc)
{
    if (psFile->eFileType != AVCFileARC)
        return -1;

    return _AVCBinWriteArc(psFile->psRawBinFile, psArc,
                           psFile->nPrecision, psFile->psIndexFile);
}


/*=====================================================================
 *                              PAL
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWritePal()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWritePal() instead)
 *
 * Write a PAL (Polygon Arc List) structure to the file.
 *
 * The contents of the psPal structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWritePal(AVCRawBinFile *psFile, AVCPal *psPal, 
                    int nPrecision, AVCRawBinFile *psIndexFile)
{
    int i, nRecSize, nCurPos;

    nCurPos = psFile->nCurPos/2;  /* Value in 2 byte words */

    AVCRawBinWriteInt32(psFile, psPal->nPolyId);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    /*-----------------------------------------------------------------
     * Record size is expressed in 2 byte words, and does not count the
     * first 8 bytes of the PAL entry.
     *----------------------------------------------------------------*/
    nRecSize = ( 4 + psPal->numArcs*3 * 4 + 
                4 * ((nPrecision == AVC_SINGLE_PREC)? 4 : 8)) / 2;

    AVCRawBinWriteInt32(psFile, nRecSize);

    if (nPrecision == AVC_SINGLE_PREC)
    {
        AVCRawBinWriteFloat(psFile, (float)psPal->sMin.x);
        AVCRawBinWriteFloat(psFile, (float)psPal->sMin.y);
        AVCRawBinWriteFloat(psFile, (float)psPal->sMax.x);
        AVCRawBinWriteFloat(psFile, (float)psPal->sMax.y);
    }
    else
    {
        AVCRawBinWriteDouble(psFile, psPal->sMin.x);
        AVCRawBinWriteDouble(psFile, psPal->sMin.y);
        AVCRawBinWriteDouble(psFile, psPal->sMax.x);
        AVCRawBinWriteDouble(psFile, psPal->sMax.y);
    }

    AVCRawBinWriteInt32(psFile, psPal->numArcs);

    for(i=0; i<psPal->numArcs; i++)
    {
        AVCRawBinWriteInt32(psFile, psPal->pasArcs[i].nArcId);
        AVCRawBinWriteInt32(psFile, psPal->pasArcs[i].nFNode);
        AVCRawBinWriteInt32(psFile, psPal->pasArcs[i].nAdjPoly);
    }

    /*-----------------------------------------------------------------
     * Write index entry (pax.adf)
     *----------------------------------------------------------------*/
    if (psIndexFile)
    {
        _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          AVCBinWritePal()
 *
 * Write a PAL (Polygon Arc List) structure to the file.
 *
 * The contents of the psPal structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWritePal(AVCBinFile *psFile, AVCPal *psPal)
{
    if (psFile->eFileType != AVCFilePAL && psFile->eFileType != AVCFileRPL)
        return -1;

    return _AVCBinWritePal(psFile->psRawBinFile, psPal,
                           psFile->nPrecision, psFile->psIndexFile);
}

/*=====================================================================
 *                              CNT
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteCnt()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteCnt() instead)
 *
 * Write a CNT (Polygon Centroid) structure to the file.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteCnt(AVCRawBinFile *psFile, AVCCnt *psCnt, 
                              int nPrecision, AVCRawBinFile *psIndexFile)
{
    int i, nRecSize, nCurPos;

    nCurPos = psFile->nCurPos/2;  /* Value in 2 byte words */

    AVCRawBinWriteInt32(psFile, psCnt->nPolyId);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    /*-----------------------------------------------------------------
     * Record size is expressed in 2 byte words, and does not count the
     * first 8 bytes of the CNT entry.
     *----------------------------------------------------------------*/
    nRecSize = ( 4 + psCnt->numLabels * 4 + 
                 2 * ((nPrecision == AVC_SINGLE_PREC)? 4 : 8)) / 2;

    AVCRawBinWriteInt32(psFile, nRecSize);

    if (nPrecision == AVC_SINGLE_PREC)
    {
        AVCRawBinWriteFloat(psFile, (float)psCnt->sCoord.x);
        AVCRawBinWriteFloat(psFile, (float)psCnt->sCoord.y);
    }
    else
    {
        AVCRawBinWriteDouble(psFile, psCnt->sCoord.x);
        AVCRawBinWriteDouble(psFile, psCnt->sCoord.y);
    }

    AVCRawBinWriteInt32(psFile, psCnt->numLabels);

    for(i=0; i<psCnt->numLabels; i++)
    {
        AVCRawBinWriteInt32(psFile, psCnt->panLabelIds[i]);
    }

    /*-----------------------------------------------------------------
     * Write index entry (cnx.adf)
     *----------------------------------------------------------------*/
    if (psIndexFile)
    {
        _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          AVCBinWriteCnt()
 *
 * Write a CNT (Polygon Centroid) structure to the file.
 *
 * The contents of the psCnt structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteCnt(AVCBinFile *psFile, AVCCnt *psCnt)
{
    if (psFile->eFileType != AVCFileCNT)
        return -1;

    return _AVCBinWriteCnt(psFile->psRawBinFile, psCnt,
                           psFile->nPrecision, psFile->psIndexFile);
}

/*=====================================================================
 *                              LAB
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteLab()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteLab() instead)
 *
 * Write a LAB (Centroid Label) structure to the file.
 *
 * The contents of the psLab structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteLab(AVCRawBinFile *psFile, AVCLab *psLab, 
                    int nPrecision)
{

    AVCRawBinWriteInt32(psFile, psLab->nValue);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    AVCRawBinWriteInt32(psFile, psLab->nPolyId);

    if (nPrecision == AVC_SINGLE_PREC)
    {
        AVCRawBinWriteFloat(psFile, (float)psLab->sCoord1.x);
        AVCRawBinWriteFloat(psFile, (float)psLab->sCoord1.y);
        AVCRawBinWriteFloat(psFile, (float)psLab->sCoord2.x);
        AVCRawBinWriteFloat(psFile, (float)psLab->sCoord2.y);
        AVCRawBinWriteFloat(psFile, (float)psLab->sCoord3.x);
        AVCRawBinWriteFloat(psFile, (float)psLab->sCoord3.y);
    }
    else
    {
        AVCRawBinWriteDouble(psFile, psLab->sCoord1.x);
        AVCRawBinWriteDouble(psFile, psLab->sCoord1.y);
        AVCRawBinWriteDouble(psFile, psLab->sCoord2.x);
        AVCRawBinWriteDouble(psFile, psLab->sCoord2.y);
        AVCRawBinWriteDouble(psFile, psLab->sCoord3.x);
        AVCRawBinWriteDouble(psFile, psLab->sCoord3.y);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}


/**********************************************************************
 *                          AVCBinWriteLab()
 *
 * Write a LAB (Centroid Label) structure to the file.
 *
 * The contents of the psLab structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteLab(AVCBinFile *psFile, AVCLab *psLab)
{
    if (psFile->eFileType != AVCFileLAB)
        return -1;

    return _AVCBinWriteLab(psFile->psRawBinFile, psLab,
                           psFile->nPrecision);
}

/*=====================================================================
 *                              TOL
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteTol()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteTol() instead)
 *
 * Write a TOL (tolerance) structure to the file.
 *
 * The contents of the psTol structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteTol(AVCRawBinFile *psFile, AVCTol *psTol, 
                    int nPrecision)
{

    AVCRawBinWriteInt32(psFile, psTol->nIndex);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    AVCRawBinWriteInt32(psFile, psTol->nFlag);

    if (nPrecision == AVC_SINGLE_PREC)
    {
        AVCRawBinWriteFloat(psFile, (float)psTol->dValue);
    }
    else
    {
        AVCRawBinWriteDouble(psFile, psTol->dValue);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          AVCBinWriteTol()
 *
 * Write a TOL (tolerance) structure to the file.
 *
 * The contents of the psTol structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteTol(AVCBinFile *psFile, AVCTol *psTol)
{
    if (psFile->eFileType != AVCFileTOL)
        return -1;

    return _AVCBinWriteTol(psFile->psRawBinFile, psTol,
                           psFile->nPrecision);
}

/*=====================================================================
 *                              PRJ
 *====================================================================*/

/**********************************************************************
 *                          AVCBinWritePrj()
 *
 * Write a PRJ (Projection info) to the file.
 *
 * Since a PRJ file is a simple text file and there is only ONE projection
 * info per prj.adf file, this function behaves differently from the 
 * other ones... all the job is done here, including creating and closing
 * the output file.
 *
 * The contents of the papszPrj is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWritePrj(AVCBinFile *psFile, char **papszPrj)
{
    if (psFile->eFileType != AVCFilePRJ)
        return -1;

    CSLSave(papszPrj, psFile->pszFilename);

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}


/*=====================================================================
 *                              TXT/TX6/TX7
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteTxt()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteTxt() instead)
 *
 * Write a TXT/TX6/TX7 (Annotation) structure to the file.
 *
 * The contents of the psTxt structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteTxt(AVCRawBinFile *psFile, AVCTxt *psTxt, 
                              int nPrecision, AVCRawBinFile *psIndexFile)
{
    int i, nRecSize, nCurPos, nStrLen, numVertices;

    nCurPos = psFile->nCurPos/2;  /* Value in 2 byte words */

    AVCRawBinWriteInt32(psFile, psTxt->nTxtId);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    /*-----------------------------------------------------------------
     * Record size is expressed in 2 byte words, and does not count the
     * first 8 bytes of the TXT entry.
     *----------------------------------------------------------------*/
    /* String uses a multiple of 4 bytes of storage */
    if (psTxt->pszText)
        nStrLen = ((strlen((char*)psTxt->pszText) + 3)/4)*4;
    else
        nStrLen = 0;

    numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
    nRecSize = (112 + 8 + nStrLen + 
                (numVertices*2+3)* ((nPrecision == AVC_SINGLE_PREC)?4:8)) / 2;

    AVCRawBinWriteInt32(psFile, nRecSize);

    AVCRawBinWriteInt32(psFile, psTxt->nUserId );
    AVCRawBinWriteInt32(psFile, psTxt->nLevel );
    AVCRawBinWriteFloat(psFile, psTxt->f_1e2 );
    AVCRawBinWriteInt32(psFile, psTxt->nSymbol );
    AVCRawBinWriteInt32(psFile, psTxt->numVerticesLine );
    AVCRawBinWriteInt32(psFile, psTxt->n28 );
    AVCRawBinWriteInt32(psFile, psTxt->numChars );
    AVCRawBinWriteInt32(psFile, psTxt->numVerticesArrow );

    for(i=0; i<20; i++)
    {
        AVCRawBinWriteInt16(psFile, psTxt->anJust1[i] );
    }
    for(i=0; i<20; i++)
    {
        AVCRawBinWriteInt16(psFile, psTxt->anJust2[i] );
    }

    if (nPrecision == AVC_SINGLE_PREC)
    {
        AVCRawBinWriteFloat(psFile, (float)psTxt->dHeight);
        AVCRawBinWriteFloat(psFile, (float)psTxt->dV2);
        AVCRawBinWriteFloat(psFile, (float)psTxt->dV3);
    }
    else
    {
        AVCRawBinWriteDouble(psFile, psTxt->dHeight);
        AVCRawBinWriteDouble(psFile, psTxt->dV2);
        AVCRawBinWriteDouble(psFile, psTxt->dV3);
    }

    if (nStrLen > 0)
        AVCRawBinWritePaddedString(psFile, nStrLen, psTxt->pszText);

    if (nPrecision == AVC_SINGLE_PREC)
    {
        for(i=0; i<numVertices; i++)
        {
            AVCRawBinWriteFloat(psFile, (float)psTxt->pasVertices[i].x);
            AVCRawBinWriteFloat(psFile, (float)psTxt->pasVertices[i].y);
        }
    }
    else
    {
        for(i=0; i<numVertices; i++)
        {
            AVCRawBinWriteDouble(psFile, psTxt->pasVertices[i].x);
            AVCRawBinWriteDouble(psFile, psTxt->pasVertices[i].y);
        }
    }

    AVCRawBinWriteZeros(psFile, 8);

    /*-----------------------------------------------------------------
     * Write index entry (cnx.adf)
     *----------------------------------------------------------------*/
    if (psIndexFile)
    {
        _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          _AVCBinWritePCCoverageTxt()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteTxt() instead)
 *
 * Write a TXT (Annotation) structure to a AVCCoverPC file.
 *
 * The contents of the psTxt structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * This function assumes that PC Coverages are always single precision.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWritePCCoverageTxt(AVCRawBinFile *psFile, AVCTxt *psTxt, 
                              int nPrecision, AVCRawBinFile *psIndexFile)
{
    int i, nRecSize, nCurPos, nStrLen, numVertices;

    CPLAssert(nPrecision == AVC_SINGLE_PREC);

    nCurPos = psFile->nCurPos/2;  /* Value in 2 byte words */

    AVCRawBinWriteInt32(psFile, psTxt->nTxtId);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    /*-----------------------------------------------------------------
     * Record size is expressed in 2 byte words, and does not count the
     * first 8 bytes of the TXT entry.
     *----------------------------------------------------------------*/
    /* String uses a multiple of 4 bytes of storage,
     * And if text is already a multiple of 4 bytes then we include 4 extra 
     * spaces anyways (was probably a bug in the software!).
     */
    if (psTxt->pszText)
        nStrLen = ((strlen((char*)psTxt->pszText) + 4)/4)*4;
    else
        nStrLen = 4;

    nRecSize = (92 - 8 + nStrLen) / 2;

    AVCRawBinWriteInt32(psFile, nRecSize);
    AVCRawBinWriteInt32(psFile, psTxt->nLevel );

    /*-----------------------------------------------------------------
     * Number of vertices to write:
     * Note that because of the way V7 binary TXT files work, the rest of the
     * lib expects to receive duplicate coords for the first vertex, so
     * we will also receive an additional vertex for that but we won't write
     * it.  We also ignore the arrow vertices if there is any.
     *----------------------------------------------------------------*/
    numVertices = ABS(psTxt->numVerticesLine) -1;
    numVertices = MIN(4, numVertices);  /* Maximum of 4 points */

    AVCRawBinWriteInt32(psFile, numVertices );

    for(i=0; i<numVertices; i++)
    {
        AVCRawBinWriteFloat(psFile, (float)psTxt->pasVertices[i+1].x);
        AVCRawBinWriteFloat(psFile, (float)psTxt->pasVertices[i+1].y);
    }

    AVCRawBinWriteZeros(psFile, (4-numVertices)*4*2 + 28);

    AVCRawBinWriteFloat(psFile, (float)psTxt->dHeight);
    AVCRawBinWriteFloat(psFile, psTxt->f_1e2 );
    AVCRawBinWriteInt32(psFile, psTxt->nSymbol );
    AVCRawBinWriteInt32(psFile, psTxt->numChars );

    if (nStrLen > 0)
        AVCRawBinWritePaddedString(psFile, nStrLen, psTxt->pszText);

    /*-----------------------------------------------------------------
     * Write index entry (cnx.adf)
     *----------------------------------------------------------------*/
    if (psIndexFile)
    {
        _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize);
    }

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}


/**********************************************************************
 *                          AVCBinWriteTxt()
 *
 * Write a TXT/TX6/TX7 (Annotation) structure to the file.
 *
 * The contents of the psTxt structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteTxt(AVCBinFile *psFile, AVCTxt *psTxt)
{
    if (psFile->eFileType != AVCFileTXT && psFile->eFileType != AVCFileTX6)
        return -1;

    /* AVCCoverPC and AVCCoverWeird have a different TXT format than AVCCoverV7
     */
    if (psFile->eCoverType == AVCCoverPC || 
        psFile->eCoverType == AVCCoverWeird)
    {
        return _AVCBinWritePCCoverageTxt(psFile->psRawBinFile, psTxt,
                                         psFile->nPrecision, 
                                         psFile->psIndexFile);
    }
    else
    {
        return _AVCBinWriteTxt(psFile->psRawBinFile, psTxt,
                               psFile->nPrecision, psFile->psIndexFile);
    }
}

/*=====================================================================
 *                              RXP
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteRxp()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteRxp() instead)
 *
 * Write a RXP (Region something...) structure to the file.
 *
 * The contents of the psRxp structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteRxp(AVCRawBinFile *psFile, AVCRxp *psRxp, 
                    int nPrecision)
{

    AVCRawBinWriteInt32(psFile, psRxp->n1);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    AVCRawBinWriteInt32(psFile, psRxp->n2);

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          AVCBinWriteRxp()
 *
 * Write a  RXP (Region something...) structure to the file.
 *
 * The contents of the psRxp structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteRxp(AVCBinFile *psFile, AVCRxp *psRxp)
{
    if (psFile->eFileType != AVCFileRXP)
        return -1;

    return _AVCBinWriteRxp(psFile->psRawBinFile, psRxp,
                           psFile->nPrecision);
}


/*=====================================================================
 *                              TABLES
 *====================================================================*/

/**********************************************************************
 *                          _AVCBinWriteArcDir()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteCreateTable() instead)
 *
 * Write an ARC.DIR entry at the current position in file.
 *
 * The contents of the psTableDef structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteArcDir(AVCRawBinFile *psFile, AVCTableDef *psTableDef)
{
    /* STRING values MUST be padded with spaces.
     */
    AVCRawBinWritePaddedString(psFile, 32, (GByte*)psTableDef->szTableName);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    AVCRawBinWritePaddedString(psFile, 8, (GByte*)psTableDef->szInfoFile);

    AVCRawBinWriteInt16(psFile, psTableDef->numFields);

    /* Record size must be a multiple of 2 bytes */
    AVCRawBinWriteInt16(psFile, (GInt16)(((psTableDef->nRecSize+1)/2)*2));

    /* ??? Unknown values ??? */
    AVCRawBinWritePaddedString(psFile, 16, (GByte*)"                    ");
    AVCRawBinWriteInt16(psFile, 132);
    AVCRawBinWriteInt16(psFile, 0);

    AVCRawBinWriteInt32(psFile, psTableDef->numRecords);

    AVCRawBinWriteZeros(psFile, 10);

    AVCRawBinWritePaddedString(psFile, 2, (GByte*)psTableDef->szExternal);

    AVCRawBinWriteZeros(psFile, 238);
    AVCRawBinWritePaddedString(psFile, 8, (GByte*)"                    ");
    AVCRawBinWriteZeros(psFile, 54);

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}


/**********************************************************************
 *                          _AVCBinWriteArcNit()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteCreateTable() instead)
 *
 * Write an ARC####.NIT entry at the current position in file.
 *
 * The contents of the psTableDef structure is assumed to be valid... this
 * function performs no validation on the consistency of what it is 
 * given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteArcNit(AVCRawBinFile *psFile, AVCFieldInfo *psField)
{
    /* STRING values MUST be padded with spaces.
     */
    AVCRawBinWritePaddedString(psFile, 16, (GByte*)psField->szName);
    if (CPLGetLastErrorNo() != 0)
        return -1;

    AVCRawBinWriteInt16(psFile, psField->nSize);
    AVCRawBinWriteInt16(psFile, psField->v2);
    AVCRawBinWriteInt16(psFile, psField->nOffset);
    AVCRawBinWriteInt16(psFile, psField->v4);
    AVCRawBinWriteInt16(psFile, psField->v5);
    AVCRawBinWriteInt16(psFile, psField->nFmtWidth);
    AVCRawBinWriteInt16(psFile, psField->nFmtPrec);
    AVCRawBinWriteInt16(psFile, psField->nType1);
    AVCRawBinWriteInt16(psFile, psField->nType2);
    AVCRawBinWriteInt16(psFile, psField->v10);
    AVCRawBinWriteInt16(psFile, psField->v11);
    AVCRawBinWriteInt16(psFile, psField->v12);
    AVCRawBinWriteInt16(psFile, psField->v13);

    AVCRawBinWritePaddedString(psFile, 16, (GByte*)psField->szAltName);

    AVCRawBinWriteZeros(psFile, 56);

    AVCRawBinWriteInt16(psFile, psField->nIndex);

    AVCRawBinWriteZeros(psFile, 28);

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}


/**********************************************************************
 *                     _AVCBinWriteCreateArcDirEntry()
 *
 * Add an entry in the ARC.DIR for the table defined in psSrcTableDef.
 *
 * If an entry with the same table name already exists then this entry
 * will be reused and overwritten.
 *
 * Note: there could be a problem if 2 processes try to add an entry
 * at the exact same time... does Arc/Info do any locking on that file???
 * 
 * Returns an integer value corresponding to the new table index (ARC####)
 * or -1 if something failed.
 **********************************************************************/

/* Prototype for _AVCBinReadNextArcDir() from avc_bin.c
 */
int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir);

int _AVCBinWriteCreateArcDirEntry(const char *pszArcDirFile,
                                  AVCTableDef *psTableDef, 
                                  AVCDBCSInfo *psDBCSInfo)
{
    int          iEntry, numDirEntries=0, nTableIndex = 0;
    VSIStatBuf   sStatBuf;
    AVCRawBinFile *hRawBinFile;
    GBool        bFound;
    AVCTableDef  sEntry;

    /*-----------------------------------------------------------------
     * Open and Scan the ARC.DIR to establish the table index (ARC####)
     *----------------------------------------------------------------*/
#ifdef _WIN32
    /*-----------------------------------------------------------------
     * Note, DM, 20010507 - We used to use VSIFStat() to establish the
     * size of arc.dir, but when working on a WinNT4 networked drive, the
     * stat() information was not always right, and we sometimes ended 
     * up overwriting arc.dir entries.  The solution: open and scan arc.dir
     * until EOF to establish its size.  
     * That trick also seems to fix another network buffer problem: when 
     * writing a coverage in a new empty dir (with no info dir yet), we
     * would get an error in fwrite() while writing the 3rd arc.dir 
     * entry of the coverage.  That second problem could also have been 
     * fixed by forcing a VSIFSeek() before the first fwrite()... we've 
     * added it below.
     *----------------------------------------------------------------*/
    FILE *fp;
    if ((fp = VSIFOpen(pszArcDirFile, "r")) != NULL)
    {
        char buf[380];
        while (!VSIFEof(fp))
        {
            if (VSIFRead(buf, 380, 1, fp) == 1)
                numDirEntries++;
        }
        VSIFClose(fp);
        hRawBinFile = AVCRawBinOpen(pszArcDirFile, "r+",
                                    AVC_COVER_BYTE_ORDER(AVCCoverV7),
                                    psDBCSInfo);
    }
    else 
#endif
    /* On Unix we can still use fstat() */
    if ( VSIStat(pszArcDirFile, &sStatBuf) != -1 )
    {
        numDirEntries = sStatBuf.st_size/380;
        hRawBinFile = AVCRawBinOpen(pszArcDirFile, "r+",
                                    AVC_COVER_BYTE_ORDER(AVCCoverV7),
                                    psDBCSInfo);
    }
    else
    {
        numDirEntries = 0;
        hRawBinFile = AVCRawBinOpen(pszArcDirFile, "w", 
                                    AVC_COVER_BYTE_ORDER(AVCCoverV7), 
                                    psDBCSInfo);
    }

    if (hRawBinFile == NULL)
    {
        /* Failed to open file... just return -1 since an error message
         * has already been issued by AVCRawBinOpen()
         */
        return -1;
    }

    /* Init nTableIndex at -1 so that first table created should have 
     * index 0 
     */
    nTableIndex = -1;
    iEntry = 0;
    bFound = FALSE;
    while(!bFound && iEntry<numDirEntries &&
          _AVCBinReadNextArcDir(hRawBinFile, &sEntry) == 0)
    {
        nTableIndex = atoi(sEntry.szInfoFile+3);
        if (EQUALN(psTableDef->szTableName, sEntry.szTableName, 
                   strlen(psTableDef->szTableName)))
        {   
            bFound = TRUE;
            break;
        }
        iEntry++;
    }

    /*-----------------------------------------------------------------
     * Reposition the file pointer and write the entry.
     *
     * We use VSIFSeek() directly since the AVCRawBin*() functions do
     * not support random access yet... it is OK to do so here since the
     * ARC.DIR does not have a header and we will close it right away.
     *----------------------------------------------------------------*/
    if (bFound)
        VSIFSeek(hRawBinFile->fp, iEntry*380, SEEK_SET);
    else
    {
        /* Not found... Use the next logical table index */
        nTableIndex++;

        /* We're already at EOF so we shouldn't need to fseek here, but
         * ANSI-C requires that a file positioning function be called 
         * between read and writes... this had never been a problem before
         * on any system except with NT4 network drives.
         */
        VSIFSeek(hRawBinFile->fp, numDirEntries*380, SEEK_SET);
    }

    sprintf(psTableDef->szInfoFile, "ARC%4.4d", nTableIndex);
    _AVCBinWriteArcDir(hRawBinFile, psTableDef);

    AVCRawBinClose(hRawBinFile);

    return nTableIndex;
}


/**********************************************************************
 *                          AVCBinWriteCreateTable()
 *
 * Open an INFO table for writing:
 *
 *  - Add an entry for the new table in the info/arc.dir
 *  - Write the attributes definitions to the info/arc####.nit
 *  - Create the data file, ready to write data records to it
 *  - If necessary, set the arc####.dat to point to the location of
 *    the data file.
 *
 * pszInfoPath is the info directory path, terminated by a '/' or a '\\'
 * It is assumed that this 'info' directory already exists and is writable.
 *
 * psTableDef should contain a valid table definition for this coverage.
 * This function will create and maintain its own copy of the structure.
 *
 * The name of the file to create and its location will be based on the 
 * table name and the external ("XX") flag values in the psTableDef 
 * structure, so you have to make sure that these values are valid.
 *
 * If a table with the same name is already present in the arc.dir, then 
 * the same arc.dir entry will be used and overwritten.  This happens
 * when a coverage directory is deleted by hand.  The behavior implemented
 * here correspond to Arc/Info's behavior.
 *
 * For internal tables, the data file goes directly in the info directory, so
 * there is not much to worry about.
 *
 * For external tables, the table name is composed of 3 parts:
 *
 *         <COVERNAME>.<EXT><SUBCLASSNAME>
 *
 *  - <COVERNAME>:
 *    The first part of the table name (before the '.') is the
 *    name of the coverage to which the table belongs, and the data file
 *    will be created in this coverage's directory... so it is assumed that
 *    the directory "../<covername>" already exists and is writable.
 *  - <EXT>:
 *    The coverage name is followed by a 3 chars extension that will be 
 *    used to build the name of the external table to create.
 *  - <SUBCLASSNAME>:
 *    For some table types, the extension is followed by a subclass name.
 *    
 *  When <SUBCLASSNAME> is present, then the data file name will be:
 *            "../<covername>/<subclassname>.<ext>"
 *
 *    e.g. The table named "TEST.PATCOUNTY" would be stored in the file
 *         "../test/county.pat" (this path is realtive to the info directory)
 *
 *  When the <SUBCLASSNAME> is not present, then the name of the data file
 *  will be the "../<covername>/<ext>.adf"
 *
 *    e.g. The table named "TEST.PAT" would be stored in the file 
 *         "../test/pat.adf"
 *
 * Of course, it would be too easy if there were no exceptions to these
 * rules!  Single precision ".TIC" and ".BND" follow the above rules and
 * will be named "tic.adf" and "bnd.adf" but in double precision coverages,
 * they will be named "dbltic.adf" and "dblbnd.adf".
 *
 * Returns a valid AVCBinFile handle, or NULL if the table could
 * not be created.
 *
 * AVCBinClose() will eventually have to be called to release the 
 * resources used by the AVCBinFile structure.
 **********************************************************************/
AVCBinFile *AVCBinWriteCreateTable(const char *pszInfoPath, 
                                   const char *pszCoverName,
                                   AVCTableDef *psSrcTableDef,
                                   AVCCoverType eCoverType,
                                   int nPrecision, AVCDBCSInfo *psDBCSInfo)
{
    AVCBinFile   *psFile;
    AVCRawBinFile *hRawBinFile;
    AVCTableDef  *psTableDef = NULL;
    char         *pszFname = NULL, szInfoFile[8]="";
    int          i, nTableIndex = 0;

    if (eCoverType == AVCCoverPC || eCoverType == AVCCoverPC2)
        return _AVCBinWriteCreateDBFTable(pszInfoPath, pszCoverName,
                                          psSrcTableDef, eCoverType, 
                                          nPrecision, psDBCSInfo);

    /*-----------------------------------------------------------------
     * Make sure precision value is valid (AVC_DEFAULT_PREC is NOT valid)
     *----------------------------------------------------------------*/
    if (nPrecision!=AVC_SINGLE_PREC && nPrecision!=AVC_DOUBLE_PREC)
    {
        CPLError(CE_Failure, CPLE_IllegalArg,
                 "AVCBinWriteCreateTable(): Invalid precision parameter "
                 "(value must be AVC_SINGLE_PREC or AVC_DOUBLE_PREC)");
        return NULL;
    }

    /* Alloc a buffer big enough for the longest possible filename...
     */
    pszFname = (char*)CPLMalloc((strlen(pszInfoPath)+81)*sizeof(char));


    /*-----------------------------------------------------------------
     * Alloc and init the AVCBinFile struct.
     *----------------------------------------------------------------*/
    psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));

    psFile->eFileType = AVCFileTABLE;
    /* Precision is not important for tables */
    psFile->nPrecision = nPrecision;
    psFile->eCoverType = eCoverType;

    psFile->hdr.psTableDef = psTableDef = _AVCDupTableDef(psSrcTableDef);

    /*-----------------------------------------------------------------
     * Add a record for this table in the "arc.dir"
     * Note: there could be a problem if 2 processes try to add an entry
     * at the exact same time... does Arc/Info do any locking on that file???
     *----------------------------------------------------------------*/
    sprintf(pszFname, "%sarc.dir", pszInfoPath);

    nTableIndex = _AVCBinWriteCreateArcDirEntry(pszFname, psTableDef, 
                                                psDBCSInfo);

    if (nTableIndex < 0)
    {
        /* Failed to add arc.dir entry... just return NULL since an error
         * message has already been issued by _AVCBinWriteCreateArcDirEntry()
         */
        _AVCDestroyTableDef(psTableDef);
        CPLFree(psFile);
        CPLFree(pszFname);
        return NULL;
    }

    sprintf(szInfoFile, "arc%4.4d", nTableIndex);

    /*-----------------------------------------------------------------
     * Create the "arc####.nit" with the attribute definitions.
     *----------------------------------------------------------------*/
    sprintf(pszFname, "%s%s.nit", pszInfoPath, szInfoFile);

    hRawBinFile = AVCRawBinOpen(pszFname, "w",
                                AVC_COVER_BYTE_ORDER(AVCCoverV7),
                                psDBCSInfo);

    if (hRawBinFile == NULL)
    {
        /* Failed to open file... just return NULL since an error message
         * has already been issued by AVCRawBinOpen()
         */
        _AVCDestroyTableDef(psTableDef);
        CPLFree(psFile);
        CPLFree(pszFname);
        return NULL;
    }

    for(i=0; i<psTableDef->numFields; i++)
    {
        _AVCBinWriteArcNit(hRawBinFile, &(psTableDef->pasFieldDef[i]));
    }

    AVCRawBinClose(hRawBinFile);
    hRawBinFile = NULL;

    /*-----------------------------------------------------------------
     * The location of the data file depends on the external flag.
     *----------------------------------------------------------------*/
    if (EQUAL(psTableDef->szExternal, "  "))
    {
        /*-------------------------------------------------------------
         * Internal table: data goes directly in "arc####.dat"
         *------------------------------------------------------------*/
        psTableDef->szDataFile[0] = '\0';
        sprintf(pszFname, "%s%s.dat", pszInfoPath, szInfoFile);
        psFile->pszFilename = CPLStrdup(pszFname);
    }
    else
    {
        /*-------------------------------------------------------------
         * External table: data stored in the coverage directory, and
         * the path to the data file is written to "arc####.dat"
         *... start by extracting the info to build the data file name...
         *------------------------------------------------------------*/
        char szCoverName[40]="", szExt[4]="", szSubclass[40]="", *pszPtr;
        int nLen;
        FILE *fpOut;

        nLen = strlen(psTableDef->szTableName);
        CPLAssert(nLen <= 32);
        if (nLen > 32) return NULL;
        pszPtr = psTableDef->szTableName;

        for(i=0; *pszPtr!='\0' && *pszPtr!='.' && *pszPtr!=' ';  i++, pszPtr++)
        {
            szCoverName[i] = tolower(*pszPtr);
        }
        szCoverName[i] = '\0';

        if (*pszPtr == '.')
            pszPtr++;

        for(i=0; i<3 && *pszPtr!='\0' && *pszPtr!=' ';  i++, pszPtr++)
        {
            szExt[i] = tolower(*pszPtr);
        }
        szExt[i] = '\0';

        for(i=0; *pszPtr!='\0' && *pszPtr!=' ';  i++, pszPtr++)
        {
            szSubclass[i] = tolower(*pszPtr);
        }
        szSubclass[i] = '\0';

        /*-------------------------------------------------------------
         * ... and build the data file name based on what we extracted
         *------------------------------------------------------------*/
        if (strlen(szSubclass) == 0)
        {
            if (nPrecision == AVC_DOUBLE_PREC &&
                (EQUAL(szExt, "TIC") || EQUAL(szExt, "BND")) )
            {
                /* "../<covername>/dbl<ext>.adf" */
                sprintf(psTableDef->szDataFile, 
                        "../%s/dbl%s.adf", szCoverName, szExt);
            }
            else
            {
                /* "../<covername>/<ext>.adf" */
                sprintf(psTableDef->szDataFile, 
                        "../%s/%s.adf", szCoverName, szExt);
            }
        }
        else
        {
            /* "../<covername>/<subclass>.<ext>" */
            sprintf(psTableDef->szDataFile, 
                    "../%s/%s.%s", szCoverName, szSubclass, szExt);
        }

        /*-------------------------------------------------------------
         * Write it to the arc####.dat
         * Note that the path that is written in the arc####.dat contains
         * '/' as a directory delimiter, even on Windows systems.
         *------------------------------------------------------------*/
        sprintf(pszFname, "%s%s.dat", pszInfoPath, szInfoFile);
        fpOut = VSIFOpen(pszFname, "wt");
        if (fpOut)
        {
            VSIFPrintf(fpOut, "%-80.80s", psTableDef->szDataFile);
            VSIFClose(fpOut);
        }
        else
        {
            CPLError(CE_Failure, CPLE_OpenFailed,
                     "Failed creating file %s.", pszFname);
            CPLFree(pszFname);
            _AVCDestroyTableDef(psTableDef);
            CPLFree(psFile);
            return NULL;
        }

        sprintf(pszFname, "%s%s", 
                pszInfoPath, psTableDef->szDataFile);
        psFile->pszFilename = CPLStrdup(pszFname);

#ifdef WIN32
        /*-------------------------------------------------------------
         * On a Windows system, we have to change the '/' to '\\' in the
         * data file path.
         *------------------------------------------------------------*/
        for(i=0; psFile->pszFilename[i] != '\0'; i++)
            if (psFile->pszFilename[i] == '/')
                psFile->pszFilename[i] = '\\';
#endif /* WIN32 */

    }/* if "XX" */

    /*-----------------------------------------------------------------
     * OK, now we're ready to create the actual data file.
     *----------------------------------------------------------------*/
    AVCAdjustCaseSensitiveFilename(psFile->pszFilename);
    psFile->psRawBinFile = AVCRawBinOpen(psFile->pszFilename, "w",
                                         AVC_COVER_BYTE_ORDER(AVCCoverV7),
                                         psDBCSInfo);

    if (psFile->psRawBinFile == NULL)
    {
        /* Failed to open file... just return NULL since an error message
         * has already been issued by AVCRawBinOpen()
         */
        CPLFree(pszFname);
        CPLFree(psFile->pszFilename);
        _AVCDestroyTableDef(psTableDef);
        CPLFree(psFile);
        return NULL;
    }

    CPLFree(pszFname);

    return psFile;
}


/**********************************************************************
 *                          _AVCBinWriteCreateDBFTable()
 *
 * Create a table (DBF file) in a PC Coverage and write the attribute defns to
 * the file.  The file will then be ready to write records to.
 *
 * In PC Coverages, only the following tables appear to be supported:
 *    - TEST.AAT -> AAT.DBF
 *    - TEST.PAT -> PAT.DBF
 *    - TEST.BND -> BND.DBF
 *    - TEST.TIC -> TIC.DBF
 *
 * However, this function will not fail if it is passed a table name not 
 * supported by PC Arc/Info. 
 * e.g. TEST.PATCOUNTY would be written as PATCOUNTY.DBF even if PC Arc/Info
 * would probably not recognize that name.
 *
 * Returns a valid AVCBinFile handle, or NULL if the table could
 * not be created.
 *
 * AVCBinClose() will eventually have to be called to release the 
 * resources used by the AVCBinFile structure.
 **********************************************************************/
AVCBinFile *_AVCBinWriteCreateDBFTable(const char *pszPath, 
                                       const char *pszCoverName,
                                       AVCTableDef *psSrcTableDef,
                                       AVCCoverType eCoverType,
                                       int nPrecision, AVCDBCSInfo *psDBCSInfo)
{
    AVCBinFile    *psFile;
    AVCTableDef   *psTableDef = NULL;
    AVCFieldInfo  *pasDef;
    char          *pszDBFBasename, szFieldName[12];
    int           i, j, nType;

    /*-----------------------------------------------------------------
     * Alloc and init the AVCBinFile struct.
     *----------------------------------------------------------------*/
    psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile));

    psFile->eFileType = AVCFileTABLE;
    /* Precision is not important for tables */
    psFile->nPrecision = nPrecision;
    psFile->eCoverType = eCoverType;

    psFile->hdr.psTableDef = psTableDef = _AVCDupTableDef(psSrcTableDef);

    /* nCurDBFRecord is used to keep track of the 0-based index of the
     * last record we read from the DBF file... this is to emulate 
     * sequential access which is assumed by the rest of the lib.
     * Since the first record (record 0) has not been written yet, then
     * we init the index at -1.
     */
    psFile->nCurDBFRecord = -1;

    /*-----------------------------------------------------------------
     * Establish name of file to create.
     *----------------------------------------------------------------*/
    psFile->pszFilename = (char*)CPLCalloc(strlen(psSrcTableDef->szTableName)+
                                           strlen(pszPath)+10, sizeof(char));

    if (EQUALN(psSrcTableDef->szTableName, pszCoverName, strlen(pszCoverName))
        && psSrcTableDef->szTableName[strlen(pszCoverName)] == '.')
    {
        pszDBFBasename = psSrcTableDef->szTableName + strlen(pszCoverName)+1;
    }
    else
    {
        pszDBFBasename = psSrcTableDef->szTableName;
    }

    strcpy(psFile->pszFilename, pszPath);

    for(i=strlen(psFile->pszFilename); *pszDBFBasename; i++, pszDBFBasename++)
    {
        psFile->pszFilename[i] = tolower(*pszDBFBasename);
    }

    strcat(psFile->pszFilename, ".dbf");

    /*-----------------------------------------------------------------
     * OK, let's try to create the DBF file.
     *----------------------------------------------------------------*/
    AVCAdjustCaseSensitiveFilename(psFile->pszFilename);
    psFile->hDBFFile = DBFCreate(psFile->pszFilename);

    if (psFile->hDBFFile == NULL)
    {
        CPLError(CE_Failure, CPLE_OpenFailed,
                 "Failed creating file %s.", psFile->pszFilename);
        CPLFree(psFile->pszFilename);
        _AVCDestroyTableDef(psTableDef);
        CPLFree(psFile);
        return NULL;
    }

    /*-----------------------------------------------------------------
     * Create fields.
     *----------------------------------------------------------------*/
    pasDef = psTableDef->pasFieldDef;
    for(i=0; i<psTableDef->numFields; i++)
    {
        nType = pasDef[i].nType1*10;

        /*-------------------------------------------------------------
         * Special characters '#' and '-' in field names have to be replaced 
         * with '_'.  PC Field names are limited to 10 chars.
         *------------------------------------------------------------*/
        strncpy(szFieldName, pasDef[i].szName, 10);
        szFieldName[10] = '\0';
        for(j=0; szFieldName[j]; j++)
        {
            if (szFieldName[j] == '#' || szFieldName[j] == '-')
                szFieldName[j] = '_';
        }

        if (nType ==  AVC_FT_DATE || nType == AVC_FT_CHAR)
        {
            /*---------------------------------------------------------
             * Values stored as strings
             *--------------------------------------------------------*/
            DBFAddField(psFile->hDBFFile, szFieldName, FTString, 
                        pasDef[i].nSize, 0);
        }
        else if (nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
        {
            /*---------------------------------------------------------
             * Numerics (internally stored as strings)
             *--------------------------------------------------------*/
            DBFAddField(psFile->hDBFFile, szFieldName, FTDouble, 
                        pasDef[i].nSize, pasDef[i].nFmtPrec);
        }
        else if (nType == AVC_FT_BININT)
        {
            /*---------------------------------------------------------
             * Integers (16 and 32 bits)
             *--------------------------------------------------------*/
            DBFAddField(psFile->hDBFFile, szFieldName, FTInteger, 11, 0);
        }
        else if (nType == AVC_FT_BINFLOAT)
        {
            /*---------------------------------------------------------
             * Single + Double precision floats
             * Set them as width=13, prec=6 in the header like PC/Arc does
             *--------------------------------------------------------*/
            DBFAddField(psFile->hDBFFile, szFieldName, FTDouble, 
                        13, 6);
        }
        else
        {
            /*---------------------------------------------------------
             * Hummm... unsupported field type...
             *--------------------------------------------------------*/
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Unsupported field type: (field=%s, type=%d, size=%d)",
                     szFieldName, nType, pasDef[i].nSize);
            _AVCBinWriteCloseTable(psFile);
            return NULL;
        }
    }

    return psFile;
}


/**********************************************************************
 *                          _AVCBinWriteCloseTable()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteClose() instead)
 *
 * Close an info table opened for wirting, and release all memory 
 * (object struct., buffers, etc.) associated with this file.
 **********************************************************************/
static void    _AVCBinWriteCloseTable(AVCBinFile *psFile)
{
    if (psFile->eFileType != AVCFileTABLE)
        return;

    /*-----------------------------------------------------------------
     * Close the data file
     *----------------------------------------------------------------*/
    if (psFile->hDBFFile)
    {
        /*-------------------------------------------------------------
         * The case of DBF files is simple!
         *------------------------------------------------------------*/
        DBFClose(psFile->hDBFFile);
        psFile->hDBFFile = NULL;
    }
    else if (psFile->psRawBinFile)
    {
        /*-------------------------------------------------------------
         * __TODO__ make sure ARC.DIR entry contains accurate info about the 
         * number of records written, etc.
         *------------------------------------------------------------*/
        AVCRawBinClose(psFile->psRawBinFile);
        psFile->psRawBinFile = NULL;
    }

    /*-----------------------------------------------------------------
     * Release other memory
     *----------------------------------------------------------------*/
    _AVCDestroyTableDef(psFile->hdr.psTableDef);

    CPLFree(psFile->pszFilename);

    CPLFree(psFile);
}


/**********************************************************************
 *                          _AVCBinWriteTableRec()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteTableRec() instead)
 *
 * Write a table data record at the current position in file.
 *
 * The contents of the pasDef and pasFields structures is assumed to 
 * be valid... this function performs no validation on the consistency 
 * of what it is given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteTableRec(AVCRawBinFile *psFile, int nFields,
                         AVCFieldInfo *pasDef, AVCField *pasFields,
                         int nRecordSize, const char *pszFname)
{
    int i, nType, nBytesWritten=0;

    if (psFile == NULL)
        return -1;

    for(i=0; i<nFields; i++)
    {
        if (CPLGetLastErrorNo() != 0)
            return -1;

        nType = pasDef[i].nType1*10;

        if (nType ==  AVC_FT_DATE || nType == AVC_FT_CHAR ||
            nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
        {
            /*---------------------------------------------------------
             * Values stored as strings (MUST be padded with spaces)
             *--------------------------------------------------------*/
            AVCRawBinWritePaddedString(psFile, pasDef[i].nSize,
                                       pasFields[i].pszStr);
        }
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
        {
            /*---------------------------------------------------------
             * 32 bit binary integers
             *--------------------------------------------------------*/
            AVCRawBinWriteInt32(psFile, pasFields[i].nInt32);
        }
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
        {
            /*---------------------------------------------------------
             * 16 bit binary integers
             *--------------------------------------------------------*/
            AVCRawBinWriteInt16(psFile, pasFields[i].nInt16);
        }
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4)
        {
            /*---------------------------------------------------------
             * Single precision floats
             *--------------------------------------------------------*/
            AVCRawBinWriteFloat(psFile, pasFields[i].fFloat);
        }
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
        {
            /*---------------------------------------------------------
             * Double precision floats
             *--------------------------------------------------------*/
            AVCRawBinWriteDouble(psFile, pasFields[i].dDouble);
        }
        else
        {
            /*---------------------------------------------------------
             * Hummm... unsupported field type...
             *--------------------------------------------------------*/
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Unsupported field type in %s: (type=%d, size=%d)",
                     pszFname, nType, pasDef[i].nSize);
            return -1;
        }

        nBytesWritten += pasDef[i].nSize;
    }

    /*-----------------------------------------------------------------
     * Record size is rounded to a multiple of 2 bytes.
     * Check the number of bytes written, and pad with zeros if 
     * necessary.
     *----------------------------------------------------------------*/
    nRecordSize = ((nRecordSize+1)/2)*2;
    if (nBytesWritten < nRecordSize)
        AVCRawBinWriteZeros(psFile, nRecordSize - nBytesWritten);

    if (CPLGetLastErrorNo() != 0)
        return -1;

    return 0;
}

/**********************************************************************
 *                          _AVCBinWriteDBFTableRec()
 *
 * (This function is for internal library use... external calls should
 * go to AVCBinWriteTableRec() instead)
 *
 * Write a table data record at the current position in DBF file.
 *
 * The contents of the pasDef and pasFields structures is assumed to 
 * be valid... this function performs no validation on the consistency 
 * of what it is given as input.
 *
 * Returns 0 on success or -1 on error.
 **********************************************************************/
int _AVCBinWriteDBFTableRec(DBFHandle hDBFFile, int nFields,
                            AVCFieldInfo *pasDef, AVCField *pasFields,
                            int *nCurDBFRecord, const char *pszFname)
{
    int i, nType, nStatus = FALSE;

    if (hDBFFile == NULL)
        return -1;

    (*nCurDBFRecord)++;

    for(i=0; i<nFields; i++)
    {
        if (CPLGetLastErrorNo() != 0)
            return -1;

        nType = pasDef[i].nType1*10;

        if (nType ==  AVC_FT_DATE || nType == AVC_FT_CHAR)
        {
            /*---------------------------------------------------------
             * Values stored as strings
             *--------------------------------------------------------*/
            nStatus = DBFWriteStringAttribute(hDBFFile, *nCurDBFRecord, i, 
                                              (char *)pasFields[i].pszStr);
        }
        else if (nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
        {
            /*---------------------------------------------------------
             * Numbers stored as strings
             *--------------------------------------------------------*/
            nStatus = DBFWriteAttributeDirectly(hDBFFile, *nCurDBFRecord, i, 
                                                pasFields[i].pszStr);
        }
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
        {
            /*---------------------------------------------------------
             * 32 bit binary integers
             *--------------------------------------------------------*/
            nStatus = DBFWriteIntegerAttribute(hDBFFile, *nCurDBFRecord, i, 
                                               pasFields[i].nInt32);
        }
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
        {
            /*---------------------------------------------------------
             * 16 bit binary integers
             *--------------------------------------------------------*/
            nStatus = DBFWriteIntegerAttribute(hDBFFile, *nCurDBFRecord, i, 
                                               pasFields[i].nInt16);
        }
        else if (nType == AVC_FT_BINFLOAT)
        {
            /*---------------------------------------------------------
             * Single+double precision floats
             *--------------------------------------------------------*/
            char szBuf[32] = "";
            int nLen;

            if (pasDef[i].nSize == 4)
                nLen = AVCPrintRealValue(szBuf, AVC_FORMAT_DBF_FLOAT,
                                         AVCFileTABLE, pasFields[i].fFloat);
            else
                nLen = AVCPrintRealValue(szBuf, AVC_FORMAT_DBF_FLOAT,
                                         AVCFileTABLE, pasFields[i].dDouble);

            szBuf[nLen] = '\0';

            nStatus = DBFWriteAttributeDirectly(hDBFFile, *nCurDBFRecord, i, 
                                                szBuf);
        }
        else
        {
            /*---------------------------------------------------------
             * Hummm... unsupported field type...
             *--------------------------------------------------------*/
            CPLError(CE_Failure, CPLE_NotSupported,
                     "Unsupported field type in %s: (type=%d, size=%d)",
                     pszFname, nType, pasDef[i].nSize);
            return -1;
        }

        if (nStatus != TRUE)
        {
            CPLError(CE_Failure, CPLE_FileIO,
                     "Failed writing table field %d to record %d in %s",
                     i, *nCurDBFRecord, pszFname);
            return -1;
        }

    }

    return 0;
}

/**********************************************************************
 *                          AVCBinWriteTableRec()
 *
 * Write a table data record at the current position in file.
 *
 * The contents of the pasDef and pasFields structures is assumed to 
 * be valid... this function performs no validation on the consistency 
 * of what it is given as input.
 *
 * Returns 0 on success or -1 on error.
 *
 * If a problem happens, then CPLError() will be called by the lower-level
 * functions and CPLGetLastErrorNo() can be used to find out what happened.
 **********************************************************************/
int AVCBinWriteTableRec(AVCBinFile *psFile, AVCField *pasFields)
{
    if (psFile->eFileType != AVCFileTABLE||
        psFile->hdr.psTableDef->numRecords == 0)
        return -1;

    if (psFile->eCoverType == AVCCoverPC || psFile->eCoverType == AVCCoverPC2)
        return _AVCBinWriteDBFTableRec(psFile->hDBFFile, 
                                       psFile->hdr.psTableDef->numFields,
                                       psFile->hdr.psTableDef->pasFieldDef,
                                       pasFields,
                                       &(psFile->nCurDBFRecord),
                                       psFile->pszFilename);
    else
        return _AVCBinWriteTableRec(psFile->psRawBinFile, 
                                    psFile->hdr.psTableDef->numFields,
                                    psFile->hdr.psTableDef->pasFieldDef,
                                    pasFields,
                                    psFile->hdr.psTableDef->nRecSize,
                                    psFile->pszFilename);
}

