|
|
#include <dos.h>
#include <stdio.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>
//#include <tchar.h>
#ifdef RLDOS
#include "dosdefs.h"
#else
#include <windows.h>
#include "windefs.h"
#endif
#include "restok.h"
#include "exe2res.h"
#include "newexe.h"
/* ----- Function prototypes ----- */
static void PrepareFiles( PSTR, PSTR, PSTR); static void ReadSegmentTable( void); static void ComputeResTableSize( void); static void ComputeStringOffsets( void); static void BuildResTable( void); static void SegsWrite( WORD); static DWORD RelocCopy( WORD); static void ResWrite( WORD); static void SetEXEHeaderFlags( void); static void RewriteTables( void); static void CopyCodeViewInfo( FILE *, FILE *); static void OutPutError( char *); static void ResTableBufferInit( WORD); static void ResTableBufferFree( void); static WORD GetAlign( DWORD, WORD); static LONG MoveFilePos( FILE *, WORD, WORD); static WORD RoundUp( LONG, WORD); static WORD AlignFilePos( FILE *, WORD, BOOL); static WORD ReadExeOldHeader( FILE *, LONG, LONG *) ; static WORD ReadExeNewHeader( FILE *, LONG, LONG, LONG *); static WORD ExtractExeResources( FILE *, FILE *, LONG , LONG); static void ExtractString( FILE *, FILE *, LONG); static WORD WriteResFromExe( FILE *, FILE *, LONG, RESNAMEINFO, RESTYPEINFO, WORD);
static TYPINFO *AddResType( CHAR *, WORD); static PSTR MyMakeStr( PSTR); static SHORT MyRead( FILE *, PSTR, WORD); static SHORT MyWrite( FILE *, PSTR, WORD); static LONG MySeek( FILE *, LONG, WORD); static void MyCopy( FILE *, FILE *, DWORD); static int ProcessBinFile( void); static PSTR RcAlloc( WORD); static void AddResToResFile( TYPINFO *, RESINFO *); static void AddDefaultTypes( void); static void GetOrdOrName( unsigned int *, unsigned char *);
/* ----- Version functions (added for 3.1) ----- */
static void RcPutWord( unsigned int); static int RcPutString( char *);
/* ----- Module variables ----- */
static struct exe_hdr OldExe; static struct new_exe NewExe; static struct new_seg *pSegTable; static PSTR pResTable; static PSTR pResNext; static FILE * fhInput; static FILE * fhOutput; static FILE * fhBin; static DWORD dwMaxFilePos; static DWORD dwNewExe; static WORD wPreloadOffset; static WORD wPreloadLength; static WORD wResTableOffset; static WORD wSegTableLen; static WORD wResTableLen; static BYTE zeros[ NUMZEROS] = ""; static DWORD dwExeEndFile; static WORD fMultipleDataSegs;
//.........................................................................
int ExtractResFromExe16A( CHAR *szInputExe, CHAR *szOutputRes, WORD wFilter) { QuitT( IDS_NO16RESWINYET, NULL, NULL);
#ifdef 0
WORD wResult = (WORD)-1; LONG lPosNewHdr; LONG lPosResourceTable; LONG lFileLen; FILE *fExeFile; FILE *fResFile; struct _stat ExeStats; /* initialize */ wResult = IDERR_SUCCESS;
/* open file for reading */ if ((fExeFile = FOPEN(szInputExe, "rb" )) == NULL ) { wResult = IDERR_OPENFAIL; }
if ((fResFile = FOPEN(szOutputRes, "wb" )) == NULL ) { wResult = IDERR_OPENFAIL; }
/* get file length */ if (wResult == IDERR_SUCCESS) { _stat(szInputExe , &ExeStats ); lFileLen = ExeStats.st_size; }
/* read old header, verify contents, and get positon of new header */ if (wResult == IDERR_SUCCESS) { wResult = ReadExeOldHeader( fExeFile, lFileLen, &lPosNewHdr ); }
/* read new header, verify contents, & get position of resource table */ if (wResult == IDERR_SUCCESS) wResult = ReadExeNewHeader( fExeFile, lFileLen, lPosNewHdr, &lPosResourceTable );
wResult = ExtractExeResources( fExeFile , fResFile, lPosResourceTable , lFileLen); return ( wResult); #endif // 0
}
//....................................................................
int BuildExeFromRes16A(
CHAR *pstrDest, CHAR *pstrRes, CHAR *pstrSource ) { QuitT( IDS_NO16WINRESYET, NULL, NULL);
#ifdef 0
SHORT nResTableDelta;
fSortSegments = TRUE; /* Get a memory block to use for MyCopy\(\) */
PrepareFiles(pstrSource, pstrDest, pstrRes);
ProcessBinFile();
/* Read the segment table */ ReadSegmentTable();
/* Compute the length of the resource table */ ComputeResTableSize();
/* Compute string offsets for non-ordinal type and resource names */ ComputeStringOffsets();
/* Build the resource table */ BuildResTable();
/* Now go back to the beginning */ MySeek(fhInput, 0L, 0);
/* Copy from input to output up to the segment table */ MyCopy(fhInput, fhOutput, dwNewExe + (DWORD)NewExe.ne_segtab);
/* Copy the segment table */ MyCopy(fhInput, fhOutput, (long)wSegTableLen);
/* Save a pointer to the start of the resource table */ wResTableOffset = (unsigned)(MySeek(fhOutput, 0L, 1) - dwNewExe);
/* Write our resource table out */ if (wResTableLen) { MyWrite(fhOutput, pResTable, wResTableLen); }
/* Now we\'re looking at the beginning of the resident name table */ MySeek(fhInput, (LONG)NewExe.ne_restab + dwNewExe, 0);
/* Copy all the other tables \(they must fall between the resident
* names table and the non-resident names table. */ MyCopy(fhInput, fhOutput, NewExe.ne_nrestab - (LONG)NewExe.ne_restab - dwNewExe);
/* Copy the nonresident name table as well */ MyCopy(fhInput, fhOutput, (LONG)NewExe.ne_cbnrestab);
/* Compute new pointers in new exe header */ NewExe.ne_rsrctab = wResTableOffset; nResTableDelta = wResTableOffset + wResTableLen - NewExe.ne_restab; NewExe.ne_restab += nResTableDelta; NewExe.ne_modtab += nResTableDelta; NewExe.ne_imptab += nResTableDelta; NewExe.ne_enttab += nResTableDelta; NewExe.ne_nrestab += nResTableDelta; #ifdef VERBOSE
/* Tell the user what we\'re doing */ if (fVerbose && fSortSegments) { fprintf(errfh, "Sorting preload segments and" " resources into fast-load section\n"); if (fBootModule) fprintf(errfh,"This is a boot module; the .DEF file" " is assumed to be correct!\n"); } #endif
/* If we\'re sorting segments, write preload segments and resources
* into a section separate from the load on call stuff. */ if (fSortSegments) { /* Save the start of the preload section */ wPreloadOffset = AlignFilePos(fhOutput, NewExe.ne_align, TRUE);
/* Write PRELOAD segments and resources */ SegsWrite(DO_PRELOAD); ResWrite(DO_PRELOAD);
/* Compute the properly aligned length of the preload section */ wPreloadLength = AlignFilePos(fhOutput, NewExe.ne_align, TRUE) - wPreloadOffset;
/* Now do the LOADONCALL segs and resources */ SegsWrite(DO_LOADONCALL); ResWrite(DO_LOADONCALL); }
/* If we\'re not sorting segments, just write them into a common block */ else { /* Write the segs and resources */ SegsWrite(DO_PRELOAD | DO_LOADONCALL); ResWrite(DO_PRELOAD | DO_LOADONCALL); }
#ifdef SETEXEFLAGS
/* Set flags and other values in the EXE header */ SetEXEHeaderFlags(); #endif
/* Rewrite the new exe header, segment table and resource table */ RewriteTables(); ResTableBufferFree();
/* Handle CodeView info */ CopyCodeViewInfo(fhInput, fhOutput);
/* Seek to end of output file and issue truncating write */ MySeek(fhOutput, 0L, 2); MyWrite(fhOutput, zeros, 0); fclose(fhInput); fclose(fhOutput); fclose(fhBin); FreePTypInfo(pTypInfo); pTypInfo=NULL; MyFree(pSegTable); pSegTable=NULL; return TRUE; #endif // 0
}
/*
* * ReadExeOldHeader\( fExeFile, lFileLen, plPosNewHdr \) : WORD; * * fExeFile file handle of .exe file being read * lFileLen length of file * plPosNewHdr pointer to file position of new header * * This function reads the old header from an executable file, checks to be * sure that it is a valid header, and saves the position of the file\'s * new header. * * This function returns IDERR_SUCCESS if there are no errors, or a non-zero * error code if there are. * */
static WORD ReadExeOldHeader(
FILE *fExeFile, LONG lFileLen, LONG *plPosNewHdr ) { LONG lPos; WORD cb; EXEHDR ehOldHeader; WORD wResult;
/* initialize */ wResult = IDERR_SUCCESS;
lPos = fseek( fExeFile, 0L, SEEK_SET );
if (lPos != 0L) wResult = IDERR_READFAIL;
if (wResult == IDERR_SUCCESS) { cb = fread( (void *) &ehOldHeader, sizeof( EXEHDR) , 1, fExeFile );
if (cb != 1 ) { wResult = IDERR_READFAIL; } else if (ehOldHeader.ehSignature != OLDEXESIGNATURE) { wResult = IDERR_FILETYPEBAD; } else if (ehOldHeader.ehPosNewHdr < sizeof(EXEHDR)) { wResult = IDERR_EXETYPEBAD; } else if ( ehOldHeader.ehPosNewHdr > (LONG)(lFileLen - sizeof(NEWHDR)) ) { wResult = IDERR_EXETYPEBAD; } else { *plPosNewHdr = ehOldHeader.ehPosNewHdr; } }
return wResult; }
/*
* * ReadExeNewHeader\( fExeFile, lFileLen, lPosNewHdr, plPosResourceTable \) : WORD; * * fExeFile file handle of .exe file being read * lFileLen length of file * lPosNewHdr file position of new header * plPosResourceTable pointer to file position of resource table * * This function reads the new header from an executable file, checks to be * sure that it is a valid header, and saves the position of the file\'s * resource table. * * This function returns IDERR_SUCCESS if there are no errors, or a non-zero * error code if there are. * */
static WORD ReadExeNewHeader(
FILE *fExeFile, LONG lFileLen, long lPosNewHdr, LONG *plPosResourceTable ) { WORD wResult; WORD cb; LONG lPos; NEWHDR nhNewHeader;
/* initialize */ wResult = IDERR_SUCCESS;
fseek( fExeFile, lPosNewHdr, SEEK_SET ); lPos = ftell( fExeFile );
if (lPos == (long) -1 || lPos > lFileLen || lPos != lPosNewHdr) { wResult = IDERR_READFAIL; } else { cb = fread( ( void *)&nhNewHeader, sizeof(nhNewHeader) , 1, fExeFile );
if (cb != 1 ) { wResult = IDERR_READFAIL; } else if (nhNewHeader.nhSignature != NEWEXESIGNATURE) { wResult = IDERR_FILETYPEBAD; } else if (nhNewHeader.nhExeType != WINDOWSEXE) { wResult = IDERR_EXETYPEBAD; } else if (nhNewHeader.nhExpVer < 0x0300) { wResult = IDERR_WINVERSIONBAD; } else if (nhNewHeader.nhoffResourceTable == 0) { wResult = IDERR_RESTABLEBAD; } else { *plPosResourceTable = lPosNewHdr + nhNewHeader.nhoffResourceTable; } }
return wResult; }
/*
* * ReadExeTable\( fExeFile , lPosResourcTable \) : WORD; * * fExeFile file handle of .exe file being read * * This function reads through the entries in an .exe file\'s resource table, * identifies any icons in that table, and saves the file offsets of the data * for those icons. This function expects the initial file position to point * to the first entry in the resource table. * * This function returns IDERR_SUCCESS if there are no errors, or a non-zero * error code if there are. * */
static WORD ExtractExeResources(
FILE *fExeFile, FILE *fResFile, LONG lPosResourceTable, LONG lFileLen ) { BOOL fLoop; WORD wResult; WORD cb; LONG lPos; WORD wShiftCount; wResult = IDERR_SUCCESS;
// posistion file pointer at resource table
fseek( fExeFile, lPosResourceTable, SEEK_SET ); lPos = ftell(fExeFile);
if (lPos == (LONG) -1 || lPos > lFileLen || lPos != lPosResourceTable) { return IDERR_READFAIL ; }
if (wResult == IDERR_SUCCESS) { cb = fread( (void *) &wShiftCount, sizeof(wShiftCount), 1 , fExeFile ); }
if (cb != 1 ) { return IDERR_READFAIL; }
if (wShiftCount > 16) { return IDERR_RESTABLEBAD; }
/* initialize */ wResult = IDERR_SUCCESS; fLoop = TRUE;
/* loop through entries in resource table */ while (fLoop == TRUE) { WORD cb; WORD iFile; RESTYPEINFO rt;
/* read RESTYPEINFO */ cb = fread( (void *)&rt, sizeof(rt), 1, fExeFile );
if (cb != 1 ) { wResult = IDERR_READFAIL; break; }
if ( rt.rtType == 0 ) break;
// now get all the resource of this type
for ( iFile = 0; iFile<rt.rtCount && wResult==IDERR_SUCCESS; iFile++ ) { RESNAMEINFO rn;
cb = fread( (void *) &rn, sizeof(rn) , 1 , fExeFile );
if (cb != 1 ) { wResult = IDERR_READFAIL; }
WriteResFromExe( fExeFile, fResFile,lPos, rn, rt, wShiftCount );
} fLoop = (rt.rtType != 0) && (wResult == IDERR_SUCCESS); } FCLOSE(fExeFile); FCLOSE(fResFile); return wResult; }
//.................................................................
static WORD WriteResFromExe(
FILE *fExeFile, FILE *fResFile, LONG lPos, RESNAMEINFO ResNameInfo, RESTYPEINFO ResTypeInfo, WORD wShiftCount ) { LONG lCurPos; LONG lResPos; LONG wLength; LONG wTmpLength;
wLength = (LONG) ResNameInfo.rnLength << wShiftCount;
lCurPos = ftell( fExeFile ); // position file pointer at resouce location
lResPos = (LONG) ResNameInfo.rnOffset << wShiftCount; fseek( fExeFile, lResPos, SEEK_SET );
if ( ResTypeInfo.rtType & 0x8000) { PutByte( fResFile, (BYTE) 0xff, NULL ); PutWord( fResFile, (WORD)(ResTypeInfo.rtType & 0x7FFF), NULL); } else { ExtractString(fExeFile,fResFile,lPos+ResTypeInfo.rtType); }
if ( ResNameInfo.rnID & 0x8000 ) { PutByte( fResFile, (BYTE) 0xff, NULL ); PutWord( fResFile, (WORD)(ResNameInfo.rnID & 0x7fFF), NULL); } else { ExtractString(fExeFile,fResFile,lPos+ResNameInfo.rnID); }
PutWord( fResFile, ResNameInfo.rnFlags , NULL ); PutdWord( fResFile, (LONG) wLength, NULL ); wTmpLength = wLength; // now write the actual data
fseek( fExeFile, lResPos, SEEK_SET ); ReadInRes( fExeFile, fResFile, &wTmpLength ); fseek( fExeFile, lCurPos, SEEK_SET );
return 0; }
//..................................................................
static void ExtractString( FILE *fExeFile, FILE *fResFile, LONG lPos) { BYTE n,b;
fseek(fExeFile, lPos, SEEK_SET);
n=GetByte(fExeFile, NULL); for (;n--; ) { b=GetByte(fExeFile, NULL); PutByte(fResFile, (CHAR) b, NULL); } PutByte(fResFile, (CHAR) 0, NULL); }
// Modifications for RLTOOLS
// Currently we dont support any dynamic flags
BOOL fBootModule = FALSE; BOOL fSortSegments = TRUE;
TYPINFO *pTypInfo = NULL;
static void FreePTypInfo( TYPINFO *pTypInfo) { RESINFO * pRes, *pRTemp; TYPINFO * pTItemp;
while (pTypInfo) { pRes = pTypInfo->pres; while (pRes) { pRTemp = pRes->next; MyFree(pRes->name); MyFree(pRes); pRes = pRTemp; } pTItemp = pTypInfo->next; MyFree(pTypInfo->type); MyFree(pTypInfo); pTypInfo = pTItemp; } }
/* ----- Helper functions ----- */
/* PrepareFiles
* Prepares the EXE files \(new and old\) for writing and verifies * that all is well. * Exits on error, returns if processing should continue. */
static void PrepareFiles(
PSTR pstrSource, PSTR pstrDest, PSTR pstrRes ) { /* Open the .EXE file the linker gave us */ if ( (fhInput = FOPEN(pstrSource, "rb" )) == NULL ) { OutPutError("Unable to open Exe Source File"); }
if ((fhBin = FOPEN(pstrRes, "rb")) == NULL ) { OutPutError("Unable to open Resource File"); }
/* Read the old format EXE header */ MyRead(fhInput, (PSTR)&OldExe, sizeof (OldExe));
/* Make sure its really an EXE file */ if (OldExe.e_magic != EMAGIC) { OutPutError("Invalid .EXE file" ); }
/* Make sure theres a new EXE header floating around somewhere */ if (!(dwNewExe = OldExe.e_lfanew)) { OutPutError("Not a Microsoft Windows format .EXE file"); }
/* Go find the new .EXE header */ MySeek(fhInput, dwNewExe, 0); MyRead(fhInput, (PSTR)&NewExe, sizeof (NewExe));
/* Check version numbers */ if (NewExe.ne_ver < 4) { OutPutError("File not created by LINK"); }
/* Were there linker errors? */ if (NewExe.ne_flags & NEIERR) { OutPutError("Errors occurred when linking file."); }
/* Make sure that this program\'s EXETYPE is WINDOWS \(2\) not OS/2 \(1\) */ if (NewExe.ne_exetyp != 2) OutPutError("The EXETYPE of the program is not WINDOWS.\n" "(Make sure the .DEF file is correct."); #ifdef VERBOSE
if (fVerbose) { fprintf(errfh, "\n"); } #endif
/* Open the all new executable file */ if ( (fhOutput = FOPEN( pstrDest, "wb")) == NULL ) { OutPutError("Unable to create destination"); } }
/* ReadSegmentTable
* Reads the segment table from the file. */
static void ReadSegmentTable( void) { struct new_seg* pSeg; WORD i;
MySeek(fhInput, (LONG)NewExe.ne_segtab + dwNewExe, 0); if ((wSegTableLen = NewExe.ne_cseg * sizeof (struct new_seg)) > 0) { pSegTable = (struct new_seg *)RcAlloc (wSegTableLen); MyRead(fhInput, (PSTR)pSegTable, wSegTableLen);
/* See if we have more than one data segment */ fMultipleDataSegs = 0; for (pSeg = pSegTable, i = NewExe.ne_cseg ; i ; --i, ++pSeg) { if ((pSeg->ns_flags & NSTYPE) == NSDATA) { ++fMultipleDataSegs; } } if (fMultipleDataSegs) { --fMultipleDataSegs; } } else { pSegTable = NULL; } }
/* ComputeResTableSize
* Computes the size of the resource table by enumerating all the * resources currently in the linked lists. */
static void ComputeResTableSize( void) { TYPINFO **pPrev; TYPINFO *pType; RESINFO *pRes;
/* Start with the minimum overhead size of the resource table. This
* is the resource alignment count and the zero WORD terminating the * table. This is necessary so that we put the correct file offset * in for the string offsets to named resources. */ wResTableLen = RESTABLEHEADER;
/* Loop over type table, computing the fixed length of the
* resource table, removing unused type entries. */ pPrev = &pTypInfo; dwMaxFilePos = 0L; while (pType = *pPrev) { if (pRes = pType->pres) { /* Size of type entry */ wResTableLen += sizeof (struct rsrc_typeinfo); while (pRes) { /* Size of resource entry */ wResTableLen += sizeof (struct rsrc_nameinfo); if (pType->next || pRes->next) { dwMaxFilePos += pRes->size; } pRes = pRes->next; } pPrev = &pType->next; } else { *pPrev = pType->next; MyFree(pType->type); MyFree(pType); } } }
/* ComputeStringOffsets
* Computes offsets to strings from named resource and types. */
static void ComputeStringOffsets( void) { TYPINFO *pType; RESINFO *pRes;
/* Loop over type table, computing string offsets for non-ordinal
* type and resource names. */ pType = pTypInfo; while (pType) { pRes = pType->pres;
/* Is there an ordinal? */ if (pType->typeord) { /* Mark the ordinal */ pType->typeord |= RSORDID;
/* Flush the string name */ MyFree(pType->type); pType->type = NULL; } else if (pType->type) { /* is there a type string? */ /* Yes, compute location of the type string */ pType->typeord = wResTableLen; wResTableLen += strlen(pType->type) + 1; }
while (pRes) { /* Is there an ordinal? */ if (pRes->nameord) { /* Mark the ordinal */ pRes->nameord |= RSORDID;
/* Flush the string name */ MyFree(pRes->name); pRes->name = NULL; }
/* Is there a resource name? */ else if (pRes->name) { /* Yes, compute location of the resource string */ pRes->nameord = wResTableLen; wResTableLen += strlen(pRes->name) + 1; } pRes = pRes->next; } pType = pType->next; } }
/* BuildResTable
* Builds the local memory image of the resource table. */
static void BuildResTable( void) { TYPINFO *pType; RESINFO *pRes;
/* Check to see if we have any resources. If not, just omit the table */ if (wResTableLen > RESTABLEHEADER) {
/* Set up the temporary resource table buffer */ ResTableBufferInit(wResTableLen);
/* Alignment shift count
* \(we default here to the segment alignment count\) */ RcPutWord(NewExe.ne_align);
pType = pTypInfo; while (pType) { /* output the type and number of resources */ RcPutWord(pType->typeord); /* DW type id */ RcPutWord(pType->nres); /* DW #resources for this type */ RcPutWord(0); /* DD type procedure */ RcPutWord(0);
/* output flags and space for the file offset for each resource */ pRes = pType->pres; while (pRes) { pRes->poffset = (WORD *)pResNext; RcPutWord(0); /* DW file offset */ RcPutWord(0); /* DW resource size */ pRes->flags |= NSDPL; RcPutWord(pRes->flags ); /* DW flags */ RcPutWord(pRes->nameord ); /* DW name id */ RcPutWord(0); /* DW handle */ RcPutWord(0); /* DW usage or minalloc */ pRes = pRes->next; } pType = pType->next; }
/* Null entry terminates table */ RcPutWord(0);
/* Output type and name strings for non-ordinal resource types
* and names */ pType = pTypInfo; while (pType) { /* Dump out any strings for this type */ if (pType->type && !(pType->typeord & RSORDID)) { RcPutString(pType->type); }
pRes = pType->pres; while (pRes) { if (pRes->name && !(pRes->nameord & RSORDID)) RcPutString(pRes->name);
pRes = pRes->next; }
pType = pType->next; } } else wResTableLen = 0; }
/* SegsWrite
* Copies segments to the file. This routine will do only preload, * only the load on call, or both types of segments depending on * the flags. */
static void SegsWrite( WORD wFlags) { WORD wExtraPadding; WORD i; static struct new_seg *pSeg; DWORD dwSegSize; DWORD dwWriteSize; WORD wTemp; WORD wcbDebug;
/* We only need extra padding in the preload section.
* Note that when wFlags == DO_PRELOAD | DO_LOADONCALL, we DON\'T * need extra padding because this is NOT a preload section. * \(hence the \'==\' instead of an \'&\'\) */ wExtraPadding = (wFlags == DO_PRELOAD);
/* Copy segment data for each segment, fixed and preload only */ for (i = 1, pSeg = pSegTable; i <= NewExe.ne_cseg; i++, pSeg++) { /* If there\'s no data in segment, skip it here */ if (!pSeg->ns_sector) { continue; }
/* Force some segments to be preload if doing preload resources */ if ((wFlags & DO_PRELOAD) && !fBootModule) { char *reason = NULL;
/* Check various conditions that would force preloading */ if (i == (unsigned)(NewExe.ne_csip >> 16)) { reason = "Entry point"; } if (!(pSeg->ns_flags & NSMOVE)) { reason = "Fixed"; } if (pSeg->ns_flags & NSDATA) { reason = "Data"; } if (!(pSeg->ns_flags & NSDISCARD)) { reason = "Non-discardable"; }
/* If this segment must be preload and the segment is not already
* marked as such, warn the user and set it. */ if (reason && !(pSeg->ns_flags & NSPRELOAD)) { #ifdef VERBOSE
fprintf(errfh, "RC: warning RW4002: %s segment %d set to PRELOAD\n", reason, i); #endif
pSeg->ns_flags |= NSPRELOAD; } }
/* Skip this segment if it doesn\'t match the current mode */ wTemp = pSeg->ns_flags & NSPRELOAD ? DO_PRELOAD : DO_LOADONCALL; if (!(wTemp & wFlags)) { continue; }
/* Get the true segment length. A zero length implies 64K */ if (pSeg->ns_cbseg) { dwSegSize = pSeg->ns_cbseg; } else { dwSegSize = 0x10000L; }
#ifdef VERBOSE
if (fVerbose) fprintf(errfh, "Copying segment %d (%lu bytes)\n", i, dwSegSize); #endif
/* Align the segment correctly and pad the file to match */ MoveFilePos(fhInput, pSeg->ns_sector, NewExe.ne_align); pSeg->ns_sector = AlignFilePos(fhOutput, NewExe.ne_align, wExtraPadding);
/* Copy the segment */ MyCopy(fhInput, fhOutput, dwSegSize);
/* Pad out all segments in the preload area to their minimum
* memory allocation size so that KERNEL doesn\'t have to realloc * the segment. */ if (wExtraPadding && pSeg->ns_cbseg != pSeg->ns_minalloc) { /* A minalloc size of zero implies 64K */ if (!pSeg->ns_minalloc) { dwWriteSize = 0x10000L - pSeg->ns_cbseg; } else { dwWriteSize = pSeg->ns_minalloc - pSeg->ns_cbseg; }
/* Add in to total size of segment */ dwSegSize += dwWriteSize;
/* Set the segment table size to this new size */ pSeg->ns_cbseg = pSeg->ns_minalloc;
/* Pad the file */ while (dwWriteSize) { dwWriteSize -= MyWrite(fhOutput, zeros, (WORD)(dwWriteSize > (DWORD) NUMZEROS ? NUMZEROS : dwWriteSize)); } }
/* Copy the relocation information */ if (pSeg->ns_flags & NSRELOC) { /* Copy the relocation stuff */ dwSegSize += RelocCopy(i);
/* Segment + padding + relocations can\'t be >64K for preload
* segments. */ if (fSortSegments && (pSeg->ns_flags & NSPRELOAD) && dwSegSize > 65536L) { #ifdef VERBOSE
fprintf(errfh, "RC : fatal error RW1031: Segment %d and its\n" " relocation information is too large for load\n" " optimization. Make the segment LOADONCALL or\n" " rerun RC using the -K switch if the segment must\n" " be preloaded.\n", i); #endif
} }
/* Copy any per-segment debug information */ if (pSeg->ns_flags & NSDEBUG) { MyRead(fhInput, (PSTR)&wcbDebug, sizeof (WORD)); MyWrite(fhOutput, (PSTR)&wcbDebug, sizeof (WORD)); MyCopy(fhInput, fhOutput, (LONG)wcbDebug); } } }
/* RelocCopy
* Copys all the relocation records for a given segment. * Also checks for invalid fixups. */
static DWORD RelocCopy( WORD wSegNum) { WORD wNumReloc; struct new_rlc RelocRec; WORD i; BYTE byFixupType; BYTE byFixupFlags; WORD wDGROUP;
/* Get the number of relocations */ MyRead(fhInput, (PSTR)&wNumReloc, sizeof (WORD)); MyWrite(fhOutput, (PSTR)&wNumReloc, sizeof (WORD));
/* Get the automatic data segment */ wDGROUP = NewExe.ne_autodata;
/* Copy and verify all relocations */ for (i = 0 ; i < wNumReloc ; ++i) { /* Copy the record */ MyRead(fhInput, (PSTR)&RelocRec, sizeof (RelocRec)); MyWrite(fhOutput, (PSTR)&RelocRec, sizeof (RelocRec));
/* Validate it only if necessary */ if ((NewExe.ne_flags & (NENOTP | NESOLO)) || wSegNum == wDGROUP || fMultipleDataSegs) { continue; }
/* Bad fixups are fixups to DGROUP in code segments in apps
* that can be multi-instanced. Since we can\'t fix up locations * that are different from instance to instance in shared code * segments, we have to warn the user. We only warn because this * may be allowable if the app only allows a single instance of * itself to run. */ byFixupType = (BYTE) (RelocRec.nr_stype & NRSTYP); byFixupFlags = (BYTE) (RelocRec.nr_flags & NRRTYP); #ifdef VERBOSE
if ((byFixupType == NRSSEG || byFixupType == NRSOFF) && byFixupFlags == NRRINT && RelocRec.nr_union.nr_intref.nr_segno == wDGROUP)
fprintf(errfh, "RC : warning RW4005: Segment %d (offset %04X) contains a\n" " relocation record pointing to the automatic\n" " data segment. This will cause the program to crash\n" " if the instruction being fixed up is executed in a\n" " multi-instance application. If this fixup is\n" " necessary, the program should be restricted to run\n" " only a single instance.\n", wSegNum, RelocRec.nr_soff); #endif
}
return wNumReloc * sizeof (struct new_rlc); }
/* ResWrite
* Copies resources to the file. This routine will do only the preload, * only the load on call, or both types of resources depending on the * flags. */
static void ResWrite( WORD wFlags) { WORD wExtraPadding; WORD wTemp; WORD wResAlign; TYPINFO *pType; RESINFO *pRes;
/* If we have no resource table, just ignore this */ if (!wResTableLen) { return; }
/* We only need extra padding in the preload section.
* Note that when wFlags == DO_PRELOAD | DO_LOADONCALL, we DON\'T * need extra padding because this is NOT a preload section. * \(hence the \'==\' instead of an \'&\'\) */ wExtraPadding = (wFlags == DO_PRELOAD);
/* Compute resource alignment. Note that the alignment is not the
* same as the segment alignment ONLY IF there is no segment sorting * and some resources cannot be reached with the current segment * align count. */ wResAlign = NewExe.ne_align;
if (!fSortSegments) { /* Compute the needed alignment */ dwMaxFilePos += MySeek(fhOutput, 0L, 2); wResAlign = GetAlign(dwMaxFilePos, NewExe.ne_align);
#ifdef VERBOSE
if (fVerbose) fprintf(errfh, "Resources will be aligned on %d byte boundaries\n", 1 << wResAlign); #endif
/* Point back to the start of the local memory resource table */ pResNext = pResTable; RcPutWord(wResAlign); }
/* Output contents associated with each resource */ for (pType = pTypInfo ; pType; pType = pType->next) { for (pRes = pType->pres ; pRes ; pRes = pRes->next) { /* Make sure this is the right kind of resource */ wTemp = pRes->flags & RNPRELOAD ? DO_PRELOAD : DO_LOADONCALL; if (!(wTemp & wFlags)) { continue; }
/* Give some info to the user */ #ifdef VERBOSE
if (fVerbose) { fprintf(errfh, "Writing resource "); if (pRes->name && !(pRes->nameord & RSORDID)) { fprintf(errfh, "%s", pRes->name); } else { fprintf(errfh, "%d", pRes->nameord & 0x7FFF); }
if (pType->type && !(pType->typeord & RSORDID)) { fprintf(errfh, ".%s", pType->type); } else { fprintf(errfh, ".%d", pType->typeord & 0x7FFF); }
fprintf(errfh, " (%lu bytes)\n", pRes->size); fflush(errfh); } #endif
/* Copy the resource from the RES file to the EXE file */ MySeek(fhBin, (long)pRes->BinOffset, 0); *(pRes->poffset)++ = AlignFilePos(fhOutput, wResAlign, wExtraPadding); *(pRes->poffset) = RoundUp(pRes->size, wResAlign); MyCopy(fhBin, fhOutput, pRes->size); } }
/* Compute the end of the EXE file thus far for the CV info */ dwExeEndFile = AlignFilePos(fhOutput, wResAlign, wExtraPadding); }
#ifdef SETEXEFLAGS
/* SetEXEHeaderFlags
* Sets necessary flags and values in the EXE header. */
static void SetEXEHeaderFlags( void) { /* Tell loader we initialized previously unused fields */ if (NewExe.ne_ver == 4) { NewExe.ne_rev = 2; }
/* Set command line values into the header */ NewExe.ne_expver = expWinVer; NewExe.ne_swaparea = swapArea;
/* Set the preload section values */ if (fSortSegments) { /* Set the new fastload section values */ NewExe.ne_gangstart = wPreloadOffset; NewExe.ne_ganglength = wPreloadLength; #ifdef VERBOSE
if (fVerbose) fprintf(errfh, "Fastload area is %ld bytes at offset 0x%lX.\n", (LONG)wPreloadLength << NewExe.ne_align, (LONG)wPreloadOffset << NewExe.ne_align); } #endif
/* Clear all the flags */ NewExe.ne_flags &= ~(NELIM32|NEMULTINST|NEEMSLIB|NEPRIVLIB|NEPRELOAD);
/* Set appropriate flags */ if (fLim32) { NewExe.ne_flags |= NELIM32; } if (fMultInst) { NewExe.ne_flags |= NEMULTINST; } if (fEmsLibrary) { NewExe.ne_flags |= NEEMSLIB; } if (fPrivateLibrary) { NewExe.ne_flags |= NEPRIVLIB; } if (fProtOnly) { NewExe.ne_flags |= NEPROT; }
if (fSortSegments && wPreloadLength) { NewExe.ne_flagsother |= NEPRELOAD; }
NewExe.ne_flags |= NEWINAPI; } #endif
/* RewriteTables
* Rewrites the EXE header and the resource and segment tables * with their newly-updated information. */
static void RewriteTables( void) { /* Write the new EXE header */ MySeek(fhOutput, (LONG)dwNewExe, 0); MyWrite(fhOutput, (PSTR)&NewExe, sizeof (NewExe));
/* Seek to the start of the segment table */ MySeek(fhOutput, dwNewExe + (LONG)NewExe.ne_segtab, 0); MyWrite(fhOutput, (PSTR)pSegTable, wSegTableLen);
/* Seek to and write the resource table */ if (wResTableLen) { MySeek(fhOutput, dwNewExe + (LONG)NewExe.ne_rsrctab, 0); MyWrite(fhOutput, pResTable, wResTableLen); } }
/* CopyCodeViewInfo
* Copies CodeView info to the new EXE file and relocates it if * necessary. This routine is designed to work with the * DNRB-style info as well as NBxx info where x is a digit. */
static void CopyCodeViewInfo( FILE *fhInput, FILE *fhOutput) { unsigned long dwcb; unsigned int i; CVINFO cvinfo; CVSECTBL cvsectbl;
/* See if old format \(DNRB\) symbols present at end of input file
* If they are, relocate the table to the new file position and * fix up the file-position dependent offsets. */ dwcb = MySeek( fhInput, -(signed long)sizeof (CVINFO), 2); MyRead( fhInput, (char *)&cvinfo, sizeof (cvinfo));
if (*(unsigned long *)cvinfo.signature == CV_OLD_SIG) { dwcb -= cvinfo.secTblOffset; MySeek( fhInput, cvinfo.secTblOffset, 0); MyRead( fhInput, (char *)&cvsectbl, sizeof (cvsectbl)); dwcb -= sizeof (cvsectbl);
for (i = 0 ; i < 5 ; ++i) { cvsectbl.secOffset[i] -= cvinfo.secTblOffset; }
cvinfo.secTblOffset = dwExeEndFile;
for (i = 0 ; i < 5 ; ++i) { cvsectbl.secOffset[i] += cvinfo.secTblOffset; }
MySeek( fhOutput, cvinfo.secTblOffset, 0); MyWrite( fhOutput, (char *)&cvsectbl, sizeof (cvsectbl)); MyCopy( fhInput, fhOutput, dwcb); MyWrite( fhOutput, (char *)&cvinfo, sizeof (cvinfo)); }
/* Check for new format \(NBxx\) symbols. Since these symbols are
* file-position independent, just copy them over; no need to * fix them up as with the old format symbols. */ else if (*(unsigned short int *)cvinfo.signature == CV_SIGNATURE && isdigit(cvinfo.signature[2]) && isdigit(cvinfo.signature[3])) { MySeek( fhOutput, 0L, 2); MySeek( fhInput, -cvinfo.secTblOffset, 2); MyCopy( fhInput, fhOutput, cvinfo.secTblOffset); } }
/* OutPutError
* Outputs a fatal error message and exits. */
static void OutPutError( char *szMessage) { QuitA( 0, szMessage, NULL); }
/* ResTableBufferInit
* Creates the resource table buffer and points global pointers * to it. This table is written to so that we can modifiy it * before writing it out to the EXE file. */
static void ResTableBufferInit( WORD wLen) { /* Allocate local storage for resource table */ pResTable = RcAlloc (wLen);
/* Point to the start of the table for the PutXXXX\(\) */ pResNext = pResTable; }
/* ResTableBufferFree
* Frees the temporary storage for resource table */
static void ResTableBufferFree( void) { /* Nuke the table */ MyFree(pResTable); }
/* GetAlign
* Computes the alignment value needed for the given maximum file * position passed in. This is done by computing the number of * bits to be shifted left in order to represent the maximum * file position in 16 bits. */
static WORD GetAlign( DWORD dwMaxpos, WORD wAlign) { DWORD dwMask; WORD i;
/* Compute the initial mask based on the input align value */ dwMask = 0xFFFFL; for (i = 0; i < wAlign ; ++i) { dwMask <<= 1; dwMask |= 1; }
/* See if we need to increase the default mask to reach the maximum
* file position. */ while (dwMaxpos > dwMask) { dwMask <<= 1; dwMask |= 1; ++wAlign; }
/* Return the new alignment */ return wAlign; }
/* MoveFilePos
* Moves the file pointer to the position indicated by wPos, using the * align shift count wAlign. This converts the WORD value wPos * into a LONG value by shifting left wAlign bits. */
static LONG MoveFilePos( FILE *fh, WORD wPos, WORD wAlign) { return MySeek(fh, ((LONG)wPos) << wAlign, 0); }
/* RoundUp
* Computes the value that should go into a 16 bit entry in an EXE * table by rounding up to the next boundary determined by the * passed in alignment value. */
static WORD RoundUp( LONG lValue, WORD wAlign) { LONG lMask;
/* Get all the default mask of all ones except in the bits below the
* alignment value. */ lMask = -1L; lMask <<= wAlign;
/* Now round up using this mask */ lValue += ~lMask; lValue &= lMask;
/* Return as a 16 bit value */ return ((WORD) (lValue >> (LONG) wAlign)); }
/* AlignFilePos
* Computes a correctly aligned file position based on the current * alignment. */
static WORD AlignFilePos( FILE *fh, WORD wAlign, BOOL fPreload) { LONG lCurPos; LONG lNewPos; LONG lMask; WORD nbytes; WORD wNewAlign;
/* If we\'re in the preload section, we have tougher alignment
* restrictions: We have to be at least 32-byte aligned and have * at least 32 bytes between objects for arena headers. It turns * out that this feature is not really used in KERNEL but could be * implemented someday. */ if (fPreload && wAlign < PRELOAD_ALIGN) { wNewAlign = PRELOAD_ALIGN; } else { wNewAlign = wAlign; }
/* Get the current file position */ lCurPos = MySeek(fh, 0L, 1);
/* Compute the new position by rounding up to the align value */ lMask = -1L; lMask <<= wNewAlign; lNewPos = lCurPos + ~lMask; lNewPos &= lMask;
/* We have to have at least 32 bytes between objects in the preload
* section. */ if (fPreload) { while (lNewPos - lCurPos < PRELOAD_MINPADDING) { lNewPos += 1 << wNewAlign; } }
/* Check to see if it\'s representable in 16 bits */ if (lNewPos >= (0x10000L << wAlign)) { OutPutError(".EXE file too large; relink with higher /ALIGN value"); }
/* Write stuff out to file until new position reached */ if (lNewPos > lCurPos) { /* Compute number of bytes to write out and write them out */ nbytes = (WORD) (lNewPos - lCurPos); while (nbytes) { nbytes -= MyWrite( fh, zeros, (WORD)(nbytes > NUMZEROS ? NUMZEROS : nbytes)); } }
/* Seek to and return this new position */ return (WORD)(MySeek(fh, lNewPos, (WORD) 0) >> (LONG) wAlign); }
/*--------------------------------------------------------------------------*/ /* */ /* AddResType\(\) - */ /* */ /*--------------------------------------------------------------------------*/
static TYPINFO *AddResType( CHAR *s, WORD n ) { TYPINFO *pType;
if (pType = pTypInfo) { while (TRUE) { /* search for resource type, return if already exists */ if ((s && !strcmp(s, pType->type)) || (!s && n && pType->typeord == n)) { return (pType); } else if (!pType->next) { break; } else { pType = pType->next; } }
/* if not in list, add space for it */ pType->next = (TYPINFO *) RcAlloc(sizeof(TYPINFO)); pType = pType->next; } else { /* allocate space for resource list */ pTypInfo = (TYPINFO *)RcAlloc (sizeof(TYPINFO)); pType = pTypInfo; }
/* fill allocated space with name and ordinal, and clear the resources
of this type */ pType->type = MyMakeStr(s); pType->typeord = n; pType->nres = 0; pType->pres = NULL; pType->next = NULL;
return (pType); }
/*--------------------------------------------------------------------------*/ /* */ /* GetOrdOrName\(\) - */ /* */ /*--------------------------------------------------------------------------*/
static void GetOrdOrName( unsigned int *pint, unsigned char *szstr) { unsigned char c1;
/* read the first character of the identifier */ MyRead(fhBin, &c1, sizeof(unsigned char));
/* if the first character is 0xff, the id is an ordinal, else a string */ if (c1 == 0xFF) { MyRead(fhBin, (PSTR)pint, sizeof (int)); } else { /* string */ *pint = 0; *szstr++ = c1; do { MyRead( fhBin, szstr, 1); } while (*szstr++ != 0); } }
/*--------------------------------------------------------------------------*/ /* */ /* AddDefaultTypes\(\) - */ /* */ /*--------------------------------------------------------------------------*/
static void AddDefaultTypes( void) { AddResType( "CURSOR", ID_RT_GROUP_CURSOR); AddResType( "ICON", ID_RT_GROUP_ICON); AddResType( "BITMAP", ID_RT_BITMAP); AddResType( "MENU", ID_RT_MENU); AddResType( "DIALOG", ID_RT_DIALOG); AddResType( "STRINGTABLE", ID_RT_STRING); AddResType( "FONTDIR", ID_RT_FONTDIR); AddResType( "FONT", ID_RT_FONT); AddResType( "ACCELERATORS", ID_RT_ACCELERATORS); AddResType( "RCDATA", ID_RT_RCDATA); AddResType( "VERSIONINFO", ID_RT_VERSION); }
/*--------------------------------------------------------------------------*/ /* */ /* ProcessBinFile\(\) - */ /* */ /*--------------------------------------------------------------------------*/
static int ProcessBinFile( void) { unsigned int ord; unsigned char tokstr[64]; RESINFO *pRes; TYPINFO *pType; long curloc; long eofloc; WORD wResType;
/* initialize for reading .RES file */ AddDefaultTypes(); eofloc = MySeek(fhBin, 0L, 2); /* get file size */ curloc = MySeek(fhBin, 0L, 0); /* go to beginning of file */
/* while there are more resources in the .RES file */ while (curloc < eofloc) {
#ifdef VERBOSE
if (fVerbose) { fprintf(errfh, "."); fflush(errfh); } #endif
/* find the resource type of the next resource */ GetOrdOrName(&ord, tokstr);
if (!ord) { pType = AddResType(tokstr, 0); } else { pType = AddResType(NULL, (WORD)ord); }
if (!pType) { break; }
/* Save the type number so we can see if we want to skip it later */ wResType = ord;
/* find the identifier \(name\) of the resource */ GetOrdOrName(&ord, tokstr); pRes = (RESINFO *)RcAlloc (sizeof(RESINFO)); if (!ord) { pRes->name = MyMakeStr(tokstr); } else { pRes->nameord = ord; }
/* read the flag bits */ MyRead(fhBin, (PSTR)&pRes->flags, sizeof(int));
/* Clear the old DISCARD bits. */ pRes->flags &= 0x1FFF;
/* find the size of the resource */ MyRead(fhBin, (PSTR)&pRes->size, sizeof(long));
/* save the position of the resource for when we add it to the .EXE */ pRes->BinOffset = (long)MySeek(fhBin, 0L, 1);
/* skip the resource to the next resource header */ curloc = MySeek(fhBin, (long)pRes->size, 1);
/* add the resource to the resource lists. We don\'t add name
* tables. They are an unnecessary 3.0 artifact. */ if (wResType != ID_RT_NAMETABLE) { AddResToResFile(pType, pRes); } else { MyFree(pRes->name); MyFree(pRes); } }
return 1; }
/*--------------------------------------------------------------------------*/ /* */ /* AddResToResFile\(pType, pRes\) */ /* */ /* Parameters: */ /* pType : Pointer to Res Type */ /* pRes : Pointer to resource */ /* */ /*--------------------------------------------------------------------------*/
static void AddResToResFile( TYPINFO *pType, RESINFO *pRes) { RESINFO *p;
p = pType->pres;
/* add resource to end of resource list for this type */ if (p) { while (p->next) { p = p->next; }
p->next = pRes; p->next->next = NULL; } else { pType->pres = pRes; pType->pres->next = NULL; } /* keep track of number of resources and types */ pType->nres++; }
/* MyMakeStr
* Makes a duplicate string from the string passed in. The new string * should be freed when it is no longer useful. */
static PSTR MyMakeStr( PSTR s) { PSTR s1;
if (s) { s1 = RcAlloc( (WORD)(strlen(s) + 1)); /* allocate buffer */ strcpy(s1, s); /* copy string */ } else { s1 = s; }
return s1; }
static SHORT MyRead( FILE *fh, PSTR p, WORD n) { size_t n1;
if ( (n1 = fread( p, 1, n, fh)) != n ) ; // quit\("RC : fatal error RW1021: I/O error reading file."\);
else return ( n1); }
/* MyWrite
* Replaces calls to write\(\) and does error checking. */
static SHORT MyWrite( FILE *fh, PSTR p, WORD n) { size_t n1;
if ( (n1 = fwrite( p, 1, n, fh)) != n ) ; // quit\("RC : fatal error RW1022: I/O error writing file."\);
else return ( n1); }
/* MySeek
* Replaces calls to lseek\(\) and does error checking */
static LONG MySeek( FILE *fh, LONG pos, WORD cmd) {
if ( (pos = fseek( fh, pos, cmd)) != 0 ) { OutPutError ("RC : fatal error RW1023: I/O error seeking in file"); } return ( pos); }
/* MyCopy
* Copies dwSize bytes from source to dest in fixed size chunks. */
static void MyCopy( FILE *srcfh, FILE *dstfh, DWORD dwSize) { WORD n; static char chCopyBuffer[ BUFSIZE];
while ( dwSize ) { n = MyRead( srcfh, chCopyBuffer, sizeof( chCopyBuffer)); MyWrite( dstfh, chCopyBuffer, n); dwSize -= n; } }
static void RcPutWord( unsigned int w) { *((WORD *)pResNext) = w; pResNext++; pResNext++; }
/* PutStringWord
* Writes a string to the static resource buffer pointed to by pResNext. * The string is stored in Pascal-format \(leading byte first\). * Returns the number of characters written. */
static int RcPutString( char *pstr) { int i;
/* Make sure we have a valid string */ if (!pstr || !(i = strlen(pstr))) { return 0; }
/* Write the length byte */ *pResNext++ = (char) i;
/* Write all the characters */ while (*pstr) { *pResNext++ = *pstr++; }
/* Return the length */ return (i + 1); }
static PSTR RcAlloc( WORD nbytes) { PSTR ps = NULL;
if ( ps = (PSTR)MyAlloc( nbytes)) { return ( ps); } }
|