/****************************************************************************
**
**  Name:  BASE.CPP
**
**  Description:
**      Routines for processing bases.
**
**  Status:  CODED
**
**  $Log:   S:/tbird/mt2_186/omf86/base.cpv  $
** 
**    Rev 1.1   18 Jun 1997 14:53:02   Judy
** 
**    Rev 1.0   26 Feb 1997 11:40:32   Judy
** Initial revision.
** 
**    Rev 1.0   14 Jun 1996 16:36:46   Judy
** Initial revision.
** 
**    Rev 1.16   13 Apr 1995 08:54:32   nghia
** Added check for valid object pointer before access the object.
** 
**    Rev 1.15   07 Apr 1995 09:24:04   nghia
** Revised to support 32K segments using Dynamic-Array template.
** Revised ProcessLNAMES() and ProcessSEGDEFS() to load all LNAMES and SEGDEF
** records to the maximum limit, or until there is no memory to allocate to
** grow the NamesList[] and SegInfoList[] arrays.
** Used DArray<X> template to generate object instances of both DArray<LNAMES>
** and DArray<SEGDEF>.
** Moved base's related functions from OMF86 module to this module for locality.
** 
**    Rev 1.14   10 Mar 1995 13:45:52   nghia
** Fixed PPR 10098 - Cannot recognize stack segment if its name is not "STACK".
** Use the class name to qualify the stack segment name to recognize stack 
** segment.
** 
**    Rev 1.13   11 Oct 1994 09:53:50   joyce
** 1. Check for the exact string "STACK" for the stack base information.
** 2. Get base type from SymFindBaseIndexFromSegment(), so the symbol types
**    (code or data) will be consistant in the same base.
** 3. Abort from the program if the TFree() failed.
**
**    Rev 1.12   06 Sep 1994 09:51:42   joyce
** Change the size of base name buffer.
**
**    Rev 1.11   23 Aug 1994 14:23:42   joyce
** 1. Modify GetBaseName to set default to BASE_DATA.
** 2. Change the algorithm of handling base :
**    . return err if base is DATA and not found in SEGDEF table.
**    . Create base if base is Code and not yet created even if it's not in
**      SEGDEF table.
**    . Change the base type to BASE_CODE if the caller is ProcessLINNUM().
** 3. Add GetBaseTypeName to retrieve base type names from PWRVIEWS.INI.
**
**    Rev 1.10   03 Aug 1994 13:25:00   steve
** Joyce's changes for 386 build 11
**
**    Rev 1.9   25 Jul 1994 12:41:10   steve
** Joyce's omf changes for 386 build 9
**
**    Rev 1.8   14 Jul 1994 15:23:14   steve
** More changes for 386 build 8 from Joyce
**
**    Rev 1.4   17 Jun 1994 14:54:50   joyce
** Check the return code from LdrWarning(), if it's not GOOD then cleanup and
** return.
**
**    Rev 1.3   16 Jun 1994 13:29:58   joyce
** Use LdrWarning() to replace Warning() and remove err.h from this module.
**
**    Rev 1.2   02 Jun 1994 18:35:34   joyce
** Changes made as the result of code reviews.
**
**    Rev 1.1   25 May 1994 16:03:28   joyce
** Make DestroyName() public so it can be called in OMF86LoaderLoad();
** this is to stop memory leak.
**
**    Rev 1.0   24 May 1994 14:47:54   joyce
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/omf86/base.cpv   1.1   18 Jun 1997 14:53:02   Judy  $
**
**  Copyright (C) 1994 Microtek International.  All rights reserved.
**
*****************************************************************************/

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

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

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

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

#ifndef _INIUTIL_
#include "iniutil.h"
#endif

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

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

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

// include DArray class template to generate class instances
#include "darray.cpp"  

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
// IMPORTANT:
// Tell the compiler that the template instances that follow will be
// defined elsewhere, and generate the necessary instances
#pragma option -Jgx -Jgd

// IMPORTANT:
// DArray<X> constructor and destructor will be called when
// the OMF86.DLL load and unload, so don't worry about how the static
// variables are initialized.  It's magic!
       
// Initialize name objects - NamesList
RETCODE LNAMES::LastError = GOOD;
RETCODE DArray<LNAMESPTR>::LastError = GOOD;
LNAMESPTR DArray<LNAMESPTR>::BadIndex = 0;
DArray<LNAMESPTR> NamesList(DEFAULT_MAX_NAMES);

// Initialize SEGDEF object variables
RETCODE SEGDEF::LastError = GOOD;
RETCODE DArray<SEGDEFPTR>::LastError = GOOD;
SEGDEFPTR DArray<SEGDEFPTR>::BadIndex; 
DArray<SEGDEFPTR> SegInfoList(DEFAULT_MAX_SEGMENTS);

STATIC U8 CacheBottom, NumInCache=0 ;
STATIC BASE_CACHE BaseCache[CACHE_SIZE];
STATIC U16 BaseCnt=0;
STATIC CHAR *NullName = NULL;
CHAR BaseTypeNames[MAX_BASE_TYPE_NAMES]
                  [BASE_NAME_LEN+1]; // 1st:code, 2nd:data


                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern BOOLEAN HasStackInfo;
extern LADDRESS StackTop;
extern U32     StackSize;
extern MODULE_BASE ModuleBase;
extern U32     LdrFlags;
extern int ExtOmf;

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/
PRIVATE RETCODE GetIndex(HANDLE hFile, U16 *index);
PRIVATE BASE_TYPE GetBaseType(LPSTR className);

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
//-----------------------------------------------------------------------------
//                              LNAMES CLASS
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Ctor LNAMES
//
// Purpose: initializes the instances of a LNAMES object.
//
//-----------------------------------------------------------------------------
LNAMES::LNAMES(LPSTR iStr) {
   if ((nameStr = (LPSTR) TMalloc((U32) strlen(iStr)+1)) != NULL)
      strcpy(nameStr, iStr);
   LastError = (nameStr) ? GOOD : ER_NO_MEMORY;
}

//-----------------------------------------------------------------------------
// Dtor LNAMES
//
// Purpose: destroy the instances of a LNAMES object.
//
//-----------------------------------------------------------------------------
LNAMES::~LNAMES() {
   if (nameStr)
      LastError = TFree(nameStr);
}

//-----------------------------------------------------------------------------
//                              SEGDEF CLASS
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Ctor SEGDEF
//
// Purpose: initializes the instances of a SEGDEF object.
//
//-----------------------------------------------------------------------------
SEGDEF::SEGDEF(VOID) {
   memset(this, NULL, sizeof(*this));
}

//-----------------------------------------------------------------------------
// Ctor SEGDEF
//
// Purpose: initializes the instances of a SEGDEF object.
//
//-----------------------------------------------------------------------------
SEGDEF::SEGDEF(U16 segNameId, U16 segId, SEG_DATA& segData,
					BASE_TYPE segType) {
   // initialize data members
   nameId    = segNameId;
   baseIndex = segId;       
   frame     = segData.segFrame;
   offset    = segData.segOffset;
   size      = segData.segLength;
   baseType  = segType;
}

//-----------------------------------------------------------------------------
// Dtor SEGDEF
//
// Purpose: deallocate instances of SEGDEF object.
//
//-----------------------------------------------------------------------------
SEGDEF::~SEGDEF() {
   // Initialize all members to 0
   memset(this, NULL, sizeof(*this));  
}

//-----------------------------------------------------------------------------
// SegName
//
// Purpose: return the segment name of SEGDEF object.
//
//-----------------------------------------------------------------------------
const LPSTR SEGDEF::SegName() {
   // Use nameId to access NamesList[]
   if (NamesList[nameId]) return NamesList[nameId]->Name();
   return NULL;
}

/******************************************************************************
**
**  FindSegment - Find base in SEGDEF Table
**
******************************************************************************/
RETCODE SEGDEF::FindSegment(BASE_ADDRESS segment, SEGDEF** segPtr) {
   for (U16 i=0; i < SegInfoList.Count(); i++) {
      if (SegInfoList[i] && !(SegInfoList[i]->SegFrame() - segment)) {
         *segPtr = SegInfoList[i];
         return GOOD;
      }
   }
   // Can't find the segment in SegInfoList[]
   return ERR_BAD_BASE;
}   

//-----------------------------------------------------------------------------
//                          BASE INTERFACES
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// DestroyNames
//
// Purpose: destroy the NamesList[].
//
//-----------------------------------------------------------------------------
RETCODE DestroyNames(VOID) {
  // get number of elements in list 
  U16 nameCounts = NamesList.Count();  
  for (U16 i = 0; i < nameCounts; i++) {
     // delete the NameList[] object (i.e. LNAMES object)
     LNAMESPTR nameObj = NamesList[i];
     if (nameObj && (DArray<LNAMESPTR>::LastError == GOOD)) {
        delete nameObj;
        NamesList[i] = NULL;
     }
     NamesList.DecrementCount();
  }
  // return the last 
  return DArray<LNAMESPTR>::LastError;
}  

/******************************************************************************
**
**  ProcessLNAMES - only process during initial load
**
******************************************************************************/
RETCODE ProcessLNAMES(OMF86_FUNC_PARA *funcPara) {
RETCODE err;
CHAR tmpName[NAME_LEN];
LNAMESPTR nameObj = NULL;
U16 growSize  = 0;
U8 length;

   if (funcPara->oFlag == MCREATE) {
      while (CurrentLocation() < funcPara->endLoc) {
         // read LNAMES record from file and insert into the NamesList 
         if ((err = GetName(funcPara->hFile, &length, tmpName)) != GOOD)
            return err;
         // allocate buffer to hold the <tmpName> string
         if ((nameObj = new LNAMES(tmpName)) == NULL) {
            DestroyNames();
            return LNAMES::LastError;
         }
         // insert nameObj into the global NamesList[]
         while ((err = NamesList.Insert(nameObj)) != GOOD) {
            // if cannot grow NamesList || reach the maximum number of names
            // return error
            if (growSize || (err != ER_NO_MEMORY) ||
                (NamesList.Count() == MAX_NAMES)) {
               delete nameObj;
               DestroyNames();
               return err;
            }
            // grow the NamesList to hold the new LNAMES object
            growSize = NamesList.Size() + DEFAULT_GROW_SIZE;
            NamesList.Grow(growSize);
         }
         // reset growSize to 0 to be reused
         growSize = 0;
      } // while
   }
   return GOOD;
} 

//-----------------------------------------------------------------------------
// DestroySegDefs
//
// Purpose: destroy the SegInfoList[].
//
//-----------------------------------------------------------------------------
RETCODE DestroySegDefs(VOID) {
  U16 numSegs = SegInfoList.Count();
  for (U16 i = 0; i < numSegs; i++) {
     // delete the SegInfoList[] object (i.e. SEGDEF object)
     SEGDEFPTR segObj = SegInfoList[i];
     if (segObj && (DArray<SEGDEFPTR>::LastError == GOOD)) {
        delete segObj;
        SegInfoList[i] = NULL;
     }
     SegInfoList.DecrementCount();
  }
  // return the last error
  return DArray<SEGDEFPTR>::LastError;
}  

/******************************************************************************
**
**  ProcessSEGDEF - only process during initial load
**
******************************************************************************/
RETCODE ProcessSEGDEF(OMF86_FUNC_PARA *funcPara) {
ACBP aCBP;
SEG_DATA segData;
RETCODE err;
LPSTR segNamePtr, classNamePtr;
SEGDEFPTR segObj = NULL;
CHAR segNameBuf[NAME_LEN];
U16 segNameIndex, classNameIndex, growSize = 0;

   if (funcPara->oFlag == MCREATE) {
      segNamePtr = classNamePtr = NullName; // unnamed segments
      if ((err=OMFGetBytes(funcPara->hFile,
          (U8 *)&aCBP, sizeof(aCBP))) != GOOD) return(err);
      if (aCBP.alignment == 0 || aCBP.alignment == 5) {
         if ((err=OMFGetBytes(funcPara->hFile, (U8 *)&segData,
              sizeof(segData))) != GOOD)
            return(err);
         if (aCBP.alignment == 0) {
            if ((err=GetIndex(funcPara->hFile, &segNameIndex)) != GOOD)
               return(err);
            if ((err=GetIndex(funcPara->hFile, &classNameIndex)) != GOOD)
               return(err);
            if ((segNameIndex > 0 && segNameIndex <= NamesList.Count()) &&
                (classNameIndex > 0 && classNameIndex <= NamesList.Count())) {
               segNamePtr = NamesList[segNameIndex-1]->Name();
               classNamePtr = NamesList[classNameIndex-1]->Name();
            }
            // 02/27/95 - Nghia
            // Fixed PPR 10098 - Get Stack information for BC40 and
            // Paradigm Tools. 
            if ((!HasStackInfo) && (segNamePtr && classNamePtr) &&
                (strstr(segNamePtr, "STACK")  != NULL) &&
                (strcmp(classNamePtr, "STACK") == NULL)) {
               U32 stackBaseOffset;
               HasStackInfo = TRUE;
               StackTop.segment = segData.segFrame;
               stackBaseOffset = segData.segOffset + segData.segLength;
               StackTop.offset  = stackBaseOffset > 0xFFFFL ?
                  0xFFFF :  (U16)stackBaseOffset;
               StackSize = segData.segLength;
            }
         }
         if (!segNamePtr || *segNamePtr == NULL) {
            // create a name if it doesn't have one
            sprintf(segNameBuf,"_SEGDEF%d", SegInfoList.Count());
            LNAMESPTR nameObj;
            // allocate buffer to hold the <tmpName> string
            if ((nameObj = new LNAMES(segNameBuf)) == NULL) {
               DestroyNames();
               return LNAMES::LastError;
            }
            // Use the NameList[] count for index
            segNameIndex = NamesList.Count();
            // insert nameObj into the global NamesList[]
            while ((err = NamesList.Insert(nameObj)) != GOOD) {
               if (growSize || (err != ER_NO_MEMORY) ||
                   (NamesList.Count() == MAX_NAMES)) {
                  delete nameObj;
                  DestroyNames();
                  return err;
               }
               // grow the NamesList to hold the new LNAMES object
               growSize = NamesList.Size() + DEFAULT_GROW_SIZE;
               // return error if cannot allocate more memory
               NamesList.Grow(growSize);
            }
            // reset growSize for SegInfoList[] to reuse 
            growSize = 0;
         }
         
         // allocate a new SEGDEF object to hold the information
         if ((segObj = new SEGDEF(segNameIndex,
											 SegInfoList.Count(),
											 segData, 
                                  GetBaseType(classNamePtr))) == NULL)
            return SEGDEF::LastError;
         
         // now insert SEGDEF object into the SegInfoTree
         while ((err = SegInfoList.Insert(segObj)) != GOOD) {
            // return error if SegInfoList already grown and still
            // cannot insert <segObj>
            if (growSize || (err != ER_NO_MEMORY) ||
                (SegInfoList.Count() == MAX_SEGMENTS)) {
               delete segObj;
               return err;
            }
            // grow the size of SegInfoList to hold more SEGDEF object
            growSize = SegInfoList.Size() + DEFAULT_GROW_SIZE;
            SegInfoList.Grow(growSize);
         }
      }
      else return ER_REC_NOT_HANDLED;
   }
   return GOOD;
}

/******************************************************************************
**
**  GetBaseIndex - search for the base of the input address, if not found
**                 create a new base.
**
******************************************************************************/
RETCODE GetBaseIndex(BASE_ADDRESS segment, SEG_ADDR_SPACE addrSpace,
                     BOOLEAN isCode, BASE_INDEX *baseIndex,
                     BASE_TYPE *baseType) {
LOOP_VAR i;
CHAR baseName[NAME_LEN];
RETCODE err;
BOOL foundSeg = FALSE;

   if (ModuleBase.baseKnown && ModuleBase.baseAddr == (U16)segment) {
      *baseIndex = ModuleBase.baseIndex;
      *baseType = ModuleBase.baseType;
      return(GOOD);
   }
   /* First lookup the base cache */
   for (i=0; i < NumInCache; i++) {
      if (BaseCache[i].baseAddr == segment &&
          BaseCache[i].addrSpace == addrSpace) {
         *baseIndex = BaseCache[i].baseIndex;
         *baseType = BaseCache[i].baseType;
         return(GOOD);
      }
   }
   *baseType = BASE_DATA;
   
   // Find base index from Symbol Table first 
   // 08/04/95 - Typecast <segment> to U16 to fix compiler waring
   if (SymFindBaseIndexFromSegment((U16)segment, addrSpace, baseIndex, baseType)
        != GOOD) {
      // See if it's defined in the global SegInfoTree, if yes, get
      // the base name and base type to create a new symbols base
      SEGDEFPTR segPtr = NULL;
      if (SegInfoList.Count() > 0) {
         // search SegInfoList for SEGDEF object contain <segment>
         foundSeg = (SEGDEF::FindSegment(segment, &segPtr) == GOOD);
      }
      if (foundSeg) {
         lstrcpy((LPSTR)baseName, (LPSTR)segPtr->SegName());
         *baseType = (isCode) ? BASE_CODE : segPtr->SegBaseType();
      } else if (isCode) {
         // if a code base is not defined in SegInfoList, add it anyway
         *baseType = BASE_CODE;
         sprintf(baseName, "_seg%d", BaseCnt+1 );
      } else
         return ERR_BAD_BASE;

      // create a new symbol base from the segment information
      // 08/04/95 - Typecast <segment> to U16 to fix compiler warning
      if ((err=SymAddBaseCreateFromSegment(baseName, BaseCnt, 
            (U16)segment, *baseType, addrSpace)) != GOOD) return(err);
      *baseIndex = BaseCnt++;
   }
   BaseCache[CacheBottom].baseAddr = segment;
   BaseCache[CacheBottom].addrSpace = addrSpace;
   BaseCache[CacheBottom].baseType = *baseType;
   BaseCache[CacheBottom++].baseIndex = *baseIndex;
   CacheBottom %= CACHE_SIZE;
   if (NumInCache < CACHE_SIZE) NumInCache++;
   return( GOOD );
}  /* GetBaseIndex */

/******************************************************************************
**
**  GetBaseTypeNames
**
**  Description:
**     Extract the base type names used by tool chains from PWRVIEWS.INI file.
**     Save the base type name into toolUseName variable.
**
******************************************************************************/
RETCODE GetBaseTypeNames(VOID) {
CHAR tmpStr[NAME_LEN];

   /* Retrieve base type names for OMF86 Loader */
   if (IniGetString((LPSTR) TOOL_CHAIN,  (LPSTR) OMF_BASE_TYPE,
                    PWRVIEWS_INI_DEFAULT, (LPSTR)tmpStr) != GOOD)
      return(ER_NO_SECTION_NAME_DEFINED);
   else {
      /* Parse the string into base type names */
      LOOP_VAR i=0;
      CHAR *loopPtr = tmpStr, *baseTypePtr = NULL;
      U16 nameLen;
      do {
         /* strtok return the pointer to the token found in tmpStr */
         if ((baseTypePtr = strtok(loopPtr, DELIMITER_SET)) != NULL) {
            nameLen = strlen(baseTypePtr) + 1; /* string terminator */
            if ((nameLen > BASE_NAME_LEN) ||
                (strncpy(BaseTypeNames[i], baseTypePtr, nameLen) == NULL)) {
               return(ER_INVALID_SECTION_NAME);
            }
         }
         else {
            if (i < MAX_BASE_TYPE_NAMES)
               return(ER_MISSING_SECTION_NAME);
            break;
         }
         ++i;
         loopPtr = NULL; /* Reset to walk the rest of the string tmpStr */
      } while (i < MAX_BASE_TYPE_NAMES);
   }
   return(GOOD);
} /* GetBaseTypeNames */

/******************************************************************************
**
**  FreeSections - free memory associated with section info struct
**
******************************************************************************/
RETCODE FreeSections(VOID) {

   DestroySegDefs();
   DestroyNames();
   BaseCnt=0;
   NumInCache = CacheBottom = 0;
   memset(BaseCache, NULL, sizeof(BaseCache));
   memset(BaseTypeNames, NULL, sizeof(BaseTypeNames));
   return(GOOD);
}  /* FreeSections */

/******************************************************************************
**
**  GetName
**
******************************************************************************/
RETCODE GetName(HANDLE hFile, U8 *length, CHAR *name) {
   RETCODE err;
   if ((err=OMFGetBytes(hFile, length, sizeof(U8))) != GOOD)
      return(err);
   if (*length > 0) {
      if ((err=OMFGetBytes(hFile, (U8 *)name, (U16)*length)) != GOOD)
         return(err);
      if (IsIgnoreCase(LdrFlags)) {
         for (U8 i=0; i < *length; i++)
             name[i] = toupper(name[i]);
      }
   }
   name[*length] = '\0';
   return(GOOD);
} /* GetName */

/******************************************************************************
**
**  GetPhysicalBase
**
******************************************************************************/
RETCODE GetPhysicalBase(HANDLE hFile, LADDRESS *addrPtr) {
   PHY_BASE frameOffset;
   RETCODE err;

   if ((err=OMFGetBytes(hFile, (U8 *)&frameOffset, sizeof(frameOffset)))
        != GOOD) return(err);
   addrPtr->segment = frameOffset.frameNum;
   addrPtr->offset = (U16)frameOffset.offset;
   return(GOOD);
} /* GetPhysicalBase */

/******************************************************************************
**
**  GetBaseOffset
**
******************************************************************************/
RETCODE GetBaseOffset(HANDLE hFile, LADDRESS *basePtr) {
   U16 offset;
   RETCODE err;

   if ((err=GetBase(hFile, basePtr)) != GOOD) return(err);
   if ((err=OMFGetBytes(hFile, (U8 *)&offset, sizeof(offset))) != GOOD)
      return(err);
   basePtr->offset += offset;
   return(GOOD);
}  /* GetBaseOffset */

/******************************************************************************
**
**  GetBase
**
******************************************************************************/
RETCODE GetBase(HANDLE hFile, LADDRESS *basePtr) {
   U16 segIndex = 0, grpIndex;
   RETCODE err;
   // skip the group index 
   if (((err=GetIndex(hFile, &grpIndex)) != GOOD) ||
       ((err=GetIndex(hFile, &segIndex)) != GOOD))
      return(err);
   
	if (segIndex) {
	   // point to a SEGDEF record in SegInfoList[]
      if (segIndex <= SegInfoList.Count()) {
         basePtr->segment = SegInfoList[segIndex-1]->SegFrame();
         basePtr->offset  = SegInfoList[segIndex-1]->SegOffset();
         if (SegInfoList.LastError != GOOD) {
            return err;
         }
      }
      else return ER_INDEX_OUT_OF_BOUNDS;
   }
   else {
      basePtr->segment = 0;
      basePtr->offset = 0;
      if ((err=OMFGetBytes( hFile, (U8 *)&basePtr->segment, sizeof(U16)))
          != GOOD) return err;
   }
   return GOOD;
} /* GetBase */

/******************************************************************************
**
**  GetIndex
**
******************************************************************************/
RETCODE GetIndex(HANDLE hFile, U16 *index) {
   RETCODE err;

   *index = 0;
   if ((err = OMFGetBytes(hFile, (U8 *)index, sizeof(U8))) != GOOD)
      return(err);
   //Hera 6/16/97 for extend Omf
   if (ExtOmf == 1) {
      if (*index == 0) {
	 if((err = OMFGetBytes(hFile, (U8 *)index,sizeof(U8))) != GOOD)
	    return(err);
      }
      else {
	 *((U8*)index + 1) = *(U8*)index;
	 if((err=OMFGetBytes(hFile, (U8 *)index,sizeof(U8))) != GOOD)
	    return(err);
      }
   }
   else {
      if (LARGE_INDEX(*index)) {    /* Two-byte index? */
         *((U8*)index + 1) = *(U8*)index & 0x7F;
         if ((err=OMFGetBytes(hFile, (U8 *)index, sizeof(U8))) != GOOD)
            return(err);
      }
   }
   return(GOOD);
} /* GetIndex */

/************************* LOCAL FUNCTIONS ***********************************/

/******************************************************************************
**
**  GetBaseType - examine the class name to decide the base type
**
******************************************************************************/
PRIVATE BASE_TYPE GetBaseType(LPSTR className) {
   if (className) {
      if (((*BaseTypeNames[0] != '\0') &&
          (strstr((CHAR *)className, BaseTypeNames[0]) != NULL)) ||
          (strstr((CHAR *)className, "CODE") != NULL))
         return(BASE_CODE);
   }
   return(BASE_DATA);
}  /* GetBaseType */

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