2009-06-19 Tristan Gingold <gingold@adacore.com>
[binutils.git] / binutils / resres.c
blob2ad40b481a39fa9532299f9cab5e0cdc6b1dc9d7
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2 Copyright 1998, 1999, 2001, 2002, 2007, 2008
3 Free Software Foundation, Inc.
4 Written by Anders Norlander <anorland@hem2.passagen.se>.
5 Rewritten by Kai Tietz, Onevision.
7 This file is part of GNU Binutils.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
22 02110-1301, USA. */
24 /* FIXME: This file does not work correctly in a cross configuration.
25 It assumes that it can use fread and fwrite to read and write
26 integers. It does no swapping. */
28 #include "sysdep.h"
29 #include "bfd.h"
30 #include "bucomm.h"
31 #include "libiberty.h"
32 #include "windres.h"
34 #include <assert.h>
35 #include <time.h>
37 static rc_uint_type write_res_directory (windres_bfd *, rc_uint_type,
38 const rc_res_directory *, const rc_res_id *,
39 const rc_res_id *, rc_uint_type *, int);
40 static rc_uint_type write_res_resource (windres_bfd *, rc_uint_type,const rc_res_id *,
41 const rc_res_id *, const rc_res_resource *,
42 rc_uint_type *);
43 static rc_uint_type write_res_bin (windres_bfd *, rc_uint_type, const rc_res_resource *,
44 const rc_res_id *, const rc_res_id *,
45 const rc_res_res_info *);
47 static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
48 static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
49 static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
51 static rc_uint_type write_res_header (windres_bfd *, rc_uint_type, rc_uint_type,
52 const rc_res_id *, const rc_res_id *,
53 const rc_res_res_info *);
55 static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
56 static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
57 rc_uint_type);
58 static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
59 static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
60 static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
61 static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
62 static int probe_binary (windres_bfd *wrbfd, rc_uint_type);
64 static unsigned long get_id_size (const rc_res_id *);
66 static void res_add_resource (rc_res_resource *, const rc_res_id *,
67 const rc_res_id *, rc_uint_type, int);
69 static void res_append_resource (rc_res_directory **, rc_res_resource *,
70 int, const rc_res_id *, int);
72 static rc_res_directory *resources = NULL;
74 static const char *filename;
76 extern char *program_name;
78 /* Read resource file */
79 rc_res_directory *
80 read_res_file (const char *fn)
82 rc_uint_type off, flen;
83 windres_bfd wrbfd;
84 bfd *abfd;
85 asection *sec;
86 filename = fn;
88 flen = (rc_uint_type) get_file_size (filename);
89 if (! flen)
90 fatal ("can't open '%s' for input.", filename);
91 abfd = windres_open_as_binary (filename, 1);
92 sec = bfd_get_section_by_name (abfd, ".data");
93 if (sec == NULL)
94 bfd_fatal ("bfd_get_section_by_name");
95 set_windres_bfd (&wrbfd, abfd, sec,
96 (target_is_bigendian ? WR_KIND_BFD_BIN_B
97 : WR_KIND_BFD_BIN_L));
98 off = 0;
100 if (! probe_binary (&wrbfd, flen))
101 set_windres_bfd_endianess (&wrbfd, ! target_is_bigendian);
103 skip_null_resource (&wrbfd, &off, flen);
105 while (read_resource_entry (&wrbfd, &off, flen))
108 bfd_close (abfd);
110 return resources;
113 /* Write resource file */
114 void
115 write_res_file (const char *fn,const rc_res_directory *resdir)
117 asection *sec;
118 rc_uint_type language;
119 bfd *abfd;
120 windres_bfd wrbfd;
121 unsigned long sec_length = 0,sec_length_wrote;
122 static const bfd_byte sign[] =
123 {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
124 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
128 filename = fn;
130 abfd = windres_open_as_binary (filename, 0);
131 sec = bfd_make_section_with_flags (abfd, ".data",
132 (SEC_HAS_CONTENTS | SEC_ALLOC
133 | SEC_LOAD | SEC_DATA));
134 if (sec == NULL)
135 bfd_fatal ("bfd_make_section");
136 /* Requiring this is probably a bug in BFD. */
137 sec->output_section = sec;
139 set_windres_bfd (&wrbfd, abfd, sec,
140 (target_is_bigendian ? WR_KIND_BFD_BIN_B
141 : WR_KIND_BFD_BIN_L));
143 language = -1;
144 sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
145 (const rc_res_id *) NULL,
146 (const rc_res_id *) NULL, &language, 1);
147 if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3))
148 bfd_fatal ("bfd_set_section_size");
149 if ((sec_length & 3) != 0)
150 set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
151 set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
152 language = -1;
153 sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
154 (const rc_res_id *) NULL,
155 (const rc_res_id *) NULL,
156 &language, 1);
157 if (sec_length != sec_length_wrote)
158 fatal ("res write failed with different sizes (%lu/%lu).",
159 (unsigned long) sec_length, (unsigned long) sec_length_wrote);
161 bfd_close (abfd);
162 return;
165 /* Read a resource entry, returns 0 when all resources are read */
166 static int
167 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
169 rc_res_id type;
170 rc_res_id name;
171 rc_res_res_info resinfo;
172 res_hdr reshdr;
173 void *buff;
175 rc_res_resource *r;
176 struct bin_res_info l;
178 off[0] = (off[0] + 3) & ~3;
180 /* Read header */
181 if ((off[0] + 8) > omax)
182 return 0;
183 read_res_data_hdr (wrbfd, off, omax, &reshdr);
185 /* read resource type */
186 read_res_id (wrbfd, off, omax, &type);
187 /* read resource id */
188 read_res_id (wrbfd, off, omax, &name);
190 off[0] = (off[0] + 3) & ~3;
192 /* Read additional resource header */
193 read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
194 resinfo.version = windres_get_32 (wrbfd, l.version, 4);
195 resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
196 resinfo.language = windres_get_16 (wrbfd, l.language, 2);
197 /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
198 resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
200 off[0] = (off[0] + 3) & ~3;
202 /* Allocate buffer for data */
203 buff = res_alloc (reshdr.data_size);
204 /* Read data */
205 read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
206 /* Convert binary data to resource */
207 r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
208 r->res_info = resinfo;
209 /* Add resource to resource directory */
210 res_add_resource (r, &type, &name, resinfo.language, 0);
212 return 1;
215 /* write resource directory to binary resource file */
216 static rc_uint_type
217 write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
218 const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
219 int level)
221 const rc_res_entry *re;
223 for (re = rd->entries; re != NULL; re = re->next)
225 switch (level)
227 case 1:
228 /* If we're at level 1, the key of this resource is the
229 type. This normally duplicates the information we have
230 stored with the resource itself, but we need to remember
231 the type if this is a user define resource type. */
232 type = &re->id;
233 break;
235 case 2:
236 /* If we're at level 2, the key of this resource is the name
237 we are going to use in the rc printout. */
238 name = &re->id;
239 break;
241 case 3:
242 /* If we're at level 3, then this key represents a language.
243 Use it to update the current language. */
244 if (! re->id.named
245 && re->id.u.id != (unsigned long) *language
246 && (re->id.u.id & 0xffff) == re->id.u.id)
248 *language = re->id.u.id;
250 break;
252 default:
253 break;
256 if (re->subdir)
257 off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
258 level + 1);
259 else
261 if (level == 3)
263 /* This is the normal case: the three levels are
264 TYPE/NAME/LANGUAGE. NAME will have been set at level
265 2, and represents the name to use. We probably just
266 set LANGUAGE, and it will probably match what the
267 resource itself records if anything. */
268 off = write_res_resource (wrbfd, off, type, name, re->u.res,
269 language);
271 else
273 fprintf (stderr, "// Resource at unexpected level %d\n", level);
274 off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
275 re->u.res, language);
280 return off;
283 static rc_uint_type
284 write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
285 const rc_res_id *name, const rc_res_resource *res,
286 rc_uint_type *language ATTRIBUTE_UNUSED)
288 int rt;
290 switch (res->type)
292 default:
293 abort ();
295 case RES_TYPE_ACCELERATOR:
296 rt = RT_ACCELERATOR;
297 break;
299 case RES_TYPE_BITMAP:
300 rt = RT_BITMAP;
301 break;
303 case RES_TYPE_CURSOR:
304 rt = RT_CURSOR;
305 break;
307 case RES_TYPE_GROUP_CURSOR:
308 rt = RT_GROUP_CURSOR;
309 break;
311 case RES_TYPE_DIALOG:
312 rt = RT_DIALOG;
313 break;
315 case RES_TYPE_FONT:
316 rt = RT_FONT;
317 break;
319 case RES_TYPE_FONTDIR:
320 rt = RT_FONTDIR;
321 break;
323 case RES_TYPE_ICON:
324 rt = RT_ICON;
325 break;
327 case RES_TYPE_GROUP_ICON:
328 rt = RT_GROUP_ICON;
329 break;
331 case RES_TYPE_MENU:
332 rt = RT_MENU;
333 break;
335 case RES_TYPE_MESSAGETABLE:
336 rt = RT_MESSAGETABLE;
337 break;
339 case RES_TYPE_RCDATA:
340 rt = RT_RCDATA;
341 break;
343 case RES_TYPE_STRINGTABLE:
344 rt = RT_STRING;
345 break;
347 case RES_TYPE_USERDATA:
348 rt = 0;
349 break;
351 case RES_TYPE_VERSIONINFO:
352 rt = RT_VERSION;
353 break;
355 case RES_TYPE_TOOLBAR:
356 rt = RT_TOOLBAR;
357 break;
360 if (rt != 0
361 && type != NULL
362 && (type->named || type->u.id != (unsigned long) rt))
364 fprintf (stderr, "// Unexpected resource type mismatch: ");
365 res_id_print (stderr, *type, 1);
366 fprintf (stderr, " != %d", rt);
367 abort ();
370 return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
373 /* Write a resource in binary resource format */
374 static rc_uint_type
375 write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
376 const rc_res_id *type, const rc_res_id *name,
377 const rc_res_res_info *resinfo)
379 rc_uint_type noff;
380 rc_uint_type datasize = 0;
382 noff = res_to_bin ((windres_bfd *) NULL, off, res);
383 datasize = noff - off;
385 off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
386 return res_to_bin (wrbfd, off, res);
389 /* Get number of bytes needed to store an id in binary format */
390 static unsigned long
391 get_id_size (id)
392 const rc_res_id *id;
394 if (id->named)
395 return sizeof (unichar) * (id->u.n.length + 1);
396 else
397 return sizeof (unichar) * 2;
400 /* Write a resource header */
401 static rc_uint_type
402 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
403 const rc_res_id *type, const rc_res_id *name,
404 const rc_res_res_info *resinfo)
406 res_hdr reshdr;
407 reshdr.data_size = datasize;
408 reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
410 reshdr.header_size = (reshdr.header_size + 3) & ~3;
412 off = (off + 3) & ~3;
414 off = write_res_data_hdr (wrbfd, off, &reshdr);
415 off = write_res_id (wrbfd, off, type);
416 off = write_res_id (wrbfd, off, name);
418 off = (off + 3) & ~3;
420 off = write_res_info (wrbfd, off, resinfo);
421 off = (off + 3) & ~3;
422 return off;
425 static rc_uint_type
426 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
428 if (wrbfd)
430 struct bin_res_hdr brh;
431 windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
432 windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
433 set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
435 return off + BIN_RES_HDR_SIZE;
438 static void
439 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
440 res_hdr *reshdr)
442 struct bin_res_hdr brh;
444 if ((off[0] + BIN_RES_HDR_SIZE) > omax)
445 fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
447 get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
448 reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
449 reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
450 off[0] += BIN_RES_HDR_SIZE;
453 /* Read data from file, abort on failure */
454 static void
455 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
456 rc_uint_type size)
458 if ((off[0] + size) > omax)
459 fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
460 (long) omax, (long) size);
461 get_windres_bfd_content (wrbfd, data, off[0], size);
462 off[0] += size;
465 /* Write a resource id */
466 static rc_uint_type
467 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
469 if (id->named)
471 rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
472 if (wrbfd)
474 rc_uint_type i;
475 bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
476 for (i = 0; i < (len - 1); i++)
477 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
478 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
479 set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
481 off += (len * sizeof (unichar));
483 else
485 if (wrbfd)
487 struct bin_res_id bid;
488 windres_put_16 (wrbfd, bid.sig, 0xffff);
489 windres_put_16 (wrbfd, bid.id, id->u.id);
490 set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
492 off += BIN_RES_ID;
494 return off;
497 /* Write resource info */
498 static rc_uint_type
499 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
501 if (wrbfd)
503 struct bin_res_info l;
505 windres_put_32 (wrbfd, l.version, info->version);
506 windres_put_16 (wrbfd, l.memflags, info->memflags);
507 windres_put_16 (wrbfd, l.language, info->language);
508 windres_put_32 (wrbfd, l.version2, info->version);
509 windres_put_32 (wrbfd, l.characteristics, info->characteristics);
510 set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
512 return off + BIN_RES_INFO_SIZE;
515 /* read a resource identifier */
516 static void
517 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
519 struct bin_res_id bid;
520 unsigned short ord;
521 unichar *id_s = NULL;
522 rc_uint_type len;
524 read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
525 ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
526 if (ord == 0xFFFF) /* an ordinal id */
528 read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
529 id->named = 0;
530 id->u.id = windres_get_16 (wrbfd, bid.id, 2);
532 else
533 /* named id */
535 off[0] -= 2;
536 id_s = read_unistring (wrbfd, off, omax, &len);
537 id->named = 1;
538 id->u.n.length = len;
539 id->u.n.name = id_s;
543 /* Read a null terminated UNICODE string */
544 static unichar *
545 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
546 rc_uint_type *len)
548 unichar *s;
549 bfd_byte d[2];
550 unichar c;
551 unichar *p;
552 rc_uint_type l;
553 rc_uint_type soff = off[0];
557 read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
558 c = windres_get_16 (wrbfd, d, 2);
560 while (c != 0);
561 l = ((soff - off[0]) / sizeof (unichar));
563 /* there are hardly any names longer than 256 characters, but anyway. */
564 p = s = (unichar *) xmalloc (sizeof (unichar) * l);
567 read_res_data (wrbfd, off, omax, d, sizeof (unichar));
568 c = windres_get_16 (wrbfd, d, 2);
569 *p++ = c;
571 while (c != 0);
572 *len = l - 1;
573 return s;
576 static int
577 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
579 rc_uint_type off;
580 res_hdr reshdr;
582 off = 0;
583 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
584 if (reshdr.data_size != 0)
585 return 1;
586 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
587 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
588 return 1;
590 /* Subtract size of HeaderSize. DataSize has to be zero. */
591 off += 0x20 - BIN_RES_HDR_SIZE;
592 if ((off + BIN_RES_HDR_SIZE) >= omax)
593 return 1;
594 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
595 /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
596 which is part of reshdr.header_size. We shouldn't take it
597 into account twice. */
598 if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
599 return 0;
600 return 1;
603 /* Check if file is a win32 binary resource file, if so
604 skip past the null resource. Returns 0 if successful, -1 on
605 error.
607 static void
608 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
610 res_hdr reshdr;
611 read_res_data_hdr (wrbfd, off, omax, &reshdr);
612 if (reshdr.data_size != 0)
613 goto skip_err;
614 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
615 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
616 goto skip_err;
618 /* Subtract size of HeaderSize. DataSize has to be zero. */
619 off[0] += 0x20 - BIN_RES_HDR_SIZE;
620 if (off[0] >= omax)
621 goto skip_err;
623 return;
625 skip_err:
626 fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
627 filename);
628 xexit (1);
631 /* Add a resource to resource directory */
632 static void
633 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
634 rc_uint_type language, int dupok)
636 rc_res_id a[3];
638 a[0] = *type;
639 a[1] = *id;
640 a[2].named = 0;
641 a[2].u.id = language;
642 res_append_resource (&resources, r, 3, a, dupok);
645 /* Append a resource to resource directory.
646 This is just copied from define_resource
647 and modified to add an existing resource.
649 static void
650 res_append_resource (rc_res_directory **resources, rc_res_resource *resource,
651 int cids, const rc_res_id *ids, int dupok)
653 rc_res_entry *re = NULL;
654 int i;
656 assert (cids > 0);
657 for (i = 0; i < cids; i++)
659 rc_res_entry **pp;
661 if (*resources == NULL)
663 static unsigned long timeval;
665 /* Use the same timestamp for every resource created in a
666 single run. */
667 if (timeval == 0)
668 timeval = time (NULL);
670 *resources = ((rc_res_directory *)
671 res_alloc (sizeof (rc_res_directory)));
672 (*resources)->characteristics = 0;
673 (*resources)->time = timeval;
674 (*resources)->major = 0;
675 (*resources)->minor = 0;
676 (*resources)->entries = NULL;
679 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
680 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
681 break;
683 if (*pp != NULL)
684 re = *pp;
685 else
687 re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
688 re->next = NULL;
689 re->id = ids[i];
690 if ((i + 1) < cids)
692 re->subdir = 1;
693 re->u.dir = NULL;
695 else
697 re->subdir = 0;
698 re->u.res = NULL;
701 *pp = re;
704 if ((i + 1) < cids)
706 if (! re->subdir)
708 fprintf (stderr, "%s: ", program_name);
709 res_ids_print (stderr, i, ids);
710 fprintf (stderr, ": expected to be a directory\n");
711 xexit (1);
714 resources = &re->u.dir;
718 if (re->subdir)
720 fprintf (stderr, "%s: ", program_name);
721 res_ids_print (stderr, cids, ids);
722 fprintf (stderr, ": expected to be a leaf\n");
723 xexit (1);
726 if (re->u.res != NULL)
728 if (dupok)
729 return;
731 fprintf (stderr, "%s: warning: ", program_name);
732 res_ids_print (stderr, cids, ids);
733 fprintf (stderr, ": duplicate value\n");
736 re->u.res = resource;