1 /* resres.c: read_res_file and write_res_file implementation for windres.
2 Copyright (C) 1998-2015 Free Software Foundation, Inc.
3 Written by Anders Norlander <anorland@hem2.passagen.se>.
4 Rewritten by Kai Tietz, Onevision.
6 This file is part of GNU Binutils.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
23 /* FIXME: This file does not work correctly in a cross configuration.
24 It assumes that it can use fread and fwrite to read and write
25 integers. It does no swapping. */
30 #include "libiberty.h"
35 static rc_uint_type write_res_directory (windres_bfd *, rc_uint_type,
36 const rc_res_directory *, const rc_res_id *,
37 const rc_res_id *, rc_uint_type *, int);
38 static rc_uint_type write_res_resource (windres_bfd *, rc_uint_type,const rc_res_id *,
39 const rc_res_id *, const rc_res_resource *,
41 static rc_uint_type write_res_bin (windres_bfd *, rc_uint_type, const rc_res_resource *,
42 const rc_res_id *, const rc_res_id *,
43 const rc_res_res_info *);
45 static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
46 static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
47 static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
49 static rc_uint_type write_res_header (windres_bfd *, rc_uint_type, rc_uint_type,
50 const rc_res_id *, const rc_res_id *,
51 const rc_res_res_info *);
53 static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
54 static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
56 static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
57 static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
58 static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
59 static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
60 static int probe_binary (windres_bfd *wrbfd, rc_uint_type);
62 static unsigned long get_id_size (const rc_res_id *);
64 static void res_add_resource (rc_res_resource *, const rc_res_id *,
65 const rc_res_id *, rc_uint_type, int);
67 static void res_append_resource (rc_res_directory **, rc_res_resource *,
68 int, const rc_res_id *, int);
70 static rc_res_directory *resources = NULL;
72 static const char *filename;
74 extern char *program_name;
76 /* Read resource file */
78 read_res_file (const char *fn)
80 rc_uint_type off, flen;
86 flen = (rc_uint_type) get_file_size (filename);
88 fatal ("can't open '%s' for input.", filename);
89 abfd = windres_open_as_binary (filename, 1);
90 sec = bfd_get_section_by_name (abfd, ".data");
92 bfd_fatal ("bfd_get_section_by_name");
93 set_windres_bfd (&wrbfd, abfd, sec,
94 (target_is_bigendian ? WR_KIND_BFD_BIN_B
95 : WR_KIND_BFD_BIN_L));
98 if (! probe_binary (&wrbfd, flen))
99 set_windres_bfd_endianness (&wrbfd, ! target_is_bigendian);
101 skip_null_resource (&wrbfd, &off, flen);
103 while (read_resource_entry (&wrbfd, &off, flen))
111 /* Write resource file */
113 write_res_file (const char *fn,const rc_res_directory *resdir)
116 rc_uint_type language;
119 unsigned long sec_length = 0,sec_length_wrote;
120 static const bfd_byte sign[] =
121 {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
122 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
128 abfd = windres_open_as_binary (filename, 0);
129 sec = bfd_make_section_with_flags (abfd, ".data",
130 (SEC_HAS_CONTENTS | SEC_ALLOC
131 | SEC_LOAD | SEC_DATA));
133 bfd_fatal ("bfd_make_section");
134 /* Requiring this is probably a bug in BFD. */
135 sec->output_section = sec;
137 set_windres_bfd (&wrbfd, abfd, sec,
138 (target_is_bigendian ? WR_KIND_BFD_BIN_B
139 : WR_KIND_BFD_BIN_L));
142 sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
143 (const rc_res_id *) NULL,
144 (const rc_res_id *) NULL, &language, 1);
145 if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3))
146 bfd_fatal ("bfd_set_section_size");
147 if ((sec_length & 3) != 0)
148 set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
149 set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
151 sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
152 (const rc_res_id *) NULL,
153 (const rc_res_id *) NULL,
155 if (sec_length != sec_length_wrote)
156 fatal ("res write failed with different sizes (%lu/%lu).",
157 (unsigned long) sec_length, (unsigned long) sec_length_wrote);
163 /* Read a resource entry, returns 0 when all resources are read */
165 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
169 rc_res_res_info resinfo;
174 struct bin_res_info l;
176 off[0] = (off[0] + 3) & ~3;
179 if ((off[0] + 8) > omax)
181 read_res_data_hdr (wrbfd, off, omax, &reshdr);
183 /* read resource type */
184 read_res_id (wrbfd, off, omax, &type);
185 /* read resource id */
186 read_res_id (wrbfd, off, omax, &name);
188 off[0] = (off[0] + 3) & ~3;
190 /* Read additional resource header */
191 read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
192 resinfo.version = windres_get_32 (wrbfd, l.version, 4);
193 resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
194 resinfo.language = windres_get_16 (wrbfd, l.language, 2);
195 /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
196 resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
198 off[0] = (off[0] + 3) & ~3;
200 /* Allocate buffer for data */
201 buff = res_alloc (reshdr.data_size);
203 read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
204 /* Convert binary data to resource */
205 r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
206 r->res_info = resinfo;
207 /* Add resource to resource directory */
208 res_add_resource (r, &type, &name, resinfo.language, 0);
213 /* write resource directory to binary resource file */
215 write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
216 const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
219 const rc_res_entry *re;
221 for (re = rd->entries; re != NULL; re = re->next)
226 /* If we're at level 1, the key of this resource is the
227 type. This normally duplicates the information we have
228 stored with the resource itself, but we need to remember
229 the type if this is a user define resource type. */
234 /* If we're at level 2, the key of this resource is the name
235 we are going to use in the rc printout. */
240 /* If we're at level 3, then this key represents a language.
241 Use it to update the current language. */
243 && re->id.u.id != (unsigned long) *language
244 && (re->id.u.id & 0xffff) == re->id.u.id)
246 *language = re->id.u.id;
255 off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
261 /* This is the normal case: the three levels are
262 TYPE/NAME/LANGUAGE. NAME will have been set at level
263 2, and represents the name to use. We probably just
264 set LANGUAGE, and it will probably match what the
265 resource itself records if anything. */
266 off = write_res_resource (wrbfd, off, type, name, re->u.res,
271 fprintf (stderr, "// Resource at unexpected level %d\n", level);
272 off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
273 re->u.res, language);
282 write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
283 const rc_res_id *name, const rc_res_resource *res,
284 rc_uint_type *language ATTRIBUTE_UNUSED)
293 case RES_TYPE_ACCELERATOR:
297 case RES_TYPE_BITMAP:
301 case RES_TYPE_CURSOR:
305 case RES_TYPE_GROUP_CURSOR:
306 rt = RT_GROUP_CURSOR;
309 case RES_TYPE_DIALOG:
317 case RES_TYPE_FONTDIR:
325 case RES_TYPE_GROUP_ICON:
333 case RES_TYPE_MESSAGETABLE:
334 rt = RT_MESSAGETABLE;
337 case RES_TYPE_RCDATA:
341 case RES_TYPE_STRINGTABLE:
345 case RES_TYPE_USERDATA:
349 case RES_TYPE_VERSIONINFO:
353 case RES_TYPE_TOOLBAR:
360 && (type->named || type->u.id != (unsigned long) rt))
362 fprintf (stderr, "// Unexpected resource type mismatch: ");
363 res_id_print (stderr, *type, 1);
364 fprintf (stderr, " != %d", rt);
368 return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
371 /* Write a resource in binary resource format */
373 write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
374 const rc_res_id *type, const rc_res_id *name,
375 const rc_res_res_info *resinfo)
378 rc_uint_type datasize = 0;
380 noff = res_to_bin ((windres_bfd *) NULL, off, res);
381 datasize = noff - off;
383 off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
384 return res_to_bin (wrbfd, off, res);
387 /* Get number of bytes needed to store an id in binary format */
393 return sizeof (unichar) * (id->u.n.length + 1);
395 return sizeof (unichar) * 2;
398 /* Write a resource header */
400 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
401 const rc_res_id *type, const rc_res_id *name,
402 const rc_res_res_info *resinfo)
405 reshdr.data_size = datasize;
406 reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
408 reshdr.header_size = (reshdr.header_size + 3) & ~3;
410 off = (off + 3) & ~3;
412 off = write_res_data_hdr (wrbfd, off, &reshdr);
413 off = write_res_id (wrbfd, off, type);
414 off = write_res_id (wrbfd, off, name);
416 off = (off + 3) & ~3;
418 off = write_res_info (wrbfd, off, resinfo);
419 off = (off + 3) & ~3;
424 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
428 struct bin_res_hdr brh;
429 windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
430 windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
431 set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
433 return off + BIN_RES_HDR_SIZE;
437 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
440 struct bin_res_hdr brh;
442 if ((off[0] + BIN_RES_HDR_SIZE) > omax)
443 fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
445 get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
446 reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
447 reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
448 off[0] += BIN_RES_HDR_SIZE;
451 /* Read data from file, abort on failure */
453 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
456 if ((off[0] + size) > omax)
457 fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
458 (long) omax, (long) size);
459 get_windres_bfd_content (wrbfd, data, off[0], size);
463 /* Write a resource id */
465 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
469 rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
473 bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
474 for (i = 0; i < (len - 1); i++)
475 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
476 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
477 set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
479 off += (len * sizeof (unichar));
485 struct bin_res_id bid;
486 windres_put_16 (wrbfd, bid.sig, 0xffff);
487 windres_put_16 (wrbfd, bid.id, id->u.id);
488 set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
495 /* Write resource info */
497 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
501 struct bin_res_info l;
503 windres_put_32 (wrbfd, l.version, info->version);
504 windres_put_16 (wrbfd, l.memflags, info->memflags);
505 windres_put_16 (wrbfd, l.language, info->language);
506 windres_put_32 (wrbfd, l.version2, info->version);
507 windres_put_32 (wrbfd, l.characteristics, info->characteristics);
508 set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
510 return off + BIN_RES_INFO_SIZE;
513 /* read a resource identifier */
515 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
517 struct bin_res_id bid;
519 unichar *id_s = NULL;
522 read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
523 ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
524 if (ord == 0xFFFF) /* an ordinal id */
526 read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
528 id->u.id = windres_get_16 (wrbfd, bid.id, 2);
534 id_s = read_unistring (wrbfd, off, omax, &len);
536 id->u.n.length = len;
541 /* Read a null terminated UNICODE string */
543 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
551 rc_uint_type soff = off[0];
555 read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
556 c = windres_get_16 (wrbfd, d, 2);
559 l = ((soff - off[0]) / sizeof (unichar));
561 /* there are hardly any names longer than 256 characters, but anyway. */
562 p = s = (unichar *) xmalloc (sizeof (unichar) * l);
565 read_res_data (wrbfd, off, omax, d, sizeof (unichar));
566 c = windres_get_16 (wrbfd, d, 2);
575 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
581 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
582 if (reshdr.data_size != 0)
584 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
585 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
588 /* Subtract size of HeaderSize. DataSize has to be zero. */
589 off += 0x20 - BIN_RES_HDR_SIZE;
590 if ((off + BIN_RES_HDR_SIZE) >= omax)
592 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
593 /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
594 which is part of reshdr.header_size. We shouldn't take it
595 into account twice. */
596 if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
601 /* Check if file is a win32 binary resource file, if so
602 skip past the null resource. Returns 0 if successful, -1 on
606 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
609 read_res_data_hdr (wrbfd, off, omax, &reshdr);
610 if (reshdr.data_size != 0)
612 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
613 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
616 /* Subtract size of HeaderSize. DataSize has to be zero. */
617 off[0] += 0x20 - BIN_RES_HDR_SIZE;
624 fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
629 /* Add a resource to resource directory */
631 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
632 rc_uint_type language, int dupok)
639 a[2].u.id = language;
640 res_append_resource (&resources, r, 3, a, dupok);
643 /* Append a resource to resource directory.
644 This is just copied from define_resource
645 and modified to add an existing resource.
648 res_append_resource (rc_res_directory **res_dirs, rc_res_resource *resource,
649 int cids, const rc_res_id *ids, int dupok)
651 rc_res_entry *re = NULL;
655 for (i = 0; i < cids; i++)
659 if (*res_dirs == NULL)
661 *res_dirs = ((rc_res_directory *)
662 res_alloc (sizeof (rc_res_directory)));
664 (*res_dirs)->characteristics = 0;
665 /* Using a real timestamp only serves to create non-deterministic
666 results. Use zero instead. */
667 (*res_dirs)->time = 0;
668 (*res_dirs)->major = 0;
669 (*res_dirs)->minor = 0;
670 (*res_dirs)->entries = NULL;
673 for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next)
674 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
681 re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
702 fprintf (stderr, "%s: ", program_name);
703 res_ids_print (stderr, i, ids);
704 fprintf (stderr, ": expected to be a directory\n");
708 res_dirs = &re->u.dir;
714 fprintf (stderr, "%s: ", program_name);
715 res_ids_print (stderr, cids, ids);
716 fprintf (stderr, ": expected to be a leaf\n");
720 if (re->u.res != NULL)
725 fprintf (stderr, "%s: warning: ", program_name);
726 res_ids_print (stderr, cids, ids);
727 fprintf (stderr, ": duplicate value\n");
730 re->u.res = resource;