Remove support for the beos file format
[binutils-gdb.git] / binutils / resres.c
blob539390419cb6afca805a57e312626cd3fb618399
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2 Copyright (C) 1998-2024 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
21 02110-1301, USA. */
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. */
27 #include "sysdep.h"
28 #include "bfd.h"
29 #include "bucomm.h"
30 #include "libiberty.h"
31 #include "windres.h"
33 #include <assert.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 *,
40 rc_uint_type *);
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 *,
55 rc_uint_type);
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 */
77 rc_res_directory *
78 read_res_file (const char *fn)
80 rc_uint_type off, flen;
81 windres_bfd wrbfd;
82 bfd *abfd;
83 asection *sec;
84 filename = fn;
86 flen = (rc_uint_type) get_file_size (filename);
87 if (! flen)
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");
91 if (sec == NULL)
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));
96 off = 0;
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))
106 bfd_close (abfd);
108 return resources;
111 /* Write resource file */
112 void
113 write_res_file (const char *fn,const rc_res_directory *resdir)
115 asection *sec;
116 rc_uint_type language;
117 bfd *abfd;
118 windres_bfd wrbfd;
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};
126 filename = fn;
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));
132 if (sec == NULL)
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));
141 language = -1;
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));
150 language = -1;
151 sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
152 (const rc_res_id *) NULL,
153 (const rc_res_id *) NULL,
154 &language, 1);
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);
159 bfd_close (abfd);
160 return;
163 /* Read a resource entry, returns 0 when all resources are read */
164 static int
165 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
167 rc_res_id type;
168 rc_res_id name;
169 rc_res_res_info resinfo;
170 res_hdr reshdr;
171 void *buff;
173 rc_res_resource *r;
174 struct bin_res_info l;
176 off[0] = (off[0] + 3) & ~3;
178 /* Read header */
179 if ((off[0] + 8) > omax)
180 return 0;
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);
202 /* Read data */
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);
210 return 1;
213 /* write resource directory to binary resource file */
214 static rc_uint_type
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,
217 int level)
219 const rc_res_entry *re;
221 for (re = rd->entries; re != NULL; re = re->next)
223 switch (level)
225 case 1:
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. */
230 type = &re->id;
231 break;
233 case 2:
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. */
236 name = &re->id;
237 break;
239 case 3:
240 /* If we're at level 3, then this key represents a language.
241 Use it to update the current language. */
242 if (! re->id.named
243 && re->id.u.id != (unsigned long) *language
244 && (re->id.u.id & 0xffff) == re->id.u.id)
246 *language = re->id.u.id;
248 break;
250 default:
251 break;
254 if (re->subdir)
255 off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
256 level + 1);
257 else
259 if (level == 3)
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,
267 language);
269 else
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);
278 return off;
281 static rc_uint_type
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)
286 int rt;
288 switch (res->type)
290 default:
291 abort ();
293 case RES_TYPE_ACCELERATOR:
294 rt = RT_ACCELERATOR;
295 break;
297 case RES_TYPE_BITMAP:
298 rt = RT_BITMAP;
299 break;
301 case RES_TYPE_CURSOR:
302 rt = RT_CURSOR;
303 break;
305 case RES_TYPE_GROUP_CURSOR:
306 rt = RT_GROUP_CURSOR;
307 break;
309 case RES_TYPE_DIALOG:
310 rt = RT_DIALOG;
311 break;
313 case RES_TYPE_FONT:
314 rt = RT_FONT;
315 break;
317 case RES_TYPE_FONTDIR:
318 rt = RT_FONTDIR;
319 break;
321 case RES_TYPE_ICON:
322 rt = RT_ICON;
323 break;
325 case RES_TYPE_GROUP_ICON:
326 rt = RT_GROUP_ICON;
327 break;
329 case RES_TYPE_MENU:
330 rt = RT_MENU;
331 break;
333 case RES_TYPE_MESSAGETABLE:
334 rt = RT_MESSAGETABLE;
335 break;
337 case RES_TYPE_RCDATA:
338 rt = RT_RCDATA;
339 break;
341 case RES_TYPE_STRINGTABLE:
342 rt = RT_STRING;
343 break;
345 case RES_TYPE_USERDATA:
346 rt = 0;
347 break;
349 case RES_TYPE_VERSIONINFO:
350 rt = RT_VERSION;
351 break;
353 case RES_TYPE_TOOLBAR:
354 rt = RT_TOOLBAR;
355 break;
358 if (rt != 0
359 && type != NULL
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);
365 abort ();
368 return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
371 /* Write a resource in binary resource format */
372 static rc_uint_type
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)
377 rc_uint_type noff;
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 */
388 static unsigned long
389 get_id_size (const rc_res_id *id)
391 if (id->named)
392 return sizeof (unichar) * (id->u.n.length + 1);
393 else
394 return sizeof (unichar) * 2;
397 /* Write a resource header */
398 static rc_uint_type
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)
403 res_hdr reshdr;
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;
419 return off;
422 static rc_uint_type
423 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
425 if (wrbfd)
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;
435 static void
436 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
437 res_hdr *reshdr)
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 */
451 static void
452 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
453 rc_uint_type size)
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);
459 off[0] += size;
462 /* Write a resource id */
463 static rc_uint_type
464 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
466 if (id->named)
468 rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
469 if (wrbfd)
471 rc_uint_type i;
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));
480 else
482 if (wrbfd)
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);
489 off += BIN_RES_ID;
491 return off;
494 /* Write resource info */
495 static rc_uint_type
496 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
498 if (wrbfd)
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 */
513 static void
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;
517 unsigned short ord;
518 unichar *id_s = NULL;
519 rc_uint_type len;
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);
526 id->named = 0;
527 id->u.id = windres_get_16 (wrbfd, bid.id, 2);
529 else
530 /* named id */
532 off[0] -= 2;
533 id_s = read_unistring (wrbfd, off, omax, &len);
534 id->named = 1;
535 id->u.n.length = len;
536 id->u.n.name = id_s;
540 /* Read a null terminated UNICODE string */
541 static unichar *
542 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
543 rc_uint_type *len)
545 unichar *s;
546 bfd_byte d[2];
547 unichar c;
548 unichar *p;
549 rc_uint_type l;
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);
557 while (c != 0);
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);
566 *p++ = c;
568 while (c != 0);
569 *len = l - 1;
570 return s;
573 static int
574 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
576 rc_uint_type off;
577 res_hdr reshdr;
579 off = 0;
580 read_res_data_hdr (wrbfd, &off, omax, &reshdr);
581 if (reshdr.data_size != 0)
582 return 1;
583 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
584 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
585 return 1;
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)
590 return 1;
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)
596 return 0;
597 return 1;
600 /* Check if file is a win32 binary resource file, if so
601 skip past the null resource. Returns 0 if successful, -1 on
602 error.
604 static void
605 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
607 res_hdr reshdr;
608 read_res_data_hdr (wrbfd, off, omax, &reshdr);
609 if (reshdr.data_size != 0)
610 goto skip_err;
611 if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
612 || (reshdr.header_size != 0x20000000 && target_is_bigendian))
613 goto skip_err;
615 /* Subtract size of HeaderSize. DataSize has to be zero. */
616 off[0] += 0x20 - BIN_RES_HDR_SIZE;
617 if (off[0] >= omax)
618 goto skip_err;
620 return;
622 skip_err:
623 fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
624 filename);
625 xexit (1);
628 /* Add a resource to resource directory */
629 static void
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)
633 rc_res_id a[3];
635 a[0] = *type;
636 a[1] = *id;
637 a[2].named = 0;
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.
646 static void
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;
651 int i;
653 assert (cids > 0);
654 for (i = 0; i < cids; i++)
656 rc_res_entry **pp;
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)
674 break;
676 if (*pp != NULL)
677 re = *pp;
678 else
680 re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
681 re->next = NULL;
682 re->id = ids[i];
683 if ((i + 1) < cids)
685 re->subdir = 1;
686 re->u.dir = NULL;
688 else
690 re->subdir = 0;
691 re->u.res = NULL;
694 *pp = re;
697 if ((i + 1) < cids)
699 if (! re->subdir)
701 fprintf (stderr, "%s: ", program_name);
702 res_ids_print (stderr, i, ids);
703 fprintf (stderr, ": expected to be a directory\n");
704 xexit (1);
707 res_dirs = &re->u.dir;
711 if (re->subdir)
713 fprintf (stderr, "%s: ", program_name);
714 res_ids_print (stderr, cids, ids);
715 fprintf (stderr, ": expected to be a leaf\n");
716 xexit (1);
719 if (re->u.res != NULL)
721 if (dupok)
722 return;
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;