/***************************************************************************
 *                                                                         *
 *   DFCAPI2.C                                                             *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Datafile conversion stuff                                             *
 *                                                                         *
 *                                           - Wally Muharsky 09/04/97     *
 *                                                                         *
 ***************************************************************************/
#include "gcomm.h"
#include "vidapi.h"
#include "majorbbs.h"
#include "dfcapi2.h"

#define FILREV      "$Revision: 25 $"

#define PRNBSZ      2000           /*  Print buffer size                   */
#define VIRDIR      "GCVIRDAT"     /*  Where blank datafiles are placed    */
#define CVTDIR      "WGSDFCVT"     /*  Datafile conversion directory       */
#define BAKTMP      "WGSDFCVT.TMP" /*  Temporary file for backup directory */
#define WGSOLD      "WGSOLD"       /*  Old datafile directory              */

/***************************************************************************
 * Variable declarations                                                   *
 ***************************************************************************/
static INT PFTypes[]={             /*  Different Platform Types            */
     (CVTPACKED),                  /*  DOS (as per DFC_DOS #define)        */
     (0),                          /*  WNT (as per DFC_WNT #define)        */
};

static ULONG cvttypes[]={          /* For conversion into flddef stuff     */
     CVTFLD_END,                   /*  DFCFLD_END                          */
     CVTFLD_CHAR,                  /*  DFCFLD_CHAR                         */
     CVTFLD_SHORT,                 /*  DFCFLD_SHORT                        */
     CVTFLD_LONG,                  /*  DFCFLD_LONG                         */
     CVTFLD_FLOAT,                 /*  DFCFLD_FLOAT                        */
     CVTFLD_DOUBLE,                /*  DFCFLD_DOUBLE                       */
     CVTFLD_OPAQUE,                /*  DFCFLD_OPAQUE                       */
     CVTFLD_STRUCT,                /*  DFCFLD_STRUCT                       */
     CVTFLD_CHAR,                  /*  DFCFLD_VCHAR (VARIABLE)             */
     CVTFLD_SHORT,                 /*  DFCFLD_VSHORT (VARIABLE)            */
     CVTFLD_LONG,                  /*  DFCFLD_VLONG (VARIABLE)             */
     CVTFLD_FLOAT,                 /*  DFCFLD_VFLOAT (VARIABLE)            */
     CVTFLD_DOUBLE,                /*  DFCFLD_VDOUBLE (VARIABLE)           */
     CVTFLD_OPAQUE,                /*  DFCFLD_VOPAQUE (VARIABLE)           */
     CVTFLD_STRUCT,                /*  DFCFLD_VSTRUCT (VARIABLE)           */
};

static INT ConvertTo=DFC_END;      /*  Converting to #define               */

static CHAR savscn[GVIDSCNSIZ];    /*  Screen holder                       */
static DFAFILE *dfaFrom=NULL;      /*  Pointer to from datafile            */
static DFAFILE *dfaTo=NULL;        /*  Pointer to to datafile              */
static const CHAR *Name;           /*  Name of the conversion file         */
static const DFCLIST *List;        /*  Pointer to DFC information          */
static const DFCPFCONVERT *pfcvt;  /*  Platform convert functions          */
static pDFCFILE ToFileList;        /*  List of files converting to         */
static pDFCFILE FileTo;            /*  File converting to                  */
static DFCFILE ** FromList;        /*  From File list                      */
static UINT FromAmount;            /*  Amount of files on from list        */
static GBOOL Generic;              /*  Is this a generic database?         */
static GBOOL Converted;            /*  Was the file converted?             */
static const CHAR *gdbModuleName;  /*  Generic database module name        */

static struct flddef **Globalf=NULL;/* Field definition array for info     */
static UINT Globalfn=0;            /*  Global Fields defined               */
static pDFCOFFSETS GlobalOff=NULL; /*  Global offset fields                */
static UINT GlobalOffn=0;          /*  Number of global offsets            */
static ULONG RecordsTotal=0;       /*  Number of records in database       */
static ULONG RecordsDone=0;        /*  Number of records done              */
static ULONG RecordsPercent=0;     /*  Percentage of records done          */
static CHAR BackupDirectory[GCMAXPTH];/*  Current backup directory         */
static GBOOL Captured=FALSE;       /*  Internal screen captured?           */
static INT dfaKey;                 /*  Key to be used in btrieve calls     */
static INT dfaDirection;           /*  Direction to read btrieve calls     */

GBOOL dfcAskForBackup=FALSE;       /*  Ask for backup directory?           */

extern unsigned char dfcscntbl[];

/***************************************************************************
 * Function declarations                                                   *
 ***************************************************************************/
static GBOOL                       /*  TRUE if we should convert generic   */
ConvertGeneric(                    /* Check for conversion                 */
const CHAR *ModuleName);           /*  Module name to check for            */

static GBOOL                       /*  TRUE if a record is found           */
GetFirstRecord(                    /* Get the first record from database   */
VOID *Data);                       /*  Data buffer to store record in      */

static GBOOL                       /*  TRUE if next record is found        */
GetNextRecord(                     /* Get next data record in sequence     */
VOID *Data);                       /*  Buffer to store returned data       */

static VOID
RenameGeneric(VOID);               /*  Rename the generic databases        */

static GBOOL
InternalRegister(                  /*  Internal registration routine       */
const CHAR *descrp,                /*  Description of datafile             */
const DFCLIST *dfc,                /*  Pointer to DFC list                 */
const DFCPFCONVERT *pfconv,        /*  Pointer to platform conversions     */
const CHAR *ModuleName,            /*  Module name for generic databases   */
INT Key,                           /*  Key number to use (-1 for physical) */
INT Direction);                    /*  Direction (DFC_FORWARD/DFC_BACKWARD)*/

static GBOOL                       /*  TRUE/FALSE                          */
IsVariableField(                   /* Is field a variable field?           */
INT FieldType);                    /*  Field type                          */

static GBOOL                       /*  TRUE if good character              */
FileVerify(                        /* Verify file name character           */
CHAR c,                            /*  Character to check against          */
CHAR *sval);                       /*  Current value                       */

static VOID
AskAndWrite(VOID);                 /*  Ask for backup dir and write it     */

static VOID
GetBackupDir(VOID);                /*  Gets current backup directory       */

static VOID
Cleanup(VOID);                     /*  Cleanup after itself                */

static VOID                        /*  Picks latest date of many           */
PickConversionFile(VOID);          /*  Picks proper conversion file        */

static VOID
DisplayStats(VOID);                /*  Display statistical information     */

static VOID
ReleaseScreen(VOID);               /*  Releases video screen               */

static VOID
CaptureScreen(VOID);               /*  Initialize video and capture scn    */

static VOID *                      /*  Pointer to new data                 */
CallConvertPlatform(               /* Call platform conversion function    */
pDFCFILEINFO finf1,                /*  Pointer to first file info          */
pDFCFILEINFO finf2);               /*  Pointer to second file info         */

static VOID
NewOffset(VOID);                   /*  Add a new offset                    */

static VOID *                      /*  Pointer to new data                 */
CallConvertFunction(               /* Call proper convert function         */
pDFCFILEINFO finf1,                /*  Pointer to first file info          */
pDFCFILEINFO finf2,                /*  Pointer to second file info         */
pDFCFUNCTION rouptr);              /*  Pointer to a routine to be called   */

static VOID
FileInfoFree(                      /* Free up file information             */
pDFCFILEINFO finf);                /*  Pointer to file info structure      */

static VOID
NewGlobalf(VOID);                  /*  Create larger size of global field  */

static INT
FindPlatform(VOID);                /*  Find the current platform of system */

static pDFCFILE                    /*  Pointer to list or NULL             */
PlatformInList(                    /* Look for OS file list                */
INT Platform);                     /*  Platform to look for                */

static pDFCFILE                    /*  Pointer to last file or NULL        */
FindLastFile(                      /* Find last file in list               */
INT Platform);                     /*  Platform to search for              */

static VOID
StartConversion(VOID);             /*  Start conversion process, setup var */

static UINT                        /*  Number of files to convert          */
CountConvertFrom(VOID);            /*  Should only be one, just in case    */

static CHAR *                      /*  Pointer to file with path           */
PathVIR(                           /* Create a file from GCVIRDAT          */
const CHAR *file);                 /*  File to create full path from       */

static CHAR *                      /*  Pointer to file with path           */
PathDAT(                           /* Create a file with .dat extension    */
const CHAR *file,                  /*  File to add extension to            */
GBOOL GenericCheck);               /*  Generic checking                    */

static CHAR *                      /*  Pointer to created path             */
PathCVT(                           /* Create CVT path from file            */
const CHAR *file);                 /*  File to create full path from       */

static VOID
ConvertFile(                       /* Actual conversion of a file          */
const DFCFILE *ff,                 /*  File pointer coming from            */
const DFCFILE *ft);                /*  File pointer going to               */

static UINT                        /*  Largest field in structure          */
LargestField(                      /* Find largest field in structure      */
pDFCDATAFIELDS dfstart);           /*  Pointer to start of structure       */

static VOID
AddLength(                         /* Add length of field                  */
UINT *Length,                      /*  Current Length/offset of struct     */
pDFCDATAFIELDS df,                 /*  Pointer to current datafield        */
INT DataType,                      /*  Datatype (PFType[])                 */
UINT *fnum,                        /*  Number of fields                    */
GBOOL CreateFDA,                   /*  Create FDA for childs?              */
UINT curglob);                     /*  Current global working with         */

static UINT                        /*  Length of the datafields passed     */
CountFields(                       /* Count length of all datafields       */
UINT *Length,                      /*  Current length/offset of fields     */
pDFCDATAFIELDS dfstart,            /*  Pointer to the datafields           */
INT DataType,                      /*  Data type (PFType[])                */
UINT *fnum,                        /*  Number of current fields            */
GBOOL CreateFDA);                  /*  Create FDA's for childs?            */

static VOID
FileInfoCreate(                    /* Create information structre on file  */
const DFCFILE *fil,                /*  Pointer to file                     */
pDFCFILEINFO finf,                 /*  Pointer to file information struct  */
INT Platform);                     /*  Operating system info               */

static VOID
AdjustOffset(                      /* Adjust offset for NON-PADDING        */
UINT *Length,                      /*  Current offset/length of struct     */
UINT fl);                          /*  Current field length                */

static VOID
NewGlobalField(                    /* New global field add routine         */
UINT glob,                         /*  Global array to define from         */
UINT *fnum);                       /*  Field number currently on           */

static VOID
DisplayWindow(                     /*  Display info window                 */
const DFCFILE *ff,                 /*  File pointer coming from            */
const DFCFILE *ft);                /*  File pointer going to               */

/***************************************************************************
 * Source code                                                             *
 ***************************************************************************/
MARKSOURCE(dfcapi2);               /*  Module export stub                  */

GBOOL
dfcRegisterGDB(                    /*  Register conversion for GDB         */
const CHAR *descrp,                /*  Description of conversion           */
const DFCLIST *dfc,                /*  Pointer to list of information      */
const DFCPFCONVERT *pfconv,        /*  Pointer to OS conversions           */
const CHAR *ModuleName)            /*  Module name registering             */
{
     return(InternalRegister(descrp,dfc,pfconv,ModuleName,1,DFC_FORWARD));
}

GBOOL                              /*  TRUE if conversion took place       */
dfcRegister(                       /*  Register and search for files       */
const CHAR *descrp,                /*  Description name                    */
const DFCLIST *dfc,                /*  pointer to all DFCFILES             */
const DFCPFCONVERT *pfconv,        /*  Platform Converter                  */
INT Key,                           /*  Key number to use (-1 for physical) */
INT Direction)                     /*  Direction (DFC_FORWARD/DFC_BACKWARD)*/
{
     return(InternalRegister(descrp,dfc,pfconv,NULL,Key,Direction));
}

static GBOOL
InternalRegister(                  /*  Internal registration routine       */
const CHAR *descrp,                /*  Description of datafile             */
const DFCLIST *dfc,                /*  Pointer to DFC list                 */
const DFCPFCONVERT *pfconv,        /*  Pointer to platform conversions     */
const CHAR *ModuleName,            /*  Module name for generic databases   */
INT Key,                           /*  Key number to use (-1 for physical) */
INT Direction)                     /*  Direction (DFC_FORWARD/DFC_BACKWARD)*/
{
     static GBOOL working=FALSE;

     ASSERT(descrp != NULL);
     ASSERT(dfc != NULL);
     ASSERT(working == FALSE);
     ASSERT(Key >= -1);
     ASSERT(Direction == DFC_BACKWARD || Direction == DFC_FORWARD);

     initvid();

     if (working == TRUE) {
          catastro("2 Conversions can not happen at the same time.");
     }
     Converted=FALSE;
     working=TRUE;
     Generic=(ModuleName != NULL);
     RenameGeneric();
     gdbModuleName=ModuleName;
     Name=descrp;
     List=dfc;
     pfcvt=pfconv;
     dfaKey=Key;
     dfaDirection=Direction;
     FromAmount=0;
     FromList=NULL;
     StartConversion();
     working=FALSE;
     clsvid();
     return(Converted);
}

static VOID
ReleaseScreen(VOID)                /*  Releases video screen               */
{
     mem2scn(savscn,0,GVIDSCNSIZ);
     cursiz(GVIDLILCURS);
     if (dfaTo != NULL) {
          dfaClose(dfaTo);
          dfaTo=NULL;
     }
     if (dfaFrom != NULL) {
          dfaClose(dfaFrom);
          dfaFrom=NULL;
     }
}

static VOID
CaptureScreen(VOID)                /*  Initialize video and capture scn    */
{
     scn2mem(savscn,0,GVIDSCNSIZ);
     cursiz(GVIDNOCURS);
}

static VOID
StartConversion(VOID)              /*  Start conversion process, setup var */
{
     struct ffblk fb;

     if ((ConvertTo=FindPlatform()) == DFC_END
      || (ToFileList=PlatformInList(ConvertTo)) == NULL) {
          dfcError("Can not determine current server version.");
          return;
     }
     if ((FileTo=FindLastFile(ConvertTo)) == NULL
      || !fndfile(&fb,PathVIR(FileTo->Name),0)) {
          return;                  /*  No file to convert to present       */
     }
     CountConvertFrom();
     if (FromAmount == 0) {
          return;                  /*  No files to convert                 */
     }
     if (!Generic && FromAmount > 0) {
          dfcAskBackup();
     }
     if (FromAmount != 1) {
          PickConversionFile();
     }
     ConvertFile(FromList[0],FileTo);
     Cleanup();
     Converted=TRUE;
}

static VOID
Cleanup(VOID)                      /*  Cleanup after itself                */
{
     UINT count;
     CHAR moveto[GCMAXPTH];


     dfcDisplayBackup();
     if (dfcBackupRequired()) {
          gmkdir(BackupDirectory);
     }
     if (!Generic) {
          for (count=0; count < FromAmount; count++) {
               if (!sameas(FromList[count]->Name,FileTo->Name)) {
                    if (dfcBackupRequired()) {
                         setmem(moveto,GCMAXPTH,0);
                         stlcpy(moveto,BackupDirectory,GCMAXPTH);
                         stlcat(moveto,SLS,GCMAXPTH);
                         stlcat(moveto,FromList[count]->Name,GCMAXPTH);
                         stlcat(moveto,".VIR",GCMAXPTH);
                         if (rename(PathVIR(FromList[count]->Name),moveto) != 0) {
                              movefile(PathVIR(FromList[count]->Name),moveto);
                         }
                    }
                    else {
                         unlink(PathVIR(FromList[count]->Name));
                    }
               }
               setmem(moveto,GCMAXPTH,0);
               stlcpy(moveto,BackupDirectory,GCMAXPTH);
               stlcat(moveto,SLS,GCMAXPTH);
               stlcat(moveto,FromList[count]->Name,GCMAXPTH);
               stlcat(moveto,".DAT",GCMAXPTH);
               if (dfcBackupRequired()) {
                    if (rename(PathDAT(FromList[count]->Name,FALSE),moveto) != 0) {
                         if (!movefile(PathDAT(FromList[count]->Name,FALSE),moveto)) {
                              dfcError("Can not move file %s to %s",
                              PathDAT(FromList[count]->Name,FALSE),WGSOLD);
                         }
                    }
               }
               else {
                    unlink(PathDAT(FromList[count]->Name,FALSE));
               }
          }
          if (rename(DFC_TEMPFILE,PathDAT(FileTo->Name,FALSE)) != 0) {
               if (!movefile(DFC_TEMPFILE,PathDAT(FileTo->Name,FALSE))) {
                    dfcError("Can not rename temporary data file to %s",
                     PathDAT(FileTo->Name,FALSE));
               }
          }
     }
     dfcDisplayShutdown();
     if (FromAmount > 0) {
          free(FromList);
     }
}

static VOID                        /*  Picks latest date of many           */
PickConversionFile(VOID)           /*  Picks proper conversion file        */
{
     UINT count;
     UINT which;
     USHORT cdat=0;
     pDFCFILE swap;
     struct ffblk fb;

     for (count=0; count < FromAmount; count ++) {
          if (!fndfile(&fb,PathDAT(FromList[count]->Name,TRUE),0)) {
               dfcError("Can not find file: %s",
                PathDAT(FromList[count]->Name,TRUE));
          }
          if (cofdat(fb.ff_fdate) > cdat) {
               which=count;
               cdat=cofdat(fb.ff_fdate);
          }
     }
     if (which != 0) {
          swap=FromList[count];
          FromList[count]=FromList[which];
          FromList[which]=swap;
     }
}

static INT
FindPlatform(VOID)                 /*  Find the current operating system   */
{
#ifdef GCDOS
     return(DFC_DOS);
#endif
#ifdef GCWINNT
     return(DFC_WNT);
#endif
}

static pDFCFILE                    /*  Pointer to list or NULL             */
PlatformInList(                    /* Look for Platform file list          */
INT Platform)                      /*  Operating system to look for        */
{
     const DFCLIST *ptr;

     for (ptr=List; ptr->Platform != DFC_END; ptr++) {
          if (ptr->Platform == Platform) {
               return(ptr->file);
          }
     }
     return(NULL);
}

static pDFCFILE                    /*  Pointer to last file or NULL        */
FindLastFile(                      /* Find last file in list               */
INT Platform)                      /*  Platform to find file in            */
{
     const DFCFILE *ret;

     ret=PlatformInList(Platform);
     ASSERT(ret != NULL);
     for (ret=ret; strlen(ret->Name) != 0; ret++) {
     }
     ASSERT(ret != ToFileList);
     if (ret != ToFileList) {
          ret--;
          return((pDFCFILE)ret);
     }
     return(NULL);
}

static UINT                        /*  Number of files to convert          */
CountConvertFrom(VOID)             /*  Should only be one, just in case    */
{
     const DFCLIST *dfclist;
     const DFCFILE *dfcfile;
     struct ffblk fb;
     CHAR fpath[GCMAXPTH];

     FromAmount=0;
     FromList=NULL;
     for (dfclist=List; dfclist->Platform != DFC_END; dfclist++) {
#ifdef GCDOS
          if (dfclist->Platform != DFC_DOS) {
               continue;
          }
#endif
          for (dfcfile=dfclist->file; strlen(dfcfile->Name) != 0; dfcfile++) {
               if (dfcfile == FileTo) {
                    continue;
               }
               strcpy(fpath,PathDAT(dfcfile->Name,TRUE));
               if (fndfile(&fb,fpath,0)
                && dfcFilePlatform(fpath) == dfclist->Platform
                && (!Generic || ConvertGeneric(gdbModuleName))) {
                    FromList=alcrsz(FromList,FromAmount*sizeof(pDFCFILE),
                     (FromAmount+1)*sizeof(pDFCFILE));
                    FromList[FromAmount]=(pDFCFILE)dfcfile;
                    FromAmount++;
               }
          }
     }
     return(FromAmount);
}

INT                                /*  DFC_DOS, DFC_WNT, DFC_END           */
dfcFilePlatform(                   /* Files operating system               */
const CHAR *file)                  /*  Pointer to file to check            */
{
#ifdef GCDOS
     (VOID)file;
     return(DFC_DOS);
#else
     DFAFILE *testbb;
     CHAR *statbuf;
     struct dfaStatFileSpec *st;
     INT ver;

     statbuf=alczer(4096);
     testbb=dfaOpen(file,1,NULL);
     dfaStatus(statbuf,4096,-1);
     st=(struct dfaStatFileSpec *)statbuf;
     ver=((CHAR*)&st->nKeys)[1];
     dfaClose(testbb);
     free(statbuf);
     return(ver == 64 ? DFC_DOS : ver == 96  ? DFC_WNT : DFC_END);
#endif
}

static CHAR *                      /*  Pointer to file with path           */
PathDAT(                           /* Create a file with .dat extension    */
const CHAR *file,                  /*  File to add extension to            */
GBOOL GenericCheck)                /*  Generic checking                    */
{
     static CHAR fullpath[GCMAXPTH];

     setmem(fullpath,GCMAXPTH,0);
     stlcpy(fullpath,file,GCMAXPTH);
     if (GenericCheck && Generic) {
          stlcat(fullpath,".D_T",GCMAXPTH);
     }
     else {
          stlcat(fullpath,".DAT",GCMAXPTH);
     }
     return(fullpath);
}

static CHAR *                      /*  Pointer to created path             */
PathCVT(                           /* Create CVT path from file            */
const CHAR *file)                  /*  File to create full path from       */
{
     static CHAR fullpath[GCMAXPTH];

     setmem(fullpath,GCMAXPTH,0);
     stlcpy(fullpath,CVTDIR,GCMAXPTH);
     stlcat(fullpath,SLS,GCMAXPTH);
     stlcat(fullpath,file,GCMAXPTH);
     return(fullpath);
}

static CHAR *                      /*  Pointer to file with path           */
PathVIR(                           /* Create a file from GCVIRDAT          */
const CHAR *file)                  /*  File to create full path from       */
{
     static CHAR fullpath[GCMAXPTH];

     setmem(fullpath,GCMAXPTH,0);
     stlcpy(fullpath,VIRDIR,GCMAXPTH);
     stlcat(fullpath,SLS,GCMAXPTH);
     stlcat(fullpath,file,GCMAXPTH);
     stlcat(fullpath,".VIR",GCMAXPTH);
     return(fullpath);
}

static VOID
ConvertFile(                       /* Actual conversion of a file          */
const DFCFILE *ff,                 /*  File pointer coming from            */
const DFCFILE *ft)                 /*  File pointer going to               */
{
     VOID *CurrentData;
     DFCFILEINFO infoto;
     DFCFILEINFO infofrom;
     DFCFILEINFO i1;
     DFCFILEINFO i2;
     INT ftpf;
     INT ffpf;
     const DFCFILE *fon;
     GBOOL switchpf=FALSE;
     CHAR ch;
     struct ffblk fb;
     
     ftpf=dfcFilePlatform(PathVIR(ft->Name));
     ffpf=dfcFilePlatform(PathDAT(ff->Name,TRUE));

     ASSERT(ftpf != DFC_END);
     ASSERT(ffpf != DFC_END);

     if (ftpf != ffpf) {           //  Converting through Platforms
          switchpf=TRUE;
          if (ftpf == DFC_DOS && ffpf == DFC_WNT) {
               dfcError("Conversion from (WNT)\"%s.DAT\" to (DOS)\"%s.DAT\""
                " is not supported\n",ff->Name,ft->Name);
               return;
          }
     }
     FileInfoCreate(ff,&infofrom,ffpf);
     FileInfoCreate(ft,&infoto,ftpf);

     if (!Generic || (Generic && !fndfile(&fb,PathDAT(ft->Name,FALSE),0))) {
          if (Generic) {
               if (!cpyutl(PathVIR(ft->Name),PathDAT(ft->Name,FALSE),FOPRB,FOPWB)) {
                    dfcError("Could not copy %s",PathVIR(ft->Name));
                    return;
               }
          }
          else {
               if (!cpyutl(PathVIR(ft->Name),DFC_TEMPFILE,FOPRB,FOPWB)) {
                    dfcError("Could not copy %s",PathVIR(ft->Name));
                    return;
               }
          }
     }
     dfaFrom=dfaOpen(PathDAT(ff->Name,TRUE),
      (Generic ? GENSIZ : infofrom.DataLength),NULL);
     RecordsTotal=dfaCountRec();
     RecordsDone=0;
     RecordsPercent=100;

     dfaTo=dfaOpen((Generic ? PathDAT(ft->Name,FALSE) : DFC_TEMPFILE),
      infoto.DataLength,NULL);

     DisplayWindow(ff,ft);
     //  Generic databse does not need to be deleted.  The only reason
     //  For records being in the generic database would be because
     //  of a conversion mixup.
     if (!Generic) {
          while (dfaStepLO(infoto.Data)) {
               dfaDelete();
          }
     }
     setmem(&i1,sizeof(DFCFILEINFO),0);
     setmem(&i2,sizeof(DFCFILEINFO),0);

     dfaSetBlk(dfaFrom);
     if (GetFirstRecord(infofrom.Data)) {
          do {
               FileInfoFree(&i1);
               FileInfoFree(&i2);
               FileInfoCreate(ff,&i1,ffpf);
               memcpy(i1.Data,infofrom.Data,infofrom.DataLength);
               i1.DataLength=dfaLastLen();
               fon=ff+1;
               while (strlen(fon->Name) > 0) {
                    FileInfoCreate(fon,&i2,ffpf);
                    CurrentData=CallConvertFunction(&i1,&i2,fon->cnvfunc);
                    if (CurrentData == NULL) {
                         CurrentData=i2.Data;
                    }
                    if (CurrentData != i2.Data) {
                         memcpy(i2.Data,CurrentData,i2.DataLength);
                    }
                    FileInfoFree(&i1);
                    memcpy(&i1,&i2,sizeof(DFCFILEINFO));
                    i2.Data=NULL;
                    i2.Offsets=NULL;
                    i2.fdefs=NULL;
                    FileInfoFree(&i2);
                    fon++;
               }
               if (switchpf) {
                    i2=infoto;
                    CurrentData=CallConvertPlatform(&i1,&infoto);
               }
               dfaSetBlk(dfaTo);

               if (Generic && CurrentData != NULL) {
                    if (dfaQueryEQ(CurrentData,0)) {
                         CurrentData=NULL;           //  Skips if already exists
                    }
               }
               if (CurrentData != NULL) {
                    dfaInsertV(CurrentData,infoto.DataLength);
               }
               if (switchpf) {
                    infoto=i2;
                    setmem(&i2,sizeof(DFCFILEINFO),0);
               }
               RecordsDone++;
               DisplayStats();
               if (kbhit()) {
                    ch=getch();
                    if (ch == ESC) {
                         dfcError("Aborted by user");
                    }
               }
               dfaSetBlk(dfaFrom);
               if (Generic) {
                    dfaDelete();
               }
          } while (GetNextRecord(infofrom.Data));
     }
     dfaClose(dfaTo);
     dfaClose(dfaFrom);
     dfaTo=NULL;
     dfaFrom=NULL;

     FileInfoFree(&i1);
     FileInfoFree(&i2);
     FileInfoFree(&infofrom);
     FileInfoFree(&infoto);
}

static VOID
DisplayWindow(                     /*  Display info window                 */
const DFCFILE *ff,                 /*  File pointer coming from            */
const DFCFILE *ft)                 /*  File pointer going to               */
{
     dfcDisplayInfo(Name,ff->Name,ft->Name,RecordsTotal);
     DisplayStats();
}

static VOID
DisplayStats(VOID)                 /*  Display statistical information     */
{
     dfcDisplayStats(RecordsTotal,RecordsDone);
}

static VOID
FileInfoFree(                      /* Free up file information             */
pDFCFILEINFO finf)                 /*  Pointer to file info structure      */
{
     UINT count;

     if (finf->Data != NULL) {
          free(finf->Data);
     }
     if (finf->fdefs != NULL) {
          for (count=0; count < finf->fdefsn; count ++) {
               if (finf->fdefs[count] != NULL) {
                    free(finf->fdefs[count]);
               }
          }
          free(finf->fdefs);
     }
     if (finf->Offsets != NULL) {
          free(finf->Offsets);
     }
     setmem(finf,sizeof(DFCFILEINFO),0);
}

static VOID
FileInfoCreate(                    /* Create information structre on file  */
const DFCFILE *fil,                /*  Pointer to file                     */
pDFCFILEINFO finf,                 /*  Pointer to file information struct  */
INT Platform)                      /*  Platform to create info for         */
{
     UINT Length=0;
     UINT Fields=0;

     setmem(finf,sizeof(DFCFILEINFO),0);
     finf->Platform=Platform;
     finf->Type=PFTypes[finf->Platform];
     finf->Fields=fil->cnvfields;
     stlcpy(finf->Name,fil->Name,GCMAXPTH);

     Globalfn=0;
     Globalf=NULL;
     GlobalOff=NULL;
     GlobalOffn=0;

     NewGlobalf();
     CountFields(&Length,fil->cnvfields,finf->Type,&Fields,TRUE);
     NewOffset();
     setmem(&GlobalOff[GlobalOffn-1],sizeof(DFCOFFSETS),0);
     GlobalOff[GlobalOffn-1].Type=DFCFLD_END;
     finf->Offsets=GlobalOff;
     finf->Offsetsn=GlobalOffn;
     finf->fdefs=Globalf;
     finf->fdefsn=Globalfn;
     finf->Data=NULL;
     finf->fdef=Globalf[0];
     finf->Data=alczer(Length);
     finf->DataLength=Length;
}

static VOID
NewGlobalf(VOID)                   /*  Create larger size of global field  */
{
     Globalf=alcrsz(Globalf,Globalfn*sizeof(struct flddef *),
      (Globalfn+1)*sizeof(struct flddef *));
     Globalf[Globalfn]=NULL;
     Globalfn++;
}

static VOID
NewGlobalField(                    /* New global field add routine         */
UINT glob,                         /*  Global array to define from         */
UINT *fnum)                        /*  Field number currently on           */
{
     Globalf[glob]=alcrsz(Globalf[glob],
      (*fnum)*sizeof(struct flddef),((*fnum)+1)*sizeof(struct flddef));
     (*fnum)++;
}

static UINT                        /*  Length of the datafields passed     */
CountFields(                       /* Count length of all datafields       */
UINT *Length,                      /*  Current length/offset of fields     */
pDFCDATAFIELDS dfstart,            /*  Pointer to the datafields           */
INT DataType,                      /*  Data type (PFType[])                */
UINT *fnum,                        /*  Number of current fields            */
GBOOL CreateFDA)                   /*  Create FDA's for childs?            */
{
     pDFCDATAFIELDS df;
     UINT StartLength=*Length;
     UINT curglob;

     if (CreateFDA) {
          curglob=Globalfn-1;
     }

     for (df=dfstart; df->Type != DFCFLD_END; df++) {
          AddLength(Length,df,DataType,fnum,CreateFDA,curglob);
     }
     if (!(DataType&CVTPACKED)) {
          AdjustOffset(Length,LargestField(dfstart));
     }
     if (CreateFDA) {
          NewGlobalField(curglob,fnum);
          setmem(&Globalf[curglob][(*fnum)-1],sizeof(struct flddef),0);
          Globalf[curglob][(*fnum)-1].type=CVTFLD_END;
     }
     return((*Length)-StartLength);
}

static VOID
NewOffset(VOID)                    /*  Add a new offset                    */
{
     GlobalOff=alcrsz(GlobalOff,GlobalOffn*sizeof(DFCOFFSETS),
      (GlobalOffn+1)*sizeof(DFCOFFSETS));
     GlobalOffn++;
}

static VOID
AddLength(                         /* Add length of field                  */
UINT *Length,                      /*  Current Length/offset of struct     */
pDFCDATAFIELDS df,                 /*  Pointer to current datafield        */
INT DataType,                      /*  Datatype (PFType[])                 */
UINT *fnum,                        /*  Number of fields                    */
GBOOL CreateFDA,                   /*  Create FDA for childs?              */
UINT curglob)                      /*  Current global working with         */
{
     UINT Largest;
     UINT fl;
     UINT count;
     UINT curfnum;
     UINT curglof;
     UINT Fields=0;
     GBOOL VariableField=FALSE;

     curfnum=*fnum;
     curglof=GlobalOffn;

     if (CreateFDA ||
      (df->Type == DFCFLD_STRUCT || df->Type == DFCFLD_VSTRUCT)) {
          NewGlobalField(curglob,fnum);
          Globalf[curglob][curfnum].type=cvttypes[df->Type];
          VariableField=IsVariableField(df->Type);
     }

     fl=dfcFieldSize(df->Type);

     switch (df->Type) {
     case DFCFLD_STRUCT:
     case DFCFLD_VSTRUCT:
          NewOffset();
          stlcpy(GlobalOff[curglof].Name,df->Name,DFC_NAMSIZ);
          GlobalOff[curglof].Type=df->Type;
          GlobalOff[curglof].nelems=df->nelems;
          GlobalOff[curglof].Offset=*Length;

          Globalf[curglob][curfnum].nelem=df->nelems;
          NewGlobalf();
          for (count=0; count < df->nelems; count++) {
               Largest=LargestField(df->substruct);
               if (!(DataType&CVTPACKED)) {
                    AdjustOffset(Length,Largest);
               }
               if (count == 0) {
                    Globalf[curglob][curfnum].offset=*Length;
               }
               CountFields(Length,df->substruct,DataType,&Fields,(count == 0));
          }
          if (VariableField) {
               Globalf[curglob][curfnum].nelem=0;
//               df->nelems=Largest;
          }
          Globalf[curglob][curfnum].substruct=&Globalf[curglob+1][0];
          return;

     default:
          if (!(DataType&CVTPACKED)) {
               AdjustOffset(Length,fl);
          }
          break;
     }
     NewOffset();
     stlcpy(GlobalOff[curglof].Name,df->Name,DFC_NAMSIZ);
     GlobalOff[curglof].Type=df->Type;
     GlobalOff[curglof].nelems=df->nelems;
     GlobalOff[curglof].Offset=*Length;

     if (CreateFDA) {
          Globalf[curglob][curfnum].offset=*Length;
          Globalf[curglob][curfnum].substruct=NULL;
          if (VariableField) {
               Globalf[curglob][curfnum].nelem=0;
          }
          else {
               Globalf[curglob][curfnum].nelem=df->nelems;
          }
     }
     (*Length)+=fl*df->nelems;
}

static UINT                        /*  Largest field in structure          */
LargestField(                      /* Find largest field in structure      */
pDFCDATAFIELDS dfstart)            /*  Pointer to start of structure       */
{
     UINT ans=0;
     pDFCDATAFIELDS df;

     for (df=dfstart; df->Type != DFCFLD_END; df++) {
          if (df->Type == DFCFLD_STRUCT ||
           df->Type == DFCFLD_VSTRUCT) {
               ans=max(ans,LargestField(df->substruct));
          }
          else {
               ans=max(ans,dfcFieldSize(df->Type));
          }
     }
     return(ans);
}

UINT                               /*  Number of bytes of field size       */
dfcFieldSize(                      /* Gets field size of data type         */
UINT Type)                         /*  Type of datafield                   */
{
     switch (Type) {
     case DFCFLD_CHAR:
     case DFCFLD_OPAQUE:
     case DFCFLD_VCHAR:
     case DFCFLD_VOPAQUE:
          return(1);

     case DFCFLD_SHORT:
     case DFCFLD_VSHORT:
          return(2);

     case DFCFLD_LONG:
     case DFCFLD_VLONG:
          return(4);

     case DFCFLD_DOUBLE:
     case DFCFLD_VDOUBLE:
     case DFCFLD_FLOAT:
     case DFCFLD_VFLOAT:
          return(8);

     default:
          return(0);
     }
}

static VOID
AdjustOffset(                      /* Adjust offset for NON-PADDING        */
UINT *Length,                      /*  Current offset/length of struct     */
UINT fl)                           /*  Current field length                */
{
     if (fl > MAXPADDING) {
          fl=MAXPADDING;
     }
     while (*Length%fl != 0) {
          (*Length)++;
     }
}

static VOID *                      /*  Pointer to new data                 */
CallConvertFunction(               /* Call proper convert function         */
pDFCFILEINFO finf1,                /*  Pointer to first file info          */
pDFCFILEINFO finf2,                /*  Pointer to second file info         */
pDFCFUNCTION rouptr)               /*  Pointer to a routine to be called   */
{
     if (rouptr == NULL) {
          rouptr=dfcConvertDefault;
     }
     return(rouptr(finf1,finf2));
}

static VOID *                      /*  Pointer to new data                 */
CallConvertPlatform(               /* Call conversion function             */
pDFCFILEINFO finf1,                /*  Pointer to first file info          */
pDFCFILEINFO finf2)                /*  Pointer to second file info         */
{
     pDFCFUNCTION rouptr=NULL;
     const DFCPFCONVERT *pfptr;

     if (pfcvt != NULL) {
          pfptr=pfcvt;
          while (pfptr->cnvfunc != NULL) {
               if (pfptr->PlatformFrom == finf1->Platform &&
                pfptr->PlatformTo == finf2->Platform) {
                    rouptr=pfptr->cnvfunc;
                    break;
               }
               pfptr++;
          }
     }
     if (rouptr == NULL) {
          rouptr=dfcConvertDefault;
     }
     return(rouptr(finf1,finf2));
}

struct flddef *                    /*  Pointer to FDA structure            */
dfcGetFDA(                         /* Get an FDA from type of convert      */
UINT Type)                         /*  Type of option                      */
{
     switch (Type) {
     case DFCFLD_OPAQUE:
     case DFCFLD_VOPAQUE:
     case DFCFLD_CHAR:
     case DFCFLD_VCHAR:
          return(charsFDA);

     case DFCFLD_SHORT:
     case DFCFLD_VSHORT:
          return(shortsFDA);

     case DFCFLD_LONG:
     case DFCFLD_VLONG:
          return(longsFDA);

     case DFCFLD_FLOAT:
     case DFCFLD_VFLOAT:
          return(floatsFDA);

     case DFCFLD_DOUBLE:
     case DFCFLD_VDOUBLE:
          return(doublesFDA);
     }
     return(charsFDA);
}

VOID
dfcError(                          /*  Print an error to screen            */
const CHAR *stg,                   /*  String to print                     */
...)                               /*  Extra arguments                     */
{
     CHAR prnbuf[PRNBSZ];
     va_list args;

     ASSERT(stg != NULL);
     ASSERT(strlen(stg) < PRNBSZ);

     va_start(args,stg);
     vsprintf(prnbuf,stg,args);
     va_end(args);

     clrscr();
     ReleaseScreen();
     catastro("WGSDFCVT ERROR: %s",prnbuf);
}

VOID
dfcDisplayInfo(                    /* Display DFC information              */
const CHAR *Description,           /*  Description to display              */
const CHAR *NameFrom,              /*  File converting from                */
const CHAR *NameTo,                /*  File converting to                  */
ULONG Records)                     /*  Number of records to display        */
{
     if (!Captured) {
          CaptureScreen();
          explodeto(dfcscntbl,0,0,53,12,15,3);
          Captured=TRUE;
     }
     setatr(0x1E);
     proff(15,3);
     if (Description != NULL) {
          prat(22,1,"%-30.30s",Description);
     }

     setatr(0x1B);
     if (NameFrom != NULL) {
          prat(22,3,"%-30s",NameFrom);
     }
     if (NameTo != NULL) {
          prat(22,4,"%-30s",NameTo);
     }
     prat(22,6,"%-30lu",Records);
}

VOID
dfcDisplayStats(                   /*  Display the stats bar               */
ULONG TotalRecords,                /*  Total records in conversion         */
ULONG DoneRecords)                 /*  Number of records completed         */
{
     UINT lines;
     ULONG NewPercent=0;
     CHAR bar[51];

     setatr(0x1B);
     proff(15,3);
     prat(22,6,"%-30lu",TotalRecords);
     prat(22,7,"%-30lu",DoneRecords);

     if (TotalRecords != 0) {
          NewPercent=(DoneRecords*100)/TotalRecords;
     }
     if (NewPercent != RecordsPercent) {
          setmem(bar,50,32);
          bar[50]='\0';
          RecordsPercent=NewPercent;
          lines=(UINT)((50*RecordsPercent)/100);
          setatr(0x7C);
          setmem(bar,lines,219);
          prat(2,9,bar);
     }
}

VOID
dfcDisplayBackup(VOID)             /*  Display backup notice               */
{
     if (dfcBackupRequired()) {
          explodeto(dfcscntbl,55,4,78,6,28,7);
     }
}

VOID
dfcDisplayShutdown(VOID)           /*  Release captured video screen       */
{
     ReleaseScreen();
     Captured=FALSE;
}

VOID
dfcDeleteBackupFile(VOID)          /*  Delete the backup file (if exist)   */
{
     unlink(PathCVT(BAKTMP));
}

VOID
dfcAskBackup(VOID)                  /*  Ask the backup directory            */
{
     struct ffblk fptr;

     setmem(BackupDirectory,GCMAXPTH,0);
     stlcpy(BackupDirectory,WGSOLD,GCMAXPTH);

     GetBackupDir();
     if (dfcAskForBackup || !fndfile(&fptr,PathCVT(BAKTMP),0)) {
          dfcAskForBackup=FALSE;
          AskAndWrite();
     }
}

static VOID
AskAndWrite(VOID)                  /*  Ask for backup dir and write it     */
{
     FILE *fptr;

     explodeto(dfcscntbl,0,13,64,20,7,8);
     while (TRUE) {
          if (edtval(9,14,62,BackupDirectory,FileVerify,ALLCAPS) == 0) {
               dfcError("Aborted by user");
          }
          if (BackupDirectory[strlen(BackupDirectory)-1] == '\\') {
               BackupDirectory[strlen(BackupDirectory)-1]='\0';
          }
          break;
     }

     if ((fptr=fopen(PathCVT(BAKTMP),FOPWA)) != NULL) {
          fprintf(fptr,"%s\n",BackupDirectory);
          fclose(fptr);
     }
}

static GBOOL                       /*  TRUE if good character              */
FileVerify(                        /* Verify file name character           */
CHAR c,                            /*  Character to check against          */
CHAR *sval)                        /*  Current value                       */
{
     (VOID)sval;
     return((c == '\\' || c == ':' || isvalfc(c)));
}

static VOID
GetBackupDir(VOID)                 /*  Gets current backup directory       */
{
     FILE *fptr;

     if ((fptr=fopen(PathCVT(BAKTMP),FOPRA)) != NULL) {
          if (fgets(BackupDirectory,GCMAXPTH,fptr) != NULL) {
               BackupDirectory[strlen(BackupDirectory)-1]='\0';
          }
          fclose(fptr);
     }
}

CHAR *
dfcBackupDirectory(VOID)           /*  Returns pointer to backup direc     */
{
     return(BackupDirectory);
}

static GBOOL                       /*  TRUE/FALSE                          */
IsVariableField(                   /* Is field a variable field?           */
INT FieldType)                     /*  Field type                          */
{
     switch (FieldType) {
     case DFCFLD_VCHAR:
     case DFCFLD_VSHORT:
     case DFCFLD_VLONG:
     case DFCFLD_VFLOAT:
     case DFCFLD_VDOUBLE:
     case DFCFLD_VOPAQUE:
     case DFCFLD_VSTRUCT:
          return(TRUE);
     }
     return(FALSE);
}

static VOID
RenameGeneric(VOID)                /*  Rename the generic databases        */
{
     struct ffblk fb;

     if (fndfile(&fb,"BBSGEN.DAT",0)) {
          movefile("BBSGEN.DAT","BBSGEN.D_T");
     }
#ifdef GCWINNT
     if (fndfile(&fb,"WGSGEN2.DAT",0)) {
          if (dfcFilePlatform("WGSGEN2.DAT") != DFC_WNT) {
               movefile("WGSGEN2.DAT","WGSGEN2.D_T");
          }
     }
#endif
}

DFCFUNCTION                        /*  Pointer to new data                 */
dfcConvertDefault(                 /* Default conversion function          */
pDFCFILEINFO from,                 /*  File information converting from    */
pDFCFILEINFO to)                   /*  File information converting to      */
{
     pDFCOFFSETS foff;
     pDFCOFFSETS toff;
     struct flddef *fda;
     UINT elems;
     UINT telems;
     UINT felems;
     GBOOL VariableStruct=FALSE;
     UINT VariableSize=0;

     setmem(to->Data,to->DataLength,0);
     for (toff=to->Offsets; toff->Type != DFCFLD_END; toff++) {
          if (toff->Type == DFCFLD_STRUCT) {
               continue;
          }
          if (toff->Type == DFCFLD_VSTRUCT) {
               VariableStruct=TRUE;
               continue;
          }
          for (foff=from->Offsets; foff->Type != DFCFLD_END; foff++) {
               if (VariableStruct) {
                    if (foff->Offset >= from->DataLength) {
                         if (VariableSize == 0) {
                              VariableSize=toff->Offset;
                         }
                         break;
                    }
               }
               if (sameas(toff->Name,foff->Name)) {
                    telems=toff->nelems;
                    felems=foff->nelems;
                    if (IsVariableField(toff->Type)) {
                         felems=(from->DataLength-foff->Offset)/
                          dfcFieldSize(foff->Type);
                    }
                    elems=min(telems,felems);
                    fda=dfcGetFDA(toff->Type);
                    if (IsVariableField(toff->Type)) {
                         to->DataLength=(to->DataLength-
                          dfcFieldSize(toff->Type)*telems)+elems;
                         telems=elems;
                    }

                    setmem(&to->Data[toff->Offset],dfcFieldSize(toff->Type)*
                     elems,0);

                    cvtData(&from->Data[foff->Offset],
                     &to->Data[toff->Offset],
                     dfcFieldSize(toff->Type)*telems,
                     fda,
                     from->Type,
                     to->Type,
                     CHAN_NUL);
                    setmem(foff->Name,DFC_NAMSIZ,0);
                    break;
               }
          }
     }
     if (VariableStruct) {
          to->DataLength=VariableSize;
     }
     return(to->Data);
}

pDFCOFFSETS                        /*   returns ptr to field offset info   */
dfcFindOffset(                     /* find offset of a particular field    */
pDFCFILEINFO info,                 /*   file information converting from   */
const CHAR *fieldName)             /*   name of field to find              */
{
     pDFCOFFSETS foff;

     for (foff=info->Offsets; foff->Type != DFCFLD_END; foff++) {
          if (sameas(fieldName,foff->Name)) {
               return(foff);
          }
     }
     return(NULL);
}

GBOOL
dfcBackupRequired(VOID)            /*  Is a backup required?               */
{
     return(strlen(BackupDirectory) != 0);
}

static GBOOL                       /*  TRUE if a record is found           */
GetFirstRecord(                    /* Get the first record from database   */
VOID *Data)                        /*  Data buffer to store record in      */
{
     if (Generic) {
          return(dfaAcqEQ(Data,gdbModuleName,1));
     }
     else if (dfaKey == -1) {
          return((dfaDirection == DFC_FORWARD
           ? dfaStepLO(Data) : dfaStepHI(Data)));
     }
     else {
          return((dfaDirection == DFC_FORWARD
           ? dfaAcqLO(Data,dfaKey) : dfaAcqHI(Data,dfaKey)));
     }
}

static GBOOL                       /*  TRUE if next record is found        */
GetNextRecord(                     /* Get next data record in sequence     */
VOID *Data)                        /*  Buffer to store returned data       */
{
     GBOOL ans;

     if (Generic) {
          return(dfaAcqEQ(Data,gdbModuleName,1));
     }
     else if (dfaKey == -1) {
          return((dfaDirection == DFC_FORWARD
           ? dfaStepNX(Data) : dfaStepPR(Data)));
     }
     else {
          ans=(dfaDirection == DFC_FORWARD ? dfaQueryNX() : dfaQueryPR());
          if (ans) {
               dfaAbsRec(Data,dfaKey);
          }
          return(ans);
     }
}

static GBOOL                       /*  TRUE if we should convert generic   */
ConvertGeneric(                    /* Check for conversion                 */
const CHAR *ModuleName)            /*  Module name to check for            */
{
     DFAFILE *gendat;
     GBOOL ans=FALSE;
     struct ffblk fb;

     if (fndfile(&fb,"WGSGEN2.D_T",0)) {
          gendat=dfaOpen("WGSGEN2.D_T",GENSIZ,NULL);
          ans=dfaQueryEQ(ModuleName,1);
          dfaClose(gendat);
     }
     return(ans);
}
