Release 950620
[wine] / rc / winerc.c
1 /*
2  *
3  * Copyright  Martin von Loewis, 1994
4  *
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sys/stat.h>
10 #include <sys/fcntl.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <windows.h>
15 #include <neexe.h>
16 #include "parser.h"
17 #include "y.tab.h"
18
19 char usage[]="winerc -dvc -p prefix -o outfile < infile \n";
20
21 /*might be overwritten by command line*/
22 char *prefix="_Resource";
23 int verbose,constant;
24 gen_res* g_start;
25 FILE *header,*code;
26 char hname[256],sname[256];
27
28 int main(int argc,char *argv[])
29 {  
30         extern int yydebug;
31         extern char* optarg;
32         int optc,lose,ret;
33         lose=0;
34 #if defined(__NetBSD__) || defined(__FreeBSD__)
35         while((optc=getopt(argc,argv,"dp:vo:"))!=EOF)
36 #else
37         while((optc=getopt(argc,argv,"dp:vo:",0))!=EOF)
38 #endif
39                 switch(optc)
40                 {
41                         /* bison will print state transitions on stderr */
42                         case 'd':yydebug=1;
43                                          setbuf(stdout,0);
44                                          setbuf(stderr,0);
45                                         break;
46                         case 'p':prefix=optarg;break;
47                         case 'c':constant=1;break;
48                         case 'v':verbose=1;
49                                          setbuf(stderr,0);
50                                         break;
51                         case 'o':set_out_file(optarg);break;
52                         default: lose++;break;
53                 }
54         if(lose)return fprintf(stderr,usage),1;
55         if(!header)header=stdout;
56         if(!code)code=stdout;
57         ret=yyparse();
58         fclose(header);
59         fclose(code);
60         return ret;
61 }
62
63 void set_out_file(char *prefix)
64 {
65         sprintf(sname,"%s.c",prefix);
66         code=fopen(sname,"w");
67         sprintf(hname,"%s.h",prefix);
68         header=fopen(hname,"w");
69 }
70
71
72 /* SunOS' memcpy is wrong for overlapping arrays */
73 char *save_memcpy(char *d,char* s,int l)
74 {
75         if(d<s)
76                 for(;l;l--)*d++=*s++;
77         else
78                 for(d+=l-1,s+=l-1;l;l--)*d--=*s--;
79         return d;
80 }
81
82 /*allow unaligned access*/
83 void put_WORD(unsigned char* p,WORD w)
84 {
85         *p=w&0xFF;
86         *(p+1)=w>>8;
87 }
88
89 void put_DWORD(unsigned char* p,DWORD d)
90 {
91         put_WORD(p,d&0xFFFF);
92         put_WORD(p+2,d>>16);
93 }
94
95 WORD get_WORD(unsigned char* p)
96 {
97         return *p|(*(p+1)<<8);
98 }
99
100 DWORD get_DWORD(unsigned char* p)
101 {
102         return get_WORD(p)|(get_WORD(p+2)<<16);
103 }
104
105
106 /*create a new gen_res, initial size 100*/
107 gen_res *new_res()
108 {       gen_res* ret=malloc(sizeof(gen_res)+100);
109         int i;
110         if(!ret)
111                 fprintf(stderr,"Out of memory\n"),exit(1);
112         for(i=0;i<sizeof(gen_res)+100;i++)*((char*)ret+i)='\0';
113         ret->g_next=g_start;
114         ret->g_prev=0;
115         g_start=ret;
116         ret->space=100;
117         return ret;
118 }
119
120 /*double the space*/
121 gen_res* grow(gen_res* res)
122 {
123         res=realloc(res,sizeof(gen_res)+2*res->space);
124         if(!res)
125                 fprintf(stderr,"Out of memory\n"),exit(1);
126         if(!res->g_prev)g_start=res;
127         else res->g_prev->g_next=res;
128         if(res->g_next)res->g_next->g_prev=res;
129         res->space=2*res->space;
130         return res;
131 }
132
133
134 /* insert bytes at offset 0, increase num_entries */
135 gen_res* insert_at_beginning(gen_res* res,char* entry,int size)
136 {
137         while(res->size+size>res->space)res=grow(res);
138         save_memcpy(res->res+size,res->res,res->size);
139         save_memcpy(res->res,entry,size);
140         res->size+=size;
141         res->num_entries++;
142         return res;
143 }
144
145 /* insert length characters from bytes into res, starting at start */
146 gen_res* insert_bytes(gen_res* res,char* bytes,int start,int length)
147 {
148         while(res->size+length>res->space)res=grow(res);
149         save_memcpy(res->res+start+length,res->res+start,res->size-start);
150         save_memcpy(res->res+start,bytes,length);
151         res->size+=length;
152         return res;
153 }
154
155 /*delete len bytes from res, starting at start*/
156 gen_res* delete_bytes(gen_res* res,int start,int len)
157 {
158         save_memcpy(res->res+start,res->res+start+len,res->size-start-len);
159         res->size-=len;
160         return res;
161 }
162
163 /*create a new style*/
164 rc_style *new_style()
165 {
166         rc_style *ret=malloc(sizeof(rc_style));
167         /*initially, no bits have to be reset*/
168         ret->and=-1;
169         /*initially, no bits are set*/
170         ret->or=0;
171         return ret;
172 }
173
174 /* entries are inserted at the beginning, starting from the last one */
175 gen_res* add_accelerator(int ev, int id, int flags, gen_res* prev)
176 {
177         char accel_entry[5];
178         if(prev->num_entries==0)flags|=0x80; /* last entry */
179         accel_entry[0]=flags;
180         put_WORD(accel_entry+1,ev);
181         put_WORD(accel_entry+3,id);
182         return insert_at_beginning(prev,accel_entry,5);
183 }
184
185
186 /* create an integer from the event, taking things as "^c" into account
187    add this as new entry */
188 gen_res* add_string_accelerator(char *ev, int id, int flags, gen_res* prev)
189 {
190         int event;
191         if(*ev=='^')
192                 event=ev[1]-'a';
193         else
194                 event=ev[0];
195         return add_accelerator(event,id,flags,prev);
196 }
197
198 /*is there a difference between ASCII and VIRTKEY accelerators? */
199
200 gen_res* add_ascii_accelerator(int ev, int id, int flags, gen_res* prev)
201 {
202         return add_accelerator(ev,id,flags,prev);
203 }
204
205 gen_res* add_vk_accelerator(int ev, int id, int flags, gen_res* prev)
206 {
207         return add_accelerator(ev,id,flags,prev);
208 }
209
210 /* create a new dialog header, set all items to 0 */
211 gen_res* new_dialog()
212 {       gen_res* ret=new_res();
213         ret->size=16; /*all strings "\0", no font*/
214         return ret;
215 }
216
217 /* the STYLE option was specified */
218 gen_res* dialog_style(rc_style* style, gen_res* attr)
219 {
220         /* default dialog styles? Do we need style->and? */
221         /* DS_SETFONT might have been specified before */
222         put_DWORD(attr->res,get_DWORD(attr->res)|style->or);
223         return attr;
224 }
225
226 /* menu name is at offset 13 */
227 int dialog_get_menu(gen_res* attr)
228 {
229         return 13;
230 }
231
232 /* the class is after the menu name */
233 int dialog_get_class(gen_res* attr)
234 {
235         int offs=dialog_get_menu(attr);
236         while(attr->res[offs])offs++;
237         offs++;
238         return offs;
239 }
240
241 /* the caption is after the class */
242 int dialog_get_caption(gen_res* attr)
243 {
244         int offs=dialog_get_class(attr);
245         while(attr->res[offs])offs++;
246         offs++;
247         return offs;
248 }
249
250 /* the fontsize, if present, is after the caption, followed by the font name */
251 int dialog_get_fontsize(gen_res* attr)
252 {
253         int offs=dialog_get_caption(attr);
254         while(attr->res[offs])offs++;
255         offs++;
256         return offs;
257 }
258
259
260 /* the CAPTION option was specified */
261 gen_res* dialog_caption(char* cap, gen_res*attr)
262 {
263         /* we don't need the terminating 0 as it's already there */
264         return insert_bytes(attr,cap,dialog_get_caption(attr),strlen(cap));
265 }
266
267
268 /* the FONT option was specified, set the DS_SETFONT flag */
269 gen_res* dialog_font(short size,char* font,gen_res *attr)
270 {
271         char c_size[2];
272         int offs=dialog_get_fontsize(attr);
273         put_DWORD(attr->res,get_DWORD(attr->res)|DS_SETFONT);
274         put_WORD(c_size,size);
275         attr=insert_bytes(attr,c_size,offs,2);
276         offs+=2;
277         /* as there is no font name by default, copy the '\0' */
278         return insert_bytes(attr,font,offs,strlen(font)+1);
279 }
280
281 gen_res* dialog_class(char* cap, gen_res*attr)
282 {
283         return insert_bytes(attr,cap,dialog_get_class(attr),strlen(cap));
284 }
285
286 gen_res* dialog_menu(char* cap, gen_res*attr)
287 {
288         return insert_bytes(attr,cap,dialog_get_menu(attr),strlen(cap));
289 }
290
291 /* set the dialogs id, position, extent, and style */
292 gen_res* create_control_desc(int id,int x,int y,int cx, int cy,rc_style *style)
293 {       gen_res* ret=new_res();
294         int s=WS_VISIBLE|WS_CHILD; /*defaults styles for any control*/
295         put_WORD(ret->res+0,x);
296         put_WORD(ret->res+2,y);
297         put_WORD(ret->res+4,cx);
298         put_WORD(ret->res+6,cy);
299         put_WORD(ret->res+8,id);
300         if(style)s=(s|style->or)&style->and;
301         put_DWORD(ret->res+10,s);
302         ret->size=17; /*empty strings, unused byte*/
303         return ret;
304 }
305
306 /* insert the control's label */
307 gen_res* label_control_desc(char* label,gen_res* cd)
308 {
309         int offs;
310         if(cd->res[14]&0x80)offs=15; /* one-character class */
311         else {
312                 for(offs=14;cd->res[offs];offs++);
313                 offs++;
314         }
315         return insert_bytes(cd,label,offs,strlen(label));
316 }
317
318 /* a CONTROL was specified */
319 gen_res* create_generic_control(char* label,int id,char* class,
320         rc_style*style,int x,int y,int cx,int cy)
321 {       char cl;
322         gen_res* ret=new_res();
323         put_WORD(ret->res+0,x);
324     put_WORD(ret->res+2,y);
325     put_WORD(ret->res+4,cx);
326     put_WORD(ret->res+6,cy);
327     put_WORD(ret->res+8,id);
328         put_DWORD(ret->res+10,style->or);
329         ret->size=17;
330         ret=insert_bytes(ret,label,15,strlen(label));
331         /* is it a predefined class? */
332         cl=0;
333         if(!strcmp(class,"BUTTON"))cl=CT_BUTTON;
334         if(!strcmp(class,"EDIT"))cl=CT_EDIT;
335         if(!strcmp(class,"STATIC"))cl=CT_STATIC;
336         if(!strcmp(class,"LISTBOX"))cl=CT_LISTBOX;
337         if(!strcmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR;
338         if(!strcmp(class,"COMBOBOX"))cl=CT_COMBOBOX;
339         if(cl)ret->res[14]=cl;
340         else ret=insert_bytes(ret,class,14,strlen(class));
341         return ret;
342 }
343
344 /* insert cd into rest, set the type, add flags */
345 gen_res* add_control(int type,int flags,gen_res*cd,gen_res* rest)
346 {
347         put_DWORD(cd->res+10,get_DWORD(cd->res+10)|flags);
348         cd->res[14]=type;
349         return insert_at_beginning(rest,cd->res,cd->size);
350 }
351
352 /* an ICON control was specified, whf contains width, height, and flags */
353 gen_res* add_icon(char* name,int id,int x,int y,gen_res* whf,gen_res* rest)
354 {
355         put_WORD(whf->res+0,x);
356         put_WORD(whf->res+2,y);
357         put_WORD(whf->res+8,id);
358         whf=label_control_desc(name,whf);
359         return add_control(CT_STATIC,SS_ICON,whf,rest);
360 }
361
362 /* insert the generic control into rest */
363 gen_res* add_generic_control(gen_res* ctl, gen_res* rest)
364 {
365         return insert_at_beginning(rest,ctl->res,ctl->size);
366 }
367
368 /* create a dialog resource by inserting the header into the controls.
369    Set position and extent */
370 gen_res* make_dialog(gen_res* header,int x,int y,int cx,int cy,gen_res* ctls)
371 {
372         header->res[4]=ctls->num_entries;
373         header->type=dlg;
374         put_WORD(header->res+5,x);
375         put_WORD(header->res+7,y);
376         put_WORD(header->res+9,cx);
377         put_WORD(header->res+11,cy);
378         return insert_bytes(header,ctls->res,header->size,ctls->size);
379 }
380
381 /* create {0x15,0x16,0xFF} from '15 16 FF' */
382 gen_res *hex_to_raw(char *hex, gen_res*rest)
383 {
384         char r2[16];
385     int i;
386         for(i=0;*hex!='\'';i++)r2[i]=strtoul(hex,&hex,16);
387         return insert_bytes(rest,r2,0,i);
388 }
389
390 /* create a bitmap resource */
391 gen_res *make_bitmap(gen_res* res)
392 {
393         res=delete_bytes(res,0,14); /* skip bitmap file header*/
394         res->type=bmp;
395         return res;
396 }
397
398 gen_res *make_icon(gen_res* res)
399 {
400         res->type=ico;
401         return res;
402 }
403
404 gen_res *make_cursor(gen_res* res)
405 {
406         res->type=cur;
407         return res;
408 }
409
410 /* load resource bytes from the file name */
411 gen_res *load_file(char* name)
412 {
413         gen_res *res;
414         struct stat st;
415         int f=open(name,O_RDONLY);
416         if(!f)perror(name);
417         fstat(f,&st);
418         res=new_res();
419         while(res->space<st.st_size)res=grow(res);
420         read(f,res->res,st.st_size);
421         res->size=st.st_size;
422         close(f);
423         return res;
424 }
425
426 /* insert a normal menu item into res, starting from the last item */
427 gen_res *add_menuitem(char* name,int id,int flags,gen_res *res)
428 {
429         char item[4];
430         if(res->num_entries==0)flags|=MF_END;
431         put_WORD(item,flags);
432         put_WORD(item+2,id);
433         res=insert_at_beginning(res,name,strlen(name)+1);
434         res=insert_bytes(res,item,0,4);
435         return res;
436 }
437
438 /* insert a popup item into res */
439 gen_res *add_popup(char *name,short flags, gen_res* body, gen_res*res)
440 {
441         char c_flags[2];
442         if(res->num_entries==0)flags|=MF_END;
443         put_WORD(c_flags,flags);
444         res=insert_at_beginning(res,body->res,body->size);
445         res=insert_bytes(res,name,0,strlen(name)+1);
446         res=insert_bytes(res,c_flags,0,2);
447         return res;
448 }
449
450 /* prefix the menu header into res */
451 gen_res *make_menu(gen_res* res)
452 {
453         static char header[4]={0,0,0,0};
454         res=insert_at_beginning(res,header,4);
455         res->type=men;
456         return res;
457 }
458
459 /* link top-level resources */
460 gen_res *add_resource(gen_res* first,gen_res *rest)
461 {
462         first->next=rest;
463         return first;
464 }
465
466 char *get_typename(gen_res* t)
467 {
468         switch(t->type){
469         case acc:return "ACCELERATOR";
470         case bmp:return "BITMAP";
471         case cur:return "CURSOR";
472         case dlg:return "DIALOG";
473         case fnt:return "FONT";
474         case ico:return "ICON";
475         case men:return "MENU";
476         case rdt:return "RCDATA";
477         case str:return "STRINGTABLE";
478         default: return "UNKNOWN";
479         }
480 }
481
482 /* create strings like _Sysres_DIALOG_2 */
483 char *get_resource_name(gen_res*it)
484 {
485         static char buf[1000];
486         if(it->n_type)
487                 sprintf(buf,"%s_%s_%s",prefix,get_typename(it),it->n.s_name);
488         else
489                 sprintf(buf,"%s_%s_%d",prefix,get_typename(it),it->n.i_name);
490         return buf;
491 }
492
493 #define ISCONSTANT      constant?"const ":""
494 /* create the final output */
495 void create_output(gen_res* top)
496 {
497         gen_res *it;
498         fprintf(header,"/*\t\t%s\n * This File is automatically generated."
499                 " Do not edit\n */\n#include \"resource.h\"\n",hname);
500         fprintf(code,"/*\t\t%s\n * This File is automatically generated."
501                 " Do not edit\n */\n",sname);
502         /* declare the resources */
503         for(it=top;it;it=it->next)
504                 fprintf(header,"extern %sunsigned char %s[];\n",ISCONSTANT,
505                 get_resource_name(it));
506         fprintf(header,"extern %sstruct ResourceTable %sTable[];\n",
507                 ISCONSTANT,prefix);
508
509         fprintf(code,"#include \"prototypes.h\"\n#include \"%s\"\n",hname);
510
511         /* print the resource table (0 terminated) */
512         fprintf(code,"\n%sstruct ResourceTable %sTable[]={\n",ISCONSTANT,prefix);
513         for(it=top;it;it=it->next)
514         {       int type;
515                 switch(it->type)
516                 {case acc:type=NE_RSCTYPE_ACCELERATOR;break;
517                  case bmp:type=NE_RSCTYPE_BITMAP;break;
518                  case cur:type=NE_RSCTYPE_CURSOR;break;
519                  case dlg:type=NE_RSCTYPE_DIALOG;break;
520                  case fnt:type=NE_RSCTYPE_FONT;break;
521                  case ico:type=NE_RSCTYPE_ICON;break;
522                  case men:type=NE_RSCTYPE_MENU;break;
523                  case rdt:type=NE_RSCTYPE_RCDATA;break;
524                  case str:type=NE_RSCTYPE_STRING;break;
525                  default:fprintf(stderr,"Unknown restype\n");type=-1;break;
526                 }
527                 if(it->n_type)
528                         fprintf(code,"{0,%d,\"%s\",%s,%d},\n",
529                         type,it->n.s_name,get_resource_name(it),it->size);
530                 else
531                         fprintf(code,"{%d,%d,\"@%d\",%s,%d},\n",
532                         it->n.i_name,type,it->n.i_name,get_resource_name(it),
533                         it->size);
534         }
535         fprintf(code,"{0,0,0,0}};\n\n");
536
537         /* print the resources */
538         for(it=top;it;it=it->next)
539         {       int i;
540                 fprintf(code,"%sunsigned char %s[]={\n",
541                         ISCONSTANT,get_resource_name(it));
542                 for(i=0;i<it->size-1;i++)
543                 {
544                         fprintf(code,"%#4x,",it->res[i]);
545                         if((i&7)==7)fputc('\n',code);
546         }
547                 fprintf(code,"%#4x};\n",it->res[i]);
548         }
549 }
550
551 void make_font()
552 {
553         fprintf(stderr,"Fonts not supported\n");
554 }
555
556 void make_raw()
557 {
558         fprintf(stderr,"RCData not supported\n");
559 }
560
561 void int_to_raw()
562 {
563         fprintf(stderr,"IntToRaw not supported\n");
564 }
565
566 /* translate "Hello,\\tworld!\\10" to "Hello,\tworld!\n" */
567 char *parse_c_string(char *in)
568 {
569         char *out=malloc(strlen(in)-1);
570         char *it;
571         char tmp[5],*tend;
572         for(it=out,in++;*in;in++)
573         if(*in=='\\')
574                 switch(*++in)
575                 {case 't':*it++='\t';break;
576                  case 'r':*it++='\r';break;
577                  case 'n':*it++='\n';break;
578                  case 'a':*it++='\a';break;
579                  case '0':
580                         memset(tmp,0,5);/*make sure it doesn't use more than 4 chars*/
581                         memcpy(tmp,in,4);
582                         *it++=strtoul(tmp,&tend,0);
583                         in+=tend-tmp-1;
584                         break;
585                  case '1':case '2':case '3':case '4':case '5':
586                  case '6':case '7':case '8':case '9':
587                         memset(tmp,0,5);
588                         memcpy(tmp,in,3);
589                         *it++=strtoul(tmp,&tend,10);
590                         in+=tend-tmp-1;
591                         break;
592                  case 'x':
593                         memset(tmp,0,5);
594                         memcpy(tmp,++in,2);
595                         *it++=strtoul(tmp,&tend,16);
596                         in+=tend-tmp-1;
597                         break;
598                  default:*it++=*in;
599                 }
600         else
601                 *it++=*in;
602         *(it-1)='\0';
603         return out;
604 }