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