1 /* resres.c: read_res_file and write_res_file implementation for windres.
2 Copyright (C) 1998-2019 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 (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 */
389 get_id_size (const rc_res_id
*id
)
392 return sizeof (unichar
) * (id
->u
.n
.length
+ 1);
394 return sizeof (unichar
) * 2;
397 /* Write a resource header */
399 write_res_header (windres_bfd
*wrbfd
, rc_uint_type off
, rc_uint_type datasize
,
400 const rc_res_id
*type
, const rc_res_id
*name
,
401 const rc_res_res_info
*resinfo
)
404 reshdr
.data_size
= datasize
;
405 reshdr
.header_size
= 24 + get_id_size (type
) + get_id_size (name
);
407 reshdr
.header_size
= (reshdr
.header_size
+ 3) & ~3;
409 off
= (off
+ 3) & ~3;
411 off
= write_res_data_hdr (wrbfd
, off
, &reshdr
);
412 off
= write_res_id (wrbfd
, off
, type
);
413 off
= write_res_id (wrbfd
, off
, name
);
415 off
= (off
+ 3) & ~3;
417 off
= write_res_info (wrbfd
, off
, resinfo
);
418 off
= (off
+ 3) & ~3;
423 write_res_data_hdr (windres_bfd
*wrbfd
, rc_uint_type off
, res_hdr
*hdr
)
427 struct bin_res_hdr brh
;
428 windres_put_32 (wrbfd
, brh
.data_size
, hdr
->data_size
);
429 windres_put_32 (wrbfd
, brh
.header_size
, hdr
->header_size
);
430 set_windres_bfd_content (wrbfd
, &brh
, off
, BIN_RES_HDR_SIZE
);
432 return off
+ BIN_RES_HDR_SIZE
;
436 read_res_data_hdr (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
,
439 struct bin_res_hdr brh
;
441 if ((off
[0] + BIN_RES_HDR_SIZE
) > omax
)
442 fatal ("%s: unexpected end of file %ld/%ld", filename
,(long) off
[0], (long) omax
);
444 get_windres_bfd_content (wrbfd
, &brh
, off
[0], BIN_RES_HDR_SIZE
);
445 reshdr
->data_size
= windres_get_32 (wrbfd
, brh
.data_size
, 4);
446 reshdr
->header_size
= windres_get_32 (wrbfd
, brh
.header_size
, 4);
447 off
[0] += BIN_RES_HDR_SIZE
;
450 /* Read data from file, abort on failure */
452 read_res_data (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
, void *data
,
455 if ((off
[0] + size
) > omax
)
456 fatal ("%s: unexpected end of file %ld/%ld %ld", filename
,(long) off
[0],
457 (long) omax
, (long) size
);
458 get_windres_bfd_content (wrbfd
, data
, off
[0], size
);
462 /* Write a resource id */
464 write_res_id (windres_bfd
*wrbfd
, rc_uint_type off
, const rc_res_id
*id
)
468 rc_uint_type len
= (((bfd_signed_vma
) id
->u
.n
.length
< 0 ? 0 : id
->u
.n
.length
) + 1);
472 bfd_byte
*d
= (bfd_byte
*) xmalloc (len
* sizeof (unichar
));
473 for (i
= 0; i
< (len
- 1); i
++)
474 windres_put_16 (wrbfd
, d
+ (i
* sizeof (unichar
)), id
->u
.n
.name
[i
]);
475 windres_put_16 (wrbfd
, d
+ (i
* sizeof (unichar
)), 0);
476 set_windres_bfd_content (wrbfd
, d
, off
, (len
* sizeof (unichar
)));
478 off
+= (len
* sizeof (unichar
));
484 struct bin_res_id bid
;
485 windres_put_16 (wrbfd
, bid
.sig
, 0xffff);
486 windres_put_16 (wrbfd
, bid
.id
, id
->u
.id
);
487 set_windres_bfd_content (wrbfd
, &bid
, off
, BIN_RES_ID
);
494 /* Write resource info */
496 write_res_info (windres_bfd
*wrbfd
, rc_uint_type off
, const rc_res_res_info
*info
)
500 struct bin_res_info l
;
502 windres_put_32 (wrbfd
, l
.version
, info
->version
);
503 windres_put_16 (wrbfd
, l
.memflags
, info
->memflags
);
504 windres_put_16 (wrbfd
, l
.language
, info
->language
);
505 windres_put_32 (wrbfd
, l
.version2
, info
->version
);
506 windres_put_32 (wrbfd
, l
.characteristics
, info
->characteristics
);
507 set_windres_bfd_content (wrbfd
, &l
, off
, BIN_RES_INFO_SIZE
);
509 return off
+ BIN_RES_INFO_SIZE
;
512 /* read a resource identifier */
514 read_res_id (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
, rc_res_id
*id
)
516 struct bin_res_id bid
;
518 unichar
*id_s
= NULL
;
521 read_res_data (wrbfd
, off
, omax
, &bid
, BIN_RES_ID
- 2);
522 ord
= (unsigned short) windres_get_16 (wrbfd
, bid
.sig
, 2);
523 if (ord
== 0xFFFF) /* an ordinal id */
525 read_res_data (wrbfd
, off
, omax
, bid
.id
, BIN_RES_ID
- 2);
527 id
->u
.id
= windres_get_16 (wrbfd
, bid
.id
, 2);
533 id_s
= read_unistring (wrbfd
, off
, omax
, &len
);
535 id
->u
.n
.length
= len
;
540 /* Read a null terminated UNICODE string */
542 read_unistring (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
,
550 rc_uint_type soff
= off
[0];
554 read_res_data (wrbfd
, &soff
, omax
, d
, sizeof (unichar
));
555 c
= windres_get_16 (wrbfd
, d
, 2);
558 l
= ((soff
- off
[0]) / sizeof (unichar
));
560 /* there are hardly any names longer than 256 characters, but anyway. */
561 p
= s
= (unichar
*) xmalloc (sizeof (unichar
) * l
);
564 read_res_data (wrbfd
, off
, omax
, d
, sizeof (unichar
));
565 c
= windres_get_16 (wrbfd
, d
, 2);
574 probe_binary (windres_bfd
*wrbfd
, rc_uint_type omax
)
580 read_res_data_hdr (wrbfd
, &off
, omax
, &reshdr
);
581 if (reshdr
.data_size
!= 0)
583 if ((reshdr
.header_size
!= 0x20 && ! target_is_bigendian
)
584 || (reshdr
.header_size
!= 0x20000000 && target_is_bigendian
))
587 /* Subtract size of HeaderSize. DataSize has to be zero. */
588 off
+= 0x20 - BIN_RES_HDR_SIZE
;
589 if ((off
+ BIN_RES_HDR_SIZE
) >= omax
)
591 read_res_data_hdr (wrbfd
, &off
, omax
, &reshdr
);
592 /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
593 which is part of reshdr.header_size. We shouldn't take it
594 into account twice. */
595 if ((off
- BIN_RES_HDR_SIZE
+ reshdr
.data_size
+ reshdr
.header_size
) > omax
)
600 /* Check if file is a win32 binary resource file, if so
601 skip past the null resource. Returns 0 if successful, -1 on
605 skip_null_resource (windres_bfd
*wrbfd
, rc_uint_type
*off
, rc_uint_type omax
)
608 read_res_data_hdr (wrbfd
, off
, omax
, &reshdr
);
609 if (reshdr
.data_size
!= 0)
611 if ((reshdr
.header_size
!= 0x20 && ! target_is_bigendian
)
612 || (reshdr
.header_size
!= 0x20000000 && target_is_bigendian
))
615 /* Subtract size of HeaderSize. DataSize has to be zero. */
616 off
[0] += 0x20 - BIN_RES_HDR_SIZE
;
623 fprintf (stderr
, "%s: %s: Not a valid WIN32 resource file\n", program_name
,
628 /* Add a resource to resource directory */
630 res_add_resource (rc_res_resource
*r
, const rc_res_id
*type
, const rc_res_id
*id
,
631 rc_uint_type language
, int dupok
)
638 a
[2].u
.id
= language
;
639 res_append_resource (&resources
, r
, 3, a
, dupok
);
642 /* Append a resource to resource directory.
643 This is just copied from define_resource
644 and modified to add an existing resource.
647 res_append_resource (rc_res_directory
**res_dirs
, rc_res_resource
*resource
,
648 int cids
, const rc_res_id
*ids
, int dupok
)
650 rc_res_entry
*re
= NULL
;
654 for (i
= 0; i
< cids
; i
++)
658 if (*res_dirs
== NULL
)
660 *res_dirs
= ((rc_res_directory
*)
661 res_alloc (sizeof (rc_res_directory
)));
663 (*res_dirs
)->characteristics
= 0;
664 /* Using a real timestamp only serves to create non-deterministic
665 results. Use zero instead. */
666 (*res_dirs
)->time
= 0;
667 (*res_dirs
)->major
= 0;
668 (*res_dirs
)->minor
= 0;
669 (*res_dirs
)->entries
= NULL
;
672 for (pp
= &(*res_dirs
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
673 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
680 re
= (rc_res_entry
*) res_alloc (sizeof (rc_res_entry
));
701 fprintf (stderr
, "%s: ", program_name
);
702 res_ids_print (stderr
, i
, ids
);
703 fprintf (stderr
, ": expected to be a directory\n");
707 res_dirs
= &re
->u
.dir
;
713 fprintf (stderr
, "%s: ", program_name
);
714 res_ids_print (stderr
, cids
, ids
);
715 fprintf (stderr
, ": expected to be a leaf\n");
719 if (re
->u
.res
!= NULL
)
724 fprintf (stderr
, "%s: warning: ", program_name
);
725 res_ids_print (stderr
, cids
, ids
);
726 fprintf (stderr
, ": duplicate value\n");
729 re
->u
.res
= resource
;