3 * Copyright Martin von Loewis, 1994
12 #include <sys/types.h>
16 #include "wine/winuser16.h"
19 char usage[]="winerc -bdvc -p prefix -o outfile < infile \n"
20 " -b Create a C array from a binary .res file\n"
21 " -c Add 'const' prefix to C constants\n"
22 " -d Output debugging information\n"
23 " -h Also generate a .h file\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"
27 " -w 16|32 Select win16 or win32 output\n";
29 /*might be overwritten by command line*/
30 char *prefix="_Resource";
34 static FILE *header = NULL, *code = NULL;
36 int transform_binary_file(void);
39 static void *xmalloc (size_t size)
43 res = malloc (size ? size : 1);
46 fprintf (stderr, "Virtual memory exhausted.\n");
53 int main(int argc,char *argv[])
57 int optc,lose = 0, ret, binary = 0, output_header = 0;
58 char output_name[256];
60 while((optc=getopt(argc,argv,"bcdhp:vo:w:"))!=EOF)
63 /* bison will print state transitions on stderr */
70 case 'h':output_header=1; break;
71 case 'p':prefix=strdup(optarg); break;
72 case 'c':constant=1;break;
76 case 'o':sprintf(output_name,"%s.c",optarg); break;
77 case 'w':if(!strcmp(optarg,"16"))win32=0;
78 else if(!strcmp(optarg,"32"))win32=1;
81 default: lose++;break;
83 if(lose)return fprintf(stderr,usage),1;
87 code = fopen( output_name, "w" );
90 output_name[strlen(output_name)-1] = 'h';
91 header = fopen( output_name, "w" );
94 if (!code) code = stdout;
96 ret=transform_binary_file();
99 if (header) fclose(header);
101 if (ret) /* There was an error */
103 if (header) unlink( output_name );
104 output_name[strlen(output_name)-1] = 'c';
105 unlink( output_name );
110 int transform_binary_file()
113 if (header) fprintf(header,"#define APPLICATION_HAS_RESOURCES 1\n");
114 fprintf(code,"char _Application_resources[]={");
119 if(i%16==0)fputc('\n',code);
120 fprintf(code,"%3d,",c);
122 fprintf(code,"\n 0};\nint _Application_resources_size=%d;\n",i);
126 /* SunOS' memcpy is wrong for overlapping arrays */
127 char *save_memcpy(char *d,char* s,int l)
130 for(;l;l--)*d++=*s++;
132 for(d+=l-1,s+=l-1;l;l--)*d--=*s--;
136 /*allow unaligned access*/
137 void put_WORD(unsigned char* p,WORD w)
143 void put_DWORD(unsigned char* p,DWORD d)
145 put_WORD(p,d&0xFFFF);
149 WORD get_WORD(unsigned char* p)
151 return *p|(*(p+1)<<8);
154 DWORD get_DWORD(unsigned char* p)
156 return get_WORD(p)|(get_WORD(p+2)<<16);
160 /*create a new gen_res, initial size 100*/
162 { gen_res* ret=xmalloc(sizeof(gen_res)+100);
164 for(i=0;i<sizeof(gen_res)+100;i++)*((char*)ret+i)='\0';
173 gen_res* grow(gen_res* res)
175 res=realloc(res,sizeof(gen_res)+2*res->space);
177 fprintf(stderr,"Out of memory\n"),exit(1);
178 if(!res->g_prev)g_start=res;
179 else res->g_prev->g_next=res;
180 if(res->g_next)res->g_next->g_prev=res;
181 res->space=2*res->space;
186 /* insert bytes at offset 0, increase num_entries */
187 gen_res* insert_at_beginning(gen_res* res,char* entry,int size)
189 while(res->size+size>res->space)res=grow(res);
190 save_memcpy(res->res+size,res->res,res->size);
191 save_memcpy(res->res,entry,size);
197 /* insert length characters from bytes into res, starting at start */
198 gen_res* insert_bytes(gen_res* res,char* bytes,int start,int length)
200 while(res->size+length>res->space)res=grow(res);
201 save_memcpy(res->res+start+length,res->res+start,res->size-start);
202 save_memcpy(res->res+start,bytes,length);
207 /* insert string into res, starting at start */
208 gen_res* insert_string(gen_res* res,unsigned char* string,int start,int terminating0)
211 int lengthA = strlen(string) + (terminating0 ? 1 : 0);
212 int length = (win32 ? 2 : 1) * lengthA;
213 while(res->size+length>res->space)res=grow(res);
214 save_memcpy(res->res+start+length,res->res+start,res->size-start);
219 put_WORD(p, *string++);
228 /* insert string at offset 0, increase num_entries */
229 gen_res* insert_string_at_beginning(gen_res* res,char* entry,int terminating0)
231 res=insert_string(res,entry,0,terminating0);
236 /*delete len bytes from res, starting at start*/
237 gen_res* delete_bytes(gen_res* res,int start,int len)
239 save_memcpy(res->res+start,res->res+start+len,res->size-start-len);
244 /*create a new style*/
245 rc_style *new_style()
247 rc_style *ret=xmalloc(sizeof(rc_style));
248 /*initially, no bits have to be reset*/
250 /*initially, no bits are set*/
251 ret->or=WS_CHILD | WS_VISIBLE;
255 /* entries are inserted at the beginning, starting from the last one */
256 gen_res* add_accelerator(int ev, int id, int flags, gen_res* prev)
259 if(prev->num_entries==0)flags|=0x80; /* last entry */
260 accel_entry[0]=flags;
261 put_WORD(accel_entry+1,ev);
262 put_WORD(accel_entry+3,id);
263 return insert_at_beginning(prev,accel_entry,5);
267 /* create an integer from the event, taking things as "^c" into account
268 add this as new entry */
269 gen_res* add_string_accelerator(char *ev, int id, int flags, gen_res* prev)
276 return add_accelerator(event,id,flags,prev);
279 /*is there a difference between ASCII and VIRTKEY accelerators? */
281 gen_res* add_ascii_accelerator(int ev, int id, int flags, gen_res* prev)
283 return add_accelerator(ev,id,flags,prev);
286 gen_res* add_vk_accelerator(int ev, int id, int flags, gen_res* prev)
288 return add_accelerator(ev,id,flags,prev);
291 /* create a new dialog header, set all items to 0 */
292 gen_res* new_dialog()
293 { gen_res* ret=new_res();
294 ret->size=win32?24:16; /*all strings "\0", no font*/
298 /* the STYLE option was specified */
299 gen_res* dialog_style(rc_style* style, gen_res* attr)
301 /* default dialog styles? Do we need style->and? */
302 /* DS_SETFONT might have been specified before */
303 put_DWORD(attr->res,get_DWORD(attr->res)|style->or);
307 /* menu name is at offset 13 (win32: 18) */
308 int dialog_get_menu(gen_res* attr)
313 /* the class is after the menu name */
314 int dialog_get_class(gen_res* attr)
316 int offs=dialog_get_menu(attr);
317 while(attr->res[offs]||(win32&&attr->res[offs+1]))offs+=win32?2:1;
322 /* the caption is after the class */
323 int dialog_get_caption(gen_res* attr)
325 int offs=dialog_get_class(attr);
326 while(attr->res[offs]||(win32&&attr->res[offs+1]))offs+=win32?2:1;
331 /* the fontsize, if present, is after the caption, followed by the font name */
332 int dialog_get_fontsize(gen_res* attr)
334 int offs=dialog_get_caption(attr);
335 while(attr->res[offs]||(win32&&attr->res[offs+1]))offs+=win32?2:1;
341 /* the CAPTION option was specified */
342 gen_res* dialog_caption(char* cap, gen_res*attr)
344 /* we don't need the terminating 0 as it's already there */
345 return insert_string(attr,cap,dialog_get_caption(attr),0);
349 /* the FONT option was specified, set the DS_SETFONT flag */
350 gen_res* dialog_font(short size,char* font,gen_res *attr)
353 int offs=dialog_get_fontsize(attr);
354 put_DWORD(attr->res,get_DWORD(attr->res)|DS_SETFONT);
355 put_WORD(c_size,size);
356 attr=insert_bytes(attr,c_size,offs,2);
358 /* as there is no font name by default, copy the '\0' */
359 return insert_string(attr,font,offs,1);
362 gen_res* dialog_class(char* cap, gen_res*attr)
364 return insert_string(attr,cap,dialog_get_class(attr),0);
367 gen_res* dialog_menu_id(short nr, gen_res*attr)
370 int offs=dialog_get_menu(attr);
371 attr->res[offs] = 0xff;
372 if (win32) attr->res[offs+1] = 0xff;
374 return insert_bytes(attr,c_nr,offs+(win32?2:1),2);
376 gen_res* dialog_menu_str(char* cap, gen_res*attr)
378 return insert_string(attr,cap,dialog_get_menu(attr),0);
381 /* set the dialogs id, position, extent, and style */
382 gen_res* create_control_desc(int id,int x,int y,int cx, int cy,rc_style *style)
383 { gen_res* ret=new_res();
384 int s=WS_VISIBLE|WS_CHILD; /*defaults styles for any control*/
387 if(style)s=(s|style->or)&style->and;
388 put_DWORD(ret->res+0,s);
390 /* put_WORD(ret->res+4, exStyle); */
391 put_WORD(ret->res+8,x);
392 put_WORD(ret->res+10,y);
393 put_WORD(ret->res+12,cx);
394 put_WORD(ret->res+14,cy);
395 put_WORD(ret->res+16,id);
396 ret->size=24; /*empty strings, unused byte*/
400 put_WORD(ret->res+0,x);
401 put_WORD(ret->res+2,y);
402 put_WORD(ret->res+4,cx);
403 put_WORD(ret->res+6,cy);
404 put_WORD(ret->res+8,id);
405 if(style)s=(s|style->or)&style->and;
406 put_DWORD(ret->res+10,s);
407 ret->size=17; /*empty strings, unused byte*/
412 /* insert the control's label */
413 gen_res* label_control_desc(char* label,gen_res* cd)
417 if(get_WORD(cd->res+18)==0xffff)offs=20; /* one-character class */
419 for(offs=18;get_WORD(cd->res+offs);offs+=2);
424 if(cd->res[14]&0x80)offs=15; /* one-character class */
426 for(offs=14;cd->res[offs];offs++);
430 return insert_string(cd,label,offs,0);
433 /* a CONTROL was specified */
434 gen_res* create_generic_control(char* label,int id,char* class,
435 rc_style*style,int x,int y,int cx,int cy)
436 { gen_res* ret=new_res();
437 int s=WS_VISIBLE|WS_CHILD; /*default styles for any control*/
438 if(style)s=(s|style->or)&style->and;
442 put_DWORD(ret->res+0,s);
444 /* put_DWORD(ret->res+4,exstyle->or); */
445 put_WORD(ret->res+8,x);
446 put_WORD(ret->res+10,y);
447 put_WORD(ret->res+12,cx);
448 put_WORD(ret->res+14,cy);
449 put_WORD(ret->res+16,id);
451 ret=insert_string(ret,label,20,0);
452 /* is it a predefined class? */
454 if(!strcasecmp(class,"BUTTON"))cl=CT_BUTTON;
455 if(!strcasecmp(class,"EDIT"))cl=CT_EDIT;
456 if(!strcasecmp(class,"STATIC"))cl=CT_STATIC;
457 if(!strcasecmp(class,"LISTBOX"))cl=CT_LISTBOX;
458 if(!strcasecmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR;
459 if(!strcasecmp(class,"COMBOBOX"))cl=CT_COMBOBOX;
461 char ffff[2]={0xff, 0xff};
462 ret=insert_bytes(ret,ffff,18,2);
463 put_WORD(ret->res+20,cl);
465 else ret=insert_string(ret,class,18,0);
470 put_WORD(ret->res+0,x);
471 put_WORD(ret->res+2,y);
472 put_WORD(ret->res+4,cx);
473 put_WORD(ret->res+6,cy);
474 put_WORD(ret->res+8,id);
475 put_DWORD(ret->res+10,s);
477 ret=insert_string(ret,label,15,0);
478 /* is it a predefined class? */
480 if(!strcasecmp(class,"BUTTON"))cl=CT_BUTTON;
481 if(!strcasecmp(class,"EDIT"))cl=CT_EDIT;
482 if(!strcasecmp(class,"STATIC"))cl=CT_STATIC;
483 if(!strcasecmp(class,"LISTBOX"))cl=CT_LISTBOX;
484 if(!strcasecmp(class,"SCROLLBAR"))cl=CT_SCROLLBAR;
485 if(!strcasecmp(class,"COMBOBOX"))cl=CT_COMBOBOX;
486 if(cl)ret->res[14]=cl;
487 else ret=insert_string(ret,class,14,0);
492 /* insert cd into rest, set the type, add flags */
493 gen_res* add_control(int type,int flags,gen_res*cd,gen_res* rest)
495 char zeros[4]={0,0,0,0};
498 char ffff[2]={0xff, 0xff};
499 put_DWORD(cd->res+0,get_DWORD(cd->res+0)|flags);
500 cd=insert_bytes(cd,ffff,18,2);
501 put_WORD(cd->res+20,type);
505 put_DWORD(cd->res+10,get_DWORD(cd->res+10)|flags);
508 /* WIN32: First control is on dword boundary */
509 if(win32 && cd->size%4)
510 cd=insert_bytes(cd,zeros,cd->size,4-cd->size%4);
511 return insert_at_beginning(rest,cd->res,cd->size);
514 /* an ICON control was specified, whf contains width, height, and flags */
515 gen_res* add_icon(char* name,int id,int x,int y,gen_res* whf,gen_res* rest)
519 put_WORD(whf->res+8,x);
520 put_WORD(whf->res+10,y);
521 put_WORD(whf->res+16,id);
525 put_WORD(whf->res+0,x);
526 put_WORD(whf->res+2,y);
527 put_WORD(whf->res+8,id);
529 whf=label_control_desc(name,whf);
530 return add_control(CT_STATIC,SS_ICON,whf,rest);
533 /* insert the generic control into rest */
534 gen_res* add_generic_control(gen_res* ctl, gen_res* rest)
536 char zeros[4]={0,0,0,0};
537 /* WIN32: Control is on dword boundary */
538 if(win32 && ctl->size%4)
539 ctl=insert_bytes(ctl,zeros,ctl->size,4-ctl->size%4);
540 return insert_at_beginning(rest,ctl->res,ctl->size);
543 /* create a dialog resource by inserting the header into the controls.
544 Set position and extent */
545 gen_res* make_dialog(gen_res* header,int x,int y,int cx,int cy,gen_res* ctls)
547 char zeros[4]={0,0,0,0};
550 put_WORD(header->res+8, ctls->num_entries);
552 put_WORD(header->res+10,x);
553 put_WORD(header->res+12,y);
554 put_WORD(header->res+14,cx);
555 put_WORD(header->res+16,cy);
559 header->res[4]=ctls->num_entries;
561 put_WORD(header->res+5,x);
562 put_WORD(header->res+7,y);
563 put_WORD(header->res+9,cx);
564 put_WORD(header->res+11,cy);
566 /* WIN32: First control is on dword boundary */
567 if(win32 && header->size%4)
568 header=insert_bytes(header,zeros,header->size,4-header->size%4);
569 return insert_bytes(header,ctls->res,header->size,ctls->size);
572 /* create {0x15,0x16,0xFF} from '15 16 FF' */
573 gen_res *hex_to_raw(char *hex, gen_res*rest)
577 for(i=0;*hex!='\'';i++)r2[i]=strtoul(hex,&hex,16);
578 return insert_bytes(rest,r2,0,i);
581 /* create a bitmap resource */
582 gen_res *make_bitmap(gen_res* res)
584 res=delete_bytes(res,0,14); /* skip bitmap file header*/
589 gen_res *make_icon(gen_res* res)
595 gen_res *make_cursor(gen_res* res)
601 /* load resource bytes from the file name */
602 gen_res *load_file(char* name)
606 int f=open(name,O_RDONLY);
614 while(res->space<st.st_size)res=grow(res);
615 read(f,res->res,st.st_size);
616 res->size=st.st_size;
621 /* insert a normal menu item into res, starting from the last item */
622 gen_res *add_menuitem(char* name,int id,int flags,gen_res *res)
625 if(res->num_entries==0)flags|=MF_END;
626 put_WORD(item,flags);
628 res=insert_string_at_beginning(res,name,1);
629 res=insert_bytes(res,item,0,4);
633 /* insert a popup item into res */
634 gen_res *add_popup(char *name,short flags, gen_res* body, gen_res*res)
638 if(res->num_entries==0)flags|=MF_END;
639 put_WORD(c_flags,flags);
640 res=insert_at_beginning(res,body->res,body->size);
641 res=insert_string(res,name,0,1);
642 res=insert_bytes(res,c_flags,0,2);
646 /* prefix the menu header into res */
647 gen_res *make_menu(gen_res* res)
649 static char header[4]={0,0,0,0};
650 res=insert_at_beginning(res,header,4);
655 /* link top-level resources */
656 gen_res *add_resource(gen_res* first,gen_res *rest)
667 typedef struct str_tbl_elm{
669 struct str_tbl_elm *next;
673 str_tbl_elm* string_table=NULL; /* sorted by group */
675 void add_str_tbl_elm(int id,char* str)
680 str_tbl_elm** elm=&string_table;
681 while(*elm && (*elm)->group<group) elm=&(*elm)->next;
682 if(!*elm || (*elm)->group!=group)
685 str_tbl_elm* new=xmalloc(sizeof(str_tbl_elm));
686 for(i=0; i<16; i++) new->strings[i] = NULL;
691 (*elm)->strings[idx]=str;
694 gen_res* add_string_table(gen_res* t)
702 if(!string_table) return t;
703 for(ste=string_table; ste; ste=ste->next)
705 for(size=0,i=0; i<16; i++)
706 size += (win32 ? 2 : 1) * (ste->strings[i] ? strlen(ste->strings[i])+1 : 1);
708 while(res->space<size)res=grow(res);
710 res->n.i_name=ste->group;
714 for(p=res->res,i=0; i<16; i++)
715 if((q=ste->strings[i])==NULL)
722 put_WORD(p, strlen(q));
731 for(p=res->res,i=0; i<16; i++)
732 if((q=ste->strings[i])==NULL)
737 while(*q) *p++ = *q++;
739 t=add_resource(res,t);
744 char *get_typename(gen_res* t)
747 case acc:return "ACCELERATOR";
748 case bmp:return "BITMAP";
749 case cur:return "CURSOR";
750 case dlg:return "DIALOG";
751 case fnt:return "FONT";
752 case ico:return "ICON";
753 case men:return "MENU";
754 case rdt:return "RCDATA";
755 case str:return "STRINGTABLE";
756 default: return "UNKNOWN";
760 /* create strings like _Sysres_DIALOG_2 */
761 char *get_resource_name(gen_res*it)
763 static char buf[1000];
765 sprintf(buf,"%s_%s_%s",prefix,get_typename(it),it->n.s_name);
767 sprintf(buf,"%s_%s_%d",prefix,get_typename(it),it->n.i_name);
771 #define ISCONSTANT (constant ? "const " : "")
773 /* create the final output */
774 void create_output(gen_res* top)
778 top=add_string_table(top);
780 /* Generate the header */
786 " * This file is automatically generated. Do not edit!\n"
790 "struct resource;\n\n",
793 /* Declare the resources */
794 for (it=top;it;it=it->next)
795 fprintf( header,"extern %sstruct resource %s;\n",
796 ISCONSTANT, get_resource_name(it) );
797 fprintf( header,"\nextern %sstruct resource * %s%s_Table[];\n\n",
798 ISCONSTANT, ISCONSTANT, prefix );
799 fprintf( header, "#endif /* __%s_H */\n", prefix );
802 /* Print the resources bytes */
804 fprintf( code, "/*\n"
805 " * This file is automatically generated. Do not edit!\n"
807 "struct resource {\n"
810 "\tconst char *name;\n"
811 "\tconst unsigned char* bytes;\n"
815 for(it=top;it;it=it->next)
818 fprintf( code, "static %sunsigned char %s__bytes[]%s = {\n",
819 ISCONSTANT, get_resource_name(it),
820 win32?"\n__attribute__ ((aligned (4)))":"");
821 for (i=0;i<it->size-1;i++)
823 fprintf(code,"0x%02x, ",it->res[i]);
824 if ((i&7)==7)fputc('\n',code);
826 fprintf(code,"0x%02x };\n\n",it->res[i]);
829 /* Print the resources names */
832 for(it=top;it;it=it->next)
835 char s_buffer[20], *s_name=s_buffer;
836 if(it->n_type) s_name=it->n.s_name;
837 else sprintf(s_name,"@%d",it->n.i_name);
838 fprintf( code, "static %sunsigned char %s__name[] = {\n",
839 ISCONSTANT, get_resource_name(it) );
840 for (i=0;*s_name;i++,s_name++)
842 fprintf(code,"0x%02x, 0x00, ",*s_name);
843 if ((i&3)==3)fputc('\n',code);
845 fprintf(code,"0x00, 0x00};\n\n");
848 /* Print the resources */
849 for (it=top;it;it=it->next)
854 case acc:type=(int)RT_ACCELERATOR16;break;
855 case bmp:type=(int)RT_BITMAP16;break;
856 case cur:type=(int)RT_CURSOR16;break;
857 case dlg:type=(int)RT_DIALOG16;break;
858 case fnt:type=(int)RT_FONT16;break;
859 case ico:type=(int)RT_ICON16;break;
860 case men:type=(int)RT_MENU16;break;
861 case rdt:type=(int)RT_RCDATA16;break;
862 case str:type=(int)RT_STRING16;break;
863 default:fprintf(stderr,"Unknown restype\n");type=-1;break;
868 fprintf(code,"%sstruct resource %s = {0,%d,%s__name,%s__bytes,%d};\n",
869 ISCONSTANT, get_resource_name(it), type, get_resource_name(it),
870 get_resource_name(it), it->size );
872 fprintf(code,"%sstruct resource %s = {%d,%d,%s__name,%s__bytes,%d};\n",
873 ISCONSTANT, get_resource_name(it), it->n.i_name, type,
874 get_resource_name(it), get_resource_name(it), it->size );
879 fprintf(code,"%sstruct resource %s = {0,%d,\"%s\",%s__bytes,%d};\n",
880 ISCONSTANT, get_resource_name(it), type, it->n.s_name,
881 get_resource_name(it), it->size );
883 fprintf(code,"%sstruct resource %s = {%d,%d,\"@%d\",%s__bytes,%d};\n",
884 ISCONSTANT, get_resource_name(it), it->n.i_name, type,
885 it->n.i_name, get_resource_name(it), it->size );
889 /* Print the resource table (NULL terminated) */
891 fprintf(code,"\n%sstruct resource * %s%s_Table[] = {\n",
892 ISCONSTANT, ISCONSTANT, prefix);
893 for (it=top;it;it=it->next)
894 fprintf( code, " &%s,\n", get_resource_name(it) );
895 fprintf( code, " 0\n};\n\n\n" );
897 /* Perform autoregistration */
900 "#if defined(__GNUC__) && (__GNUC__ == 2) && (__GNUC_MINOR__ >= 7)\n"
901 "static void DoIt(void) __attribute__((constructor));\n"
903 "static void DoIt(void);\n"
904 "void LIBWINE_Register_%s(void) { DoIt(); }\n"
906 "static void DoIt(void)\n"
908 "\textern void LIBRES_RegisterResources(const struct resource* const * Res);\n"
909 "\tLIBRES_RegisterResources(%s_Table);\n"
911 "#endif /* __WINE__ */\n"
915 gen_res* make_font(gen_res* res)
917 fprintf(stderr,"Fonts not supported\n");
921 gen_res* make_raw(gen_res* res)
923 fprintf(stderr,"RCData not supported\n");
927 gen_res* int_to_raw(int i,gen_res* res)
929 fprintf(stderr,"IntToRaw not supported\n");
933 /* translate "Hello,\\tworld!\\10" to "Hello,\tworld!\n" */
934 char *parse_c_string(char *in)
936 char *out=xmalloc(strlen(in)-1);
939 for(it=out,in++;*in;in++)
942 {case 't':*it++='\t';break;
943 case 'r':*it++='\r';break;
944 case 'n':*it++='\n';break;
945 case 'a':*it++='\a';break;
947 memset(tmp,0,5);/*make sure it doesn't use more than 4 chars*/
949 *it++=strtoul(tmp,&tend,0);
952 case '1':case '2':case '3':case '4':case '5':
953 case '6':case '7':case '8':case '9':
956 *it++=strtoul(tmp,&tend,10);
962 *it++=strtoul(tmp,&tend,16);