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