Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

715 lines
17 KiB

  1. /* generate wowit.h and wowit.c from wow.it
  2. *
  3. * 20-Feb-1997 DaveHart created
  4. */
  5. #include <time.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <io.h>
  10. #include <sys\stat.h>
  11. #include <fcntl.h>
  12. #include <windows.h>
  13. VOID ErrorAbort(PSZ pszMsg);
  14. BYTE GetReturnOpcode(PSZ *ppsz);
  15. PSZ GetApiName(PSZ *ppsz);
  16. PSZ GetApi32Name(PSZ *ppsz, PSZ pszApi16);
  17. BYTE GetArgOpcode(PSZ *ppsz);
  18. PSZ GetOpcodeName(BYTE bInstr);
  19. PSZ GetLine(PSZ pszBuf, int cbBuf, FILE *fp);
  20. VOID ReadTypeNames(FILE *fIn, PSZ szTypesPrefix, PSZ *OpcodeNamesArray, int *pnOpcodeNames);
  21. PSZ DateTimeString(VOID);
  22. #define IS_RET_OPCODE(b) (b & 0x80)
  23. #define MAX_IT_INSTR 16
  24. typedef struct tagITINSTR {
  25. int cbInstr;
  26. int offSwamp;
  27. BYTE Instr[MAX_IT_INSTR];
  28. } ITINSTR;
  29. #define MAX_INSTR_TABLE_SIZE 512
  30. ITINSTR InstrTable[MAX_INSTR_TABLE_SIZE];
  31. int iNextInstrSlot = 0;
  32. typedef struct tagTHUNKTABLESLOT {
  33. PSZ pszAPI;
  34. PSZ pszAPI32; // if Win32 routine name doesn't match pszAPI
  35. int iInstrSlot;
  36. int cbInstr; // how much of this slot we're using
  37. } THUNKTABLESLOT;
  38. #define MAX_THUNK_TABLE_SIZE 1024
  39. THUNKTABLESLOT ThunkTable[MAX_THUNK_TABLE_SIZE];
  40. int iNextThunkSlot = 0;
  41. #define MAX_ARG_OPCODE_NAMES 32
  42. PSZ ArgOpcodeNames[MAX_ARG_OPCODE_NAMES];
  43. int nArgOpcodeNames;
  44. #define MAX_RET_OPCODE_NAMES 32
  45. PSZ RetOpcodeNames[MAX_RET_OPCODE_NAMES];
  46. int nRetOpcodeNames;
  47. static char szArgumentTypes[] = "Argument Types:";
  48. static char szReturnTypes[] = "Return Types:";
  49. int __cdecl main(int argc, char **argv)
  50. {
  51. FILE *fIn, *fOutH, *fOutC;
  52. char szBuf[256], szOff1[32], szOff2[32];
  53. PSZ psz, pszAPI, pszAPI32;
  54. ITINSTR ThisInstr;
  55. BYTE bRetInstr;
  56. BYTE *pbInstr;
  57. int i, iSwampOffset;
  58. int iMaxArgs = 0;
  59. int cbDiff;
  60. if (argc != 2) {
  61. ErrorAbort("Usage:\n genwowit <inputfile>\n");
  62. }
  63. if (!(fIn = fopen(argv[1], "rt"))) {
  64. ErrorAbort("Unable to open input file\n");
  65. }
  66. //
  67. // The input file (wow.it) uses # to begin comment lines.
  68. // Aside from comments, it must begin with two special lines
  69. // to define the available type names for arguments and
  70. // function return values.
  71. //
  72. // They look like:
  73. //
  74. // Argument Types: WORD, INT, DWORD, LPDWORD, PTR, PTRORATOM, HGDI, HUSER, COLOR, HINST, HICON, POINT, 16ONLY, 32ONLY;
  75. // Return Types: DWORD, WORD, INT, HGDI, HUSER, ZERO, HICON, ONE, HPRNDWP;
  76. //
  77. // Read these lines into the ArgOpcodeNames and RetOpcodeNames arrays.
  78. //
  79. ReadTypeNames(fIn, szArgumentTypes, ArgOpcodeNames, &nArgOpcodeNames);
  80. ReadTypeNames(fIn, szReturnTypes, RetOpcodeNames, &nRetOpcodeNames);
  81. //
  82. // Each input line in the main part has a very restricted syntax:
  83. //
  84. // RETTYPE Api16[=Api32](TYPE1, TYPE2, ... TYPEn); # comment
  85. //
  86. // If Api32 isn't specified it's the same as Api16.
  87. // The types come from the set above only.
  88. //
  89. // Actually everything following the ) is ignored now.
  90. //
  91. while (GetLine(szBuf, sizeof szBuf, fIn)) {
  92. psz = szBuf;
  93. //
  94. // Pick up the return type, space-delimited
  95. //
  96. bRetInstr = GetReturnOpcode(&psz);
  97. //
  98. // Pick up the API name, leaving psz pointing past the open-paren
  99. //
  100. pszAPI = GetApiName(&psz);
  101. //
  102. // Pick up the 32-bit name if it exists
  103. //
  104. pszAPI32 = GetApi32Name(&psz, pszAPI);
  105. //
  106. // Pick up the arg types into Instr array
  107. //
  108. memset(&ThisInstr, 0, sizeof ThisInstr);
  109. pbInstr = ThisInstr.Instr;
  110. while (*psz && *psz != ')') {
  111. *pbInstr++ = GetArgOpcode(&psz);
  112. }
  113. //
  114. // Keep track of the max used args
  115. //
  116. iMaxArgs = max(iMaxArgs, (pbInstr - ThisInstr.Instr));
  117. //
  118. // Tack on the return opcode
  119. //
  120. *pbInstr++ = bRetInstr;
  121. //
  122. // Record instruction bytes used for this one.
  123. //
  124. ThisInstr.cbInstr = (pbInstr - ThisInstr.Instr);
  125. //
  126. // Make sure we haven't overrun
  127. //
  128. if ( ThisInstr.cbInstr > MAX_IT_INSTR ) {
  129. printf("Thunk for %s too many args (%d) increase MAX_IT_INSTR beyond %d.\n",
  130. pszAPI, ThisInstr.cbInstr, MAX_IT_INSTR);
  131. ErrorAbort("Increase MAX_IT_INSTR in intthunk.h\n");
  132. }
  133. //
  134. // Now we have a fully-formed opcode stream, see if we can pack it
  135. // in with any previously recorded ones. Walk through the table
  136. // from the start looking for any entry which already contains this
  137. // opcode sequence (possibly as part of a longer sequence) or which
  138. // is itself contained by this opcode sequence. If we find one,
  139. // change it to be the longer sequence if needed and use it. We'll
  140. // distinguish later between the multiple uses using the cbInstr in
  141. // each thunk table entry. The logic here assumes the matches will
  142. // always be at the end, since ret opcodes always have 0x80 bit set
  143. // and no others do, and each sequence ends with one.
  144. //
  145. for (i = 0; i < iNextInstrSlot; i++) {
  146. //if (0 == memcmp(Instr, InstrTable[i], sizeof Instr)) {
  147. // break;
  148. //}
  149. //
  150. // Is ThisInstr a subsequence of this table entry?
  151. //
  152. if (ThisInstr.cbInstr <= InstrTable[i].cbInstr &&
  153. 0 == memcmp(ThisInstr.Instr,
  154. InstrTable[i].Instr + (InstrTable[i].cbInstr -
  155. ThisInstr.cbInstr),
  156. ThisInstr.cbInstr)) {
  157. break;
  158. }
  159. //
  160. // Is this table entry a subsequence of ThisInstr?
  161. //
  162. if (InstrTable[i].cbInstr < ThisInstr.cbInstr &&
  163. 0 == memcmp(InstrTable[i].Instr,
  164. ThisInstr.Instr + (ThisInstr.cbInstr -
  165. InstrTable[i].cbInstr),
  166. InstrTable[i].cbInstr)) {
  167. //
  168. // Blast the longer ThisInstr over the existing shorter
  169. // instruction.
  170. //
  171. memcpy(&InstrTable[i], &ThisInstr, sizeof InstrTable[i]);
  172. break;
  173. }
  174. //
  175. // Check the next instruction table entry.
  176. //
  177. }
  178. //
  179. // If we didn't find a match, add to the end.
  180. //
  181. if (i == iNextInstrSlot) {
  182. memcpy(&InstrTable[i], &ThisInstr, sizeof InstrTable[i]);
  183. iNextInstrSlot++;
  184. if (iNextInstrSlot == MAX_INSTR_TABLE_SIZE) {
  185. ErrorAbort("Increase MAX_INSTR_TABLE_SIZE in genwowit.c\n");
  186. }
  187. }
  188. //
  189. // Add this one to the thunk table.
  190. //
  191. ThunkTable[iNextThunkSlot].pszAPI = pszAPI;
  192. ThunkTable[iNextThunkSlot].pszAPI32 = pszAPI32;
  193. ThunkTable[iNextThunkSlot].iInstrSlot = i;
  194. ThunkTable[iNextThunkSlot].cbInstr = ThisInstr.cbInstr;
  195. iNextThunkSlot++;
  196. if (iNextThunkSlot == MAX_THUNK_TABLE_SIZE) {
  197. ErrorAbort("Increase MAX_THUNK_TABLE_SIZE in genwowit.c\n");
  198. }
  199. }
  200. fclose(fIn);
  201. //
  202. // Now we're ready to output the results.
  203. //
  204. if (!(fOutH = fopen("wowit.h", "wt"))) {
  205. ErrorAbort("Cannot open wowit.h output file\n");
  206. }
  207. fprintf(fOutH,
  208. "//\n"
  209. "// DO NOT EDIT.\n"
  210. "//\n"
  211. "// wowit.h generated by genwowit.exe from wow.it on\n"
  212. "//\n"
  213. "// %s\n"
  214. "//\n\n", DateTimeString());
  215. fprintf(fOutH, "#include \"intthunk.h\"\n\n");
  216. fprintf(fOutH, "#define MAX_IT_ARGS %d\n\n", iMaxArgs);
  217. //
  218. // Spit out the two types of opcode manifests.
  219. //
  220. for (i = 0; i < nArgOpcodeNames; i++) {
  221. fprintf(fOutH, "#define IT_%-20s ( (UCHAR) 0x%x )\n", ArgOpcodeNames[i], i);
  222. }
  223. fprintf(fOutH, "\n#define IT_RETMASK ( (UCHAR) 0x80 )\n");
  224. for (i = 0; i < nRetOpcodeNames; i++) {
  225. sprintf(szBuf, "%sRET", RetOpcodeNames[i]);
  226. fprintf(fOutH, "#define IT_%-20s ( IT_RETMASK | (UCHAR) 0x%x )\n", szBuf, i);
  227. }
  228. fprintf(fOutH, "\n");
  229. //
  230. // ITID_ manifests map an API name to its slot
  231. // in the thunk table. Each one looks like:
  232. //
  233. // #define ITID_ApiName 0
  234. //
  235. for (i = 0; i < iNextThunkSlot; i++) {
  236. fprintf(fOutH, "#define ITID_%-40s %d\n", ThunkTable[i].pszAPI, i);
  237. }
  238. fprintf(fOutH, "\n#define ITID_MAX %d\n", i-1);
  239. fclose(fOutH);
  240. //
  241. // wowit.c has two tables, the instruction table and
  242. // the thunk table.
  243. //
  244. if (!(fOutC = fopen("wowit.c", "wt"))) {
  245. ErrorAbort("Cannot open wowit.c output file\n");
  246. }
  247. fprintf(fOutC,
  248. "//\n"
  249. "// DO NOT EDIT.\n"
  250. "//\n"
  251. "// wowit.c generated by genwowit.exe from wow.it on\n"
  252. "//\n"
  253. "// %s\n"
  254. "//\n\n", DateTimeString());
  255. fprintf(fOutC, "#include \"precomp.h\"\n");
  256. fprintf(fOutC, "#pragma hdrstop\n");
  257. fprintf(fOutC, "#define WOWIT_C\n");
  258. fprintf(fOutC, "#include \"wowit.h\"\n\n");
  259. //
  260. // Spit out the instruction table, packing bytes in the process
  261. // and filling in the aoffInstrTable array with offsets for each
  262. // entry in this program's InstrTable. Those offsets are used
  263. // in writing the final thunk table.
  264. //
  265. iSwampOffset = 0;
  266. fprintf(fOutC, "CONST BYTE InstrSwamp[] = {\n");
  267. for (i = 0; i < iNextInstrSlot; i++) {
  268. fprintf(fOutC, " /* %3d 0x%-3x */ ", i, iSwampOffset);
  269. pbInstr = InstrTable[i].Instr;
  270. InstrTable[i].offSwamp = iSwampOffset;
  271. do {
  272. fprintf(fOutC, "%s, ", GetOpcodeName(*pbInstr));
  273. iSwampOffset++;
  274. } while (!IS_RET_OPCODE(*pbInstr++));
  275. fprintf(fOutC, "\n");
  276. }
  277. fprintf(fOutC, "};\n\n");
  278. fprintf(fOutC, "CONST INT_THUNK_TABLEENTRY IntThunkTable[] = {\n");
  279. for (i = 0; i < iNextThunkSlot; i++) {
  280. //
  281. // Concatenate the API name followed by a comma into
  282. // szBuf, so the combination can be left-justified in the output.
  283. //
  284. sprintf(szBuf, "%s,", ThunkTable[i].pszAPI32);
  285. //
  286. // cbDiff is the offset into the instruction stream where
  287. // this thunks instruction stream begins.
  288. //
  289. cbDiff = InstrTable[ ThunkTable[i].iInstrSlot ].cbInstr -
  290. ThunkTable[i].cbInstr;
  291. //
  292. // Format the swamp offset so it can be left-justified in the output.
  293. //
  294. sprintf(szOff1, "%x",
  295. InstrTable[ ThunkTable[i].iInstrSlot ].offSwamp + cbDiff);
  296. //
  297. // If this thunk table entry will point past the start of
  298. // an instruction (because of sharing), format the offset
  299. // past the start of the instruction into szOff2
  300. //
  301. if (cbDiff) {
  302. sprintf(szOff2, "+ %d ", cbDiff);
  303. } else {
  304. szOff2[0] = '\0';
  305. }
  306. fprintf(fOutC,
  307. " /* %3d */ { (FARPROC) %-32s InstrSwamp + 0x%-4s }, /* %d %s*/ \n",
  308. i,
  309. szBuf,
  310. szOff1,
  311. ThunkTable[i].iInstrSlot,
  312. szOff2);
  313. }
  314. fprintf(fOutC, "};\n\n");
  315. fclose(fOutC);
  316. printf("Generated wowit.h and wowit.c from wow.it\n"
  317. "%d thunks, %d unique instruction streams, %d instruction bytes, %d max args.\n",
  318. iNextThunkSlot, iNextInstrSlot, iSwampOffset, iMaxArgs);
  319. return 0;
  320. }
  321. BYTE GetReturnOpcode(PSZ *ppsz)
  322. {
  323. int i;
  324. char szBuf[32];
  325. PSZ psz;
  326. //
  327. // Copy the name up to the first space to szBuf,
  328. // then skip any remaining spaces leaving caller's
  329. // pointer pointing at API name.
  330. //
  331. psz = szBuf;
  332. while (**ppsz != ' ') {
  333. *psz++ = *((*ppsz)++);
  334. };
  335. *psz = 0;
  336. while (**ppsz == ' ') {
  337. (*ppsz)++;
  338. };
  339. i = 0;
  340. while (i < nRetOpcodeNames &&
  341. strcmp(szBuf, RetOpcodeNames[i])) {
  342. i++;
  343. }
  344. if (i == nRetOpcodeNames) {
  345. printf("%s is not a valid return type.\n", szBuf);
  346. ErrorAbort("Invalid return type.\n");
  347. }
  348. return (BYTE)i | 0x80;
  349. }
  350. PSZ GetApiName(PSZ *ppsz)
  351. {
  352. char szBuf[128];
  353. PSZ psz;
  354. //
  355. // Copy the name up to the first space or open-paren or equals sign
  356. // to szBuf, then skip any remaining spaces and open-parens leaving caller's
  357. // pointer pointing at first arg type or equals sign
  358. //
  359. psz = szBuf;
  360. while (**ppsz != ' ' && **ppsz != '(' && **ppsz != '=') {
  361. *psz++ = *((*ppsz)++);
  362. };
  363. *psz = 0;
  364. while (**ppsz == ' ' || **ppsz == '(') {
  365. (*ppsz)++;
  366. };
  367. if (!strlen(szBuf)) {
  368. ErrorAbort("Empty API name\n");
  369. }
  370. return _strdup(szBuf);
  371. }
  372. PSZ GetApi32Name(PSZ *ppsz, PSZ pszApi16)
  373. {
  374. char szBuf[128];
  375. PSZ psz;
  376. if (**ppsz != '=') {
  377. return pszApi16;
  378. }
  379. (*ppsz)++; // skip =
  380. //
  381. // Copy the name up to the first space or open-paren
  382. // to szBuf, then skip any remaining spaces and open-parens leaving caller's
  383. // pointer pointing at first arg type
  384. //
  385. psz = szBuf;
  386. while (**ppsz != ' ' && **ppsz != '(') {
  387. *psz++ = *((*ppsz)++);
  388. };
  389. *psz = 0;
  390. while (**ppsz == ' ' || **ppsz == '(') {
  391. (*ppsz)++;
  392. };
  393. if (!strlen(szBuf)) {
  394. ErrorAbort("Empty API32 name\n");
  395. }
  396. return _strdup(szBuf);
  397. }
  398. BYTE GetArgOpcode(PSZ *ppsz)
  399. {
  400. char szBuf[32];
  401. PSZ psz;
  402. int i;
  403. //
  404. // Copy the name up to the first space or comma close-paren
  405. // to szBuf, then skip any remaining spaces and commas,
  406. // leaving caller's pointer pointing at next arg type
  407. // or close-paren.
  408. //
  409. psz = szBuf;
  410. while (**ppsz != ' ' && **ppsz != ',' && **ppsz != ')') {
  411. *psz++ = *((*ppsz)++);
  412. };
  413. *psz = 0;
  414. while (**ppsz == ' ' || **ppsz == ',') {
  415. (*ppsz)++;
  416. };
  417. //
  418. // szBuf has the type name, find it in the table.
  419. //
  420. i = 0;
  421. while (i < nArgOpcodeNames &&
  422. strcmp(szBuf, ArgOpcodeNames[i])) {
  423. i++;
  424. }
  425. if (i == nArgOpcodeNames) {
  426. printf("%s is not a valid arg type.\n", szBuf);
  427. ErrorAbort("Invalid arg type.\n");
  428. }
  429. return (BYTE)i;
  430. }
  431. PSZ GetOpcodeName(BYTE bInstr)
  432. {
  433. char szBuf[64];
  434. if (!IS_RET_OPCODE(bInstr)) {
  435. sprintf(szBuf, "IT_%s", ArgOpcodeNames[bInstr]);
  436. } else {
  437. sprintf(szBuf, "IT_%sRET", RetOpcodeNames[bInstr & 0x7f]);
  438. }
  439. return _strdup(szBuf);
  440. }
  441. VOID ErrorAbort(PSZ pszMsg)
  442. {
  443. printf("GENWOWIT : fatal error GWI0001: Unable to process wow.it: %s\n", pszMsg);
  444. exit(1);
  445. }
  446. //
  447. // Read a line from the input file skipping
  448. // comment lines with '#' in the first column.
  449. //
  450. PSZ GetLine(PSZ pszBuf, int cbBuf, FILE *fp)
  451. {
  452. do {
  453. pszBuf = fgets(pszBuf, cbBuf, fp);
  454. } while (pszBuf && '#' == *pszBuf);
  455. return pszBuf;
  456. }
  457. //
  458. // Read one of the two special lines at the start that
  459. // define the available types.
  460. //
  461. VOID ReadTypeNames(FILE *fIn, PSZ pszTypesPrefix, PSZ *OpcodeNamesArray, int *pnOpcodeNames)
  462. {
  463. char chSave, szBuf[512];
  464. PSZ psz, pszType;
  465. if ( ! GetLine(szBuf, sizeof szBuf, fIn) ||
  466. _memicmp(szBuf, pszTypesPrefix, strlen(pszTypesPrefix)) ) {
  467. ErrorAbort("First line of input file must be 'Argument Types:', second 'Return Types:' ...\n");
  468. }
  469. psz = szBuf + strlen(pszTypesPrefix);
  470. //
  471. // Skip whitespace and commas
  472. //
  473. while (' ' == *psz || '\t' == *psz) {
  474. psz++;
  475. }
  476. if ( ! *psz) {
  477. ErrorAbort("No types found.\n");
  478. }
  479. do {
  480. //
  481. // Now we're looking at the first character of the type name.
  482. //
  483. pszType = psz;
  484. //
  485. // Find next whitespace, comma, semi, or null and turn it into a null.
  486. // This turns this type name into a zero-terminated string.
  487. //
  488. while (*psz && ' ' != *psz && '\t' != *psz && ',' != *psz && ';' != *psz) {
  489. psz++;
  490. }
  491. chSave = *psz;
  492. *psz = 0;
  493. OpcodeNamesArray[*pnOpcodeNames] = _strdup(pszType);
  494. (*pnOpcodeNames)++;
  495. *psz = chSave;
  496. //
  497. // Skip whitespace and commas
  498. //
  499. while (' ' == *psz || '\t' == *psz || ',' == *psz) {
  500. psz++;
  501. }
  502. } while (*psz && ';' != *psz);
  503. if ( ! *pnOpcodeNames) {
  504. ErrorAbort("No types found.\n");
  505. }
  506. }
  507. //
  508. // Return a formatted date/time string for now.
  509. // Only checks system time once so that wowit.c and wowit.h
  510. // will have same date/time string.
  511. //
  512. PSZ DateTimeString(VOID)
  513. {
  514. static char sz[256];
  515. static int fSetupAlready;
  516. if (!fSetupAlready) {
  517. time_t UnixTimeNow;
  518. struct tm *ptmNow;
  519. fSetupAlready = TRUE;
  520. _tzset();
  521. time(&UnixTimeNow);
  522. ptmNow = localtime(&UnixTimeNow);
  523. strftime(sz, sizeof sz, "%#c", ptmNow);
  524. strcat(sz, " (");
  525. strcat(sz, _strupr(_tzname[0])); // naughty me
  526. strcat(sz, ")");
  527. }
  528. return sz;
  529. }