1 /* resres.c: read_res_file and write_res_file implementation for windres.
2 Copyright 1998, 1999, 2001, 2002, 2005, 2007, 2008, 2011
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_endianness (&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_with_flags (abfd
, ".data",
132 (SEC_HAS_CONTENTS
| SEC_ALLOC
133 | SEC_LOAD
| SEC_DATA
));
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
));
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
));
153 sec_length_wrote
= write_res_directory (&wrbfd
, 0x20UL
, resdir
,
154 (const rc_res_id
*) NULL
,
155 (const rc_res_id
*) NULL
,
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
);
165 /* Read a resource entry, returns 0 when all resources are read */
167 read_resource_entry (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
)
171 rc_res_res_info resinfo
;
176 struct bin_res_info l
;
178 off
[0] = (off
[0] + 3) & ~3;
181 if ((off
[0] + 8) > omax
)
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
);
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);
215 /* write resource directory to binary resource file */
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
,
221 const rc_res_entry
*re
;
223 for (re
= rd
->entries
; re
!= NULL
; re
= re
->next
)
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. */
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. */
242 /* If we're at level 3, then this key represents a language.
243 Use it to update the current language. */
245 && re
->id
.u
.id
!= (unsigned long) *language
246 && (re
->id
.u
.id
& 0xffff) == re
->id
.u
.id
)
248 *language
= re
->id
.u
.id
;
257 off
= write_res_directory (wrbfd
, off
, re
->u
.dir
, type
, name
, language
,
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
,
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
);
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
)
295 case RES_TYPE_ACCELERATOR
:
299 case RES_TYPE_BITMAP
:
303 case RES_TYPE_CURSOR
:
307 case RES_TYPE_GROUP_CURSOR
:
308 rt
= RT_GROUP_CURSOR
;
311 case RES_TYPE_DIALOG
:
319 case RES_TYPE_FONTDIR
:
327 case RES_TYPE_GROUP_ICON
:
335 case RES_TYPE_MESSAGETABLE
:
336 rt
= RT_MESSAGETABLE
;
339 case RES_TYPE_RCDATA
:
343 case RES_TYPE_STRINGTABLE
:
347 case RES_TYPE_USERDATA
:
351 case RES_TYPE_VERSIONINFO
:
355 case RES_TYPE_TOOLBAR
:
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
);
370 return write_res_bin (wrbfd
, off
, res
, type
, name
, &res
->res_info
);
373 /* Write a resource in binary resource format */
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
)
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 */
395 return sizeof (unichar
) * (id
->u
.n
.length
+ 1);
397 return sizeof (unichar
) * 2;
400 /* Write a resource header */
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
)
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;
426 write_res_data_hdr (windres_bfd
*wrbfd
, rc_uint_type off
, res_hdr
*hdr
)
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
;
439 read_res_data_hdr (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
,
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 */
455 read_res_data (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
, void *data
,
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
);
465 /* Write a resource id */
467 write_res_id (windres_bfd
*wrbfd
, rc_uint_type off
, const rc_res_id
*id
)
471 rc_uint_type len
= (((bfd_signed_vma
) id
->u
.n
.length
< 0 ? 0 : id
->u
.n
.length
) + 1);
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
));
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
);
497 /* Write resource info */
499 write_res_info (windres_bfd
*wrbfd
, rc_uint_type off
, const rc_res_res_info
*info
)
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 */
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
;
521 unichar
*id_s
= NULL
;
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);
530 id
->u
.id
= windres_get_16 (wrbfd
, bid
.id
, 2);
536 id_s
= read_unistring (wrbfd
, off
, omax
, &len
);
538 id
->u
.n
.length
= len
;
543 /* Read a null terminated UNICODE string */
545 read_unistring (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
,
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);
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);
577 probe_binary (windres_bfd
*wrbfd
, rc_uint_type omax
)
583 read_res_data_hdr (wrbfd
, &off
, omax
, &reshdr
);
584 if (reshdr
.data_size
!= 0)
586 if ((reshdr
.header_size
!= 0x20 && ! target_is_bigendian
)
587 || (reshdr
.header_size
!= 0x20000000 && target_is_bigendian
))
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
)
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
)
603 /* Check if file is a win32 binary resource file, if so
604 skip past the null resource. Returns 0 if successful, -1 on
608 skip_null_resource (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
)
611 read_res_data_hdr (wrbfd
, off
, omax
, &reshdr
);
612 if (reshdr
.data_size
!= 0)
614 if ((reshdr
.header_size
!= 0x20 && ! target_is_bigendian
)
615 || (reshdr
.header_size
!= 0x20000000 && target_is_bigendian
))
618 /* Subtract size of HeaderSize. DataSize has to be zero. */
619 off
[0] += 0x20 - BIN_RES_HDR_SIZE
;
626 fprintf (stderr
, "%s: %s: Not a valid WIN32 resource file\n", program_name
,
631 /* Add a resource to resource directory */
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
)
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.
650 res_append_resource (rc_res_directory
**res_dirs
, rc_res_resource
*resource
,
651 int cids
, const rc_res_id
*ids
, int dupok
)
653 rc_res_entry
*re
= NULL
;
657 for (i
= 0; i
< cids
; i
++)
661 if (*res_dirs
== NULL
)
663 static unsigned long timeval
;
665 /* Use the same timestamp for every resource created in a
668 timeval
= time (NULL
);
670 *res_dirs
= ((rc_res_directory
*)
671 res_alloc (sizeof (rc_res_directory
)));
672 (*res_dirs
)->characteristics
= 0;
673 (*res_dirs
)->time
= timeval
;
674 (*res_dirs
)->major
= 0;
675 (*res_dirs
)->minor
= 0;
676 (*res_dirs
)->entries
= NULL
;
679 for (pp
= &(*res_dirs
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
680 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
687 re
= (rc_res_entry
*) res_alloc (sizeof (rc_res_entry
));
708 fprintf (stderr
, "%s: ", program_name
);
709 res_ids_print (stderr
, i
, ids
);
710 fprintf (stderr
, ": expected to be a directory\n");
714 res_dirs
= &re
->u
.dir
;
720 fprintf (stderr
, "%s: ", program_name
);
721 res_ids_print (stderr
, cids
, ids
);
722 fprintf (stderr
, ": expected to be a leaf\n");
726 if (re
->u
.res
!= NULL
)
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
;