Release 961102
[wine/hacks.git] / rc / winerc.c
blob43a27f3be7783fe97a2626260884f5c87554304b
1 /*
3 * Copyright Martin von Loewis, 1994
5 */
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/stat.h>
11 #include <sys/fcntl.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include "windows.h"
16 #include "parser.h"
18 char usage[]="winerc -bdvc -p prefix -o outfile < infile \n"
19 " -b Create a C array from a binary .res file\n"
20 " -c Add 'const' prefix to C constants\n"
21 " -d Output debugging information\n"
22 " -p prefix Give a prefix for the generated names\n"
23 " -v Show each resource as it is processed\n"
24 " -o file Output to file.c and file.h\n";
27 /*might be overwritten by command line*/
28 char *prefix="_Resource";
29 int verbose,constant;
30 gen_res* g_start;
31 FILE *header,*code;
32 char hname[256],sname[256];
34 int transform_binary_file(void);
35 int yyparse(void);
37 static void *xmalloc (size_t size)
39 void *res;
41 res = malloc (size ? size : 1);
42 if (res == NULL)
44 fprintf (stderr, "Virtual memory exhausted.\n");
45 exit (1);
47 return res;
51 int main(int argc,char *argv[])
53 extern int yydebug;
54 extern char* optarg;
55 char* tmpc;
56 int optc,lose,ret,binary;
57 lose=binary=0;
58 while((optc=getopt(argc,argv,"bcdp:vo:"))!=EOF)
59 switch(optc)
61 /* bison will print state transitions on stderr */
62 case 'b':binary=1;
63 break;
64 case 'd':yydebug=1;
65 setbuf(stdout,0);
66 setbuf(stderr,0);
67 break;
68 case 'p':prefix=strdup(optarg);
69 if(!isalpha(*prefix))*prefix='_';
70 for(tmpc=prefix;*tmpc;tmpc++)
71 if( !isalnum(*tmpc) && *tmpc!='_')
72 *tmpc='_';
73 break;
74 case 'c':constant=1;break;
75 case 'v':verbose=1;
76 setbuf(stderr,0);
77 break;
78 case 'o':set_out_file(optarg);break;
79 default: lose++;break;
81 if(lose)return fprintf(stderr,usage),1;
82 if(!header)header=stdout;
83 if(!code)code=stdout;
84 if(binary)
85 ret=transform_binary_file();
86 else
87 ret=yyparse();
88 fclose(header);
89 fclose(code);
90 return ret;
93 void set_out_file(char *prefix)
95 sprintf(sname,"%s.c",prefix);
96 code=fopen(sname,"w");
97 sprintf(hname,"%s.h",prefix);
98 header=fopen(hname,"w");
101 int transform_binary_file()
103 int i,c;
104 fprintf(header,"#define APPLICATION_HAS_RESOURCES 1\n");
105 fprintf(code,"char _Application_resources[]={");
106 for(i=0;;i++)
108 c=getchar();
109 if(c==-1)break;
110 if(i%16==0)fputc('\n',code);
111 fprintf(code,"%3d,",c);
113 fprintf(code,"\n 0};\nint _Application_resources_size=%d;\n",i);
114 return 0;
117 /* SunOS' memcpy is wrong for overlapping arrays */
118 char *save_memcpy(char *d,char* s,int l)
120 if(d<s)
121 for(;l;l--)*d++=*s++;
122 else
123 for(d+=l-1,s+=l-1;l;l--)*d--=*s--;
124 return d;
127 /*allow unaligned access*/
128 void put_WORD(unsigned char* p,WORD w)
130 *p=w&0xFF;
131 *(p+1)=w>>8;
134 void put_DWORD(unsigned char* p,DWORD d)
136 put_WORD(p,d&0xFFFF);
137 put_WORD(p+2,d>>16);
140 WORD get_WORD(unsigned char* p)
142 return *p|(*(p+1)<<8);
145 DWORD get_DWORD(unsigned char* p)
147 return get_WORD(p)|(get_WORD(p+2)<<16);
151 /*create a new gen_res, initial size 100*/
152 gen_res *new_res()
153 { gen_res* ret=xmalloc(sizeof(gen_res)+100);
154 int i;
155 for(i=0;i<sizeof(gen_res)+100;i++)*((char*)ret+i)='\0';
156 ret->g_next=g_start;
157 ret->g_prev=0;
158 g_start=ret;
159 ret->space=100;
160 return ret;
163 /*double the space*/
164 gen_res* grow(gen_res* res)
166 res=realloc(res,sizeof(gen_res)+2*res->space);
167 if(!res)
168 fprintf(stderr,"Out of memory\n"),exit(1);
169 if(!res->g_prev)g_start=res;
170 else res->g_prev->g_next=res;
171 if(res->g_next)res->g_next->g_prev=res;
172 res->space=2*res->space;
173 return res;
177 /* insert bytes at offset 0, increase num_entries */
178 gen_res* insert_at_beginning(gen_res* res,char* entry,int size)
180 while(res->size+size>res->space)res=grow(res);
181 save_memcpy(res->res+size,res->res,res->size);
182 save_memcpy(res->res,entry,size);
183 res->size+=size;
184 res->num_entries++;
185 return res;
188 /* insert length characters from bytes into res, starting at start */
189 gen_res* insert_bytes(gen_res* res,char* bytes,int start,int length)
191 while(res->size+length>res->space)res=grow(res);
192 save_memcpy(res->res+start+length,res->res+start,res->size-start);
193 save_memcpy(res->res+start,bytes,length);
194 res->size+=length;
195 return res;
198 /*delete len bytes from res, starting at start*/
199 gen_res* delete_bytes(gen_res* res,int start,int len)
201 save_memcpy(res->res+start,res->res+start+len,res->size-start-len);
202 res->size-=len;
203 return res;
206 /*create a new style*/
207 rc_style *new_style()
209 rc_style *ret=xmalloc(sizeof(rc_style));
210 /*initially, no bits have to be reset*/
211 ret->and=-1;
212 /*initially, no bits are set*/
213 ret->or=WS_CHILD | WS_VISIBLE;
214 return ret;
217 /* entries are inserted at the beginning, starting from the last one */
218 gen_res* add_accelerator(int ev, int id, int flags, gen_res* prev)
220 char accel_entry[5];
221 if(prev->num_entries==0)flags|=0x80; /* last entry */
222 accel_entry[0]=flags;
223 put_WORD(accel_entry+1,ev);
224 put_WORD(accel_entry+3,id);
225 return insert_at_beginning(prev,accel_entry,5);
229 /* create an integer from the event, taking things as "^c" into account
230 add this as new entry */
231 gen_res* add_string_accelerator(char *ev, int id, int flags, gen_res* prev)
233 int event;
234 if(*ev=='^')
235 event=ev[1]-'a';
236 else
237 event=ev[0];
238 return add_accelerator(event,id,flags,prev);
241 /*is there a difference between ASCII and VIRTKEY accelerators? */
243 gen_res* add_ascii_accelerator(int ev, int id, int flags, gen_res* prev)
245 return add_accelerator(ev,id,flags,prev);
248 gen_res* add_vk_accelerator(int ev, int id, int flags, gen_res* prev)
250 return add_accelerator(ev,id,flags,prev);
253 /* create a new dialog header, set all items to 0 */
254 gen_res* new_dialog()
255 { gen_res* ret=new_res();
256 ret->size=16; /*all strings "\0", no font*/
257 return ret;
260 /* the STYLE option was specified */
261 gen_res* dialog_style(rc_style* style, gen_res* attr)
263 /* default dialog styles? Do we need style->and? */
264 /* DS_SETFONT might have been specified before */
265 put_DWORD(attr->res,get_DWORD(attr->res)|style->or);
266 return attr;
269 /* menu name is at offset 13 */
270 int dialog_get_menu(gen_res* attr)
272 return 13;
275 /* the class is after the menu name */
276 int dialog_get_class(gen_res* attr)
278 int offs=dialog_get_menu(attr);
279 while(attr->res[offs])offs++;
280 offs++;
281 return offs;
284 /* the caption is after the class */
285 int dialog_get_caption(gen_res* attr)
287 int offs=dialog_get_class(attr);
288 while(attr->res[offs])offs++;
289 offs++;
290 return offs;
293 /* the fontsize, if present, is after the caption, followed by the font name */
294 int dialog_get_fontsize(gen_res* attr)
296 int offs=dialog_get_caption(attr);
297 while(attr->res[offs])offs++;
298 offs++;
299 return offs;
303 /* the CAPTION option was specified */
304 gen_res* dialog_caption(char* cap, gen_res*attr)
306 /* we don't need the terminating 0 as it's already there */
307 return insert_bytes(attr,cap,dialog_get_caption(attr),strlen(cap));
311 /* the FONT option was specified, set the DS_SETFONT flag */
312 gen_res* dialog_font(short size,char* font,gen_res *attr)
314 char c_size[2];
315 int offs=dialog_get_fontsize(attr);
316 put_DWORD(attr->res,get_DWORD(attr->res)|DS_SETFONT);
317 put_WORD(c_size,size);
318 attr=insert_bytes(attr,c_size,offs,2);
319 offs+=2;
320 /* as there is no font name by default, copy the '\0' */
321 return insert_bytes(attr,font,offs,strlen(font)+1);
324 gen_res* dialog_class(char* cap, gen_res*attr)
326 return insert_bytes(attr,cap,dialog_get_class(attr),strlen(cap));
329 gen_res* dialog_menu(char* cap, gen_res*attr)
331 return insert_bytes(attr,cap,dialog_get_menu(attr),strlen(cap));
334 /* set the dialogs id, position, extent, and style */
335 gen_res* create_control_desc(int id,int x,int y,int cx, int cy,rc_style *style)
336 { gen_res* ret=new_res();
337 int s=WS_VISIBLE|WS_CHILD; /*defaults styles for any control*/
338 put_WORD(ret->res+0,x);
339 put_WORD(ret->res+2,y);
340 put_WORD(ret->res+4,cx);
341 put_WORD(ret->res+6,cy);
342 put_WORD(ret->res+8,id);
343 if(style)s=(s|style->or)&style->and;
344 put_DWORD(ret->res+10,s);
345 ret->size=17; /*empty strings, unused byte*/
346 return ret;
349 /* insert the control's label */
350 gen_res* label_control_desc(char* label,gen_res* cd)
352 int offs;
353 if(cd->res[14]&0x80)offs=15; /* one-character class */
354 else {
355 for(offs=14;cd->res[offs];offs++);
356 offs++;
358 return insert_bytes(cd,label,offs,strlen(label));
361 /* a CONTROL was specified */
362 gen_res* create_generic_control(char* label,int id,char* class,
363 rc_style*style,int x,int y,int cx,int cy)
364 { char cl;
365 gen_res* ret=new_res();
366 put_WORD(ret->res+0,x);
367 put_WORD(ret->res+2,y);
368 put_WORD(ret->res+4,cx);
369 put_WORD(ret->res+6,cy);
370 put_WORD(ret->res+8,id);
371 put_DWORD(ret->res+10,style->or);
372 ret->size=17;
373 ret=insert_bytes(ret,label,15,strlen(label));
374 /* is it a predefined class? */
375 cl=0;
376 if(!strcmp(class,"BUTTON"))cl=CT_BUTTON;
377 if(!strcmp(class,"EDIT"))cl=CT_EDIT;
378 if(!strcmp(class,"STATIC"))cl=CT_STATIC;
379 if(!strcmp(class,"LISTBOX"))cl=CT_LISTBOX;
380 if(!strcmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR;
381 if(!strcmp(class,"COMBOBOX"))cl=CT_COMBOBOX;
382 if(cl)ret->res[14]=cl;
383 else ret=insert_bytes(ret,class,14,strlen(class));
384 return ret;
387 /* insert cd into rest, set the type, add flags */
388 gen_res* add_control(int type,int flags,gen_res*cd,gen_res* rest)
390 put_DWORD(cd->res+10,get_DWORD(cd->res+10)|flags);
391 cd->res[14]=type;
392 return insert_at_beginning(rest,cd->res,cd->size);
395 /* an ICON control was specified, whf contains width, height, and flags */
396 gen_res* add_icon(char* name,int id,int x,int y,gen_res* whf,gen_res* rest)
398 put_WORD(whf->res+0,x);
399 put_WORD(whf->res+2,y);
400 put_WORD(whf->res+8,id);
401 whf=label_control_desc(name,whf);
402 return add_control(CT_STATIC,SS_ICON,whf,rest);
405 /* insert the generic control into rest */
406 gen_res* add_generic_control(gen_res* ctl, gen_res* rest)
408 return insert_at_beginning(rest,ctl->res,ctl->size);
411 /* create a dialog resource by inserting the header into the controls.
412 Set position and extent */
413 gen_res* make_dialog(gen_res* header,int x,int y,int cx,int cy,gen_res* ctls)
415 header->res[4]=ctls->num_entries;
416 header->type=dlg;
417 put_WORD(header->res+5,x);
418 put_WORD(header->res+7,y);
419 put_WORD(header->res+9,cx);
420 put_WORD(header->res+11,cy);
421 return insert_bytes(header,ctls->res,header->size,ctls->size);
424 /* create {0x15,0x16,0xFF} from '15 16 FF' */
425 gen_res *hex_to_raw(char *hex, gen_res*rest)
427 char r2[16];
428 int i;
429 for(i=0;*hex!='\'';i++)r2[i]=strtoul(hex,&hex,16);
430 return insert_bytes(rest,r2,0,i);
433 /* create a bitmap resource */
434 gen_res *make_bitmap(gen_res* res)
436 res=delete_bytes(res,0,14); /* skip bitmap file header*/
437 res->type=bmp;
438 return res;
441 gen_res *make_icon(gen_res* res)
443 res->type=ico;
444 return res;
447 gen_res *make_cursor(gen_res* res)
449 res->type=cur;
450 return res;
453 /* load resource bytes from the file name */
454 gen_res *load_file(char* name)
456 gen_res *res;
457 struct stat st;
458 int f=open(name,O_RDONLY);
459 if(!f)perror(name);
460 fstat(f,&st);
461 res=new_res();
462 while(res->space<st.st_size)res=grow(res);
463 read(f,res->res,st.st_size);
464 res->size=st.st_size;
465 close(f);
466 return res;
469 /* insert a normal menu item into res, starting from the last item */
470 gen_res *add_menuitem(char* name,int id,int flags,gen_res *res)
472 char item[4];
473 if(res->num_entries==0)flags|=MF_END;
474 put_WORD(item,flags);
475 put_WORD(item+2,id);
476 res=insert_at_beginning(res,name,strlen(name)+1);
477 res=insert_bytes(res,item,0,4);
478 return res;
481 /* insert a popup item into res */
482 gen_res *add_popup(char *name,short flags, gen_res* body, gen_res*res)
484 char c_flags[2];
485 flags|=MF_POPUP;
486 if(res->num_entries==0)flags|=MF_END;
487 put_WORD(c_flags,flags);
488 res=insert_at_beginning(res,body->res,body->size);
489 res=insert_bytes(res,name,0,strlen(name)+1);
490 res=insert_bytes(res,c_flags,0,2);
491 return res;
494 /* prefix the menu header into res */
495 gen_res *make_menu(gen_res* res)
497 static char header[4]={0,0,0,0};
498 res=insert_at_beginning(res,header,4);
499 res->type=men;
500 return res;
503 /* link top-level resources */
504 gen_res *add_resource(gen_res* first,gen_res *rest)
506 if(first)
508 first->next=rest;
509 return first;
511 else
512 return rest;
515 typedef struct str_tbl_elm{
516 int group;
517 struct str_tbl_elm *next;
518 char* strings[16];
519 } str_tbl_elm;
521 str_tbl_elm* string_table=NULL; /* sorted by group */
523 void add_str_tbl_elm(int id,char* str)
525 int group=(id>>4)+1;
526 int idx=id & 0x000f;
528 str_tbl_elm** elm=&string_table;
529 while(*elm && (*elm)->group<group) elm=&(*elm)->next;
530 if(!*elm || (*elm)->group!=group)
532 str_tbl_elm* new=xmalloc(sizeof(str_tbl_elm));
533 new->group=group;
534 new->next=*elm;
535 *elm=new;
537 (*elm)->strings[idx]=str;
540 gen_res* add_string_table(gen_res* t)
542 str_tbl_elm* ste;
543 int size,i;
544 gen_res* res;
545 unsigned char* p;
546 char* q;
548 if(!string_table) return t;
549 for(ste=string_table; ste; ste=ste->next)
551 for(size=0,i=0; i<16; i++)
552 size += ste->strings[i] ? strlen(ste->strings[i])+1 : 1;
553 res=new_res();
554 while(res->space<size)res=grow(res);
555 res->type=str;
556 res->n.i_name=ste->group;
557 res->n_type=0;
558 res->size=size;
559 for(p=res->res,i=0; i<16; i++)
560 if((q=ste->strings[i])==NULL)
561 *p++ = 0;
562 else
564 *p++ = strlen(q);
565 while(*q) *p++ = *q++;
567 t=add_resource(res,t);
569 return t;
572 char *get_typename(gen_res* t)
574 switch(t->type){
575 case acc:return "ACCELERATOR";
576 case bmp:return "BITMAP";
577 case cur:return "CURSOR";
578 case dlg:return "DIALOG";
579 case fnt:return "FONT";
580 case ico:return "ICON";
581 case men:return "MENU";
582 case rdt:return "RCDATA";
583 case str:return "STRINGTABLE";
584 default: return "UNKNOWN";
588 /* create strings like _Sysres_DIALOG_2 */
589 char *get_resource_name(gen_res*it)
591 static char buf[1000];
592 if(it->n_type)
593 sprintf(buf,"%s_%s_%s",prefix,get_typename(it),it->n.s_name);
594 else
595 sprintf(buf,"%s_%s_%d",prefix,get_typename(it),it->n.i_name);
596 return buf;
599 #define ISCONSTANT (constant ? "const " : "")
601 /* create the final output */
602 void create_output(gen_res* top)
604 gen_res *it;
606 top=add_string_table(top);
608 fprintf( header, "/* %s\n"
609 " * This file is automatically generated. Do not edit!\n"
610 " */\n\n"
611 "#include \"resource.h\"\n", hname );
613 /* Declare the resources */
615 for (it=top;it;it=it->next)
616 fprintf( header,"extern %sstruct resource %s;\n",
617 ISCONSTANT, get_resource_name(it) );
618 fprintf( header,"\nextern %sstruct resource * %s%s_Table[];\n",
619 ISCONSTANT, ISCONSTANT, prefix );
621 /* Print the resources bytes */
623 fprintf( code, "/* %s\n"
624 " * This file is automatically generated. Do not edit!\n"
625 " */\n\n"
626 "#include \"%s\"\n", sname, hname );
628 for(it=top;it;it=it->next)
630 int i;
631 fprintf( code, "static %sunsigned char %s__bytes[] = {\n",
632 ISCONSTANT, get_resource_name(it) );
633 for (i=0;i<it->size-1;i++)
635 fprintf(code,"0x%02x, ",it->res[i]);
636 if ((i&7)==7)fputc('\n',code);
638 fprintf(code,"0x%02x };\n\n",it->res[i]);
641 /* Print the resources */
642 for (it=top;it;it=it->next)
644 int type;
645 switch(it->type)
647 case acc:type=(int)RT_ACCELERATOR;break;
648 case bmp:type=(int)RT_BITMAP;break;
649 case cur:type=(int)RT_CURSOR;break;
650 case dlg:type=(int)RT_DIALOG;break;
651 case fnt:type=(int)RT_FONT;break;
652 case ico:type=(int)RT_ICON;break;
653 case men:type=(int)RT_MENU;break;
654 case rdt:type=(int)RT_RCDATA;break;
655 case str:type=(int)RT_STRING;break;
656 default:fprintf(stderr,"Unknown restype\n");type=-1;break;
658 if(it->n_type)
659 fprintf(code,"%sstruct resource %s = {0,%d,\"%s\",%s__bytes,%d};\n",
660 ISCONSTANT, get_resource_name(it), type, it->n.s_name,
661 get_resource_name(it), it->size );
662 else
663 fprintf(code,"%sstruct resource %s = {%d,%d,\"@%d\",%s__bytes,%d};\n",
664 ISCONSTANT, get_resource_name(it), it->n.i_name, type,
665 it->n.i_name, get_resource_name(it), it->size );
668 /* Print the resource table (NULL terminated) */
670 fprintf(code,"\n%sstruct resource * %s%s_Table[] = {\n",
671 ISCONSTANT, ISCONSTANT, prefix);
672 for (it=top;it;it=it->next)
673 fprintf( code, " &%s,\n", get_resource_name(it) );
674 fprintf( code, " 0\n};\n" );
676 /* Perform autoregistration */
677 fprintf( code,
678 "#ifdef WINELIB\n"
679 "static void DoIt() WINE_CONSTRUCTOR;\n"
680 "static void DoIt()\n"
681 "{\n"
682 "\tLIBRES_RegisterResources(%s_Table);\n"
683 "}\n\n"
684 "#ifndef HAVE_WINE_CONSTRUCTOR\n"
685 "void LIBWINE_Register_%s(){\n"
686 "\tDoIt();\n"
687 "}\n"
688 "#endif\n"
689 "#endif /*WINELIB*/\n"
690 ,prefix,prefix);
693 gen_res* make_font(gen_res* res)
695 fprintf(stderr,"Fonts not supported\n");
696 return NULL;
699 gen_res* make_raw(gen_res* res)
701 fprintf(stderr,"RCData not supported\n");
702 return NULL;
705 gen_res* int_to_raw(int i,gen_res* res)
707 fprintf(stderr,"IntToRaw not supported\n");
708 return NULL;
711 /* translate "Hello,\\tworld!\\10" to "Hello,\tworld!\n" */
712 char *parse_c_string(char *in)
714 char *out=xmalloc(strlen(in)-1);
715 char *it;
716 char tmp[5],*tend;
717 for(it=out,in++;*in;in++)
718 if(*in=='\\')
719 switch(*++in)
720 {case 't':*it++='\t';break;
721 case 'r':*it++='\r';break;
722 case 'n':*it++='\n';break;
723 case 'a':*it++='\a';break;
724 case '0':
725 memset(tmp,0,5);/*make sure it doesn't use more than 4 chars*/
726 memcpy(tmp,in,4);
727 *it++=strtoul(tmp,&tend,0);
728 in+=tend-tmp-1;
729 break;
730 case '1':case '2':case '3':case '4':case '5':
731 case '6':case '7':case '8':case '9':
732 memset(tmp,0,5);
733 memcpy(tmp,in,3);
734 *it++=strtoul(tmp,&tend,10);
735 in+=tend-tmp-1;
736 break;
737 case 'x':
738 memset(tmp,0,5);
739 memcpy(tmp,++in,2);
740 *it++=strtoul(tmp,&tend,16);
741 in+=tend-tmp-1;
742 break;
743 default:*it++=*in;
745 else
746 *it++=*in;
747 *(it-1)='\0';
748 return out;