Release 960506
[wine.git] / rc / winerc.c
blob32a4dd17c133df56f2d7e55bebb52a50662f54e5
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 <neexe.h> */
17 #include "parser.h"
18 #include "y.tab.h"
20 char usage[]="winerc -bdvc -p prefix -o outfile < infile \n"
21 " -b Create a C array from a binary .res file\n"
22 " -c Add 'const' prefix to C constants\n"
23 " -d Output debugging information\n"
24 " -p prefix Give a prefix for the generated names\n"
25 " -v Show each resource as it is processed\n"
26 " -o file Output to file.c and file.h\n";
29 /*might be overwritten by command line*/
30 char *prefix="_Resource";
31 int verbose,constant;
32 gen_res* g_start;
33 FILE *header,*code;
34 char hname[256],sname[256];
36 int transform_binary_file(void);
37 int yyparse(void);
39 static void *xmalloc (size_t size)
41 void *res;
43 res = malloc (size ? size : 1);
44 if (res == NULL)
46 fprintf (stderr, "Virtual memory exhausted.\n");
47 exit (1);
49 return res;
53 int main(int argc,char *argv[])
55 extern int yydebug;
56 extern char* optarg;
57 char* tmpc;
58 int optc,lose,ret,binary;
59 lose=binary=0;
60 while((optc=getopt(argc,argv,"bcdp:vo:"))!=EOF)
61 switch(optc)
63 /* bison will print state transitions on stderr */
64 case 'b':binary=1;
65 break;
66 case 'd':yydebug=1;
67 setbuf(stdout,0);
68 setbuf(stderr,0);
69 break;
70 case 'p':prefix=strdup(optarg);
71 if(!isalpha(*prefix))*prefix='_';
72 for(tmpc=prefix;*tmpc;tmpc++)
73 if( !isalnum(*tmpc) && *tmpc!='_')
74 *tmpc='_';
75 break;
76 case 'c':constant=1;break;
77 case 'v':verbose=1;
78 setbuf(stderr,0);
79 break;
80 case 'o':set_out_file(optarg);break;
81 default: lose++;break;
83 if(lose)return fprintf(stderr,usage),1;
84 if(!header)header=stdout;
85 if(!code)code=stdout;
86 if(binary)
87 ret=transform_binary_file();
88 else
89 ret=yyparse();
90 fclose(header);
91 fclose(code);
92 return ret;
95 void set_out_file(char *prefix)
97 sprintf(sname,"%s.c",prefix);
98 code=fopen(sname,"w");
99 sprintf(hname,"%s.h",prefix);
100 header=fopen(hname,"w");
103 int transform_binary_file()
105 int i,c;
106 fprintf(header,"#define APPLICATION_HAS_RESOURCES 1\n");
107 fprintf(code,"char _Application_resources[]={");
108 for(i=0;;i++)
110 c=getchar();
111 if(c==-1)break;
112 if(i%16==0)fputc('\n',code);
113 fprintf(code,"%3d,",c);
115 fprintf(code,"\n 0};\nint _Application_resources_size=%d;\n",i);
116 return 0;
119 /* SunOS' memcpy is wrong for overlapping arrays */
120 char *save_memcpy(char *d,char* s,int l)
122 if(d<s)
123 for(;l;l--)*d++=*s++;
124 else
125 for(d+=l-1,s+=l-1;l;l--)*d--=*s--;
126 return d;
129 /*allow unaligned access*/
130 void put_WORD(unsigned char* p,WORD w)
132 *p=w&0xFF;
133 *(p+1)=w>>8;
136 void put_DWORD(unsigned char* p,DWORD d)
138 put_WORD(p,d&0xFFFF);
139 put_WORD(p+2,d>>16);
142 WORD get_WORD(unsigned char* p)
144 return *p|(*(p+1)<<8);
147 DWORD get_DWORD(unsigned char* p)
149 return get_WORD(p)|(get_WORD(p+2)<<16);
153 /*create a new gen_res, initial size 100*/
154 gen_res *new_res()
155 { gen_res* ret=xmalloc(sizeof(gen_res)+100);
156 int i;
157 for(i=0;i<sizeof(gen_res)+100;i++)*((char*)ret+i)='\0';
158 ret->g_next=g_start;
159 ret->g_prev=0;
160 g_start=ret;
161 ret->space=100;
162 return ret;
165 /*double the space*/
166 gen_res* grow(gen_res* res)
168 res=realloc(res,sizeof(gen_res)+2*res->space);
169 if(!res)
170 fprintf(stderr,"Out of memory\n"),exit(1);
171 if(!res->g_prev)g_start=res;
172 else res->g_prev->g_next=res;
173 if(res->g_next)res->g_next->g_prev=res;
174 res->space=2*res->space;
175 return res;
179 /* insert bytes at offset 0, increase num_entries */
180 gen_res* insert_at_beginning(gen_res* res,char* entry,int size)
182 while(res->size+size>res->space)res=grow(res);
183 save_memcpy(res->res+size,res->res,res->size);
184 save_memcpy(res->res,entry,size);
185 res->size+=size;
186 res->num_entries++;
187 return res;
190 /* insert length characters from bytes into res, starting at start */
191 gen_res* insert_bytes(gen_res* res,char* bytes,int start,int length)
193 while(res->size+length>res->space)res=grow(res);
194 save_memcpy(res->res+start+length,res->res+start,res->size-start);
195 save_memcpy(res->res+start,bytes,length);
196 res->size+=length;
197 return res;
200 /*delete len bytes from res, starting at start*/
201 gen_res* delete_bytes(gen_res* res,int start,int len)
203 save_memcpy(res->res+start,res->res+start+len,res->size-start-len);
204 res->size-=len;
205 return res;
208 /*create a new style*/
209 rc_style *new_style()
211 rc_style *ret=xmalloc(sizeof(rc_style));
212 /*initially, no bits have to be reset*/
213 ret->and=-1;
214 /*initially, no bits are set*/
215 ret->or=WS_CHILD | WS_VISIBLE;
216 return ret;
219 /* entries are inserted at the beginning, starting from the last one */
220 gen_res* add_accelerator(int ev, int id, int flags, gen_res* prev)
222 char accel_entry[5];
223 if(prev->num_entries==0)flags|=0x80; /* last entry */
224 accel_entry[0]=flags;
225 put_WORD(accel_entry+1,ev);
226 put_WORD(accel_entry+3,id);
227 return insert_at_beginning(prev,accel_entry,5);
231 /* create an integer from the event, taking things as "^c" into account
232 add this as new entry */
233 gen_res* add_string_accelerator(char *ev, int id, int flags, gen_res* prev)
235 int event;
236 if(*ev=='^')
237 event=ev[1]-'a';
238 else
239 event=ev[0];
240 return add_accelerator(event,id,flags,prev);
243 /*is there a difference between ASCII and VIRTKEY accelerators? */
245 gen_res* add_ascii_accelerator(int ev, int id, int flags, gen_res* prev)
247 return add_accelerator(ev,id,flags,prev);
250 gen_res* add_vk_accelerator(int ev, int id, int flags, gen_res* prev)
252 return add_accelerator(ev,id,flags,prev);
255 /* create a new dialog header, set all items to 0 */
256 gen_res* new_dialog()
257 { gen_res* ret=new_res();
258 ret->size=16; /*all strings "\0", no font*/
259 return ret;
262 /* the STYLE option was specified */
263 gen_res* dialog_style(rc_style* style, gen_res* attr)
265 /* default dialog styles? Do we need style->and? */
266 /* DS_SETFONT might have been specified before */
267 put_DWORD(attr->res,get_DWORD(attr->res)|style->or);
268 return attr;
271 /* menu name is at offset 13 */
272 int dialog_get_menu(gen_res* attr)
274 return 13;
277 /* the class is after the menu name */
278 int dialog_get_class(gen_res* attr)
280 int offs=dialog_get_menu(attr);
281 while(attr->res[offs])offs++;
282 offs++;
283 return offs;
286 /* the caption is after the class */
287 int dialog_get_caption(gen_res* attr)
289 int offs=dialog_get_class(attr);
290 while(attr->res[offs])offs++;
291 offs++;
292 return offs;
295 /* the fontsize, if present, is after the caption, followed by the font name */
296 int dialog_get_fontsize(gen_res* attr)
298 int offs=dialog_get_caption(attr);
299 while(attr->res[offs])offs++;
300 offs++;
301 return offs;
305 /* the CAPTION option was specified */
306 gen_res* dialog_caption(char* cap, gen_res*attr)
308 /* we don't need the terminating 0 as it's already there */
309 return insert_bytes(attr,cap,dialog_get_caption(attr),strlen(cap));
313 /* the FONT option was specified, set the DS_SETFONT flag */
314 gen_res* dialog_font(short size,char* font,gen_res *attr)
316 char c_size[2];
317 int offs=dialog_get_fontsize(attr);
318 put_DWORD(attr->res,get_DWORD(attr->res)|DS_SETFONT);
319 put_WORD(c_size,size);
320 attr=insert_bytes(attr,c_size,offs,2);
321 offs+=2;
322 /* as there is no font name by default, copy the '\0' */
323 return insert_bytes(attr,font,offs,strlen(font)+1);
326 gen_res* dialog_class(char* cap, gen_res*attr)
328 return insert_bytes(attr,cap,dialog_get_class(attr),strlen(cap));
331 gen_res* dialog_menu(char* cap, gen_res*attr)
333 return insert_bytes(attr,cap,dialog_get_menu(attr),strlen(cap));
336 /* set the dialogs id, position, extent, and style */
337 gen_res* create_control_desc(int id,int x,int y,int cx, int cy,rc_style *style)
338 { gen_res* ret=new_res();
339 int s=WS_VISIBLE|WS_CHILD; /*defaults styles for any control*/
340 put_WORD(ret->res+0,x);
341 put_WORD(ret->res+2,y);
342 put_WORD(ret->res+4,cx);
343 put_WORD(ret->res+6,cy);
344 put_WORD(ret->res+8,id);
345 if(style)s=(s|style->or)&style->and;
346 put_DWORD(ret->res+10,s);
347 ret->size=17; /*empty strings, unused byte*/
348 return ret;
351 /* insert the control's label */
352 gen_res* label_control_desc(char* label,gen_res* cd)
354 int offs;
355 if(cd->res[14]&0x80)offs=15; /* one-character class */
356 else {
357 for(offs=14;cd->res[offs];offs++);
358 offs++;
360 return insert_bytes(cd,label,offs,strlen(label));
363 /* a CONTROL was specified */
364 gen_res* create_generic_control(char* label,int id,char* class,
365 rc_style*style,int x,int y,int cx,int cy)
366 { char cl;
367 gen_res* ret=new_res();
368 put_WORD(ret->res+0,x);
369 put_WORD(ret->res+2,y);
370 put_WORD(ret->res+4,cx);
371 put_WORD(ret->res+6,cy);
372 put_WORD(ret->res+8,id);
373 put_DWORD(ret->res+10,style->or);
374 ret->size=17;
375 ret=insert_bytes(ret,label,15,strlen(label));
376 /* is it a predefined class? */
377 cl=0;
378 if(!strcmp(class,"BUTTON"))cl=CT_BUTTON;
379 if(!strcmp(class,"EDIT"))cl=CT_EDIT;
380 if(!strcmp(class,"STATIC"))cl=CT_STATIC;
381 if(!strcmp(class,"LISTBOX"))cl=CT_LISTBOX;
382 if(!strcmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR;
383 if(!strcmp(class,"COMBOBOX"))cl=CT_COMBOBOX;
384 if(cl)ret->res[14]=cl;
385 else ret=insert_bytes(ret,class,14,strlen(class));
386 return ret;
389 /* insert cd into rest, set the type, add flags */
390 gen_res* add_control(int type,int flags,gen_res*cd,gen_res* rest)
392 put_DWORD(cd->res+10,get_DWORD(cd->res+10)|flags);
393 cd->res[14]=type;
394 return insert_at_beginning(rest,cd->res,cd->size);
397 /* an ICON control was specified, whf contains width, height, and flags */
398 gen_res* add_icon(char* name,int id,int x,int y,gen_res* whf,gen_res* rest)
400 put_WORD(whf->res+0,x);
401 put_WORD(whf->res+2,y);
402 put_WORD(whf->res+8,id);
403 whf=label_control_desc(name,whf);
404 return add_control(CT_STATIC,SS_ICON,whf,rest);
407 /* insert the generic control into rest */
408 gen_res* add_generic_control(gen_res* ctl, gen_res* rest)
410 return insert_at_beginning(rest,ctl->res,ctl->size);
413 /* create a dialog resource by inserting the header into the controls.
414 Set position and extent */
415 gen_res* make_dialog(gen_res* header,int x,int y,int cx,int cy,gen_res* ctls)
417 header->res[4]=ctls->num_entries;
418 header->type=dlg;
419 put_WORD(header->res+5,x);
420 put_WORD(header->res+7,y);
421 put_WORD(header->res+9,cx);
422 put_WORD(header->res+11,cy);
423 return insert_bytes(header,ctls->res,header->size,ctls->size);
426 /* create {0x15,0x16,0xFF} from '15 16 FF' */
427 gen_res *hex_to_raw(char *hex, gen_res*rest)
429 char r2[16];
430 int i;
431 for(i=0;*hex!='\'';i++)r2[i]=strtoul(hex,&hex,16);
432 return insert_bytes(rest,r2,0,i);
435 /* create a bitmap resource */
436 gen_res *make_bitmap(gen_res* res)
438 res=delete_bytes(res,0,14); /* skip bitmap file header*/
439 res->type=bmp;
440 return res;
443 gen_res *make_icon(gen_res* res)
445 res->type=ico;
446 return res;
449 gen_res *make_cursor(gen_res* res)
451 res->type=cur;
452 return res;
455 /* load resource bytes from the file name */
456 gen_res *load_file(char* name)
458 gen_res *res;
459 struct stat st;
460 int f=open(name,O_RDONLY);
461 if(!f)perror(name);
462 fstat(f,&st);
463 res=new_res();
464 while(res->space<st.st_size)res=grow(res);
465 read(f,res->res,st.st_size);
466 res->size=st.st_size;
467 close(f);
468 return res;
471 /* insert a normal menu item into res, starting from the last item */
472 gen_res *add_menuitem(char* name,int id,int flags,gen_res *res)
474 char item[4];
475 if(res->num_entries==0)flags|=MF_END;
476 put_WORD(item,flags);
477 put_WORD(item+2,id);
478 res=insert_at_beginning(res,name,strlen(name)+1);
479 res=insert_bytes(res,item,0,4);
480 return res;
483 /* insert a popup item into res */
484 gen_res *add_popup(char *name,short flags, gen_res* body, gen_res*res)
486 char c_flags[2];
487 flags|=MF_POPUP;
488 if(res->num_entries==0)flags|=MF_END;
489 put_WORD(c_flags,flags);
490 res=insert_at_beginning(res,body->res,body->size);
491 res=insert_bytes(res,name,0,strlen(name)+1);
492 res=insert_bytes(res,c_flags,0,2);
493 return res;
496 /* prefix the menu header into res */
497 gen_res *make_menu(gen_res* res)
499 static char header[4]={0,0,0,0};
500 res=insert_at_beginning(res,header,4);
501 res->type=men;
502 return res;
505 /* link top-level resources */
506 gen_res *add_resource(gen_res* first,gen_res *rest)
508 if(first)
510 first->next=rest;
511 return first;
513 else
514 return rest;
517 typedef struct str_tbl_elm{
518 int group;
519 struct str_tbl_elm *next;
520 char* strings[16];
521 } str_tbl_elm;
523 str_tbl_elm* string_table=NULL; /* sorted by group */
525 void add_str_tbl_elm(int id,char* str)
527 int group=(id>>4)+1;
528 int idx=id & 0x000f;
530 str_tbl_elm** elm=&string_table;
531 while(*elm && (*elm)->group<group) elm=&(*elm)->next;
532 if(!*elm || (*elm)->group!=group)
534 str_tbl_elm* new=xmalloc(sizeof(str_tbl_elm));
535 new->group=group;
536 new->next=*elm;
537 *elm=new;
539 (*elm)->strings[idx]=str;
542 gen_res* add_string_table(gen_res* t)
544 str_tbl_elm* ste;
545 int size,i;
546 gen_res* res;
547 unsigned char* p;
548 char* q;
550 if(!string_table) return t;
551 for(ste=string_table; ste; ste=ste->next)
553 for(size=0,i=0; i<16; i++)
554 size += ste->strings[i] ? strlen(ste->strings[i])+1 : 1;
555 res=new_res();
556 while(res->space<size)res=grow(res);
557 res->type=str;
558 res->n.i_name=ste->group;
559 res->n_type=0;
560 res->size=size;
561 for(p=res->res,i=0; i<16; i++)
562 if((q=ste->strings[i])==NULL)
563 *p++ = 0;
564 else
566 *p++ = strlen(q);
567 while(*q) *p++ = *q++;
569 t=add_resource(res,t);
571 return t;
574 char *get_typename(gen_res* t)
576 switch(t->type){
577 case acc:return "ACCELERATOR";
578 case bmp:return "BITMAP";
579 case cur:return "CURSOR";
580 case dlg:return "DIALOG";
581 case fnt:return "FONT";
582 case ico:return "ICON";
583 case men:return "MENU";
584 case rdt:return "RCDATA";
585 case str:return "STRINGTABLE";
586 default: return "UNKNOWN";
590 /* create strings like _Sysres_DIALOG_2 */
591 char *get_resource_name(gen_res*it)
593 static char buf[1000];
594 if(it->n_type)
595 sprintf(buf,"%s_%s_%s",prefix,get_typename(it),it->n.s_name);
596 else
597 sprintf(buf,"%s_%s_%d",prefix,get_typename(it),it->n.i_name);
598 return buf;
601 #define ISCONSTANT (constant ? "const " : "")
603 /* create the final output */
604 void create_output(gen_res* top)
606 gen_res *it;
608 top=add_string_table(top);
610 fprintf( header, "/* %s\n"
611 " * This file is automatically generated. Do not edit!\n"
612 " */\n\n"
613 "#include \"resource.h\"\n", hname );
615 /* Declare the resources */
617 for (it=top;it;it=it->next)
618 fprintf( header,"extern %sstruct resource %s;\n",
619 ISCONSTANT, get_resource_name(it) );
620 fprintf( header,"\nextern %sstruct resource * %s%s_Table[];\n",
621 ISCONSTANT, ISCONSTANT, prefix );
623 /* Print the resources bytes */
625 fprintf( code, "/* %s\n"
626 " * This file is automatically generated. Do not edit!\n"
627 " */\n\n"
628 "#include \"%s\"\n", sname, hname );
630 for(it=top;it;it=it->next)
632 int i;
633 fprintf( code, "static %sunsigned char %s__bytes[] = {\n",
634 ISCONSTANT, get_resource_name(it) );
635 for (i=0;i<it->size-1;i++)
637 fprintf(code,"0x%02x, ",it->res[i]);
638 if ((i&7)==7)fputc('\n',code);
640 fprintf(code,"0x%02x };\n\n",it->res[i]);
643 /* Print the resources */
644 for (it=top;it;it=it->next)
646 int type;
647 switch(it->type)
649 case acc:type=(int)RT_ACCELERATOR;break;
650 case bmp:type=(int)RT_BITMAP;break;
651 case cur:type=(int)RT_CURSOR;break;
652 case dlg:type=(int)RT_DIALOG;break;
653 case fnt:type=(int)RT_FONT;break;
654 case ico:type=(int)RT_ICON;break;
655 case men:type=(int)RT_MENU;break;
656 case rdt:type=(int)RT_RCDATA;break;
657 case str:type=(int)RT_STRING;break;
658 default:fprintf(stderr,"Unknown restype\n");type=-1;break;
660 if(it->n_type)
661 fprintf(code,"%sstruct resource %s = {0,%d,\"%s\",%s__bytes,%d};\n",
662 ISCONSTANT, get_resource_name(it), type, it->n.s_name,
663 get_resource_name(it), it->size );
664 else
665 fprintf(code,"%sstruct resource %s = {%d,%d,\"@%d\",%s__bytes,%d};\n",
666 ISCONSTANT, get_resource_name(it), it->n.i_name, type,
667 it->n.i_name, get_resource_name(it), it->size );
670 /* Print the resource table (NULL terminated) */
672 fprintf(code,"\n%sstruct resource * %s%s_Table[] = {\n",
673 ISCONSTANT, ISCONSTANT, prefix);
674 for (it=top;it;it=it->next)
675 fprintf( code, " &%s,\n", get_resource_name(it) );
676 fprintf( code, " 0\n};\n" );
678 /* Perform autoregistration */
679 fprintf( code,
680 "#ifdef WINELIB\n"
681 "static void DoIt() WINE_CONSTRUCTOR;\n"
682 "static void DoIt()\n"
683 "{\n"
684 "\tLIBRES_RegisterResources(%s_Table);\n"
685 "}\n\n"
686 "#ifndef HAVE_WINE_CONSTRUCTOR\n"
687 "void LIBWINE_Register_%s(){\n"
688 "\tDoIt();\n"
689 "}\n"
690 "#endif\n"
691 "#endif /*WINELIB*/\n"
692 ,prefix,prefix);
695 gen_res* make_font(gen_res* res)
697 fprintf(stderr,"Fonts not supported\n");
698 return NULL;
701 gen_res* make_raw(gen_res* res)
703 fprintf(stderr,"RCData not supported\n");
704 return NULL;
707 gen_res* int_to_raw(int i,gen_res* res)
709 fprintf(stderr,"IntToRaw not supported\n");
710 return NULL;
713 /* translate "Hello,\\tworld!\\10" to "Hello,\tworld!\n" */
714 char *parse_c_string(char *in)
716 char *out=xmalloc(strlen(in)-1);
717 char *it;
718 char tmp[5],*tend;
719 for(it=out,in++;*in;in++)
720 if(*in=='\\')
721 switch(*++in)
722 {case 't':*it++='\t';break;
723 case 'r':*it++='\r';break;
724 case 'n':*it++='\n';break;
725 case 'a':*it++='\a';break;
726 case '0':
727 memset(tmp,0,5);/*make sure it doesn't use more than 4 chars*/
728 memcpy(tmp,in,4);
729 *it++=strtoul(tmp,&tend,0);
730 in+=tend-tmp-1;
731 break;
732 case '1':case '2':case '3':case '4':case '5':
733 case '6':case '7':case '8':case '9':
734 memset(tmp,0,5);
735 memcpy(tmp,in,3);
736 *it++=strtoul(tmp,&tend,10);
737 in+=tend-tmp-1;
738 break;
739 case 'x':
740 memset(tmp,0,5);
741 memcpy(tmp,++in,2);
742 *it++=strtoul(tmp,&tend,16);
743 in+=tend-tmp-1;
744 break;
745 default:*it++=*in;
747 else
748 *it++=*in;
749 *(it-1)='\0';
750 return out;