1 /* resres.c: read_res_file and write_res_file implementation for windres.
2 Copyright 1998, 1999, 2001, 2002, 2007
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
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. */
31 #include "libiberty.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 *,
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 *,
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 */
80 read_res_file (const char *fn)
82 rc_uint_type off, flen;
88 flen = (rc_uint_type) get_file_size (filename);
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");
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));
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))
113 /* Write resource file */
115 write_res_file (const char *fn,const rc_res_directory *resdir)
118 rc_uint_type language;
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};
130 abfd = windres_open_as_binary (filename, 0);
131 sec = bfd_make_section (abfd, ".data");
133 bfd_fatal ("bfd_make_section");
134 if (! bfd_set_section_flags (abfd, sec,
135 (SEC_HAS_CONTENTS | SEC_ALLOC
136 | SEC_LOAD | SEC_DATA)))
137 bfd_fatal ("bfd_set_section_flags");
138 /* Requiring this is probably a bug in BFD. */
139 sec->output_section = sec;
141 set_windres_bfd (&wrbfd, abfd, sec,
142 (target_is_bigendian ? WR_KIND_BFD_BIN_B
143 : WR_KIND_BFD_BIN_L));
146 sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
147 (const rc_res_id *) NULL,
148 (const rc_res_id *) NULL, &language, 1);
149 if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3))
150 bfd_fatal ("bfd_set_section_size");
151 if ((sec_length & 3) != 0)
152 set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
153 set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
155 sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
156 (const rc_res_id *) NULL,
157 (const rc_res_id *) NULL,
159 if (sec_length != sec_length_wrote)
160 fatal ("res write failed with different sizes (%lu/%lu).", (long) sec_length,
161 (long) sec_length_wrote);
167 /* Read a resource entry, returns 0 when all resources are read */
169 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
173 rc_res_res_info resinfo;
178 struct bin_res_info l;
180 off[0] = (off[0] + 3) & ~3;
183 if ((off[0] + 8) > omax)
185 read_res_data_hdr (wrbfd, off, omax, &reshdr);
187 /* read resource type */
188 read_res_id (wrbfd, off, omax, &type);
189 /* read resource id */
190 read_res_id (wrbfd, off, omax, &name);
192 off[0] = (off[0] + 3) & ~3;
194 /* Read additional resource header */
195 read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
196 resinfo.version = windres_get_32 (wrbfd, l.version, 4);
197 resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
198 resinfo.language = windres_get_16 (wrbfd, l.language, 2);
199 /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
200 resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
202 off[0] = (off[0] + 3) & ~3;
204 /* Allocate buffer for data */
205 buff = res_alloc (reshdr.data_size);
207 read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
208 /* Convert binary data to resource */
209 r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
210 r->res_info = resinfo;
211 /* Add resource to resource directory */
212 res_add_resource (r, &type, &name, resinfo.language, 0);
217 /* write resource directory to binary resource file */
219 write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
220 const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
223 const rc_res_entry *re;
225 for (re = rd->entries; re != NULL; re = re->next)
230 /* If we're at level 1, the key of this resource is the
231 type. This normally duplicates the information we have
232 stored with the resource itself, but we need to remember
233 the type if this is a user define resource type. */
238 /* If we're at level 2, the key of this resource is the name
239 we are going to use in the rc printout. */
244 /* If we're at level 3, then this key represents a language.
245 Use it to update the current language. */
247 && re->id.u.id != (unsigned long) *language
248 && (re->id.u.id & 0xffff) == re->id.u.id)
250 *language = re->id.u.id;
259 off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
265 /* This is the normal case: the three levels are
266 TYPE/NAME/LANGUAGE. NAME will have been set at level
267 2, and represents the name to use. We probably just
268 set LANGUAGE, and it will probably match what the
269 resource itself records if anything. */
270 off = write_res_resource (wrbfd, off, type, name, re->u.res,
275 fprintf (stderr, "// Resource at unexpected level %d\n", level);
276 off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
277 re->u.res, language);
286 write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
287 const rc_res_id *name, const rc_res_resource *res,
288 rc_uint_type *language ATTRIBUTE_UNUSED)
297 case RES_TYPE_ACCELERATOR:
301 case RES_TYPE_BITMAP:
305 case RES_TYPE_CURSOR:
309 case RES_TYPE_GROUP_CURSOR:
310 rt = RT_GROUP_CURSOR;
313 case RES_TYPE_DIALOG:
321 case RES_TYPE_FONTDIR:
329 case RES_TYPE_GROUP_ICON:
337 case RES_TYPE_MESSAGETABLE:
338 rt = RT_MESSAGETABLE;
341 case RES_TYPE_RCDATA:
345 case RES_TYPE_STRINGTABLE:
349 case RES_TYPE_USERDATA:
353 case RES_TYPE_VERSIONINFO:
357 case RES_TYPE_TOOLBAR:
364 && (type->named || type->u.id != (unsigned long) rt))
366 fprintf (stderr, "// Unexpected resource type mismatch: ");
367 res_id_print (stderr, *type, 1);
368 fprintf (stderr, " != %d", rt);
372 return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
375 /* Write a resource in binary resource format */
377 write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
378 const rc_res_id *type, const rc_res_id *name,
379 const rc_res_res_info *resinfo)
382 rc_uint_type datasize = 0;
384 noff = res_to_bin ((windres_bfd *) NULL, off, res);
385 datasize = noff - off;
387 off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
388 return res_to_bin (wrbfd, off, res);
391 /* Get number of bytes needed to store an id in binary format */
397 return sizeof (unichar) * (id->u.n.length + 1);
399 return sizeof (unichar) * 2;
402 /* Write a resource header */
404 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
405 const rc_res_id *type, const rc_res_id *name,
406 const rc_res_res_info *resinfo)
409 reshdr.data_size = datasize;
410 reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
412 reshdr.header_size = (reshdr.header_size + 3) & ~3;
414 off = (off + 3) & ~3;
416 off = write_res_data_hdr (wrbfd, off, &reshdr);
417 off = write_res_id (wrbfd, off, type);
418 off = write_res_id (wrbfd, off, name);
420 off = (off + 3) & ~3;
422 off = write_res_info (wrbfd, off, resinfo);
423 off = (off + 3) & ~3;
428 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
432 struct bin_res_hdr brh;
433 windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
434 windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
435 set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
437 return off + BIN_RES_HDR_SIZE;
441 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
444 struct bin_res_hdr brh;
446 if ((off[0] + BIN_RES_HDR_SIZE) > omax)
447 fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
449 get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
450 reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
451 reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
452 off[0] += BIN_RES_HDR_SIZE;
455 /* Read data from file, abort on failure */
457 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
460 if ((off[0] + size) > omax)
461 fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
462 (long) omax, (long) size);
463 get_windres_bfd_content (wrbfd, data, off[0], size);
467 /* Write a resource id */
469 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
473 rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
477 bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
478 for (i = 0; i < (len - 1); i++)
479 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
480 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
481 set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
483 off += (len * sizeof (unichar));
489 struct bin_res_id bid;
490 windres_put_16 (wrbfd, bid.sig, 0xffff);
491 windres_put_16 (wrbfd, bid.id, id->u.id);
492 set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
499 /* Write resource info */
501 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
505 struct bin_res_info l;
507 windres_put_32 (wrbfd, l.version, info->version);
508 windres_put_16 (wrbfd, l.memflags, info->memflags);
509 windres_put_16 (wrbfd, l.language, info->language);
510 windres_put_32 (wrbfd, l.version2, info->version);
511 windres_put_32 (wrbfd, l.characteristics, info->characteristics);
512 set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
514 return off + BIN_RES_INFO_SIZE;
517 /* read a resource identifier */
519 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
521 struct bin_res_id bid;
523 unichar *id_s = NULL;
526 read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
527 ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
528 if (ord == 0xFFFF) /* an ordinal id */
530 read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
532 id->u.id = windres_get_16 (wrbfd, bid.id, 2);
538 id_s = read_unistring (wrbfd, off, omax, &len);
540 id->u.n.length = len;
545 /* Read a null terminated UNICODE string */
547 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
555 rc_uint_type soff = off[0];
559 read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
560 c = windres_get_16 (wrbfd, d, 2);
563 l = ((soff - off[0]) / sizeof (unichar));
565 /* there are hardly any names longer than 256 characters, but anyway. */
566 p = s = (unichar *) xmalloc (sizeof (unichar) * l);
569 read_res_data (wrbfd, off, omax, d, sizeof (unichar));
570 c = windres_get_16 (wrbfd, d, 2);
579 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
585 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
586 if (reshdr.data_size != 0)
588 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
589 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
592 /* Subtract size of HeaderSize. DataSize has to be zero. */
593 off += 0x20 - BIN_RES_HDR_SIZE;
594 if ((off + BIN_RES_HDR_SIZE) >= omax)
596 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
597 /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
598 which is part of reshdr.header_size. We shouldn't take it
599 into account twice. */
600 if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
605 /* Check if file is a win32 binary resource file, if so
606 skip past the null resource. Returns 0 if successful, -1 on
610 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
613 read_res_data_hdr (wrbfd, off, omax, &reshdr);
614 if (reshdr.data_size != 0)
616 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
617 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
620 /* Subtract size of HeaderSize. DataSize has to be zero. */
621 off[0] += 0x20 - BIN_RES_HDR_SIZE;
628 fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
633 /* Add a resource to resource directory */
635 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
636 rc_uint_type language, int dupok)
643 a[2].u.id = language;
644 res_append_resource (&resources, r, 3, a, dupok);
647 /* Append a resource to resource directory.
648 This is just copied from define_resource
649 and modified to add an existing resource.
652 res_append_resource (rc_res_directory **resources, rc_res_resource *resource,
653 int cids, const rc_res_id *ids, int dupok)
655 rc_res_entry *re = NULL;
659 for (i = 0; i < cids; i++)
663 if (*resources == NULL)
665 static unsigned long timeval;
667 /* Use the same timestamp for every resource created in a
670 timeval = time (NULL);
672 *resources = ((rc_res_directory *)
673 res_alloc (sizeof (rc_res_directory)));
674 (*resources)->characteristics = 0;
675 (*resources)->time = timeval;
676 (*resources)->major = 0;
677 (*resources)->minor = 0;
678 (*resources)->entries = NULL;
681 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
682 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
689 re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
710 fprintf (stderr, "%s: ", program_name);
711 res_ids_print (stderr, i, ids);
712 fprintf (stderr, ": expected to be a directory\n");
716 resources = &re->u.dir;
722 fprintf (stderr, "%s: ", program_name);
723 res_ids_print (stderr, cids, ids);
724 fprintf (stderr, ": expected to be a leaf\n");
728 if (re->u.res != NULL)
733 fprintf (stderr, "%s: warning: ", program_name);
734 res_ids_print (stderr, cids, ids);
735 fprintf (stderr, ": duplicate value\n");
738 re->u.res = resource;