#include #include #include #include #include #include #include #include #include //#include #ifdef RLDOS #include "dosdefs.h" #else #include #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; iFilepres; 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); } }