#include <dos.h>
#include <stdio.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
//#include <tchar.h>


#ifdef RLDOS
    #include "dosdefs.h"
#else
    #include <windows.h>
    #include "windefs.h"
#endif

#include "restok.h"
#include "exe2res.h"
#include "newexe.h"


/* ----- Function prototypes ----- */

static void  PrepareFiles(         PSTR, PSTR, PSTR);
static void  ReadSegmentTable(     void);
static void  ComputeResTableSize(  void);
static void  ComputeStringOffsets( void);
static void  BuildResTable(        void);
static void  SegsWrite(            WORD);
static DWORD RelocCopy(            WORD);
static void  ResWrite(             WORD);
static void  SetEXEHeaderFlags(    void);
static void  RewriteTables(        void);
static void  CopyCodeViewInfo(     FILE *, FILE *);
static void  OutPutError(          char *);
static void  ResTableBufferInit(   WORD);
static void  ResTableBufferFree(   void);
static WORD  GetAlign(             DWORD,  WORD);
static LONG  MoveFilePos(          FILE *, WORD, WORD);
static WORD  RoundUp(              LONG,   WORD);
static WORD  AlignFilePos(         FILE *, WORD, BOOL);
static WORD  ReadExeOldHeader(     FILE *, LONG, LONG *) ;
static WORD  ReadExeNewHeader(     FILE *, LONG, LONG, LONG *);
static WORD  ExtractExeResources(  FILE *, FILE *, LONG , LONG);
static void  ExtractString(        FILE *, FILE *, LONG);
static WORD  WriteResFromExe(      FILE *,
                                   FILE *,
                                   LONG,
                                   RESNAMEINFO,
                                   RESTYPEINFO,
                                   WORD);

static TYPINFO *AddResType(      CHAR *, WORD);
static PSTR  MyMakeStr(          PSTR);
static SHORT MyRead(             FILE *, PSTR, WORD);
static SHORT MyWrite(            FILE *, PSTR, WORD);
static LONG  MySeek(             FILE *, LONG, WORD);
static void  MyCopy(             FILE *, FILE *, DWORD);
static int   ProcessBinFile(     void);
static PSTR  RcAlloc(            WORD);
static void  AddResToResFile(    TYPINFO *, RESINFO *);
static void  AddDefaultTypes(    void);
static void  GetOrdOrName(       unsigned int *, unsigned char *);

/* ----- Version functions (added for 3.1) ----- */

static void RcPutWord(   unsigned int);
static int  RcPutString( char *);



/* ----- Module variables ----- */

static struct exe_hdr  OldExe;
static struct new_exe  NewExe;
static struct new_seg *pSegTable;
static PSTR   pResTable;
static PSTR   pResNext;
static FILE * fhInput;
static FILE * fhOutput;
static FILE * fhBin;
static DWORD  dwMaxFilePos;
static DWORD  dwNewExe;
static WORD   wPreloadOffset;
static WORD   wPreloadLength;
static WORD   wResTableOffset;
static WORD   wSegTableLen;
static WORD   wResTableLen;
static BYTE   zeros[ NUMZEROS] = "";
static DWORD  dwExeEndFile;
static WORD   fMultipleDataSegs;



//.........................................................................

int ExtractResFromExe16A( CHAR *szInputExe, CHAR *szOutputRes, WORD wFilter)
{
    QuitT( IDS_NO16RESWINYET, NULL, NULL);

#ifdef 0
    WORD    wResult = (WORD)-1;
    LONG    lPosNewHdr;
    LONG    lPosResourceTable;
    LONG    lFileLen;
    FILE    *fExeFile;
    FILE    *fResFile;
    struct     _stat ExeStats;
    /* initialize */
    wResult      = IDERR_SUCCESS;



    /* open file for reading */
    if ((fExeFile = FOPEN(szInputExe, "rb" )) == NULL ) {
        wResult = IDERR_OPENFAIL;
    }

    if ((fResFile = FOPEN(szOutputRes, "wb" )) == NULL ) {
        wResult = IDERR_OPENFAIL;
    }

    /* get file length */
    if (wResult == IDERR_SUCCESS) {
        _stat(szInputExe , &ExeStats );
        lFileLen = ExeStats.st_size;
    }

    /* read old header, verify contents, and get positon of new header */
    if (wResult == IDERR_SUCCESS) {
        wResult = ReadExeOldHeader( fExeFile, lFileLen, &lPosNewHdr );
    }

    /* read new header, verify contents, &  get position of resource table */
    if (wResult == IDERR_SUCCESS)
        wResult = ReadExeNewHeader(
                                  fExeFile,
                                  lFileLen,
                                  lPosNewHdr,
                                  &lPosResourceTable
                                  );

    wResult = ExtractExeResources( fExeFile , fResFile, lPosResourceTable , lFileLen);
    return ( wResult);
#endif // 0
}

//....................................................................

int BuildExeFromRes16A(

                      CHAR *pstrDest,
                      CHAR *pstrRes,
                      CHAR *pstrSource )
{
    QuitT( IDS_NO16WINRESYET, NULL, NULL);

#ifdef 0
    SHORT nResTableDelta;

    fSortSegments = TRUE;
    /* Get a memory block to use for MyCopy\(\) */


    PrepareFiles(pstrSource, pstrDest, pstrRes);

    ProcessBinFile();

    /* Read the segment table */
    ReadSegmentTable();

    /* Compute the length of the resource table */
    ComputeResTableSize();

    /* Compute string offsets for non-ordinal type and resource names */
    ComputeStringOffsets();

    /* Build the resource table */
    BuildResTable();

    /* Now go back to the beginning */
    MySeek(fhInput, 0L, 0);

    /* Copy from input to output up to the segment table */
    MyCopy(fhInput, fhOutput, dwNewExe + (DWORD)NewExe.ne_segtab);

    /* Copy the segment table */
    MyCopy(fhInput, fhOutput, (long)wSegTableLen);

    /* Save a pointer to the start of the resource table */
    wResTableOffset = (unsigned)(MySeek(fhOutput, 0L, 1) - dwNewExe);

    /* Write our resource table out */
    if (wResTableLen) {
        MyWrite(fhOutput, pResTable, wResTableLen);
    }

    /* Now we\'re looking at the beginning of the resident name table */
    MySeek(fhInput, (LONG)NewExe.ne_restab + dwNewExe, 0);

    /* Copy all the other tables \(they must fall between the resident
     *  names table and the non-resident names table.
     */
    MyCopy(fhInput, fhOutput,
           NewExe.ne_nrestab - (LONG)NewExe.ne_restab - dwNewExe);

    /* Copy the nonresident name table as well */
    MyCopy(fhInput, fhOutput, (LONG)NewExe.ne_cbnrestab);

    /* Compute new pointers in new exe header */
    NewExe.ne_rsrctab = wResTableOffset;
    nResTableDelta = wResTableOffset + wResTableLen - NewExe.ne_restab;
    NewExe.ne_restab += nResTableDelta;
    NewExe.ne_modtab += nResTableDelta;
    NewExe.ne_imptab += nResTableDelta;
    NewExe.ne_enttab += nResTableDelta;
    NewExe.ne_nrestab += nResTableDelta;
    #ifdef VERBOSE
    /* Tell the user what we\'re doing */
    if (fVerbose && fSortSegments) {
        fprintf(errfh, "Sorting preload segments and"
                " resources into fast-load section\n");
        if (fBootModule)
            fprintf(errfh,"This is a boot module; the .DEF file"
                    " is assumed to be correct!\n");
    }
    #endif

    /* If we\'re sorting segments, write preload segments and resources
     *  into a section separate from the load on call stuff.
     */
    if (fSortSegments) {
        /* Save the start of the preload section */
        wPreloadOffset = AlignFilePos(fhOutput, NewExe.ne_align, TRUE);

        /* Write PRELOAD segments and resources */
        SegsWrite(DO_PRELOAD);
        ResWrite(DO_PRELOAD);

        /* Compute the properly aligned length of the preload section */
        wPreloadLength = AlignFilePos(fhOutput, NewExe.ne_align, TRUE) -
                         wPreloadOffset;

        /* Now do the LOADONCALL segs and resources */
        SegsWrite(DO_LOADONCALL);
        ResWrite(DO_LOADONCALL);
    }

    /* If we\'re not sorting segments, just write them into a common block */
    else {
        /* Write the segs and resources */
        SegsWrite(DO_PRELOAD | DO_LOADONCALL);
        ResWrite(DO_PRELOAD | DO_LOADONCALL);
    }

    #ifdef SETEXEFLAGS
    /* Set flags and other values in the EXE header */
    SetEXEHeaderFlags();
    #endif

    /* Rewrite the new exe header, segment table and resource table */
    RewriteTables();
    ResTableBufferFree();

    /* Handle CodeView info */
    CopyCodeViewInfo(fhInput, fhOutput);

    /* Seek to end of output file and issue truncating write */
    MySeek(fhOutput, 0L, 2);
    MyWrite(fhOutput, zeros, 0);
    fclose(fhInput);
    fclose(fhOutput);
    fclose(fhBin);
    FreePTypInfo(pTypInfo);
    pTypInfo=NULL;
    MyFree(pSegTable);
    pSegTable=NULL;
    return TRUE;
#endif // 0
}

/*
 *
 * ReadExeOldHeader\( fExeFile, lFileLen, plPosNewHdr \) : WORD;
 *
 *    fExeFile        file handle of .exe file being read
 *    lFileLen         length of file
 *    plPosNewHdr      pointer to file position of new header
 *
 *     This function reads the old header from an executable file, checks to be
 * sure that it is a valid header, and saves the position of the file\'s
 * new header.
 *
 * This function returns IDERR_SUCCESS if there are no errors, or a non-zero
 * error code if there are.
 *
 */

static WORD ReadExeOldHeader(

                            FILE  *fExeFile,
                            LONG   lFileLen,
                            LONG  *plPosNewHdr )
{
    LONG  lPos;
    WORD    cb;
    EXEHDR        ehOldHeader;
    WORD  wResult;

    /* initialize */
    wResult = IDERR_SUCCESS;

    lPos = fseek( fExeFile, 0L, SEEK_SET );


    if (lPos != 0L)
        wResult = IDERR_READFAIL;

    if (wResult == IDERR_SUCCESS) {
        cb = fread(  (void *) &ehOldHeader, sizeof( EXEHDR) , 1, fExeFile );

        if (cb != 1 ) {
            wResult = IDERR_READFAIL;
        } else if (ehOldHeader.ehSignature != OLDEXESIGNATURE) {
            wResult = IDERR_FILETYPEBAD;
        } else if (ehOldHeader.ehPosNewHdr < sizeof(EXEHDR)) {
            wResult = IDERR_EXETYPEBAD;
        } else if ( ehOldHeader.ehPosNewHdr > (LONG)(lFileLen - sizeof(NEWHDR)) ) {
            wResult = IDERR_EXETYPEBAD;
        } else {
            *plPosNewHdr = ehOldHeader.ehPosNewHdr;
        }
    }

    return wResult;
}

/*
 *
 * ReadExeNewHeader\( fExeFile, lFileLen, lPosNewHdr, plPosResourceTable \) : WORD;
 *
 *    fExeFile        file handle of .exe file being read
 *    lFileLen         length of file
 *    lPosNewHdr       file position of new header
 *    plPosResourceTable   pointer to file position of resource table
 *
 *     This function reads the new header from an executable file, checks to be
 * sure that it is a valid header, and saves the position of the file\'s
 * resource table.
 *
 * This function returns IDERR_SUCCESS if there are no errors, or a non-zero
 * error code if there are.
 *
 */

static WORD ReadExeNewHeader(

                            FILE *fExeFile,
                            LONG  lFileLen,
                            long  lPosNewHdr,
                            LONG *plPosResourceTable )
{
    WORD wResult;
    WORD cb;
    LONG lPos;
    NEWHDR       nhNewHeader;

    /* initialize */
    wResult = IDERR_SUCCESS;

    fseek( fExeFile, lPosNewHdr, SEEK_SET );
    lPos = ftell( fExeFile );

    if (lPos == (long) -1 || lPos > lFileLen || lPos != lPosNewHdr) {
        wResult = IDERR_READFAIL;
    } else {
        cb = fread( ( void *)&nhNewHeader, sizeof(nhNewHeader) , 1, fExeFile );

        if (cb != 1 ) {
            wResult = IDERR_READFAIL;
        } else if (nhNewHeader.nhSignature != NEWEXESIGNATURE) {
            wResult = IDERR_FILETYPEBAD;
        } else if (nhNewHeader.nhExeType != WINDOWSEXE) {
            wResult = IDERR_EXETYPEBAD;
        } else if (nhNewHeader.nhExpVer < 0x0300) {
            wResult = IDERR_WINVERSIONBAD;
        } else if (nhNewHeader.nhoffResourceTable == 0) {
            wResult = IDERR_RESTABLEBAD;
        } else {
            *plPosResourceTable = lPosNewHdr + nhNewHeader.nhoffResourceTable;
        }
    }

    return wResult;
}

/*
 *
 * ReadExeTable\( fExeFile , lPosResourcTable \) : WORD;
 *
 *    fExeFile      file handle of .exe file being read
 *
 * This function reads through the entries in an .exe file\'s resource table,
 * identifies any icons in that table, and saves the file offsets of the data
 * for those icons. This function expects the initial file position to point
 * to the first entry in the resource table.
 *
 * This function returns IDERR_SUCCESS if there are no errors, or a non-zero
 * error code if there are.
 *
 */

static WORD ExtractExeResources(

                               FILE  *fExeFile,
                               FILE  *fResFile,
                               LONG   lPosResourceTable,
                               LONG   lFileLen )
{
    BOOL    fLoop;
    WORD    wResult;
    WORD    cb;
    LONG    lPos;
    WORD    wShiftCount;
    wResult = IDERR_SUCCESS;


    // posistion file pointer at resource table
    fseek( fExeFile, lPosResourceTable, SEEK_SET );
    lPos = ftell(fExeFile);

    if (lPos == (LONG) -1 || lPos > lFileLen || lPos != lPosResourceTable) {
        return  IDERR_READFAIL ;
    }

    if (wResult == IDERR_SUCCESS) {
        cb = fread( (void *) &wShiftCount, sizeof(wShiftCount), 1 , fExeFile );
    }

    if (cb != 1 ) {
        return IDERR_READFAIL;
    }

    if (wShiftCount > 16) {
        return IDERR_RESTABLEBAD;
    }

    /* initialize */
    wResult       = IDERR_SUCCESS;
    fLoop         = TRUE;


    /* loop through entries in resource table */
    while (fLoop == TRUE) {
        WORD    cb;
        WORD    iFile;
        RESTYPEINFO   rt;

        /* read RESTYPEINFO */
        cb = fread( (void *)&rt, sizeof(rt), 1, fExeFile );

        if (cb != 1 ) {
            wResult = IDERR_READFAIL;
            break;
        }

        if ( rt.rtType == 0 )
            break;

        // now get all the resource of this type
        for (
            iFile = 0;
            iFile<rt.rtCount && wResult==IDERR_SUCCESS;
            iFile++
            ) {
            RESNAMEINFO rn;

            cb = fread(  (void *) &rn, sizeof(rn) , 1 , fExeFile );

            if (cb != 1 ) {
                wResult = IDERR_READFAIL;
            }

            WriteResFromExe( fExeFile, fResFile,lPos, rn, rt, wShiftCount );

        }
        fLoop = (rt.rtType != 0) && (wResult == IDERR_SUCCESS);
    }
    FCLOSE(fExeFile);
    FCLOSE(fResFile);
    return wResult;
}

//.................................................................

static WORD WriteResFromExe(

                           FILE        *fExeFile,
                           FILE        *fResFile,
                           LONG         lPos,
                           RESNAMEINFO  ResNameInfo,
                           RESTYPEINFO  ResTypeInfo,
                           WORD         wShiftCount )
{
    LONG lCurPos;
    LONG lResPos;
    LONG wLength;
    LONG wTmpLength;

    wLength =  (LONG) ResNameInfo.rnLength << wShiftCount;

    lCurPos = ftell( fExeFile );
    // position file pointer at resouce location
    lResPos = (LONG) ResNameInfo.rnOffset << wShiftCount;
    fseek( fExeFile, lResPos, SEEK_SET );

    if ( ResTypeInfo.rtType & 0x8000) {
        PutByte( fResFile, (BYTE) 0xff, NULL );
        PutWord( fResFile, (WORD)(ResTypeInfo.rtType & 0x7FFF), NULL);
    } else {
        ExtractString(fExeFile,fResFile,lPos+ResTypeInfo.rtType);
    }

    if ( ResNameInfo.rnID & 0x8000 ) {
        PutByte( fResFile, (BYTE) 0xff, NULL );
        PutWord( fResFile, (WORD)(ResNameInfo.rnID & 0x7fFF), NULL);
    } else {
        ExtractString(fExeFile,fResFile,lPos+ResNameInfo.rnID);
    }

    PutWord( fResFile, ResNameInfo.rnFlags , NULL );
    PutdWord( fResFile, (LONG) wLength, NULL );
    wTmpLength = wLength;
    // now write the actual data

    fseek( fExeFile, lResPos, SEEK_SET );
    ReadInRes( fExeFile, fResFile, &wTmpLength );
    fseek( fExeFile, lCurPos, SEEK_SET );

    return 0;
}

//..................................................................

static void ExtractString( FILE *fExeFile, FILE *fResFile, LONG lPos)
{
    BYTE n,b;

    fseek(fExeFile, lPos, SEEK_SET);

    n=GetByte(fExeFile, NULL);
    for (;n--; ) {
        b=GetByte(fExeFile, NULL);
        PutByte(fResFile, (CHAR) b, NULL);
    }
    PutByte(fResFile, (CHAR) 0, NULL);
}


// Modifications for RLTOOLS

// Currently we dont support any dynamic flags
BOOL    fBootModule   = FALSE;
BOOL    fSortSegments = TRUE;

TYPINFO *pTypInfo = NULL;

static void FreePTypInfo( TYPINFO *pTypInfo)
{
    RESINFO * pRes, *pRTemp;
    TYPINFO * pTItemp;

    while (pTypInfo) {
        pRes = pTypInfo->pres;
        while (pRes) {
            pRTemp = pRes->next;
            MyFree(pRes->name);
            MyFree(pRes);
            pRes = pRTemp;
        }
        pTItemp = pTypInfo->next;
        MyFree(pTypInfo->type);
        MyFree(pTypInfo);
        pTypInfo = pTItemp;
    }
}

/* ----- Helper functions ----- */


/*  PrepareFiles
 *  Prepares the EXE files \(new and old\) for writing and verifies
 *  that all is well.
 *  Exits on error, returns if processing should continue.
 */

static void PrepareFiles(

                        PSTR pstrSource,
                        PSTR pstrDest,
                        PSTR pstrRes )
{
    /* Open the .EXE file the linker gave us */
    if ( (fhInput = FOPEN(pstrSource, "rb" )) == NULL ) {
        OutPutError("Unable to open Exe Source  File");
    }

    if ((fhBin = FOPEN(pstrRes, "rb")) == NULL ) {
        OutPutError("Unable to open Resource File");
    }

    /* Read the old format EXE header */
    MyRead(fhInput, (PSTR)&OldExe, sizeof (OldExe));

    /* Make sure its really an EXE file */
    if (OldExe.e_magic != EMAGIC) {
        OutPutError("Invalid .EXE file" );
    }

    /* Make sure theres a new EXE header floating around somewhere */
    if (!(dwNewExe = OldExe.e_lfanew)) {
        OutPutError("Not a Microsoft Windows format .EXE file");
    }

    /* Go find the new .EXE header */
    MySeek(fhInput, dwNewExe, 0);
    MyRead(fhInput, (PSTR)&NewExe, sizeof (NewExe));

    /* Check version numbers */
    if (NewExe.ne_ver < 4) {
        OutPutError("File not created by LINK");
    }

    /* Were there linker errors? */
    if (NewExe.ne_flags & NEIERR) {
        OutPutError("Errors occurred when linking file.");
    }

    /* Make sure that this program\'s EXETYPE is WINDOWS \(2\) not OS/2 \(1\) */
    if (NewExe.ne_exetyp != 2)
        OutPutError("The EXETYPE of the program is not WINDOWS.\n"
                    "(Make sure the .DEF file is correct.");
#ifdef VERBOSE
    if (fVerbose) {
        fprintf(errfh, "\n");
    }
#endif

    /* Open the all new executable file */
    if ( (fhOutput = FOPEN( pstrDest, "wb")) == NULL ) {
        OutPutError("Unable to create destination");
    }
}


/*  ReadSegmentTable
 *  Reads the segment table from the file.
 */

static void ReadSegmentTable( void)
{
    struct new_seg* pSeg;
    WORD i;

    MySeek(fhInput, (LONG)NewExe.ne_segtab + dwNewExe, 0);
    if ((wSegTableLen = NewExe.ne_cseg * sizeof (struct new_seg)) > 0) {
        pSegTable = (struct new_seg *)RcAlloc   (wSegTableLen);
        MyRead(fhInput, (PSTR)pSegTable, wSegTableLen);

        /* See if we have more than one data segment */
        fMultipleDataSegs = 0;
        for (pSeg = pSegTable, i = NewExe.ne_cseg ; i ; --i, ++pSeg) {
            if ((pSeg->ns_flags & NSTYPE) == NSDATA) {
                ++fMultipleDataSegs;
            }
        }
        if (fMultipleDataSegs) {
            --fMultipleDataSegs;
        }
    } else {
        pSegTable = NULL;
    }
}

/*  ComputeResTableSize
 *  Computes the size of the resource table by enumerating all the
 *  resources currently in the linked lists.
 */

static void ComputeResTableSize( void)
{
    TYPINFO **pPrev;
    TYPINFO *pType;
    RESINFO *pRes;

    /* Start with the minimum overhead size of the resource table.  This
     *  is the resource alignment count and the zero WORD terminating the
     *  table.  This is necessary so that we put the correct file offset
     *  in for the string offsets to named resources.
     */
    wResTableLen = RESTABLEHEADER;

    /* Loop over type table, computing the fixed length of the
     *  resource table, removing unused type entries.
     */
    pPrev = &pTypInfo;
    dwMaxFilePos = 0L;
    while (pType = *pPrev) {
        if (pRes = pType->pres) {
            /* Size of type entry */
            wResTableLen += sizeof (struct rsrc_typeinfo);
            while (pRes) {
                /* Size of resource entry */
                wResTableLen += sizeof (struct rsrc_nameinfo);
                if (pType->next || pRes->next) {
                    dwMaxFilePos += pRes->size;
                }
                pRes = pRes->next;
            }
            pPrev = &pType->next;
        } else {
            *pPrev = pType->next;
            MyFree(pType->type);
            MyFree(pType);
        }
    }
}


/*  ComputeStringOffsets
 *  Computes offsets to strings from named resource and types.
 */

static void ComputeStringOffsets( void)
{
    TYPINFO *pType;
    RESINFO *pRes;

    /* Loop over type table, computing string offsets for non-ordinal
     *  type and resource names.
     */
    pType = pTypInfo;
    while (pType) {
        pRes = pType->pres;

        /* Is there an ordinal? */
        if (pType->typeord) {
            /* Mark the ordinal */
            pType->typeord |= RSORDID;

            /* Flush the string name */
            MyFree(pType->type);
            pType->type = NULL;
        } else if (pType->type) {           /* is there a type string? */
            /* Yes, compute location of the type string */
            pType->typeord = wResTableLen;
            wResTableLen += strlen(pType->type) + 1;
        }

        while (pRes) {
            /* Is there an ordinal? */
            if (pRes->nameord) {
                /* Mark the ordinal */
                pRes->nameord |= RSORDID;

                /* Flush the string name */
                MyFree(pRes->name);
                pRes->name = NULL;
            }

            /* Is there a resource name? */
            else if (pRes->name) {
                /* Yes, compute location of the resource string */
                pRes->nameord = wResTableLen;
                wResTableLen += strlen(pRes->name) + 1;
            }
            pRes = pRes->next;
        }
        pType = pType->next;
    }
}


/*  BuildResTable
 *  Builds the local memory image of the resource table.
 */

static void BuildResTable( void)
{
    TYPINFO *pType;
    RESINFO *pRes;

    /* Check to see if we have any resources.  If not, just omit the table */
    if (wResTableLen > RESTABLEHEADER) {

        /* Set up the temporary resource table buffer */
        ResTableBufferInit(wResTableLen);

        /* Alignment shift count
         *  \(we default here to the segment alignment count\)
         */
        RcPutWord(NewExe.ne_align);

        pType = pTypInfo;
        while (pType) {
            /* output the type and number of resources */
            RcPutWord(pType->typeord); /* DW type id */
            RcPutWord(pType->nres);    /* DW #resources for this type */
            RcPutWord(0);              /* DD type procedure */
            RcPutWord(0);

            /* output flags and space for the file offset for each resource */
            pRes = pType->pres;
            while (pRes) {
                pRes->poffset = (WORD *)pResNext;
                RcPutWord(0);           /* DW file offset */
                RcPutWord(0);           /* DW resource size */
                pRes->flags |= NSDPL;
                RcPutWord(pRes->flags ); /* DW flags */
                RcPutWord(pRes->nameord ); /* DW name id */
                RcPutWord(0);              /* DW handle */
                RcPutWord(0);              /* DW usage or minalloc */
                pRes = pRes->next;
            }
            pType = pType->next;
        }

        /* Null entry terminates table */
        RcPutWord(0);

        /* Output type and name strings for non-ordinal resource types
         *  and names */
        pType = pTypInfo;
        while (pType) {
            /* Dump out any strings for this type */
            if (pType->type && !(pType->typeord & RSORDID)) {
                RcPutString(pType->type);
            }

            pRes = pType->pres;
            while (pRes) {
                if (pRes->name && !(pRes->nameord & RSORDID))
                    RcPutString(pRes->name);

                pRes = pRes->next;
            }

            pType = pType->next;
        }
    } else
        wResTableLen = 0;
}


/*  SegsWrite
 *  Copies segments to the file.  This routine will do only preload,
 *  only the load on call, or both types of segments depending on
 *  the flags.
 */

static void SegsWrite( WORD wFlags)
{
    WORD wExtraPadding;
    WORD i;
    static struct new_seg *pSeg;
    DWORD dwSegSize;
    DWORD dwWriteSize;
    WORD wTemp;
    WORD wcbDebug;

    /* We only need extra padding in the preload section.
     *  Note that when wFlags == DO_PRELOAD | DO_LOADONCALL, we DON\'T
     *  need extra padding because this is NOT a preload section.
     *  \(hence the \'==\' instead of an \'&\'\)
     */
    wExtraPadding = (wFlags == DO_PRELOAD);

    /* Copy segment data for each segment, fixed and preload only */
    for (i = 1, pSeg = pSegTable; i <= NewExe.ne_cseg; i++, pSeg++) {
        /* If there\'s no data in segment, skip it here */
        if (!pSeg->ns_sector) {
            continue;
        }

        /* Force some segments to be preload if doing preload resources */
        if ((wFlags & DO_PRELOAD) && !fBootModule) {
            char *reason = NULL;

            /* Check various conditions that would force preloading */
            if (i == (unsigned)(NewExe.ne_csip >> 16)) {
                reason = "Entry point";
            }
            if (!(pSeg->ns_flags & NSMOVE)) {
                reason = "Fixed";
            }
            if (pSeg->ns_flags & NSDATA) {
                reason = "Data";
            }
            if (!(pSeg->ns_flags & NSDISCARD)) {
                reason = "Non-discardable";
            }

            /* If this segment must be preload and the segment is not already
             *  marked as such, warn the user and set it.
             */
            if (reason && !(pSeg->ns_flags & NSPRELOAD)) {
#ifdef VERBOSE
                fprintf(errfh,
                        "RC: warning RW4002: %s segment %d set to PRELOAD\n",
                        reason, i);
#endif
                pSeg->ns_flags |= NSPRELOAD;
            }
        }

        /* Skip this segment if it doesn\'t match the current mode */
        wTemp = pSeg->ns_flags & NSPRELOAD ? DO_PRELOAD : DO_LOADONCALL;
        if (!(wTemp & wFlags)) {
            continue;
        }

        /* Get the true segment length.  A zero length implies 64K */
        if (pSeg->ns_cbseg) {
            dwSegSize = pSeg->ns_cbseg;
        } else {
            dwSegSize = 0x10000L;
        }

#ifdef VERBOSE

        if (fVerbose)
            fprintf(errfh, "Copying segment %d (%lu bytes)\n", i, dwSegSize);
#endif

        /* Align the segment correctly and pad the file to match */
        MoveFilePos(fhInput, pSeg->ns_sector, NewExe.ne_align);
        pSeg->ns_sector = AlignFilePos(fhOutput, NewExe.ne_align,
                                       wExtraPadding);

        /* Copy the segment */
        MyCopy(fhInput, fhOutput, dwSegSize);

        /* Pad out all segments in the preload area to their minimum
         *  memory allocation size so that KERNEL doesn\'t have to realloc
         *  the segment.
         */
        if (wExtraPadding && pSeg->ns_cbseg != pSeg->ns_minalloc) {
            /* A minalloc size of zero implies 64K */
            if (!pSeg->ns_minalloc) {
                dwWriteSize = 0x10000L - pSeg->ns_cbseg;
            } else {
                dwWriteSize = pSeg->ns_minalloc - pSeg->ns_cbseg;
            }

            /* Add in to total size of segment */
            dwSegSize += dwWriteSize;

            /* Set the segment table size to this new size */
            pSeg->ns_cbseg = pSeg->ns_minalloc;

            /* Pad the file */
            while (dwWriteSize) {
                dwWriteSize -= MyWrite(fhOutput,
                                       zeros,
                                       (WORD)(dwWriteSize > (DWORD) NUMZEROS
                                              ? NUMZEROS : dwWriteSize));
            }
        }

        /* Copy the relocation information */
        if (pSeg->ns_flags & NSRELOC) {
            /* Copy the relocation stuff */
            dwSegSize += RelocCopy(i);

            /* Segment + padding + relocations can\'t be >64K for preload
             *  segments.
             */
            if (fSortSegments && (pSeg->ns_flags & NSPRELOAD) &&
                dwSegSize > 65536L) {
#ifdef VERBOSE
                fprintf(errfh,
                        "RC : fatal error RW1031: Segment %d and its\n"
                        "     relocation information is too large for load\n"
                        "     optimization. Make the segment LOADONCALL or\n"
                        "     rerun RC using the -K switch if the segment must\n"
                        "     be preloaded.\n", i);
#endif
            }
        }

        /* Copy any per-segment debug information */
        if (pSeg->ns_flags & NSDEBUG) {
            MyRead(fhInput, (PSTR)&wcbDebug, sizeof (WORD));
            MyWrite(fhOutput, (PSTR)&wcbDebug, sizeof (WORD));
            MyCopy(fhInput, fhOutput, (LONG)wcbDebug);
        }
    }
}


/*  RelocCopy
 *  Copys all the relocation records for a given segment.
 *  Also checks for invalid fixups.
 */

static DWORD RelocCopy( WORD wSegNum)
{
    WORD wNumReloc;
    struct new_rlc RelocRec;
    WORD i;
    BYTE byFixupType;
    BYTE byFixupFlags;
    WORD wDGROUP;

    /* Get the number of relocations */
    MyRead(fhInput, (PSTR)&wNumReloc, sizeof (WORD));
    MyWrite(fhOutput, (PSTR)&wNumReloc, sizeof (WORD));

    /* Get the automatic data segment */
    wDGROUP = NewExe.ne_autodata;

    /* Copy and verify all relocations */
    for (i = 0 ; i < wNumReloc ; ++i) {
        /* Copy the record */
        MyRead(fhInput, (PSTR)&RelocRec, sizeof (RelocRec));
        MyWrite(fhOutput, (PSTR)&RelocRec, sizeof (RelocRec));

        /* Validate it only if necessary */
        if ((NewExe.ne_flags & (NENOTP | NESOLO)) ||
            wSegNum == wDGROUP || fMultipleDataSegs) {
            continue;
        }

        /* Bad fixups are fixups to DGROUP in code segments in apps
         *  that can be multi-instanced.  Since we can\'t fix up locations
         *  that are different from instance to instance in shared code
         *  segments, we have to warn the user.  We only warn because this
         *  may be allowable if the app only allows a single instance of
         *  itself to run.
         */
        byFixupType = (BYTE) (RelocRec.nr_stype & NRSTYP);
        byFixupFlags = (BYTE) (RelocRec.nr_flags & NRRTYP);
#ifdef VERBOSE
        if ((byFixupType == NRSSEG || byFixupType == NRSOFF) &&
            byFixupFlags == NRRINT &&
            RelocRec.nr_union.nr_intref.nr_segno == wDGROUP)

            fprintf(errfh,
                    "RC : warning RW4005: Segment %d (offset %04X) contains a\n"
                    "     relocation record pointing to the automatic\n"
                    "     data segment.  This will cause the program to crash\n"
                    "     if the instruction being fixed up is executed in a\n"
                    "     multi-instance application.  If this fixup is\n"
                    "     necessary, the program should be restricted to run\n"
                    "     only a single instance.\n", wSegNum, RelocRec.nr_soff);
#endif
    }

    return wNumReloc * sizeof (struct new_rlc);
}


/*  ResWrite
 *  Copies resources to the file.  This routine will do only the preload,
 *  only the load on call, or both types of resources depending on the
 *  flags.
 */

static void ResWrite( WORD wFlags)
{
    WORD wExtraPadding;
    WORD wTemp;
    WORD wResAlign;
    TYPINFO *pType;
    RESINFO *pRes;

    /* If we have no resource table, just ignore this */
    if (!wResTableLen) {
        return;
    }

    /* We only need extra padding in the preload section.
     *  Note that when wFlags == DO_PRELOAD | DO_LOADONCALL, we DON\'T
     *  need extra padding because this is NOT a preload section.
     *  \(hence the \'==\' instead of an \'&\'\)
     */
    wExtraPadding = (wFlags == DO_PRELOAD);

    /* Compute resource alignment.  Note that the alignment is not the
     *  same as the segment alignment ONLY IF there is no segment sorting
     *  and some resources cannot be reached with the current segment
     *  align count.
     */
    wResAlign = NewExe.ne_align;

    if (!fSortSegments) {
        /* Compute the needed alignment */
        dwMaxFilePos += MySeek(fhOutput, 0L, 2);
        wResAlign = GetAlign(dwMaxFilePos, NewExe.ne_align);

#ifdef VERBOSE
        if (fVerbose)
            fprintf(errfh, "Resources will be aligned on %d byte boundaries\n",
                    1 << wResAlign);
#endif

        /* Point back to the start of the local memory resource table */
        pResNext = pResTable;
        RcPutWord(wResAlign);
    }

    /* Output contents associated with each resource */
    for (pType = pTypInfo ; pType; pType = pType->next) {
        for (pRes = pType->pres ; pRes ; pRes = pRes->next) {
            /* Make sure this is the right kind of resource */
            wTemp = pRes->flags & RNPRELOAD ? DO_PRELOAD : DO_LOADONCALL;
            if (!(wTemp & wFlags)) {
                continue;
            }

            /* Give some info to the user */
#ifdef VERBOSE
            if (fVerbose) {
                fprintf(errfh, "Writing resource ");
                if (pRes->name && !(pRes->nameord & RSORDID)) {
                    fprintf(errfh, "%s", pRes->name);
                } else {
                    fprintf(errfh, "%d", pRes->nameord & 0x7FFF);
                }

                if (pType->type && !(pType->typeord & RSORDID)) {
                    fprintf(errfh, ".%s", pType->type);
                } else {
                    fprintf(errfh, ".%d", pType->typeord & 0x7FFF);
                }

                fprintf(errfh, " (%lu bytes)\n", pRes->size);
                fflush(errfh);
            }
#endif

            /* Copy the resource from the RES file to the EXE file */
            MySeek(fhBin, (long)pRes->BinOffset, 0);
            *(pRes->poffset)++ =
            AlignFilePos(fhOutput, wResAlign, wExtraPadding);
            *(pRes->poffset) = RoundUp(pRes->size, wResAlign);
            MyCopy(fhBin, fhOutput, pRes->size);
        }
    }

    /* Compute the end of the EXE file thus far for the CV info */
    dwExeEndFile = AlignFilePos(fhOutput, wResAlign, wExtraPadding);
}

#ifdef SETEXEFLAGS
/*  SetEXEHeaderFlags
 *  Sets necessary flags and values in the EXE header.
 */

static void SetEXEHeaderFlags( void)
{
    /* Tell loader we initialized previously unused fields */
    if (NewExe.ne_ver == 4) {
        NewExe.ne_rev = 2;
    }

    /* Set command line values into the header */
    NewExe.ne_expver   = expWinVer;
    NewExe.ne_swaparea = swapArea;

    /* Set the preload section values */
    if (fSortSegments) {
        /* Set the new fastload section values */
        NewExe.ne_gangstart = wPreloadOffset;
        NewExe.ne_ganglength = wPreloadLength;
    #ifdef VERBOSE
        if (fVerbose)
            fprintf(errfh, "Fastload area is %ld bytes at offset 0x%lX.\n",
                    (LONG)wPreloadLength << NewExe.ne_align,
                    (LONG)wPreloadOffset << NewExe.ne_align);
    }
    #endif

    /* Clear all the flags */
    NewExe.ne_flags &=
    ~(NELIM32|NEMULTINST|NEEMSLIB|NEPRIVLIB|NEPRELOAD);

    /* Set appropriate flags */
    if (fLim32) {
        NewExe.ne_flags |= NELIM32;
    }
    if (fMultInst) {
        NewExe.ne_flags |= NEMULTINST;
    }
    if (fEmsLibrary) {
        NewExe.ne_flags |= NEEMSLIB;
    }
    if (fPrivateLibrary) {
        NewExe.ne_flags |= NEPRIVLIB;
    }
    if (fProtOnly) {
        NewExe.ne_flags |= NEPROT;
    }

    if (fSortSegments && wPreloadLength) {
        NewExe.ne_flagsother |= NEPRELOAD;
    }

    NewExe.ne_flags |= NEWINAPI;
}
#endif

/*  RewriteTables
 *  Rewrites the EXE header and the resource and segment tables
 *  with their newly-updated information.
 */

static void RewriteTables( void)
{
    /* Write the new EXE header */
    MySeek(fhOutput, (LONG)dwNewExe, 0);
    MyWrite(fhOutput, (PSTR)&NewExe, sizeof (NewExe));

    /* Seek to the start of the segment table */
    MySeek(fhOutput, dwNewExe + (LONG)NewExe.ne_segtab, 0);
    MyWrite(fhOutput, (PSTR)pSegTable, wSegTableLen);

    /* Seek to and write the resource table */
    if (wResTableLen) {
        MySeek(fhOutput, dwNewExe + (LONG)NewExe.ne_rsrctab, 0);
        MyWrite(fhOutput, pResTable, wResTableLen);
    }
}



/*  CopyCodeViewInfo
 *  Copies CodeView info to the new EXE file and relocates it if
 *  necessary.  This routine is designed to work with the
 *  DNRB-style info as well as NBxx info where x is a digit.
 */

static void CopyCodeViewInfo( FILE *fhInput, FILE *fhOutput)
{
    unsigned long dwcb;
    unsigned int i;
    CVINFO cvinfo;
    CVSECTBL cvsectbl;

    /* See if old format \(DNRB\) symbols present at end of input file
     *  If they are, relocate the table to the new file position and
     *  fix up the file-position dependent offsets.
     */
    dwcb = MySeek( fhInput, -(signed long)sizeof (CVINFO), 2);
    MyRead( fhInput, (char *)&cvinfo, sizeof (cvinfo));

    if (*(unsigned long *)cvinfo.signature == CV_OLD_SIG) {
        dwcb -= cvinfo.secTblOffset;
        MySeek( fhInput, cvinfo.secTblOffset, 0);
        MyRead( fhInput, (char *)&cvsectbl, sizeof (cvsectbl));
        dwcb -= sizeof (cvsectbl);

        for (i = 0 ; i < 5 ; ++i) {
            cvsectbl.secOffset[i] -= cvinfo.secTblOffset;
        }

        cvinfo.secTblOffset = dwExeEndFile;

        for (i = 0 ; i < 5 ; ++i) {
            cvsectbl.secOffset[i] += cvinfo.secTblOffset;
        }

        MySeek( fhOutput, cvinfo.secTblOffset, 0);
        MyWrite( fhOutput, (char *)&cvsectbl, sizeof (cvsectbl));
        MyCopy( fhInput, fhOutput, dwcb);
        MyWrite( fhOutput, (char *)&cvinfo, sizeof (cvinfo));
    }

    /* Check for new format \(NBxx\) symbols.  Since these symbols are
     *  file-position independent, just copy them over; no need to
     *  fix them up as with the old format symbols.
     */
    else if (*(unsigned short int *)cvinfo.signature == CV_SIGNATURE &&
             isdigit(cvinfo.signature[2]) && isdigit(cvinfo.signature[3])) {
        MySeek( fhOutput, 0L, 2);
        MySeek( fhInput, -cvinfo.secTblOffset, 2);
        MyCopy( fhInput, fhOutput, cvinfo.secTblOffset);
    }
}

/*  OutPutError
 *  Outputs a fatal error message and exits.
 */

static void OutPutError( char *szMessage)
{
    QuitA( 0, szMessage, NULL);
}


/*  ResTableBufferInit
 *  Creates the resource table buffer and points global pointers
 *  to it.  This table is written to so that we can modifiy it
 *  before writing it out to the EXE file.
 */

static void ResTableBufferInit( WORD wLen)
{
    /* Allocate local storage for resource table */
    pResTable = RcAlloc   (wLen);

    /* Point to the start of the table for the PutXXXX\(\) */
    pResNext = pResTable;
}

/*  ResTableBufferFree
 *  Frees the temporary storage for resource table
 */

static void ResTableBufferFree( void)
{
    /* Nuke the table */
    MyFree(pResTable);
}



/*  GetAlign
 *  Computes the alignment value needed for the given maximum file
 *  position passed in.  This is done by computing the number of
 *  bits to be shifted left in order to represent the maximum
 *  file position in 16 bits.
 */

static WORD GetAlign( DWORD dwMaxpos, WORD wAlign)
{
    DWORD dwMask;
    WORD i;

    /* Compute the initial mask based on the input align value */
    dwMask = 0xFFFFL;
    for (i = 0; i < wAlign ; ++i) {
        dwMask <<= 1;
        dwMask |= 1;
    }

    /* See if we need to increase the default mask to reach the maximum
     *  file position.
     */
    while (dwMaxpos > dwMask) {
        dwMask <<= 1;
        dwMask |= 1;
        ++wAlign;
    }

    /* Return the new alignment */
    return wAlign;
}


/*  MoveFilePos
 *  Moves the file pointer to the position indicated by wPos, using the
 *  align shift count wAlign.  This converts the WORD value wPos
 *  into a LONG value by shifting left wAlign bits.
 */

static LONG MoveFilePos( FILE *fh, WORD wPos, WORD wAlign)
{
    return MySeek(fh, ((LONG)wPos) << wAlign, 0);
}


/*  RoundUp
 *  Computes the value that should go into a 16 bit entry in an EXE
 *  table by rounding up to the next boundary determined by the
 *  passed in alignment value.
 */

static WORD RoundUp( LONG lValue, WORD wAlign)
{
    LONG lMask;

    /* Get all the default mask of all ones except in the bits below the
     *  alignment value.
     */
    lMask = -1L;
    lMask <<= wAlign;

    /* Now round up using this mask */
    lValue += ~lMask;
    lValue &= lMask;

    /* Return as a 16 bit value */
    return ((WORD) (lValue >> (LONG) wAlign));
}


/*  AlignFilePos
 *  Computes a correctly aligned file position based on the current
 *  alignment.
 */

static WORD AlignFilePos( FILE *fh, WORD wAlign, BOOL fPreload)
{
    LONG lCurPos;
    LONG lNewPos;
    LONG lMask;
    WORD nbytes;
    WORD wNewAlign;

    /* If we\'re in the preload section, we have tougher alignment
     *  restrictions:  We have to be at least 32-byte aligned and have
     *  at least 32 bytes between objects for arena headers.  It turns
     *  out that this feature is not really used in KERNEL but could be
     *  implemented someday.
     */
    if (fPreload && wAlign < PRELOAD_ALIGN) {
        wNewAlign = PRELOAD_ALIGN;
    } else {
        wNewAlign = wAlign;
    }

    /* Get the current file position */
    lCurPos = MySeek(fh, 0L, 1);

    /* Compute the new position by rounding up to the align value */
    lMask = -1L;
    lMask <<= wNewAlign;
    lNewPos = lCurPos + ~lMask;
    lNewPos &= lMask;

    /* We have to have at least 32 bytes between objects in the preload
     *  section.
     */
    if (fPreload) {
        while (lNewPos - lCurPos < PRELOAD_MINPADDING) {
            lNewPos += 1 << wNewAlign;
        }
    }

    /* Check to see if it\'s representable in 16 bits */
    if (lNewPos >= (0x10000L << wAlign)) {
        OutPutError(".EXE file too large; relink with higher /ALIGN value");
    }

    /* Write stuff out to file until new position reached */
    if (lNewPos > lCurPos) {
        /* Compute number of bytes to write out and write them out */
        nbytes = (WORD) (lNewPos - lCurPos);
        while (nbytes) {
            nbytes -= MyWrite( fh,
                               zeros,
                               (WORD)(nbytes > NUMZEROS ? NUMZEROS : nbytes));
        }
    }

    /* Seek to and return this new position */
    return (WORD)(MySeek(fh, lNewPos, (WORD) 0) >> (LONG) wAlign);
}


/*--------------------------------------------------------------------------*/
/*                                      */
/*  AddResType\(\) -                              */
/*                                      */
/*--------------------------------------------------------------------------*/

static TYPINFO *AddResType( CHAR *s, WORD n )
{
    TYPINFO *pType;

    if (pType = pTypInfo) {
        while (TRUE) {
            /* search for resource type, return if already exists */
            if ((s && !strcmp(s, pType->type)) || (!s && n && pType->typeord == n)) {
                return (pType);
            } else if (!pType->next) {
                break;
            } else {
                pType = pType->next;
            }
        }

        /* if not in list, add space for it */
        pType->next = (TYPINFO *) RcAlloc(sizeof(TYPINFO));
        pType = pType->next;
    } else {
        /* allocate space for resource list */
        pTypInfo = (TYPINFO *)RcAlloc   (sizeof(TYPINFO));
        pType = pTypInfo;
    }

    /* fill allocated space with name and ordinal, and clear the resources
       of this type */
    pType->type = MyMakeStr(s);
    pType->typeord = n;
    pType->nres = 0;
    pType->pres = NULL;
    pType->next = NULL;

    return (pType);
}


/*--------------------------------------------------------------------------*/
/*                                      */
/*  GetOrdOrName\(\) -                                */
/*                                      */
/*--------------------------------------------------------------------------*/

static void GetOrdOrName( unsigned int *pint, unsigned char *szstr)
{
    unsigned char c1;

    /* read the first character of the identifier */
    MyRead(fhBin, &c1, sizeof(unsigned char));

    /* if the first character is 0xff, the id is an ordinal, else a string */
    if (c1 == 0xFF) {
        MyRead(fhBin, (PSTR)pint, sizeof (int));
    } else {                                   /* string */
        *pint = 0;
        *szstr++ = c1;
        do {
            MyRead( fhBin, szstr, 1);
        }
        while (*szstr++ != 0);
    }
}



/*--------------------------------------------------------------------------*/
/*                                      */
/*  AddDefaultTypes\(\) -                             */
/*                                      */
/*--------------------------------------------------------------------------*/

static void AddDefaultTypes( void)
{
    AddResType( "CURSOR",       ID_RT_GROUP_CURSOR);
    AddResType( "ICON",         ID_RT_GROUP_ICON);
    AddResType( "BITMAP",       ID_RT_BITMAP);
    AddResType( "MENU",         ID_RT_MENU);
    AddResType( "DIALOG",       ID_RT_DIALOG);
    AddResType( "STRINGTABLE",  ID_RT_STRING);
    AddResType( "FONTDIR",      ID_RT_FONTDIR);
    AddResType( "FONT",         ID_RT_FONT);
    AddResType( "ACCELERATORS", ID_RT_ACCELERATORS);
    AddResType( "RCDATA",       ID_RT_RCDATA);
    AddResType( "VERSIONINFO",  ID_RT_VERSION);
}


/*--------------------------------------------------------------------------*/
/*                                      */
/*  ProcessBinFile\(\) -                              */
/*                                      */
/*--------------------------------------------------------------------------*/

static int ProcessBinFile( void)
{
    unsigned int  ord;
    unsigned char tokstr[64];
    RESINFO   *pRes;
    TYPINFO   *pType;
    long      curloc;
    long      eofloc;
    WORD wResType;

    /* initialize for reading .RES file */
    AddDefaultTypes();
    eofloc = MySeek(fhBin, 0L, 2);      /* get file size */
    curloc = MySeek(fhBin, 0L, 0);      /* go to beginning of file */

    /* while there are more resources in the .RES file */
    while (curloc < eofloc) {

#ifdef VERBOSE
        if (fVerbose) {
            fprintf(errfh, ".");
            fflush(errfh);
        }
#endif

        /* find the resource type of the next resource */
        GetOrdOrName(&ord, tokstr);

        if (!ord) {
            pType = AddResType(tokstr, 0);
        } else {
            pType = AddResType(NULL, (WORD)ord);
        }

        if (!pType) {
            break;
        }

        /* Save the type number so we can see if we want to skip it later */
        wResType = ord;

        /* find the identifier \(name\) of the resource */
        GetOrdOrName(&ord, tokstr);
        pRes = (RESINFO *)RcAlloc   (sizeof(RESINFO));
        if (!ord) {
            pRes->name = MyMakeStr(tokstr);
        } else {
            pRes->nameord = ord;
        }

        /* read the flag bits */
        MyRead(fhBin, (PSTR)&pRes->flags, sizeof(int));

        /* Clear the old DISCARD bits. */
        pRes->flags &= 0x1FFF;

        /* find the size of the resource */
        MyRead(fhBin, (PSTR)&pRes->size, sizeof(long));

        /* save the position of the resource for when we add it to the .EXE */
        pRes->BinOffset = (long)MySeek(fhBin, 0L, 1);

        /* skip the resource to the next resource header */
        curloc = MySeek(fhBin, (long)pRes->size, 1);

        /* add the resource to the resource lists.  We don\'t add name
         *  tables.  They are an unnecessary 3.0 artifact.
         */
        if (wResType != ID_RT_NAMETABLE) {
            AddResToResFile(pType, pRes);
        } else {
            MyFree(pRes->name);
            MyFree(pRes);
        }
    }

    return 1;
}



/*--------------------------------------------------------------------------*/
/*                                      */
/*  AddResToResFile\(pType, pRes\)          */
/*                                      */
/*  Parameters:                                 */
/*  pType  : Pointer to Res Type                        */
/*  pRes   : Pointer to resource                        */
/*                                      */
/*--------------------------------------------------------------------------*/

static void AddResToResFile( TYPINFO *pType, RESINFO *pRes)
{
    RESINFO *p;

    p = pType->pres;

    /* add resource to end of resource list for this type */
    if (p) {
        while (p->next) {
            p = p->next;
        }

        p->next = pRes;
        p->next->next = NULL;
    } else {
        pType->pres = pRes;
        pType->pres->next = NULL;
    }
    /* keep track of number of resources and types */
    pType->nres++;
}



/*  MyMakeStr
 *  Makes a duplicate string from the string passed in.  The new string
 *  should be freed when it is no longer useful.
 */

static PSTR MyMakeStr( PSTR s)
{
    PSTR s1;

    if (s) {
        s1 = RcAlloc( (WORD)(strlen(s) + 1)); /* allocate buffer */
        strcpy(s1, s);                  /* copy string */
    } else {
        s1 = s;
    }

    return s1;
}




static SHORT MyRead( FILE *fh, PSTR p, WORD n)
{
    size_t n1;

    if ( (n1 = fread( p, 1, n, fh)) != n )
        ;                               //  quit\("RC : fatal error RW1021: I/O error reading file."\);
    else
        return ( n1);
}


/*  MyWrite
 *  Replaces calls to write\(\) and does error checking.
 */

static SHORT MyWrite( FILE *fh, PSTR p, WORD n)
{
    size_t n1;

    if ( (n1 = fwrite( p, 1, n, fh)) != n )
        ;                               // quit\("RC : fatal error RW1022: I/O error writing file."\);
    else
        return ( n1);
}



/*  MySeek
 *  Replaces calls to lseek\(\) and does error checking
 */

static LONG MySeek( FILE *fh, LONG pos, WORD cmd)
{

    if ( (pos = fseek( fh, pos, cmd)) != 0 ) {
        OutPutError ("RC : fatal error RW1023: I/O error seeking in file");
    }
    return ( pos);
}


/*  MyCopy
 *  Copies dwSize bytes from source to dest in fixed size chunks.
 */

static void MyCopy( FILE *srcfh, FILE *dstfh, DWORD dwSize)
{
    WORD n;
    static char  chCopyBuffer[ BUFSIZE];

    while ( dwSize ) {
        n = MyRead( srcfh, chCopyBuffer, sizeof( chCopyBuffer));
        MyWrite( dstfh, chCopyBuffer, n);
        dwSize -= n;
    }
}


static void RcPutWord( unsigned int w)
{
    *((WORD *)pResNext) = w;
    pResNext++;
    pResNext++;
}


/*  PutStringWord
 *  Writes a string to the static resource buffer pointed to by pResNext.
 *  The string is stored in Pascal-format \(leading byte first\).
 *  Returns the number of characters written.
 */

static int RcPutString( char *pstr)
{
    int i;

    /* Make sure we have a valid string */
    if (!pstr || !(i = strlen(pstr))) {
        return 0;
    }

    /* Write the length byte */
    *pResNext++ = (char) i;

    /* Write all the characters */
    while (*pstr) {
        *pResNext++ = *pstr++;
    }

    /* Return the length */
    return (i + 1);
}


static PSTR RcAlloc( WORD nbytes)
{
    PSTR ps = NULL;

    if ( ps = (PSTR)MyAlloc( nbytes)) {
        return ( ps);
    }
}