/***    engine.c
 *
 * basic engine routine to be called for each module; also, rewrite routines
 * to convert to CodeView 4 format.
 *
 */

#include "compact.h"


LOCAL   void    RewriteSrcLnSeg (uchar *, OMFDirEntry *, PMOD);
LOCAL   void    CopySrcMod (uchar *, OMFDirEntry *, PMOD);
LOCAL   void    ReformatMod (ulong, PMOD);
LOCAL   void    CopyModule (OMFDirEntry *, PMOD);
LOCAL   void    ReadTypes (OMFDirEntry *, bool_t);
LOCAL   void    ReadPublics (ulong, OMFDirEntry *);
LOCAL   void    ReadSymbols (ulong, OMFDirEntry *);
LOCAL   void    ReadSrcLnSeg (ulong, OMFDirEntry *);
LOCAL   void    ReadSrcModule (ulong, OMFDirEntry *);
LOCAL   void    ReadPublicSym (ulong, OMFDirEntry *);

LOCAL   int     VerifySrcMod (PMOD);
LOCAL   void    FixSrcMod (PMOD);
LOCAL   void    FixEnds (OMFModule *, OMFSourceModule *);


void DebugBreak (void);

extern ushort recursive;
extern ushort AddNewSymbols;
extern uchar  Signature[];
extern ulong  PublicSymbols;

uchar       ptr32;
uchar      *SymbolSegment;

ushort      SymbolSizeAdd;
ushort      SymbolSizeSub;
ushort      UDTAdd;
uchar     **ExtraSymbolLink;
uchar      *ExtraSymbols;
ulong       InitialSymInfoSize = 0;
ulong       InitialPubInfoSize = 0;
ulong       FinalSymInfoSize = 0;
char       *ModAddr;
ushort      usCurFirstNonPrim; // The current first non primitive type index
ulong       ulCVTypeSignature; // The signature from the modules type segment
ulong       iSym;
ulong       iPub;
ulong       iPubSym;
ulong       iSrcLn;
ulong       iSrcMod;
ushort      SeekCnt = 0;
CV_typ_t    maxPreComp;         // maximum type index allowed during precomp
bool_t      PackingPreComp;


/**     CompactOneModule - compact next module
 *
 *      CompactOneModule (iMod)
 *
 *      Entry   iMod = module index
 *
 *      Exit    information for module iMod compacted
 *
 *      Returns TRUE if module compacted
 *              FALSE if module not found
 *
 */

bool_t CompactOneModule (ushort iPData)
{
    ulong       i;
    PACKDATA   *pPData;
    ushort      iMod;

    ptr32 = fLinearExe;

    SymbolSizeAdd = 0;
    SymbolSizeSub = 0;
    UDTAdd = 0;
    ExtraSymbols = NULL;

    AddNewSymbols = FALSE;
    ulCVTypeSignature = 0xFFFFFFFL; // We don't know the type signature yet

    // search directory table for module table entry and following
    // sstTypes, sstPublics, sstSymbols and sstSrcLnSeg

    iSym = 0;
    iPub = 0;
    iPubSym = 0;
    iSrcLn = 0;
    iSrcMod = 0;
    maxPreComp = 0;
    PackingPreComp = FALSE;
    pPData = PackOrder + iPData;
    i = pPData->iDir;
    pCurMod = pPData->pMod;
    pRefMod = NULL;
    iMod = pPData->iMod;
    if (pDir[i].SubSection == SSTMODULE) {
        ReformatMod (i, pCurMod);
    }
    else if (pDir[i].SubSection == sstModule) {
        CopyModule (&pDir[i], pCurMod);
    }
    else {
        // we should have been pointing at a module entry
        DASSERT (FALSE);
    }

    while ((++i < cSST) && (pDir[i].iMod == iMod)) {
        if (pDir[i].cb != 0) {
            switch (pDir[i].SubSection) {
                case SSTTYPES:
                case sstTypes:
                    ReadTypes (&pDir[i], FALSE);
                    break;

                case sstPreComp:
                    PackingPreComp = TRUE;
                    ReadTypes (&pDir[i], TRUE);
                    PackPreComp (pCurMod);
                    break;

                case SSTPUBLIC:
                case sstPublic:
                    ReadPublics (i, &pDir[i]);
                    break;

                case sstPublicSym:
                    ReadPublicSym (i, &pDir[i]);
                    break;

                case SSTSYMBOLS:
                case sstSymbols:
                    ReadSymbols (i, &pDir[i]);
                    break;

                case SSTSRCLNSEG:
                case sstSrcLnSeg:
                    ReadSrcLnSeg (i, &pDir[i]);
                    break;

                case sstSrcModule:
                    ReadSrcModule (i, &pDir[i]);
                    break;
            }
        }
    }
    if (iPub != 0) {
        FixupPublicsC6 (pPublics, pDir[iPub].cb);
        RewritePublicsC6 (pPublics, &pDir[iPub]);
    }
    else if (iPubSym != 0) {
        switch (*((ulong *)pPublics)) {
            case CV_SIGNATURE_C7:
                // compensate for signature which is not copied

                C7RewritePublics (pPublics, &pDir[iPubSym]);
                InitialPubInfoSize += pDir[iPubSym].cb;
                break;

            default:
                DASSERT (FALSE);

        }
    }
    if (iSym != 0) {
        if (delete == TRUE) {
            pDir[iSym].cb = 0;
        }
        else {
            switch (*((ulong *)pSymbols)) {
                case CV_SIGNATURE_C7:
                    // C7 format symbols
                    InitialSymInfoSize += pDir[iSym].cb;
                    C7CalcNewSizeOfSymbols (pSymbols, pDir[iSym].cb,
                      &SymbolSizeAdd, &SymbolSizeSub);
                    C7RewriteAndFixupSymbols (pSymbols, &pDir[iSym],
                      pCurMod, &SymbolSizeAdd, &SymbolSizeSub);
                    break;

                default:
                    // C6 format symbols
                    C6CalcNewSizeOfSymbols (pSymbols, pDir[iSym].cb);
                    C6RewriteAndFixupSymbols (pSymbols, &pDir[iSym], ModAddr, pCurMod);
                    break;
            }
        }
    }
    if (iSrcLn != 0) {
        RewriteSrcLnSeg (pSrcLn, &pDir[iSrcLn], pCurMod);
    }
    else if (iSrcMod != 0) {
        CopySrcMod (pSrcLn, &pDir[iSrcMod], pCurMod);
    }
    return (TRUE);
}





LOCAL void ReadTypes (OMFDirEntry *pDir, bool_t fPreComp)
{
    pTypes = pTypeSeg[0];

    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    } else {
        DASSERT( lseek(exefile, 0, SEEK_CUR) == filepos + lfoBase );
    }

    // Read in the signature byte

    if (read (exefile, pTypes, sizeof (ulong)) != sizeof (ulong)) {
        ErrorExit (ERR_INVALIDEXE, NULL, NULL);
    }

    // Read in according to signature.


    switch (ulCVTypeSignature = *((ulong *)pTypes)) {
        case CV_SIGNATURE_C7:
            // C7 format types

            usCurFirstNonPrim = CV_FIRST_NONPRIM;
            C7ReadTypes (pDir->cb - sizeof (ulong), fPreComp);
            filepos += pDir->cb;
            break;

        default:
        {
            ulong cbToRead = pDir->cb - sizeof (ulong);

            if (*pTypes != 0x01) {
                ErrorExit (ERR_INVALIDTABLE, "Types", FormatMod (pCurMod));
            }

            ulCVTypeSignature = CV_SIGNATURE_C6;
            usCurFirstNonPrim = 512;
            if (read (exefile, pTypes + sizeof (ulong), (int)cbToRead) != (int)cbToRead) {
                ErrorExit (ERR_INVALIDEXE, NULL, NULL);
            }
            filepos += pDir->cb;
            C6ReadTypes (pTypes, pDir->cb);
            break;
        }
    }
}




void ReadPublics (ulong i, OMFDirEntry *pDir)
{
    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if (read (exefile, pPublics, (int)pDir->cb) != (int)pDir->cb) {
        ErrorExit (ERR_INVALIDTABLE, "Publics", FormatMod (pCurMod));
    }
    filepos += pDir->cb;
    iPub = i;
}




void ReadSymbols (ulong i, OMFDirEntry *pDir)
{
    if (pDir->cb > maxSymbolsSub) {
        ErrorExit (ERR_SYMLARGE, FormatMod (pCurMod), NULL);
    }
    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if (read (exefile, pSymbols, (int)pDir->cb) != (int)pDir->cb) {
        ErrorExit (ERR_INVALIDTABLE, "Symbols", FormatMod (pCurMod));
    }
    filepos += pDir->cb;
    SymbolSegment = pSymbols;
    iSym = i;
}




void ReadPublicSym (ulong i, OMFDirEntry *pDir)
{
    DASSERT (pDir->cb <= maxPublicsSub);
    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if (read (exefile, pPublics, (int)pDir->cb) != (int)pDir->cb) {
        ErrorExit (ERR_INVALIDTABLE, "Publics", FormatMod (pCurMod));
    }
    filepos += pDir->cb;
    iPubSym = i;
}



void ReadSrcLnSeg (ulong i, OMFDirEntry *pDir)
{
    DASSERT (pDir->cb <= maxSrcLnSub);
    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if (read (exefile, pSrcLn, (int)pDir->cb) != (int)pDir->cb) {
        ErrorExit (ERR_INVALIDEXE, "SrcLnSeg", FormatMod (pCurMod));
    }
    filepos += pDir->cb;
    iSrcLn = i;
}



void ReadSrcModule (ulong i, OMFDirEntry *pDir)
{
    DASSERT (pDir->cb <= maxSrcLnSub);
    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if (read (exefile, pSrcLn, (int)pDir->cb) != (int)pDir->cb) {
        ErrorExit (ERR_INVALIDEXE, "Source Module", FormatMod (pCurMod));
    }
    filepos += pDir->cb;
    iSrcMod = i;
}



void CopyModule (OMFDirEntry *pDir, PMOD pMod)
{
    _vmhnd_t    vp;

    // read the module table into virtual memory
    if ((vp = (_vmhnd_t)VmAlloc ((int)pDir->cb)) == _VM_NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    if (pDir->lfo != filepos) {
        filepos = pDir->lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if ((ModAddr = (char *) VmLoad (vp, _VM_DIRTY)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    if (read (exefile, ModAddr, (int)pDir->cb) != (int)pDir->cb) {
        ErrorExit (ERR_INVALIDEXE, NULL, NULL);
    }

    filepos += pDir->cb;
    pDir->lfo = (ulong)vp;
    pMod->ModuleSize = pDir->cb;
    pMod->ModulesAddr = (ulong)vp;
}


void ReformatMod (ulong iSST, PMOD pMod)
{
    ushort      NewSize;
    ushort      cbName;
    OMFModule  *pOMFMod;
    ushort      cSeg;
    ushort      i;
    oldnsg     *pOldNsg;
    oldnsg32   *pOldNsg32;
    char       *pName;
    _vmhnd_t    vp;

    // read the module table into real memory

    if (pDir[iSST].lfo != filepos) {
        filepos = pDir[iSST].lfo;
        SeekCnt++;
        lseek (exefile, filepos + lfoBase, SEEK_SET);
    }
    if (read (exefile, pSSTMOD, (int)pDir[iSST].cb) != (int)pDir[iSST].cb) {
        ErrorExit (ERR_INVALIDEXE, NULL, NULL);
    }
    filepos += pDir[iSST].cb;

    // compute length of sstModule table

    NewSize = sizeof (OMFModule);

    if (fLinearExe) {
        cSeg = ((oldsmd32 *) pSSTMOD)->cSeg;
    }
    else {
        cSeg = pSSTMOD->cSeg;
    }
    if (cSeg == 0) {
        cSeg = 1;
    }
    NewSize += sizeof (OMFSegDesc) * (cSeg - 1);

    if (fLinearExe) {
        cbName = ((oldsmd32 *) pSSTMOD)->cbName[0];
        pOldNsg32 = (oldnsg32 *)(((char *)pSSTMOD) +
            offsetof (oldsmd32, cbName[0]) + sizeof (pSSTMOD->cbName) + cbName);
        NewSize += ((cbName + sizeof (ushort) + sizeof (ulong) - 1) /
            sizeof (ulong)) * sizeof (ulong);
    }
    else {
        cbName = pSSTMOD->cbName[0];
        pOldNsg = (oldnsg *)(((char *)pSSTMOD) + offsetof (oldsmd, cbName[0]) +
            sizeof (pSSTMOD->cbName) + cbName);
        NewSize += ((cbName + sizeof (ushort) + sizeof (ulong) - 1) /
            sizeof (ulong)) * sizeof (ulong);
    }
    if ((vp = (_vmhnd_t)VmAlloc (NewSize)) == _VM_NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    if ((ModAddr = (char *) VmLoad (vp, _VM_DIRTY)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    pOMFMod = (OMFModule *)ModAddr;
    pOMFMod->cSeg = cSeg;
    pOMFMod->Style[0] = 'C';
    pOMFMod->Style[1] = 'V';
    pOMFMod->SegInfo[0].pad = 0;

    if (fLinearExe) {
        pOMFMod->ovlNumber = ((oldsmd32 *) pSSTMOD)->ovlNbr;
        pOMFMod->iLib = ((oldsmd32 *) pSSTMOD)->iLib;
        pOMFMod->SegInfo[0].Off = ((oldsmd32 *) pSSTMOD)->SegInfo.Off;
        pOMFMod->SegInfo[0].cbSeg = ((oldsmd32 *) pSSTMOD)->SegInfo.cbSeg;
        pOMFMod->SegInfo[0].Seg = ((oldsmd32 *) pSSTMOD)->SegInfo.Seg;
    }
    else {
        pOMFMod->ovlNumber = pSSTMOD->ovlNbr;
        pOMFMod->iLib = pSSTMOD->iLib;
        pOMFMod->SegInfo[0].Off = (CV_uoff32_t)pSSTMOD->SegInfo.Off;
        pOMFMod->SegInfo[0].cbSeg = (ulong)pSSTMOD->SegInfo.cbSeg;
        pOMFMod->SegInfo[0].Seg = pSSTMOD->SegInfo.Seg;
    }

    // copy information about additional segments

    for (i = 1; i < cSeg; i++) {
        pOMFMod->SegInfo[i].pad = 0;
        if (fLinearExe) {
            pOMFMod->SegInfo[i].Seg = pOldNsg32[i-1].Seg;
            pOMFMod->SegInfo[i].Off = pOldNsg32[i-1].Off;
            pOMFMod->SegInfo[i].cbSeg = pOldNsg32[i-1].cbSeg;
        }
        else {
            pOMFMod->SegInfo[i].Seg = pOldNsg[i-1].Seg;
            pOMFMod->SegInfo[i].Off = (CV_uoff32_t)pOldNsg[i-1].Off;
            pOMFMod->SegInfo[i].cbSeg = (ulong)pOldNsg[i-1].cbSeg;
        }
    }
    pName = ((char *)pOMFMod) + sizeof (OMFModule) + sizeof (OMFSegDesc) * (cSeg - 1);
    *((uchar *)pName)++ = (uchar) cbName;
    if (fLinearExe) {
        memmove (pName, &((oldsmd32 *) pSSTMOD)->cbName[1], cbName);
    }
    else {
        memmove (pName, &pSSTMOD->cbName[1], cbName);
    }
    pDir[iSST].SubSection = sstModule;
    pDir[iSST].lfo = (ulong)vp;
    pDir[iSST].cb = NewSize;
    pMod->ModuleSize = sizeof (OMFModule) + (sizeof (OMFSegDesc) * (cSeg - 1)) +
      sizeof (cbName) + cbName;
    pMod->ModulesAddr = (ulong)vp;
}


uchar *GetSymString (ushort SymOffset)
{
    return (SymbolSegment + SymOffset);
}




/**     CopySrcMod - copy sstSrcModule table to VM
 *
 *      CopySrcMod (addr, pDir, pMod);
 *
 *      Entry   addr = address of SrcLnSeg table
 *              pDir = address of directory entry
 *              pMod = module table entry
 *
 *      Exit    pDir->lfo = address of rewritten table
 *              pDir->Size = size of rewritten table
 *
 *      Return  none
 *
 */

LOCAL void CopySrcMod (uchar *OldSrcMod, OMFDirEntry *pDir, PMOD pMod)
{
    _vmhnd_t    SrcModAddr;
    char       *pSrcMod;

    if ((SrcModAddr = (_vmhnd_t)VmAlloc (pDir->cb)) == _VM_NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    if ((pSrcMod = (char *) VmLoad (SrcModAddr, _VM_DIRTY)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    memcpy (pSrcMod, OldSrcMod, (int)pDir->cb);
    pDir->lfo = (ulong)SrcModAddr;
    pMod->SrcLnSize = pDir->cb;
    pMod->SrcLnAddr = (ulong)SrcModAddr;
}



/**     RewriteSrcLnSeg - reformat source line to new format and store in VM
 *
 *      RewriteSrcLnSeg (addr, pDir, pMod);
 *
 *      Entry   addr = address of SrcLnSeg table
 *              pDir = address of directory entry
 *              pMod = module table entry
 *
 *      Exit    pDir->lfo = address of rewritten table
 *              pDir->Size = size of rewritten table
 *
 *      Return  none
 *
 */
#define MAXSEG  256
#define MAXFILE 20

LOCAL void RewriteSrcLnSeg (uchar *OldSrcLn, OMFDirEntry *pDir, PMOD pMod)
{
    ushort      cFile = 0;          // number of files in module
    ushort      SegFile[MAXSEG];    // list of segments for current file
    ushort      cSegMod = 0;        // number of segments for module
    ushort      SegMod[MAXSEG];     // list of segments for module
    ushort      Seg;                // current segment
    ushort      PairSize;           // sizeof offset/line pair
    ushort      cPair;              // number of pairs in current line table
    ulong       cbLineTable;        // total size of OMFSourceLine table
    ulong       cbFileTable;        // total size of OMFSourceFile table
    ulong       cbModTable = 0;     // total size of OMFSourceModule table
    uchar      *pOld;
    uchar      *pEnd;
    char       *pFileName = NULL;
    char       *pLineBase;
    char       *pLineLim;
    char       *pFile;
    char       *pOffset;
    char       *pLine;
    char       *pLineTable;
    ushort      cbFileName;
    ushort      i;
    ushort      fAdd;
    ulong       cbModHeader = 0;
    ulong       cbFileHeader[MAXFILE];
    ulong       FileBase[MAXFILE];  // OMFSourceFile base offsets
    ushort      LineFile[MAXFILE];  // number of line tables per file
    ushort      SegPerFile[MAXFILE];// number of segments per file
    _vmhnd_t    SrcModAddr;
    char       *pSrcMod;
    char       *pFileMod;
    char       *Save;

    if (fLinearExe) {
        PairSize = sizeof (CV_uoff32_t) + sizeof (ushort);
    }
    else {
        PairSize = sizeof (CV_uoff16_t) + sizeof (ushort);
    }

    // sweep the SrcLnSeg table and compute the size of the SrcModule table

    pOld = pSrcLn;
    pEnd = pOld + pDir->cb;
    while (pOld < pEnd) {
        if ((pFileName == NULL) ||
          (strncmp ((char *)pOld, pFileName, cbFileName + 1) != 0)) {
            if (pFileName != NULL) {
                // the file name has changed.

                FileBase[cFile] = cbModTable;
                cbFileHeader[cFile] = offsetof (OMFSourceFile, baseSrcLn[0]) +
                  SegPerFile[cFile] * sizeof (ulong) +
                  SegPerFile[cFile] * 2 * sizeof (CV_uoff32_t);
                cbFileHeader[cFile] += sizeof (ushort);
                cbFileHeader[cFile] += cbFileName;
                cbFileHeader[cFile] = ((cbFileHeader[cFile] +
                  sizeof (ulong) - 1) / sizeof (ulong)) * sizeof (ulong);
                cbFileTable += cbFileHeader[cFile];
                cFile++;
                cbModTable += cbFileTable;
                DASSERT (cbFileTable % sizeof (ulong) == 0);
                cbModTable += cbFileTable;
            }
            // set pointer to file name and name length
            pFileName = (char *)pOld;
            cbFileName = *pFileName;
            // initialize sizeof OMFSourceFile header for this file
            cbFileTable = 0;
            // initialize sount of segments for this file
            SegPerFile[cFile] = 0;
            // set base offset of the OMFSourceFile header (will bias by cbModHeader)
            FileBase[cFile] = cbModTable;
            LineFile[cFile] = 0;
        }
        pOld += *pOld + sizeof (char);

        // we now have a new segment index for this source file.  We verify
        // that it is not already known to this source file.

        Seg = *((ushort UNALIGNED *)pOld)++;
        if (SegPerFile[cFile] < MAXSEG) {
            SegFile[SegPerFile[cFile]++] = Seg;
        }
        else {
            // M00NOTDONE - this should be a warning message and the
            //              table purged
            DASSERT (FALSE);
        }

        // we now add the segment to the module table if it is not
        // already referenced

        for (fAdd = TRUE, i = 0; i < cSegMod; i++) {
            if (SegMod[i] == Seg) {
                fAdd = FALSE;
                break;
            }
        }
        if (fAdd == TRUE) {
            if (cSegMod < MAXSEG) {
                SegMod[cSegMod++] = Seg;
            }
            else {
                // M00NOTDONE - this should be a warning message and the
                //              table purged
                DASSERT (FALSE);
            }
        }

        // M00NOTDONE - need to compute seg start/end per file and segment
        LineFile[cFile]++;
        cPair = *((ushort UNALIGNED *)pOld)++;
        pOld += cPair * PairSize;

        cbLineTable = offsetof (OMFSourceLine, offset[0]) +
          cPair * sizeof (ulong) +
          cPair * sizeof (ushort);
        if (cPair & 1) {
            // the next OMFSourceLine table will not be long aligned
            cbLineTable += sizeof (ushort);
        }
        DASSERT (cbLineTable % sizeof (ulong) == 0);
        cbFileTable += cbLineTable;
    }

    // compute size of final OMFSourceFile table and size of OMFSourceModule

    cbFileHeader[cFile] = offsetof (OMFSourceFile, baseSrcLn[0]) +
      SegPerFile[cFile] * sizeof (ulong) +
      SegPerFile[cFile] * 2 * sizeof (CV_uoff32_t);
    cbFileHeader[cFile] += sizeof (uchar);
    cbFileHeader[cFile] += cbFileName;
    cbFileHeader[cFile] = ((cbFileHeader[cFile] +
      sizeof (ulong) - 1) / sizeof (ulong)) * sizeof (ulong);
    cbFileTable += cbFileHeader[cFile];
    cFile++;
    cbModTable += cbFileTable;
    DASSERT (cbFileTable % sizeof (ulong) == 0);

    cbModHeader = offsetof (OMFSourceModule, baseSrcFile[0]) +
      cFile * sizeof (ulong) +
      cSegMod * sizeof (ushort) +
      2 * sizeof (CV_uoff32_t) * cSegMod;
    if (cSegMod & 1) {
        // the next subsection will not be long aligned
        cbModHeader += sizeof (ushort);
    }
    cbModTable += cbModHeader;
    DASSERT (cbModTable < 0x10000);
    DASSERT (cbModTable % sizeof (ulong) == 0);
    if ((SrcModAddr = (_vmhnd_t)VmAlloc (cbModTable)) == _VM_NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    if ((pSrcMod = (char *)VmLoad (SrcModAddr, _VM_DIRTY)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }
    Save = pSrcMod;
    pOld = OldSrcLn;

    // at the end of the above loop, we have the following information
    //  cbModHeader = size of the OMFSourceModule Structure
    //  cbModTable = size of sstSrcModule table
    //  cFile = number of source files in the module
    //  cbFileHeader[] = length of the OMFSourceFile structure for each file
    //  FileBase[] = offset of OMFSourceFile structure from end of OMFSourceModule

    *((ushort *)pSrcMod)++ = cFile;
    *((ushort *)pSrcMod)++ = cSegMod;
    for (i = 0; i < cFile; i++) {
        *((ulong *)pSrcMod)++ = FileBase[i] + cbModHeader;
    }
    for (i = 0; i < cSegMod; i++) {
        *((CV_uoff32_t *)pSrcMod)++ = 0;  // M00BUG - should insert seg start/end
        *((CV_uoff32_t *)pSrcMod)++ = 0;
    }
    for (i = 0; i < cSegMod; i++) {
        *((ushort *)pSrcMod)++ = SegMod[i];
    }
    if (cSegMod & 1) {
        *((ushort *)pSrcMod)++ = 0;
    }

    // we now sweep the old source line table actually doing the rewrite

    cFile = 0;
    pFileName = NULL;
    while (pOld < pEnd) {
        if ((pFileName == NULL) ||
          (strncmp ((char *)pOld, pFileName, cbFileName + 1) != 0)) {
            // set the base address of OMFSourceFile for this file
            pFileMod = Save + FileBase[cFile] + cbModHeader;
            // set the base address of the first OMFSourceLine for this file
            pLineTable = pFileMod + cbFileHeader[cFile];
            // set the number of segments for this file
            *((ushort *)pFileMod)++ = SegPerFile[cFile];
            *((ushort *)pFileMod)++ = 0;
            pLineBase = pFileMod;
            pLineLim = pFileMod + SegPerFile[cFile] * sizeof (ulong);
            pFile = pLineLim + SegPerFile[cFile] * 2 * sizeof (CV_uoff32_t);
            pFileName = (char *)pOld;
            cbFileName = *pFileName;
            *((uchar *)pFile)++ = (uchar) cbFileName;
            memcpy (pFile, pFileName + 1, cbFileName);
            pFile += cbFileName;
            while (pFile < pLineTable) {
                *pFile++ = 0;
            }
            cFile++;
        }
        pOld += *pOld + sizeof (char);
        Seg = *((ushort UNALIGNED *)pOld)++;
        cPair = *((ushort UNALIGNED *)pOld)++;
        *((ulong *)pLineBase)++ = pLineTable - Save;
        *((CV_off32_t *)pLineLim)++ = 0;  //M00BUG - needs start here
        *((CV_off32_t *)pLineLim)++ = 0;
        pOffset = pLineTable;
        *((ushort *)pOffset)++ = Seg;
        *((ushort *)pOffset)++ = cPair;
        pLine = pOffset + cPair * sizeof (CV_uoff32_t);
        for (i = 0; i < cPair; i++) {
            *((ushort *)pLine)++ = *((ushort UNALIGNED *)pOld)++;
            if (fLinearExe) {
                *((ulong *)pOffset)++ = *((ulong UNALIGNED *)pOld)++;
            }
            else {
                *((ulong *)pOffset)++ = *((ushort UNALIGNED *)pOld)++;
            }
        }
        if (cPair & 1) {
            *((ushort *)pLine)++ = 0;
        }
        pLineTable = pLine;
    }
    pDir->lfo = (ulong)SrcModAddr;
    pDir->cb = (ushort)cbModTable;
    pMod->SrcLnSize = (ushort)cbModTable;
    pMod->SrcLnAddr = (ulong)SrcModAddr;

    if (pCurMod->SrcLnAddr != 0) {
        FixSrcMod (pCurMod);
    }
}

typedef struct _OFP {
    ulong offStart;
    ulong offEnd;
} OFP;
typedef OFP *POFP;

typedef struct _SGI {
  ushort seg;
  ushort pad;
    OFP     ofp;
    ushort   cln;
    char     rgbVar [ 0 ];  // Offsets & line numbers
} SGI;
typedef SGI *PSGI;

typedef struct _FLI {
  ushort  cseg;
  uchar  *szName;
  PSGI    rgpsgi [ 0 ];
} FLI;
typedef FLI *PFLI;


LOCAL void FixSrcMod (PMOD pmod) {
    OMFModule       *pmds   = NULL;
    OMFSourceModule *psrc   = NULL;
    OMFSourceModule *psrcT  = NULL;
    _vmhnd_t         vpsrc  = _VM_NULL;
    unsigned int     cbsrcT = 0;
    unsigned int     cbsrc  = 0;
    ushort           ifile  = 0;
    ushort           ifli   = 0;

    // Set the top line source table contributers to be the same
    //  as the module contributers

    if ((pmds = (OMFModule *)VmLock ((_vmhnd_t)pmod->ModulesAddr)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }

    if ((psrc = (OMFSourceModule *)VmLoad ((_vmhnd_t)pmod->SrcLnAddr, _VM_CLEAN)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }

    cbsrcT =
        sizeof (short) * 2 +
        psrc->cFile * sizeof (long) +
        pmds->cSeg * (sizeof (ushort) + sizeof (OFP));

    // Long-align cbsrcT

    cbsrcT = (cbsrcT + 3) & ~0x03;
    cbsrc = cbsrcT;


    if ((psrcT = malloc (cbsrcT)) == NULL) {
        ErrorExit (ERR_NOMEM, NULL, NULL);
    }

    memset (psrcT, 0, cbsrcT);
    psrcT->cFile = psrc->cFile;
    psrcT->cSeg  = pmds->cSeg;

    // Fixup the seg-start/ends for the module level source table

    {
        POFP      rgofp;
        ushort   *rgseg;
        ushort    iseg;

        rgofp = (POFP) &psrcT->baseSrcFile [ psrcT->cFile ];
        rgseg = (ushort *) &psrcT->baseSrcFile [ psrcT->cFile + psrcT->cSeg * 2 ];

        for (iseg = 0; iseg < pmds->cSeg; iseg++) {
            OMFSegDesc *psgd = &pmds->SegInfo [ iseg ];

            rgseg [ iseg ]          = psgd->Seg;
            rgofp [ iseg ].offStart = psgd->Off;
            rgofp [ iseg ].offEnd   = psgd->Off + psgd->cbSeg;
        }
    }

    for (ifile = 0; ifile < psrc->cFile; ifile++) {
        int            cbfli = sizeof (FLI) + sizeof (PSGI) * psrcT->cSeg;
        PFLI           pfli  = NULL;
        OMFSourceFile *psf   = (OMFSourceFile *) ((char *) psrc + psrc->baseSrcFile [ ifile ]);
        uchar         *lpb   = (uchar *) &psf->baseSrcLn [ psf->cSeg * 3 ];
        int            cln   = 0;
        ushort         isgi  = 0;
        ushort         isegM = 0;
        ushort         isegF = 0;

        if ((pfli = malloc (cbfli)) == NULL) {
            ErrorExit (ERR_NOMEM, NULL, NULL);
        }
        psrcT->baseSrcFile [ ifile ] = (ulong) pfli;
        memset (pfli, 0, cbfli);

        if ((pfli->szName = malloc (*lpb + 1)) == NULL) {
            ErrorExit (ERR_NOMEM, NULL, NULL);
        }
        memcpy (pfli->szName, lpb, *lpb + 1);

        // Find the number of line number pairs in the entire file

        for (isegF = 0; isegF < psf->cSeg; isegF++) {
            OMFSourceLine *psl = (OMFSourceLine *)
              ((char *) psrc + psf->baseSrcLn [ isegF ]);

            cln += psl->cLnOff;
        }

        for (isegM = 0; isegM < pmds->cSeg; isegM++) {
            OMFSegDesc *psi   = &pmds->SegInfo [ isegM ];
            PSGI        psgi  = NULL;
            ushort      clnT  = 0;
            ulong UNALIGNED *rgoff;
            ushort     *rgln;

            // Find the number of line number pairs associated with
            //  the segment contributer isegM

            for (isegF = 0; isegF < psf->cSeg; isegF++) {
                OMFSourceLine *psl = (OMFSourceLine *)
                    ((char *) psrc + psf->baseSrcLn [ isegF ]);
                if (psl->Seg == psi->Seg) {
                    ushort iln = 0;

                    for (iln = 0; iln < psl->cLnOff; iln++) {

                        if (
                            psl->offset [ iln ] >= psi->Off &&
                            psl->offset [ iln ] < psi->Off + psi->cbSeg
                        ) {
                            clnT += 1;
                        }
                    }
                }
            }

            // Now that we know how big the table will be, allocate it.

            if (clnT > 0) {

                // Add the size of the source line table

                cbsrc +=
                    sizeof (OMFSourceLine) +
                    (sizeof (ulong) + sizeof (ushort)) * (clnT - 1);

                // Long-align cbsrc

                cbsrc = (cbsrc + 3) & ~0x03;

                psgi = malloc (
                    sizeof (SGI) +
                    (sizeof (ulong) + sizeof (ushort)) * clnT
                );
                pfli->rgpsgi [ isgi ] = psgi;

                psgi->seg = psi->Seg;
                psgi->cln = clnT;
                rgoff = (ulong *) psgi->rgbVar;
                rgln  = (ushort *) &psgi->rgbVar [ clnT * sizeof (ulong) ];
                clnT = 0;

              for (isegF = 0; isegF < psf->cSeg; isegF++) {
                    OMFSourceLine *psl = (OMFSourceLine *)
                        ((char *) psrc + psf->baseSrcLn [ isegF ]);

                    if (psl->Seg == psi->Seg) {
                        ushort iln = 0;

                        for (iln = 0; iln < psl->cLnOff; iln++) {

                            if (
                                psl->offset [ iln ] >= psi->Off &&
                                psl->offset [ iln ] < psi->Off + psi->cbSeg
                            ) {
                                rgoff [ clnT ] = psl->offset [ iln ];
                                rgln  [ clnT ] = *((ushort *)
                                    &psl->offset [ psl->cLnOff ] + iln
                                );
                                clnT += 1;
                            }
                        }
                    }

                    if (clnT == psgi->cln) {
                        break;
                    }

                    DASSERT (clnT < psgi->cln);
                }

                psgi->ofp.offStart = rgoff [ 0 ];
                psgi->ofp.offEnd   = rgoff [ clnT - 1 ];

                isgi += 1;
            }
        }

        pfli->cseg = isgi;

        // Add the size of the source file table

        cbsrc +=
            sizeof (OMFSourceFile) -
            (sizeof (long) + sizeof (char)) +
            (sizeof (long) + sizeof (OFP) + sizeof (ushort)) * isgi +
            *pfli->szName;

        // At this point we must long-align cbsrc

        cbsrc = (cbsrc + 3) & ~0x03;
    }

    // Now we allocate VM and write out the fixed source table

    if ((vpsrc = (_vmhnd_t)VmAlloc (cbsrc)) == _VM_NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }

    if ((psrc = (OMFSourceModule *)VmLoad (vpsrc, _VM_CLEAN)) == NULL) {
        ErrorExit (ERR_NOVM, NULL, NULL);
    }

    memset (psrc, 0, cbsrc);

    // Copy the base source module table into virtual memory

    memcpy (psrc, psrcT, cbsrcT);

    // Now move each of the source file tables

    for (ifli = 0; ifli < psrc->cFile; ifli++) {
        PFLI pfli = (PFLI) psrc->baseSrcFile [ ifli ];
        OMFSourceFile *psf = (OMFSourceFile *) ((char *) psrc + cbsrcT);
        uchar *lpb =  pfli->szName;
        ushort isgi = 0;

        psrc->baseSrcFile [ ifli ] = cbsrcT;
        psf->cSeg = pfli->cseg;
        psf->reserved = 0;

        memcpy (
            (uchar *) &psf->baseSrcLn [ 0 ] +
              psf->cSeg * 3 * sizeof (ulong),
            pfli->szName,
            *pfli->szName + 1
        );

        // Add the size of the source file table

        cbsrcT +=
            sizeof (OMFSourceFile) -
            (sizeof (long) + sizeof (char)) +
            (sizeof (long) + sizeof (OFP) + sizeof (ushort)) * pfli->cseg +
            *pfli->szName;

        cbsrcT = (cbsrcT + 3) & ~0x03;

        // Now move each of the source line tables and place the
        //  pertinent information in the source file table at
        //  the same time.

        for (isgi = 0; isgi < pfli->cseg; isgi++) {
            PSGI psgi = pfli->rgpsgi [ isgi ];
            OMFSourceLine *psl = (OMFSourceLine *) ((char *) psrc + cbsrcT);

            psf->baseSrcLn [ isgi ] = cbsrcT;
            psf->baseSrcLn [ pfli->cseg + isgi * 2 ]     = psgi->ofp.offStart;
            psf->baseSrcLn [ pfli->cseg + isgi * 2 + 1 ] = psgi->ofp.offEnd;

            psl->Seg    = psgi->seg;
            psl->cLnOff = psgi->cln;
            memcpy (
                &psl->offset [ 0 ],
                &psgi->rgbVar [ 0 ],
                (sizeof (ulong) + sizeof (ushort)) * psgi->cln
            );

            cbsrcT +=
                sizeof (OMFSourceLine) +
                (sizeof (ulong) + sizeof (ushort)) * (psgi->cln - 1);

            cbsrcT = (cbsrcT + 3) & ~0x03;

            free (psgi);
        }

        free (pfli->szName);
        free (pfli);
    }

    DASSERT (cbsrcT == cbsrc);

    free (psrcT);

    FixEnds (pmds, psrc);

    VmFree ((_vmhnd_t) pmod->SrcLnAddr);

    pmod->SrcLnAddr = (ulong) vpsrc;

    VmUnlock ((_vmhnd_t) pmod->ModulesAddr, _VM_DIRTY);

    pmod->SrcLnSize = cbsrc;
}


typedef struct _SS {
    ushort    seg;
    ulong     offStart;
    ulong    *poffEnd;
} SS;  // Source line Sort structure
typedef SS *PSS;

LOCAL int _CRTAPI1 SLCmp (void const * pv1, void const * pv2)
{
    PSS pss1 = (PSS) pv1;
    PSS pss2 = (PSS) pv2;
    int fRet = 0;

    fRet = (int) pss1->seg - (int) pss2->seg;

    if (fRet == 0) {
        if ( pss1->offStart < pss2->offStart ) {
            fRet = -1;
        }
        else if ( pss1->offStart > pss2->offStart ) {
            fRet = 1;
        }
        else {
            fRet = 0;
        }
    }

    return fRet;
}


LOCAL void FixEnds (OMFModule *pmds, OMFSourceModule *psrc)
{
    ushort isf  = 0;
    ushort csl  = 0;
    ushort iss  = 0;
    PSS    pss  = NULL;
    ushort iseg = 0;

    // Calculate the number of segment contributers

    for (isf = 0; isf < psrc->cFile; isf++) {
        OMFSourceFile *psf = (OMFSourceFile *)
            ((char *) psrc + psrc->baseSrcFile [ isf ]);

        csl += psf->cSeg;
    }

    if (csl == 0) 
       DebugBreak();

    // Now copy the relevent info into our temporary structure

    pss = malloc (csl * sizeof (SS));

    for (isf = 0; isf < psrc->cFile; isf++) {
        OMFSourceFile *psf = (OMFSourceFile *)
            ((char *) psrc + psrc->baseSrcFile [ isf ]);
        ushort isl;

        for (isl = 0; isl < psf->cSeg; isl++) {
            OMFSourceLine *psl = (OMFSourceLine *)
                ((char *) psrc + psf->baseSrcLn [ isl ]);

            pss [ iss ].seg      = psl->Seg;
            pss [ iss ].offStart = psf->baseSrcLn [ psf->cSeg + isl * 2 ];
            pss [ iss ].poffEnd  = &psf->baseSrcLn [ psf->cSeg + isl * 2 + 1 ];

            iss += 1;
        }
    }

    // Now sort it by segment/offset

    qsort ((void *) pss, csl, sizeof (SS), SLCmp);

    // Now do the actual back/patch of the ends

    for (iss = 0; iss < csl; iss++) {
        OMFSegDesc *psi = NULL;

        // Find the least module segment contributer that has an end
        //  greater than the start of the source line table

        for (iseg = 0; iseg < pmds->cSeg; iseg++) {
            OMFSegDesc *psiT = &pmds->SegInfo [ iseg ];

            if (
                psiT->Seg == pss [ iss ].seg &&
                psiT->Off + psiT->cbSeg > pss [ iss ].offStart &&
              (psi == NULL || psi->Off > psiT->Off)
         ) {
                psi = psiT;
            }
        }

        DASSERT (psi != NULL);

        if (
          (ushort) (iss + 1) < csl &&
            pss [ iss ].seg == pss [ iss + 1 ].seg &&
            pss [ iss + 1 ].offStart < psi->Off + psi->cbSeg
        ) {

            *(pss [ iss ].poffEnd) = pss [ iss + 1 ].offStart - 1;
        }
        else {
            *(pss [ iss ].poffEnd) = psi->Off + psi->cbSeg - 1;
        }
    }

    // For each sstModule segment contributer, find the first associated
    //  source line contributer and make the source line start == the
    //  module start.

    for ( iseg = 0; iseg < pmds->cSeg; iseg++ ) {
        OMFSegDesc *psi = &pmds->SegInfo [ iseg ];

        iss = 0;
        while ( TRUE ) {

            // We have gone one past the desired source segment contributer
            //  so back up one and break;
            // This will happen if the module segment is strictly greater
            //  than the source segment or if the segments are equal and
            //  the module offset is strictly greater than the source segment.

            if (
                iss > 0 &&
                ( psi->Seg < pss [ iss ].seg ||
                    ( psi->Seg == pss [ iss ].seg &&
                      psi->Off >  pss [ iss ].offStart
                    )
                )
            ) {
                iss -= 1;
                break;
            }


            // We are exactly on the desired source segment, so just break;

            if (
                psi->Seg == pss [ iss ].seg &&
                psi->Off == pss [ iss ].offStart
            ) {
                break;
            }

            iss += 1;

            // We went off the end of the table, so (hopefully) the last
            //  source contributer was the one we were looking for.

            if ( iss == csl ) {
                iss -= 1;
                break;
            }
        }

        // The linker/compiler is sometimes inserting a segment that has
        //  no source but is listed in the module contributers.  In this
        //  case we will have passed the above tests but ended up on
        //  the wrong segment, so just ignore it.
#if 0
// I don't understand what this is doing but it must be wrong.

        if ( pss [ iss ].seg == psi->Seg) {
            *(pss [ iss ].poffEnd - 1) = psi->Off;
        }
#endif
    }

    // Free up the temporary memory

  free (pss);
}