/****************************************************************************
**
**  Name:  OMF86.CPP
**
**  Description:
**      Entry point of the OMF86 Loader DLL (OMF86.DLL).
**
**  Status:  CODED
**
**  $Log:   S:/tbird/mt2_186/omf86/omf86.cpv  $
** 
**    Rev 1.2   19 Jun 1997 17:10:42   Judy
** No change.
** 
**    Rev 1.1   18 Jun 1997 14:53:22   Judy
** 
**    Rev 1.0   26 Feb 1997 11:40:36   Judy
** Initial revision.
** 
**    Rev 1.0   14 Jun 1996 16:36:56   Judy
** Initial revision.
** 
**    Rev 1.18   09 May 1995 10:56:32   nghia
** Added check for register table loading.  Reload if not loaded.
** 
**    Rev 1.17   07 Apr 1995 09:33:32   nghia
** Moved Base's related functions to BASE module for locality.
** Cleanup, use FreeSections() to init and cleanup segments information tables.
** Revised to support 32K segments using Dynamic-Array class template.
** 
**    Rev 1.16   12 Dec 1994 14:33:00   nghia
** Fixed bug in if (!IsReportWarning) OK to load.
** 
**    Rev 1.15   11 Oct 1994 09:44:20   joyce
** 1. Check pmode only if LOAD_CODE option is on.
** 2. Extend the function OMF86LoaderLoadModuleByDesc() to get the LoadFlags 
**    from the loader launcher.
**
**    Rev 1.14   28 Sep 1994 17:40:50   joyce
** Fix a bug which caused on-demand loading failure -- failed to initialize
** loadfile handle.
**
**    Rev 1.13   26 Sep 1994 13:40:14   joyce
** 1. Reset the cpu when users attempt to load a omf86 loadfile and the
**    processor is in protected mode.
**
**    Rev 1.12   20 Sep 1994 11:50:44   joyce
** 1. Remove extra checkings for the exitance of StatBlock.
**
**    Rev 1.11   14 Sep 1994 10:42:04   joyce
** 1. Remove BkGetEmulationStatus() since loader launcher has done that.
** 2. Add LdrCppClearFunctionTable() to clear cpp function table for on demand
**    load.
**
**    Rev 1.10   07 Sep 1994 12:47:18   joyce
** 1. Change MODE_REAL to PMODE_REAL.
**
**    Rev 1.9   06 Sep 1994 09:57:02   joyce
** Add pmode checking, so if the processor is not in real mode, the omf86 loader
** will issue a warning message.
**
**    Rev 1.8   23 Aug 1994 14:14:48   joyce
** 1. Remove Checksum checkings
** 2. Remove TypeMap since type processing algorithm has been changed.
** 3. Remove event propagations.
** 4. Remove performance evaluation messages.
** 5. Change "DoNothing" function calls to Null pointers to save time.
**
**    Rev 1.7   03 Aug 1994 13:25:24   steve
** Joyce's changes for 386 build 11
**
**    Rev 1.6   14 Jul 1994 15:18:52   steve
** Joyce's omf86 changes for 386 build 8
**
**    Rev 1.4   17 Jun 1994 15:00:40   joyce
** 1. Expend the function Cleanup() to include:close loadfile, show warning msg,
**    restore cursor, pull progress indicator down, cleanup symbol table,
**    memory deallocation, clean up loader queue with LdrQueueWait().
** 2. Add retcode checking for LdrWarning(): if retcode is not GOOD, return.
**
**    Rev 1.3   16 Jun 1994 13:16:54   joyce
** 1. Use global interface LdrWarning() which allows user to cancel loading when
**    errors occur to replace Warning().
** 2. Initialize lastLoadAddr = 0ul in OMF86LoaderLoad().
** 3. Move Query() from err.cpp and remove include "err.h" from the code since
**    Query() is only used by this module.
**
**    Rev 1.2   02 Jun 1994 18:11:32   joyce
** 1. Add _PERFORMANCE_ sessions for performance evaluation.
**
**    Rev 1.1   25 May 1994 16:09:24   joyce
** Add processor family checking to ensure that the loadfile is compatible
** with the target processor type.
**
**    Rev 1.0   24 May 1994 14:48:28   joyce
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/omf86/omf86.cpv   1.2   19 Jun 1997 17:10:42   Judy  $
**
**  Copyright (C) 1994 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#include <ctype.h>
#include <io.h>
#include <sys\timeb.h>
#include <fcntl.h>

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

#ifndef _BKROOT_
#include "bkroot.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _CPU_
#include "cpu.h"
#endif

#ifndef _LMANGLE_
#include "lmangle.h"
#endif

#ifndef __OMF86__
#include "omf86.h"
#endif

#ifndef __OMF86LDR__
#include "omf86ldr.h"
#endif

#ifndef __TYPE__
#include "type.h"
#endif

#ifndef __BASE__
#include "base.h"
#endif

#ifndef __PLM86__
#include "plm86.h"
#endif

/* remove when the performance test is done */
// #ifndef _PERFORMANCE_
// #define _PERFORMANCE_
// #endif

#ifdef _PERFORMANCE_
#include <time.h>
#endif
                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/
PRIVATE RETCODE Cleanup(HANDLE, U32, HCURSOR, BOOLEAN, RETCODE);
PRIVATE RETCODE UnknownRecord(OMF86_FUNC_PARA *);
PRIVATE U16  Query(RETCODE);
                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
#pragma warn -par
//#define __DEBUG__
LPLDRSTATBLOCK StatBlock = NULL;
BOOLEAN HasStackInfo;
BOOLEAN HasStartPC;
U32 BytesLoaded;
U32 StackSize;
U32 LoadFileSize;
TIMESTAMP_TYPE LoadFileTimeStamp;
STATIC HWND Hwnd;
U32 LdrFlags;
STATIC DESCRIPTOR FAR *PtrAddrStartPC;
STATIC DESCRIPTOR FAR *PtrAddrStackTop;
STATIC DESCRIPTOR FAR *PtrAddrLoadRegion;
STATIC BOOLEAN NeedToEmptyQueue;
STATIC OMF86_REC_TYPE_INFO RecType[] = {
   {"RHEADR", NULL, },                    // 0x6E
   {"REGINT", ProcessREGINT, },           // 0x70
   {"REDATA", ProcessREDATA, },           // 0x72
   {"RIDATA", ProcessRIDATA, },           // 0x74
   {"OVLDEF", NULL, },                    // 0x76
   {"ENDREC", NULL, },                    // 0x78
   {"BLKDEF", ProcessBLKDEF, },           // 0x7A
   {"BLKEND", ProcessBLKEND, },           // 0x7C
   {"DEBSYM", ProcessDEBSYM, },           // 0x7E
   {"THEADR", ProcessTHEADR, },           // 0x80
   {"LHEADR", NULL, },                    // 0x82
   {"PEDATA", ProcessPEDATA, },           // 0x84
   {"PIDATA", ProcessPIDATA, },           // 0x86
   {"COMENT", ProcessCOMENT, },           // 0x88
   {"MODEND", ProcessMODEND, },           // 0x8A
   {"EXTDEF", NULL, },                    // 0x8C
   {"TYPDEF", ProcessTYPDEF, },           // 0x8E
   {"PUBDEF", ProcessPUBDEF, },           // 0x90
   {"LOCSYM", ProcessLOCSYM, },           // 0x92
   {"LINNUM", ProcessLINNUM, },           // 0x94
   {"LNAMES", ProcessLNAMES, },           // 0x96
   {"SEGDEF", ProcessSEGDEF, },           // 0x98
   {"GRPDEF", NULL, },                    // 0x9A
   {"FIXUPP", NULL, },                    // 0x9C
   {"(none)", UnknownRecord, },           // 0x9E
   {"LEDATA", NULL, },                    // 0xA0
   {"LIDATA", NULL, },                    // 0xA2
   {"LIBHED", NULL, },                    // 0xA4
   {"LIBNAM", NULL, },                    // 0xA6
   {"LIBLOC", NULL, },                    // 0xA8
   {"LIBDIC", NULL, }                     // 0xAA
};

BOOLEAN Paradigm;
int ExtOmf;
                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern LADDRESS StartPC, StackTop;
extern U32 LastLoadAddr;
extern U16 RegistersCnt;  // defined in REGTBL.CPP
extern CHAR ModName[NAME_LEN+1];
extern BOOLEAN FirstComment;

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
/******************************************************************************
**
**  OMF86LoaderLoad
**
******************************************************************************/
RETCODE EXPORT OMF86LoaderLoad(LPSTR fileName, U32 flags, HWND parentHwnd,
                               ADDR_SPACE addrSpace) {
CHAR loadFile[MAX_PATH_SIZE];  // near buffer
HANDLE hLoadFile;
OFSTRUCT ofStruct;
OMF86_REC_HEADER recHdr;
#ifdef _PERFORMANCE_
U16 i;
#endif
U16 recIdx, status;
RETCODE err, err2;
BOOLEAN abortFromEsc, cursorOn = FALSE;
HCURSOR hOldCursor, hHourGlassCursor;
OMF86_FUNC_PARA funcPara;
LPSTR   lpStrTmp;
PMODE   pmode;
CHAR    buf[NAME_LEN];

#ifdef _PERFORMANCE_
    // To measure loader download performance
    CHAR timebuf[80];
    time_t first, second;
    first = time(NULL);
#endif
   //Hera 6/13/97
   ExtOmf = -1;
   Paradigm = FALSE;
   FirstComment = TRUE;
   /* Gets the processor mode, for OMF86, it should be in REAL_MODE */
   if (IsLoadCode(flags)) {
      if ((err = AdrGetPmode(&pmode,buf)) != GOOD) {
         if ((err=LdrWarning(err, NULL, NULL, NULL)) != GOOD) {
            LdrProgressDone(err);
            return(err);
         }
      }
      else if (pmode != PMODE_REAL) {
         if ((err=LdrWarning(ER_OMF86_NOT_REAL, NULL, NULL, NULL)) != GOOD) {
            LdrProgressDone(err);
            return(err);
         }
         if (((err=CpuResetCPU()) != GOOD) &&
             ((err=LdrWarning(err, NULL, NULL, NULL)) != GOOD)) {
            LdrProgressDone(err);
            return(err);
         }
      }
   }

   // Cleanup for last loading session
   FreeSections();

   /* check for the null filename */
   if (*fileName == '\0') return(ER_CANNOT_OPEN);

   /* Set/Clear initial state for user-abort checking */
   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD) {
      LdrProgressDone(err);
      return(err);
   }
   /* Check to make sure that the RegTable is loaded, else load it */
   if (!RegistersCnt && ((err = SetupRegisterTable()) != GOOD) &&
      ((err = LdrWarning(err, NULL, NULL, NULL)) != GOOD)) {
      LdrProgressDone(err);
      return err;
   }
   if (((err = LdrGetStatBlock((LPLDRSTATBLOCK FAR *)&StatBlock)) != GOOD) ||
       (StatBlock == NULL)) {
      LdrProgressDone(err);
      return(err);
   }
   HasStackInfo = HasStartPC = NeedToEmptyQueue = FALSE;
   PtrAddrStartPC = PtrAddrStackTop = PtrAddrLoadRegion = NULL;
   InitModuleParam();

   // get the base type names used for the OMF86 loader
   if ((err = GetBaseTypeNames()) != GOOD) {
      LdrProgressDone(err);
      return(err);
   }

   // for debug only
#ifdef __DEBUG__
   for (i = 0; i < sizeof(RecType) / sizeof(OMF86_REC_TYPE_INFO); i++)
      RecType[i].count = 0;
#endif

   // Copy filename to near buffer so we can use fopen
   lstrcpy((LPSTR)loadFile, fileName);
   lpStrTmp = loadFile;
   // make sure it will fit in the status buffer (and in the dialog)
   // adjust the length of the text string
   while (lstrlen(lpStrTmp) + 1 >= MAX_LOADFILE_NAME_LEN)
      lpStrTmp++;

   lstrcpy((LPSTR)(StatBlock->curLoadFile), (LPSTR)lpStrTmp);

   // Save the load options
   LdrFlags = flags;
   Hwnd = parentHwnd;

   // Prepare to put up hourglass for a lengthy operation
   if ((hHourGlassCursor = LoadCursor(NULL, IDC_WAIT)) != NULL) {
      hOldCursor = SetCursor(hHourGlassCursor);
      cursorOn = TRUE;
   }

   // Open loadfile
   if ((err = OMFOpenFile(loadFile, &hLoadFile, &ofStruct)) != GOOD) {
      if (cursorOn) SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }

   // Rewind to the beginning of the file
   if (SeekFile(hLoadFile, 0L, SEEK_SET) == -1L) {
      OMFCloseFile(hLoadFile);
      if (cursorOn) SetCursor(hOldCursor);
      LdrProgressDone(ER_BAD_SEEK);
      return(ER_BAD_SEEK);
   }

   // Get the timestamp of the loadfile
   if ((err = LdrGetTimestamp(hLoadFile,
                      (TIMESTAMP_TYPE FAR*)&LoadFileTimeStamp)) != GOOD) {
      OMFCloseFile(hLoadFile);
      if (cursorOn) SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }
   // Initialize the progress indicator StatBlock
   StatBlock->loadFileSize = LoadFileSize = filelength(hLoadFile);
   StatBlock->curLocation = 0L;
   if (IsLoadSymbol(flags)) {
      GetPathName(); // for PL/M 86 support
      if ((err=InitializeTypeInfo()) != GOOD) {
         OMFCloseFile(hLoadFile);
         if (cursorOn) SetCursor(hOldCursor);
         LdrProgressDone(err);
         return(err);
      }
      SymAddCaseSensitive(IsIgnoreCase(flags) ? FALSE : TRUE);
      if ((err=SymAddLoadStart((LPSTR)ofStruct.szPathName, MCREATE,
          (TIMESTAMP_TYPE FAR*)&LoadFileTimeStamp)) == ER_SYMBOLS_LOADED) {
         err = ER_SYM_ALREADY;
         if (IsLoadFromShell(flags)) {
            if (IsReload(flags) || !IsReportWarning(flags)) {
               status = QOK;
            } else {
               OMFCloseFile(hLoadFile);
               if (cursorOn) SetCursor(hOldCursor);
               LdrProgressDone(ER_SYM_ALREADY_CLI);
               return(ER_SYM_ALREADY_CLI);
            }
         } else {
            if (IsReportWarning(flags)) {
               status = Query(ER_SYM_ALREADY);
            } else {
               status = QOK; /* OK to load */
            }
         }
         if (status == QOK) {
            // Unload symbols from Symbol Server
            if (((err = SymRemoveSymbols()) != GOOD) &&
                ((err2= LdrWarning(err, NULL, NULL, NULL)) != GOOD))
               return(LdrProgressDone(err2));
            // try again
            if ((err = SymAddLoadStart((LPSTR)ofStruct.szPathName, MCREATE,
                                 &LoadFileTimeStamp)) == ER_SYMBOLS_LOADED) {
               // Give up, symbol table refuses to clear
               OMFCloseFile(hLoadFile);
               if (cursorOn) SetCursor(hOldCursor);
               LdrProgressDone(err);
               return(err);
            }
         } else if (status == QCANCEL) {
            OMFCloseFile(hLoadFile);
            if (cursorOn) SetCursor(hOldCursor);
            LdrProgressDone(ER_LDR_ABORT);
            return(ER_LDR_ABORT);
         } else {
            /* Unrecognized error probably from CLI */
            OMFCloseFile(hLoadFile);
            LdrProgressDone(ER_SYM_ALREADY);
            if (IsLoadFromShell(flags))
               return(ER_SYM_ALREADY_CLI);
            return(ER_SYM_ALREADY);
         }
      } /* if (symbol loaded) */
      else if (err != GOOD) {
         OMFCloseFile(hLoadFile);
         if (cursorOn) SetCursor(hOldCursor);
         LdrProgressDone(err);
         return(err);
      }
   } // LoadSymbol

   // Load code here
   if (IsLoadCode(flags)) {
      BytesLoaded = 0L;
      LastLoadAddr = 0ul;
      if (((err = BkProcessorMustBeHalted()) != GOOD) ||
          ((err = LdrQueueInit(addrSpace)) != GOOD))
         return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err));
      NeedToEmptyQueue = TRUE;
   }

   // Process Loadfile
   funcPara.hFile = hLoadFile;
   funcPara.flags = flags;
   funcPara.oFlag = MCREATE;
   funcPara.moduleDesc = 0;

   /* convert address space */
   if (addrSpace == SPACE_SMM)
      funcPara.addrSpace = SEG_SPACE_SMM;
   else if (addrSpace == SPACE_USER)
      funcPara.addrSpace = SEG_SPACE_USER;
   else return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn,
                       ER_ADR_SPACE_INVALID));

   while (OMFGetBytes(hLoadFile, (U8 *)&recHdr, sizeof(recHdr)) == GOOD) {
      /* Check for user abort load process */
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) ||
          (abortFromEsc != FALSE)) {
         err = (err != GOOD) ? err : ER_LDR_ABORT;
         return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err));
      }
      // Process the record
      if ((recHdr.type >= MIN_TYPE_REC) && (recHdr.type <= MAX_TYPE_REC)) {
         recIdx = (recHdr.type - MIN_TYPE_REC) / 2;
         RecType[ recIdx ].count++;
         funcPara.endLoc = CurrentLocation() + recHdr.len - 1;
         if ((RecType[recIdx].proc != NULL) &&
             ((err = (*RecType[recIdx].proc)(&funcPara)) != GOOD))  {
            // 10/31/95 - Nghia
            // Added check memory verify error to stop loading
            if ((err == ER_MEMORY_VERIFY) || (err == ER_LDR_ABORT))
               return Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err);
            if ((err2= LdrWarning(err, NULL, NULL, NULL)) != GOOD)
               return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err2));
         }
         if ((err=Flush(hLoadFile, funcPara.endLoc+1L)) != GOOD) {
            // corrupted file, can't proceed loading
            err2= LdrWarning(err, NULL, NULL, NULL);
            return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err2));
         }
         // Update the progress indicator
         StatBlock->numBytes = BytesLoaded;
         StatBlock->curLocation = tell(hLoadFile);
         if ((err = LdrProgressInc()) != GOOD)
            return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err));
      }
      else {
         err2 = LdrWarning(ER_LDR_MISSING_LOADER_SIGNATURE, NULL, NULL, NULL);
         return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err2));
      }
   } // while

   if (cursorOn) SetCursor(hOldCursor);
   OMFCloseFile(hLoadFile);
   if (!IsLoadOnDemand(flags)) {
      // Ondemand = OFF, all symbols loaded, cleanup sections information
      FreeSections();
   }
   if (IsLoadSymbol(flags)) {
      DestroyModNames();
      /* Call SymAddLoadEnd to sort symbols */
      if (((err = SymAddLoadEnd()) != GOOD) &&
          ((err = LdrWarning(err, NULL, NULL, NULL)) != GOOD))
         return(Cleanup(hLoadFile, flags, hOldCursor, cursorOn, err));
   }

   if (IsLoadCode(flags)) {
      /* send partially filled load buffer to box */
      err = LdrQueueFlush(LastLoadAddr);
      err2 = LdrQueueWait();
      NeedToEmptyQueue = FALSE;
      // Handle Loading verify error
      // 10/31/95 - Nghia
      // Let LOADER.DLL report the error
      if ((err = (err ? err : err2)) == ER_MEMORY_VERIFY) { 
         return LdrProgressDone(err);
      }
      // Update the progress indicator
      StatBlock->numBytes = BytesLoaded;
      StatBlock->curLocation = tell(hLoadFile);
      if (HasStackInfo) {
         sprintf((LPSTR)StatBlock->stackBase, "%04X:%04X",
                 StackTop.segment, StackTop.offset );
         sprintf((LPSTR)StatBlock->stackSize, "0x%lX", StackSize);
      }
      if (HasStartPC)
         sprintf((LPSTR)StatBlock->startPC, "%04X:%04X",
                 StartPC.segment, StartPC.offset );

#ifdef _PERFORMANCE_
      second = time(NULL);
      wsprintf(timebuf, " %ld bytes loaded in %ld seconds", BytesLoaded,
         (U32)(second-first));
      LdrSendMessageToCLI(timebuf);
#endif
   }
   return(LdrProgressDone(GOOD));
}  /* OMF86LoaderLoad */

/******************************************************************************
**
**  OMF86LoaderLoadByModuleDesc
**
******************************************************************************/
RETCODE EXPORT OMF86LoaderLoadModuleByDesc(LPSTR lpBinfile, LPSTR moduleName,
                                           SYM_DESCRIPTOR moduleDesc,
                                           ADDR_SPACE addrSpace, U32 flags) {
OMF86_FUNC_PARA funcPara;
HANDLE hLoadFile;
OFSTRUCT ofStruct;
RETCODE err, symErr;
U32 moduleFileOffset = 0L, typeDelta = 0L;
OMF86_REC_HEADER recHdr;
U16 recIdx;
U8 dummy;
CHAR tModuleName[NAME_LEN];
BOOLEAN abortFromEsc;
HCURSOR hOldCursor, hHourGlassCursor;
BOOLEAN hourGlassOn = FALSE;
U32 auxOffset;
LANGUAGE_TYPE langType;

   /* convert address space */
   if (addrSpace == SPACE_SMM)
      funcPara.addrSpace = SEG_SPACE_SMM;
   else if (addrSpace == SPACE_USER)
      funcPara.addrSpace = SEG_SPACE_USER;
   else {
      err= ER_ADR_SPACE_INVALID;
      goto CleanUpAndExit;
   }
   /* We only need to open modules/functions/line blocks (not create them);
   ** they were created by the initial load.
   */
   if ((err = OMFOpenFile(lpBinfile, &hLoadFile, &ofStruct)) != GOOD)
      return(err);
   /*
   ** For module load, we aren't going to use the environment stored time,
   ** we'll go directly to the disk file.
   */
   if ((err = LdrGetTimestamp(hLoadFile,
                      (TIMESTAMP_TYPE FAR*)&LoadFileTimeStamp)) != GOOD)
      return(err);
   /*
   ** For module-only load, ST returns error if timestamp passed is later than
   ** the initial timestamp for this module.  Note that we don't really track
   ** timestamps on individual modules, but on loadfiles.  What is a module-
   ** is it an object, or a source file??
   */
   symErr = SymAddLoadStart((LPSTR)ofStruct.szPathName, MOPEN,
                            &LoadFileTimeStamp);
   /* If timestamps don't match, any subsequent attempt to get module offset
   ** from ST is going to be bogus, so quit now.
   */
   if (symErr == ER_TIMESTAMPS_DONT_MATCH) {
      err = ER_MODTIME;
      /* This is fatal, since we only wanted to load this module */
      goto CleanUpAndExit;
   }
   /*
   ** Open the Symbol Table context for adding local symbols:
   ** returns moffset (file offset of module in loadfile),
   ** toffset (type fix-up value).
   */
   if ((symErr = SymAddModuleOpenByDesc(moduleDesc,  &moduleFileOffset,
            &typeDelta)) != GOOD) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   /* Move the file pointer to moduleFileOffset */
   if (SeekFile(hLoadFile, moduleFileOffset, SEEK_SET) == -1L) {
      err = ER_BAD_SEEK;
      goto CleanUpAndExit;
   }
   /* Check if this is a THEADR(0x80) record; if not, it's an error */
   if ((OMFGetBytes(hLoadFile, (U8 *)&recHdr, sizeof(recHdr)) != GOOD) ||
       recHdr.type != THEADR) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   if ((err = GetName(hLoadFile, &dummy, tModuleName)) != GOOD) { // skip over module name
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   if ((err = SymGetModuleExtraInfo(moduleDesc,&auxOffset,&langType)) != GOOD)
      goto CleanUpAndExit;
   if (langType == LANG_PLM)
      lstrcpy(ModName, moduleName);
   else if (lstrcmpi((LPSTR)tModuleName, moduleName) != 0) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   if ((err=OMFGetByte(hLoadFile, &dummy)) != GOOD) // skip checksum byte
      goto CleanUpAndExit;

   /* Put up hourglass for a lengthy operation */
   hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
   hOldCursor = SetCursor(hHourGlassCursor);
   hourGlassOn = TRUE;

   /* Clear function list before loading (for C++ duplicate detection) */
   LdrCppClearFunctionTable();

   /* Initialize module-active variables and flags */
   InitModuleParam();

   /* Process parameters */
   funcPara.hFile = hLoadFile;
   funcPara.flags = flags & (~(U32)LOAD_CODE);
   funcPara.oFlag = MOPEN;
   funcPara.moduleDesc = moduleDesc;

   /* Process records */
   while (OMFGetBytes(hLoadFile, (U8 *)&recHdr, sizeof(recHdr)) == GOOD) {
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) ||
          (abortFromEsc != FALSE)) {
         err = (err != GOOD) ? err : ER_LDR_ABORT;
         goto CleanUpAndExit;
      }
      if ((recHdr.type >= MIN_TYPE_REC) && (recHdr.type <= MAX_TYPE_REC)) {
         recIdx = (recHdr.type - MIN_TYPE_REC) / 2;
         RecType[ recIdx ].count++;
         funcPara.endLoc = CurrentLocation() + recHdr.len - 1;
         if ((RecType[recIdx].proc != NULL) &&
             ((err = (*RecType[recIdx].proc)(&funcPara)) != GOOD))
            goto CleanUpAndExit;
         if ((err=Flush(hLoadFile, funcPara.endLoc+1L)) != GOOD)
            // corrupted file
            goto CleanUpAndExit;
         if ((recHdr.type == THEADR) || (recHdr.type == MODEND))
            break;
      }
      else {  /* invalid record */
         err = ER_LDR_MISSING_LOADER_SIGNATURE;
         goto CleanUpAndExit;
      }
   }
   /* Call SymAddLoadEnd to sort symbols */
   if ((symErr = SymAddLoadEnd()) != GOOD) {
      LdrWarning(symErr, NULL, NULL, NULL);
      if (err == GOOD) err = ER_CLOSE_SYM;
   }
CleanUpAndExit:
   if (hourGlassOn)
      SetCursor(hOldCursor);
   OMFCloseFile(hLoadFile);
   return(err);

} /* OMF86LoaderLoadModuleByDesc */

/******************************************************************************
**
**  OMF86LoaderGetStartPC
**
******************************************************************************/
RETCODE EXPORT OMF86LoaderGetStartPC(DESCRIPTOR FAR *ptrStartPC) {
RETCODE aderr;
CHAR addrStr[SEGMENT_OFFSET_LEN];

   if (!HasStartPC)
      return(ER_NO_PC);
   sprintf( addrStr, "%04X:%04X", StartPC.segment, StartPC.offset );
   /* Uses static data */
   if ((aderr = AdrCreateAddressFromText((LPSTR)addrStr, NULL,
        ptrStartPC)) != GOOD)
      return(aderr);
   /* retain static copy in case of cleanup */
   PtrAddrStartPC = ptrStartPC;
   return(GOOD);
}  /* OMF86LoaderGetStartPC */

/******************************************************************************
**
**  OMF86LoaderGetStackInfo
**
******************************************************************************/
RETCODE EXPORT OMF86LoaderGetStackInfo(DESCRIPTOR FAR *ptrStackTop,
                                       U32 FAR *stkSize) {
RETCODE err;
CHAR addrStr[SEGMENT_OFFSET_LEN];
   if (!HasStackInfo)
      return(ER_STK_STK_BASE_INVALID);
   sprintf( addrStr, "%04X:%04X", StackTop.segment, StackTop.offset );
   if ((err = AdrCreateAddressFromText((LPSTR)addrStr, NULL,
        ptrStackTop)) != GOOD)
      return(err);
   /* retain static copy in case of cleanup */
   PtrAddrStackTop = ptrStackTop;
   *stkSize = StackSize;
   return(GOOD);
}  /* OMF86LoaderGetStack */

/******************************************************************************
**
**  Cleanup
**
******************************************************************************/
PRIVATE RETCODE Cleanup(HANDLE hLoadFile, U32 flags, HCURSOR hOldCursor,
                        BOOLEAN cursorOn, RETCODE err) {
 RETCODE symErr;

   /* When user decides to abort load operation, or for some error
   ** condition encountered, cleanup.  This routine is *not* called
   ** for a successful load.
   */
   OMFCloseFile(hLoadFile);
   if (IsLoadCode(flags) && NeedToEmptyQueue)
      LdrQueueWait();
   if (IsLoadSymbol(flags)) {
      if ((symErr = SymAddLoadEnd()) != GOOD)
         LdrWarning(symErr, NULL, NULL, NULL);
      FreeSections();
      SymRemoveSymbols();  /* let's clean up */
      FreePublics(GOOD);
      DestroyModNames();
   }
   /*  Clean up address descriptors created in this load session */
   if (PtrAddrStartPC)
      AdrDestroyAddress(*PtrAddrStartPC);
   if (PtrAddrStackTop)
      AdrDestroyAddress(*PtrAddrStackTop);
   if (PtrAddrLoadRegion)
      AdrDestroyAddress(*PtrAddrLoadRegion);
   PtrAddrStartPC = PtrAddrStackTop = PtrAddrLoadRegion = NULL;
   HasStackInfo = FALSE;
   if (cursorOn) SetCursor(hOldCursor);
   // LdrProgressDone will report the error
   return LdrProgressDone(err);
}  /* Cleanup */

/******************************************************************************
**
**  UnknownRecord
**
******************************************************************************/
PRIVATE RETCODE UnknownRecord(OMF86_FUNC_PARA *funcPara) {
    return(ER_LDR_MISSING_LOADER_SIGNATURE);
}

/******************************************************************************
**
**  Query
**
******************************************************************************/
PRIVATE U16 Query(RETCODE ret) {
   S16 status;

   ErrDisplayErrorEx(ret, CHECK_MODE, MB_TYPE_OKCANCEL,
       (S16 FAR *)&status);
   /* Put up hourglass for loader to continue */
   SetCursor(LoadCursor(NULL, IDC_WAIT));

   if (status == IDOK)
      return (QOK);
   else if (status == IDCANCEL)
      return (QCANCEL);

   /* default action is to abort the load process */
   return (QCANCEL);
}  /* Query */

/******************************** E O F ***********************************/
