|
|
/* generate wowit.h and wowit.c from wow.it
* * 20-Feb-1997 DaveHart created */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <windows.h>
VOID ErrorAbort(PSZ pszMsg); BYTE GetReturnOpcode(PSZ *ppsz); PSZ GetApiName(PSZ *ppsz); PSZ GetApi32Name(PSZ *ppsz, PSZ pszApi16); BYTE GetArgOpcode(PSZ *ppsz); PSZ GetOpcodeName(BYTE bInstr); PSZ GetLine(PSZ pszBuf, int cbBuf, FILE *fp); VOID ReadTypeNames(FILE *fIn, PSZ szTypesPrefix, PSZ *OpcodeNamesArray, int *pnOpcodeNames); PSZ DateTimeString(VOID);
#define IS_RET_OPCODE(b) (b & 0x80)
#define MAX_IT_INSTR 16
typedef struct tagITINSTR { int cbInstr; int offSwamp; BYTE Instr[MAX_IT_INSTR]; } ITINSTR;
#define MAX_INSTR_TABLE_SIZE 512
ITINSTR InstrTable[MAX_INSTR_TABLE_SIZE]; int iNextInstrSlot = 0;
typedef struct tagTHUNKTABLESLOT { PSZ pszAPI; PSZ pszAPI32; // if Win32 routine name doesn't match pszAPI
int iInstrSlot; int cbInstr; // how much of this slot we're using
} THUNKTABLESLOT;
#define MAX_THUNK_TABLE_SIZE 1024
THUNKTABLESLOT ThunkTable[MAX_THUNK_TABLE_SIZE]; int iNextThunkSlot = 0;
#define MAX_ARG_OPCODE_NAMES 32
PSZ ArgOpcodeNames[MAX_ARG_OPCODE_NAMES]; int nArgOpcodeNames = 0;
#define MAX_RET_OPCODE_NAMES 32
PSZ RetOpcodeNames[MAX_RET_OPCODE_NAMES]; int nRetOpcodeNames = 0;
static char szArgumentTypes[] = "Argument Types:"; static char szReturnTypes[] = "Return Types:";
int __cdecl main(int argc, char **argv) { FILE *fIn, *fOutH, *fOutC; char szBuf[256], szOff1[32], szOff2[32]; PSZ psz, pszAPI, pszAPI32; ITINSTR ThisInstr; BYTE bRetInstr; BYTE *pbInstr; int i, iSwampOffset; int iMaxArgs = 0; int cbDiff;
if (argc != 2) { ErrorAbort("Usage:\n genwowit <inputfile>\n"); }
if (!(fIn = fopen(argv[1], "rt"))) { ErrorAbort("Unable to open input file\n"); }
//
// The input file (wow.it) uses # to begin comment lines.
// Aside from comments, it must begin with two special lines
// to define the available type names for arguments and
// function return values.
//
// They look like:
//
// Argument Types: WORD, INT, DWORD, LPDWORD, PTR, PTRORATOM, HGDI, HUSER, COLOR, HINST, HICON, POINT, 16ONLY, 32ONLY;
// Return Types: DWORD, WORD, INT, HGDI, HUSER, ZERO, HICON, ONE, HPRNDWP;
//
// Read these lines into the ArgOpcodeNames and RetOpcodeNames arrays.
//
ReadTypeNames(fIn, szArgumentTypes, ArgOpcodeNames, &nArgOpcodeNames); if(nArgOpcodeNames > MAX_ARG_OPCODE_NAMES) { ErrorAbort("Too many ARG op codes!\n"); } ReadTypeNames(fIn, szReturnTypes, RetOpcodeNames, &nRetOpcodeNames); if(nRetOpcodeNames > MAX_RET_OPCODE_NAMES) { ErrorAbort("Too many RET op codes!\n"); }
//
// Each input line in the main part has a very restricted syntax:
//
// RETTYPE Api16[=Api32](TYPE1, TYPE2, ... TYPEn); # comment
//
// If Api32 isn't specified it's the same as Api16.
// The types come from the set above only.
//
// Actually everything following the ) is ignored now.
//
while (GetLine(szBuf, sizeof szBuf, fIn)) {
psz = szBuf;
//
// Pick up the type, space-delimited, leaving psz at API name start
//
bRetInstr = GetReturnOpcode(&psz);
//
// Pick up the API name, leaving psz pointing past the open-paren or
// to '=' char
//
pszAPI = GetApiName(&psz);
//
// Pick up the 32-bit name if it exists
//
pszAPI32 = GetApi32Name(&psz, pszAPI);
//
// Pick up the arg types into Instr array
//
memset(&ThisInstr, 0, sizeof ThisInstr); pbInstr = ThisInstr.Instr;
while (*psz && *psz != ')') { *pbInstr++ = GetArgOpcode(&psz); }
//
// Keep track of the max used args
//
iMaxArgs = max(iMaxArgs, (pbInstr - ThisInstr.Instr));
//
// Tack on the return opcode
//
*pbInstr++ = bRetInstr;
//
// Record instruction bytes used for this one.
//
ThisInstr.cbInstr = (pbInstr - ThisInstr.Instr);
//
// Make sure we haven't overrun
//
if ( ThisInstr.cbInstr > MAX_IT_INSTR ) { printf("Thunk for %s too many args (%d) increase MAX_IT_INSTR beyond %d.\n", pszAPI, ThisInstr.cbInstr, MAX_IT_INSTR); ErrorAbort("Increase MAX_IT_INSTR in intthunk.h\n"); }
//
// Now we have a fully-formed opcode stream, see if we can pack it
// in with any previously recorded ones. Walk through the table
// from the start looking for any entry which already contains this
// opcode sequence (possibly as part of a longer sequence) or which
// is itself contained by this opcode sequence. If we find one,
// change it to be the longer sequence if needed and use it. We'll
// distinguish later between the multiple uses using the cbInstr in
// each thunk table entry. The logic here assumes the matches will
// always be at the end, since ret opcodes always have 0x80 bit set
// and no others do, and each sequence ends with one.
//
for (i = 0; i < iNextInstrSlot; i++) { //if (0 == memcmp(Instr, InstrTable[i], sizeof Instr)) {
// break;
//}
//
// Is ThisInstr a subsequence of this table entry?
//
if (ThisInstr.cbInstr <= InstrTable[i].cbInstr && 0 == memcmp(ThisInstr.Instr, InstrTable[i].Instr + (InstrTable[i].cbInstr - ThisInstr.cbInstr), ThisInstr.cbInstr)) {
break; }
//
// Is this table entry a subsequence of ThisInstr?
//
if (InstrTable[i].cbInstr < ThisInstr.cbInstr && 0 == memcmp(InstrTable[i].Instr, ThisInstr.Instr + (ThisInstr.cbInstr - InstrTable[i].cbInstr), InstrTable[i].cbInstr)) {
//
// Blast the longer ThisInstr over the existing shorter
// instruction.
//
memcpy(&InstrTable[i], &ThisInstr, sizeof InstrTable[i]); break; }
//
// Check the next instruction table entry.
//
}
//
// If we didn't find a match, add to the end.
//
if (i == iNextInstrSlot) { memcpy(&InstrTable[i], &ThisInstr, sizeof InstrTable[i]); iNextInstrSlot++;
if (iNextInstrSlot == MAX_INSTR_TABLE_SIZE) { ErrorAbort("Increase MAX_INSTR_TABLE_SIZE in genwowit.c\n"); } }
//
// Add this one to the thunk table.
//
ThunkTable[iNextThunkSlot].pszAPI = pszAPI; ThunkTable[iNextThunkSlot].pszAPI32 = pszAPI32; ThunkTable[iNextThunkSlot].iInstrSlot = i; ThunkTable[iNextThunkSlot].cbInstr = ThisInstr.cbInstr; iNextThunkSlot++;
if (iNextThunkSlot == MAX_THUNK_TABLE_SIZE) { ErrorAbort("Increase MAX_THUNK_TABLE_SIZE in genwowit.c\n"); } }
fclose(fIn);
//
// Now we're ready to output the results.
//
if (!(fOutH = fopen("wowit.h", "wt"))) { ErrorAbort("Cannot open wowit.h output file\n"); }
fprintf(fOutH, "//\n" "// DO NOT EDIT.\n" "//\n" "// wowit.h generated by genwowit.exe from wow.it on\n" "//\n" "// %s\n" "//\n\n", DateTimeString());
fprintf(fOutH, "#include \"intthunk.h\"\n\n");
fprintf(fOutH, "#define MAX_IT_ARGS %d\n\n", iMaxArgs);
//
// Spit out the two types of opcode manifests.
//
for (i = 0; i < nArgOpcodeNames; i++) { fprintf(fOutH, "#define IT_%-20s ( (UCHAR) 0x%x )\n", ArgOpcodeNames[i], i); }
fprintf(fOutH, "\n#define IT_RETMASK ( (UCHAR) 0x80 )\n");
for (i = 0; i < nRetOpcodeNames; i++) { sprintf(szBuf, "%sRET", RetOpcodeNames[i]); fprintf(fOutH, "#define IT_%-20s ( IT_RETMASK | (UCHAR) 0x%x )\n", szBuf, i); }
fprintf(fOutH, "\n");
//
// ITID_ manifests map an API name to its slot
// in the thunk table. Each one looks like:
//
// #define ITID_ApiName 0
//
for (i = 0; i < iNextThunkSlot; i++) { fprintf(fOutH, "#define ITID_%-40s %d\n", ThunkTable[i].pszAPI, i); }
fprintf(fOutH, "\n#define ITID_MAX %d\n", i-1);
fclose(fOutH);
//
// wowit.c has two tables, the instruction table and
// the thunk table.
//
if (!(fOutC = fopen("wowit.c", "wt"))) { ErrorAbort("Cannot open wowit.c output file\n"); }
fprintf(fOutC, "//\n" "// DO NOT EDIT.\n" "//\n" "// wowit.c generated by genwowit.exe from wow.it on\n" "//\n" "// %s\n" "//\n\n", DateTimeString());
fprintf(fOutC, "#include \"precomp.h\"\n"); fprintf(fOutC, "#pragma hdrstop\n"); fprintf(fOutC, "#define WOWIT_C\n"); fprintf(fOutC, "#include \"wowit.h\"\n\n");
//
// Spit out the instruction table, packing bytes in the process
// and filling in the aoffInstrTable array with offsets for each
// entry in this program's InstrTable. Those offsets are used
// in writing the final thunk table.
//
iSwampOffset = 0;
fprintf(fOutC, "CONST BYTE InstrSwamp[] = {\n");
for (i = 0; i < iNextInstrSlot; i++) {
fprintf(fOutC, " /* %3d 0x%-3x */ ", i, iSwampOffset);
pbInstr = InstrTable[i].Instr; InstrTable[i].offSwamp = iSwampOffset;
do { fprintf(fOutC, "%s, ", GetOpcodeName(*pbInstr)); iSwampOffset++; } while (!IS_RET_OPCODE(*pbInstr++));
fprintf(fOutC, "\n");
}
fprintf(fOutC, "};\n\n");
fprintf(fOutC, "CONST INT_THUNK_TABLEENTRY IntThunkTable[] = {\n");
for (i = 0; i < iNextThunkSlot; i++) {
//
// Concatenate the API name followed by a comma into
// szBuf, so the combination can be left-justified in the output.
//
sprintf(szBuf, "%s,", ThunkTable[i].pszAPI32);
//
// cbDiff is the offset into the instruction stream where
// this thunks instruction stream begins.
//
cbDiff = InstrTable[ ThunkTable[i].iInstrSlot ].cbInstr - ThunkTable[i].cbInstr;
//
// Format the swamp offset so it can be left-justified in the output.
//
sprintf(szOff1, "%x", InstrTable[ ThunkTable[i].iInstrSlot ].offSwamp + cbDiff);
//
// If this thunk table entry will point past the start of
// an instruction (because of sharing), format the offset
// past the start of the instruction into szOff2
//
if (cbDiff) { sprintf(szOff2, "+ %d ", cbDiff); } else { szOff2[0] = '\0'; }
fprintf(fOutC, " /* %3d */ { (FARPROC) %-32s InstrSwamp + 0x%-4s }, /* %d %s*/ \n", i, szBuf, szOff1, ThunkTable[i].iInstrSlot, szOff2); }
fprintf(fOutC, "};\n\n");
fclose(fOutC);
printf("Generated wowit.h and wowit.c from wow.it\n" "%d thunks, %d unique instruction streams, %d instruction bytes, %d max args.\n", iNextThunkSlot, iNextInstrSlot, iSwampOffset, iMaxArgs);
return 0; }
BYTE GetReturnOpcode(PSZ *ppsz) { int i; char szBuf[32]; PSZ psz;
//
// Copy the name up to the first space to szBuf,
// then skip any remaining spaces leaving caller's
// pointer pointing at API name.
//
psz = szBuf; while (**ppsz != ' ') { *psz++ = *((*ppsz)++); };
*psz = 0;
if(strlen(szBuf) > 32-1) { ErrorAbort("Return Opcode too long.\n"); }
// advance to the API name start
while (**ppsz == ' ') { (*ppsz)++; };
i = 0; while (i < nRetOpcodeNames && strcmp(szBuf, RetOpcodeNames[i])) { i++; }
if (i == nRetOpcodeNames) { printf("%s is not a valid return type.\n", szBuf); ErrorAbort("Invalid return type.\n"); }
return (BYTE)i | 0x80; }
PSZ GetApiName(PSZ *ppsz) { char szBuf[128]; PSZ psz;
//
// Copy the name up to the first space or open-paren or equals sign
// to szBuf, then skip any remaining spaces and open-parens leaving caller's
// pointer pointing at first arg type or equals sign
//
psz = szBuf; while (**ppsz != ' ' && **ppsz != '(' && **ppsz != '=') { *psz++ = *((*ppsz)++); };
*psz = 0;
if(strlen(szBuf) > 128-1) { ErrorAbort("API Name too long.\n"); }
// advance to 1st parameter type or '=' char if any
while (**ppsz == ' ' || **ppsz == '(') { (*ppsz)++; };
if (!strlen(szBuf)) { ErrorAbort("Empty API name\n"); }
return _strdup(szBuf); }
PSZ GetApi32Name(PSZ *ppsz, PSZ pszApi16) { char szBuf[128]; PSZ psz;
if (**ppsz != '=') { return pszApi16; }
(*ppsz)++; // skip =
//
// Copy the name up to the first space or open-paren
// to szBuf, then skip any remaining spaces and open-parens leaving caller's
// pointer pointing at first arg type
//
psz = szBuf; while (**ppsz != ' ' && **ppsz != '(') { *psz++ = *((*ppsz)++); };
*psz = 0;
if(strlen(szBuf) > 128-1) { ErrorAbort("API32 Name too long.\n"); }
// advance to the 1st parameter type
while (**ppsz == ' ' || **ppsz == '(') { (*ppsz)++; };
if (!strlen(szBuf)) { ErrorAbort("Empty API32 name\n"); }
return _strdup(szBuf); }
BYTE GetArgOpcode(PSZ *ppsz) { char szBuf[32]; PSZ psz; int i;
//
// Copy the name up to the first space or comma close-paren
// to szBuf, then skip any remaining spaces and commas,
// leaving caller's pointer pointing at next arg type
// or close-paren.
//
psz = szBuf; while (**ppsz != ' ' && **ppsz != ',' && **ppsz != ')') { *psz++ = *((*ppsz)++); };
*psz = 0;
if(strlen(szBuf) > 32-1) { ErrorAbort("Opcode too long.\n"); }
while (**ppsz == ' ' || **ppsz == ',') { (*ppsz)++; };
//
// szBuf has the type name, find it in the table.
//
i = 0; while (i < nArgOpcodeNames && strcmp(szBuf, ArgOpcodeNames[i])) { i++; }
if (i == nArgOpcodeNames) { printf("%s is not a valid arg type.\n", szBuf); ErrorAbort("Invalid arg type.\n"); }
return (BYTE)i; }
PSZ GetOpcodeName(BYTE bInstr) { char szBuf[64];
if (!IS_RET_OPCODE(bInstr)) { sprintf(szBuf, "IT_%s", ArgOpcodeNames[bInstr]); } else { sprintf(szBuf, "IT_%sRET", RetOpcodeNames[bInstr & 0x7f]); }
return _strdup(szBuf); }
VOID ErrorAbort(PSZ pszMsg) { printf("GENWOWIT : fatal error GWI0001: Unable to process wow.it: %s\n", pszMsg); exit(1); }
//
// Read a line from the input file skipping
// comment lines with '#' in the first column.
//
PSZ GetLine(PSZ pszBuf, int cbBuf, FILE *fp) { do {
pszBuf = fgets(pszBuf, cbBuf, fp);
} while (pszBuf && '#' == *pszBuf);
if(pszBuf) { pszBuf[cbBuf-1] = '\0'; } return pszBuf; }
//
// Read one of the two special lines at the start that
// define the available types.
//
VOID ReadTypeNames(FILE *fIn, PSZ pszTypesPrefix, PSZ *OpcodeNamesArray, int *pnOpcodeNames) { char chSave, szBuf[512]; PSZ psz, pszType;
if ( ! GetLine(szBuf, sizeof szBuf, fIn) || _memicmp(szBuf, pszTypesPrefix, strlen(pszTypesPrefix)) ) {
ErrorAbort("First line of input file must be 'Argument Types:', second 'Return Types:' ...\n"); }
psz = szBuf + strlen(pszTypesPrefix);
//
// Skip whitespace and commas
//
while (' ' == *psz || '\t' == *psz) { psz++; }
if ( ! *psz) { ErrorAbort("No types found.\n"); }
do { //
// Now we're looking at the first character of the type name.
//
pszType = psz;
//
// Find next whitespace, comma, semi, or null and turn it into a null.
// This turns this type name into a zero-terminated string.
//
while (*psz && ' ' != *psz && '\t' != *psz && ',' != *psz && ';' != *psz) { psz++; }
chSave = *psz; *psz = 0;
OpcodeNamesArray[*pnOpcodeNames] = _strdup(pszType); (*pnOpcodeNames)++;
*psz = chSave;
//
// Skip whitespace and commas
//
while (' ' == *psz || '\t' == *psz || ',' == *psz) { psz++; }
} while (*psz && ';' != *psz);
if(';' != *psz) { ErrorAbort("Semi-colon not found. Size of szBuf[] probably needs to be increased.\n"); }
if ( ! *pnOpcodeNames) { ErrorAbort("No types found.\n"); } }
//
// Return a formatted date/time string for now.
// Only checks system time once so that wowit.c and wowit.h
// will have same date/time string.
//
PSZ DateTimeString(VOID) { static char sz[256]; static int fSetupAlready;
if (!fSetupAlready) { time_t UnixTimeNow; struct tm *ptmNow;
fSetupAlready = TRUE;
_tzset();
time(&UnixTimeNow);
ptmNow = localtime(&UnixTimeNow);
strftime(sz, sizeof sz, "%#c", ptmNow);
strcat(sz, " ("); strcat(sz, _strupr(_tzname[0])); // naughty me
strcat(sz, ")"); }
return sz; }
|