/* generate wowit.h and wowit.c from wow.it * * 20-Feb-1997 DaveHart created */ #include #include #include #include #include #include #include #include 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; #define MAX_RET_OPCODE_NAMES 32 PSZ RetOpcodeNames[MAX_RET_OPCODE_NAMES]; int nRetOpcodeNames; 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 \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); ReadTypeNames(fIn, szReturnTypes, RetOpcodeNames, &nRetOpcodeNames); // // 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 return type, space-delimited // bRetInstr = GetReturnOpcode(&psz); // // Pick up the API name, leaving psz pointing past the open-paren // 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; 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; 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; 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; 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); 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 ( ! *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; }