make_xftmpl.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /*
  2. * Binary encode X templates from text format.
  3. *
  4. * Copyright 2011 Dylan Smith
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. */
  20. #include "config.h"
  21. #include <signal.h>
  22. #include <stdarg.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include "windef.h"
  26. #include "guiddef.h"
  27. #include "tools.h"
  28. #define TOKEN_NAME 1
  29. #define TOKEN_STRING 2
  30. #define TOKEN_INTEGER 3
  31. #define TOKEN_GUID 5
  32. #define TOKEN_INTEGER_LIST 6
  33. #define TOKEN_FLOAT_LIST 7
  34. #define TOKEN_OBRACE 10
  35. #define TOKEN_CBRACE 11
  36. #define TOKEN_OPAREN 12
  37. #define TOKEN_CPAREN 13
  38. #define TOKEN_OBRACKET 14
  39. #define TOKEN_CBRACKET 15
  40. #define TOKEN_OANGLE 16
  41. #define TOKEN_CANGLE 17
  42. #define TOKEN_DOT 18
  43. #define TOKEN_COMMA 19
  44. #define TOKEN_SEMICOLON 20
  45. #define TOKEN_TEMPLATE 31
  46. #define TOKEN_WORD 40
  47. #define TOKEN_DWORD 41
  48. #define TOKEN_FLOAT 42
  49. #define TOKEN_DOUBLE 43
  50. #define TOKEN_CHAR 44
  51. #define TOKEN_UCHAR 45
  52. #define TOKEN_SWORD 46
  53. #define TOKEN_SDWORD 47
  54. #define TOKEN_VOID 48
  55. #define TOKEN_LPSTR 49
  56. #define TOKEN_UNICODE 50
  57. #define TOKEN_CSTRING 51
  58. #define TOKEN_ARRAY 52
  59. struct keyword
  60. {
  61. const char *word;
  62. WORD token;
  63. };
  64. static const struct keyword reserved_words[] = {
  65. {"ARRAY", TOKEN_ARRAY},
  66. {"CHAR", TOKEN_CHAR},
  67. {"CSTRING", TOKEN_CSTRING},
  68. {"DOUBLE", TOKEN_DOUBLE},
  69. {"DWORD", TOKEN_DWORD},
  70. {"FLOAT", TOKEN_FLOAT},
  71. {"SDWORD", TOKEN_SDWORD},
  72. {"STRING", TOKEN_LPSTR},
  73. {"SWORD", TOKEN_SWORD},
  74. {"TEMPLATE", TOKEN_TEMPLATE},
  75. {"UCHAR", TOKEN_UCHAR},
  76. {"UNICODE", TOKEN_UNICODE},
  77. {"VOID", TOKEN_VOID},
  78. {"WORD", TOKEN_WORD}
  79. };
  80. static BOOL option_header;
  81. static char *option_inc_var_name = NULL;
  82. static char *option_inc_size_name = NULL;
  83. static const char *option_outfile_name = "-";
  84. static char *program_name;
  85. static FILE *infile;
  86. static int line_no;
  87. static const char *infile_name;
  88. static FILE *outfile;
  89. static BYTE *output_data;
  90. static UINT output_pos, output_size;
  91. static void fatal_error( const char *msg, ... ) __attribute__ ((__format__ (__printf__, 1, 2)));
  92. static void fatal_error( const char *msg, ... )
  93. {
  94. va_list valist;
  95. va_start( valist, msg );
  96. if (infile_name)
  97. {
  98. fprintf( stderr, "%s:%d:", infile_name, line_no );
  99. fprintf( stderr, " error: " );
  100. }
  101. else fprintf( stderr, "%s: error: ", program_name );
  102. vfprintf( stderr, msg, valist );
  103. va_end( valist );
  104. exit( 1 );
  105. }
  106. static inline BOOL read_byte( char *byte )
  107. {
  108. int c = fgetc(infile);
  109. *byte = c;
  110. if (c == '\n') line_no++;
  111. return c != EOF;
  112. }
  113. static inline BOOL unread_byte( char last_byte )
  114. {
  115. if (last_byte == '\n') line_no--;
  116. return ungetc(last_byte, infile) != EOF;
  117. }
  118. static inline BOOL read_bytes( void *data, DWORD size )
  119. {
  120. return fread(data, size, 1, infile) > 0;
  121. }
  122. static BOOL write_c_hex_bytes(void)
  123. {
  124. UINT i;
  125. for (i = 0; i < output_pos; i++)
  126. {
  127. if (i % 12 == 0)
  128. fprintf(outfile, "\n ");
  129. fprintf(outfile, " 0x%02x,", output_data[i]);
  130. }
  131. return TRUE;
  132. }
  133. static BOOL write_raw_bytes(void)
  134. {
  135. return fwrite(output_data, output_pos, 1, outfile) > 0;
  136. }
  137. static inline BOOL write_bytes(const void *data, DWORD size)
  138. {
  139. if (output_pos + size > output_size)
  140. {
  141. output_size = max( output_size * 2, size );
  142. output_data = realloc( output_data, output_size );
  143. if (!output_data) return FALSE;
  144. }
  145. memcpy( output_data + output_pos, data, size );
  146. output_pos += size;
  147. return TRUE;
  148. }
  149. static inline BOOL write_byte(BYTE value)
  150. {
  151. return write_bytes( &value, sizeof(value) );
  152. }
  153. static inline BOOL write_word(WORD value)
  154. {
  155. return write_byte( value ) &&
  156. write_byte( value >> 8 );
  157. }
  158. static inline BOOL write_dword(DWORD value)
  159. {
  160. return write_word( value ) &&
  161. write_word( value >> 16 );
  162. }
  163. static inline BOOL write_float(float value)
  164. {
  165. DWORD val;
  166. memcpy( &val, &value, sizeof(value) );
  167. return write_dword( val );
  168. }
  169. static inline BOOL write_guid(const GUID *guid)
  170. {
  171. return write_dword( guid->Data1 ) &&
  172. write_word( guid->Data2 ) &&
  173. write_word( guid->Data3 ) &&
  174. write_bytes( guid->Data4, sizeof(guid->Data4) );
  175. }
  176. static int compare_names(const void *a, const void *b)
  177. {
  178. return strcasecmp(*(const char **)a, *(const char **)b);
  179. }
  180. static BOOL parse_keyword( const char *name )
  181. {
  182. const struct keyword *keyword;
  183. keyword = bsearch(&name, reserved_words, ARRAY_SIZE(reserved_words),
  184. sizeof(reserved_words[0]), compare_names);
  185. if (!keyword)
  186. return FALSE;
  187. return write_word(keyword->token);
  188. }
  189. static BOOL parse_guid(void)
  190. {
  191. char buf[39];
  192. GUID guid;
  193. DWORD tab[10];
  194. BOOL ret;
  195. static const char *guidfmt = "<%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X>";
  196. buf[0] = '<';
  197. if (!read_bytes(buf + 1, 37)) fatal_error( "truncated GUID\n" );
  198. buf[38] = 0;
  199. ret = sscanf(buf, guidfmt, &guid.Data1, tab, tab+1, tab+2, tab+3, tab+4, tab+5, tab+6, tab+7, tab+8, tab+9);
  200. if (ret != 11) fatal_error( "invalid GUID '%s'\n", buf );
  201. guid.Data2 = tab[0];
  202. guid.Data3 = tab[1];
  203. guid.Data4[0] = tab[2];
  204. guid.Data4[1] = tab[3];
  205. guid.Data4[2] = tab[4];
  206. guid.Data4[3] = tab[5];
  207. guid.Data4[4] = tab[6];
  208. guid.Data4[5] = tab[7];
  209. guid.Data4[6] = tab[8];
  210. guid.Data4[7] = tab[9];
  211. return write_word(TOKEN_GUID) &&
  212. write_guid(&guid);
  213. }
  214. static BOOL parse_name(void)
  215. {
  216. char c;
  217. int len = 0;
  218. char name[512];
  219. while (read_byte(&c) && len < sizeof(name) &&
  220. (isalnum(c) || c == '_' || c == '-'))
  221. {
  222. if (len + 1 < sizeof(name))
  223. name[len++] = c;
  224. }
  225. unread_byte(c);
  226. name[len] = 0;
  227. if (parse_keyword(name)) {
  228. return TRUE;
  229. } else {
  230. return write_word(TOKEN_NAME) &&
  231. write_dword(len) &&
  232. write_bytes(name, len);
  233. }
  234. }
  235. static BOOL parse_number(void)
  236. {
  237. int len = 0;
  238. char c;
  239. char buffer[512];
  240. BOOL dot = FALSE;
  241. BOOL ret;
  242. while (read_byte(&c) &&
  243. ((!len && c == '-') || (!dot && c == '.') || isdigit(c)))
  244. {
  245. if (len + 1 < sizeof(buffer))
  246. buffer[len++] = c;
  247. if (c == '.')
  248. dot = TRUE;
  249. }
  250. unread_byte(c);
  251. buffer[len] = 0;
  252. if (dot) {
  253. float value;
  254. ret = sscanf(buffer, "%f", &value);
  255. if (!ret) fatal_error( "invalid float token\n" );
  256. ret = write_word(TOKEN_FLOAT) &&
  257. write_float(value);
  258. } else {
  259. int value;
  260. ret = sscanf(buffer, "%d", &value);
  261. if (!ret) fatal_error( "invalid integer token\n" );
  262. ret = write_word(TOKEN_INTEGER) &&
  263. write_dword(value);
  264. }
  265. return ret;
  266. }
  267. static BOOL parse_token(void)
  268. {
  269. char c;
  270. int len;
  271. char *tok, buffer[512];
  272. if (!read_byte(&c))
  273. return FALSE;
  274. switch (c)
  275. {
  276. case '\n':
  277. case '\r':
  278. case ' ':
  279. case '\t':
  280. return TRUE;
  281. case '{': return write_word(TOKEN_OBRACE);
  282. case '}': return write_word(TOKEN_CBRACE);
  283. case '[': return write_word(TOKEN_OBRACKET);
  284. case ']': return write_word(TOKEN_CBRACKET);
  285. case '(': return write_word(TOKEN_OPAREN);
  286. case ')': return write_word(TOKEN_CPAREN);
  287. case ',': return write_word(TOKEN_COMMA);
  288. case ';': return write_word(TOKEN_SEMICOLON);
  289. case '.': return write_word(TOKEN_DOT);
  290. case '/':
  291. if (!read_byte(&c) || c != '/')
  292. fatal_error( "invalid single '/' comment token\n" );
  293. while (read_byte(&c) && c != '\n');
  294. return c == '\n';
  295. case '#':
  296. len = 0;
  297. while (read_byte(&c) && c != '\n')
  298. if (len + 1 < sizeof(buffer)) buffer[len++] = c;
  299. if (c != '\n') fatal_error( "line too long\n" );
  300. buffer[len] = 0;
  301. tok = strtok( buffer, " \t" );
  302. if (!tok || strcmp( tok, "pragma" )) return TRUE;
  303. tok = strtok( NULL, " \t" );
  304. if (!tok || strcmp( tok, "xftmpl" )) return TRUE;
  305. tok = strtok( NULL, " \t" );
  306. if (!tok) return TRUE;
  307. if (!strcmp( tok, "name" ))
  308. {
  309. tok = strtok( NULL, " \t" );
  310. if (tok && !option_inc_var_name) option_inc_var_name = xstrdup( tok );
  311. }
  312. else if (!strcmp( tok, "size" ))
  313. {
  314. tok = strtok( NULL, " \t" );
  315. if (tok && !option_inc_size_name) option_inc_size_name = xstrdup( tok );
  316. }
  317. return TRUE;
  318. case '<':
  319. return parse_guid();
  320. case '"':
  321. len = 0;
  322. /* FIXME: Handle '\' (e.g. "valid\"string") */
  323. while (read_byte(&c) && c != '"') {
  324. if (len + 1 < sizeof(buffer))
  325. buffer[len++] = c;
  326. }
  327. if (c != '"') fatal_error( "unterminated string\n" );
  328. return write_word(TOKEN_STRING) &&
  329. write_dword(len) &&
  330. write_bytes(buffer, len);
  331. default:
  332. unread_byte(c);
  333. if (isdigit(c) || c == '-')
  334. return parse_number();
  335. if (isalpha(c) || c == '_')
  336. return parse_name();
  337. fatal_error( "invalid character '%c' to start token\n", c );
  338. }
  339. return TRUE;
  340. }
  341. static const char *output_file;
  342. static void cleanup_files(void)
  343. {
  344. if (output_file) unlink(output_file);
  345. }
  346. static void exit_on_signal( int sig )
  347. {
  348. exit(1); /* this will call the atexit functions */
  349. }
  350. static void usage(void)
  351. {
  352. fprintf(stderr, "Usage: %s [OPTIONS] INFILE\n"
  353. "Options:\n"
  354. " -H Output to a c header file instead of a binary file\n"
  355. " -i NAME Output to a c header file, data in variable NAME\n"
  356. " -s NAME In a c header file, define NAME to be the data size\n"
  357. " -o FILE Write output to FILE\n",
  358. program_name);
  359. }
  360. static void option_callback( int optc, char *optarg )
  361. {
  362. switch (optc)
  363. {
  364. case 'h':
  365. usage();
  366. exit(0);
  367. case 'H':
  368. option_header = TRUE;
  369. break;
  370. case 'i':
  371. option_header = TRUE;
  372. option_inc_var_name = xstrdup(optarg);
  373. break;
  374. case 'o':
  375. option_outfile_name = xstrdup(optarg);
  376. break;
  377. case 's':
  378. option_inc_size_name = xstrdup(optarg);
  379. break;
  380. case '?':
  381. fprintf( stderr, "%s: %s\n", program_name, optarg );
  382. exit(1);
  383. }
  384. }
  385. int main(int argc, char **argv)
  386. {
  387. char header[16];
  388. struct strarray args;
  389. char *header_name = NULL;
  390. program_name = argv[0];
  391. args = parse_options(argc, argv, "hHi:o:s:", NULL, 0, option_callback );
  392. if (!args.count)
  393. {
  394. usage();
  395. return 1;
  396. }
  397. infile_name = args.str[0];
  398. infile = stdin;
  399. outfile = NULL;
  400. if (!strcmp(infile_name, "-")) {
  401. infile_name = "stdin";
  402. } else if (!(infile = fopen(infile_name, "rb"))) {
  403. perror(infile_name);
  404. goto error;
  405. }
  406. if (!read_bytes(header, sizeof(header))) {
  407. fprintf(stderr, "%s: Failed to read file header\n", program_name);
  408. goto error;
  409. }
  410. if (strncmp(header, "xof ", 4))
  411. {
  412. fprintf(stderr, "%s: Invalid magic value '%.4s'\n", program_name, header);
  413. goto error;
  414. }
  415. if (strncmp(header + 4, "0302", 4) && strncmp(header + 4, "0303", 4))
  416. {
  417. fprintf(stderr, "%s: Unsupported version '%.4s'\n", program_name, header + 4);
  418. goto error;
  419. }
  420. if (strncmp(header + 8, "txt ", 4))
  421. {
  422. fprintf(stderr, "%s: Only support conversion from text encoded X files.",
  423. program_name);
  424. goto error;
  425. }
  426. if (strncmp(header + 12, "0032", 4) && strncmp(header + 12, "0064", 4))
  427. {
  428. fprintf(stderr, "%s: Only 32-bit or 64-bit float format supported, not '%.4s'.\n",
  429. program_name, header + 12);
  430. goto error;
  431. }
  432. if (!strcmp(option_outfile_name, "-")) {
  433. option_outfile_name = "stdout";
  434. outfile = stdout;
  435. } else {
  436. output_file = option_outfile_name;
  437. atexit(cleanup_files);
  438. signal(SIGTERM, exit_on_signal);
  439. signal(SIGINT, exit_on_signal);
  440. #ifdef SIGHUP
  441. signal(SIGHUP, exit_on_signal);
  442. #endif
  443. if (!(outfile = fopen(output_file, "wb"))) {
  444. perror(option_outfile_name);
  445. goto error;
  446. }
  447. }
  448. if (!write_bytes("xof 0302bin 0064", 16))
  449. goto error;
  450. line_no = 1;
  451. while (parse_token());
  452. if (ferror(outfile) || ferror(infile))
  453. goto error;
  454. if (option_header)
  455. {
  456. char *str_ptr;
  457. if (!option_inc_var_name)
  458. fatal_error( "variable name must be specified with -i or #pragma name\n" );
  459. header_name = get_basename( option_outfile_name );
  460. str_ptr = header_name;
  461. while (*str_ptr) {
  462. if (*str_ptr == '.')
  463. *str_ptr = '_';
  464. else
  465. *str_ptr = toupper(*str_ptr);
  466. str_ptr++;
  467. }
  468. fprintf(outfile,
  469. "/* File generated automatically from %s; do not edit */\n"
  470. "\n"
  471. "#ifndef __WINE_%s\n"
  472. "#define __WINE_%s\n"
  473. "\n"
  474. "unsigned char %s[] = {",
  475. infile_name, header_name, header_name, option_inc_var_name);
  476. write_c_hex_bytes();
  477. fprintf(outfile, "\n};\n\n");
  478. if (option_inc_size_name)
  479. fprintf(outfile, "#define %s %u\n\n", option_inc_size_name, output_pos);
  480. fprintf(outfile, "#endif /* __WINE_%s */\n", header_name);
  481. if (ferror(outfile))
  482. goto error;
  483. }
  484. else write_raw_bytes();
  485. fclose(infile);
  486. fclose(outfile);
  487. output_file = NULL;
  488. return 0;
  489. error:
  490. if (infile) {
  491. if (ferror(infile))
  492. perror(infile_name);
  493. fclose(infile);
  494. }
  495. if (outfile) {
  496. if (ferror(outfile))
  497. perror(option_outfile_name);
  498. fclose(outfile);
  499. }
  500. return 1;
  501. }