160484Sobrien/* resres.c: read_res_file and write_res_file implementation for windres.
2218822Sdim   Copyright 1998, 1999, 2001, 2002, 2007
3218822Sdim   Free Software Foundation, Inc.
460484Sobrien   Written by Anders Norlander <anorland@hem2.passagen.se>.
5218822Sdim   Rewritten by Kai Tietz, Onevision.
660484Sobrien
760484Sobrien   This file is part of GNU Binutils.
860484Sobrien
960484Sobrien   This program is free software; you can redistribute it and/or modify
1060484Sobrien   it under the terms of the GNU General Public License as published by
1160484Sobrien   the Free Software Foundation; either version 2 of the License, or
1260484Sobrien   (at your option) any later version.
1360484Sobrien
1460484Sobrien   This program is distributed in the hope that it will be useful,
1560484Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1660484Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1760484Sobrien   GNU General Public License for more details.
1860484Sobrien
1960484Sobrien   You should have received a copy of the GNU General Public License
2060484Sobrien   along with this program; if not, write to the Free Software
21218822Sdim   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
22218822Sdim   02110-1301, USA.  */
2360484Sobrien
2460484Sobrien/* FIXME: This file does not work correctly in a cross configuration.
2560484Sobrien   It assumes that it can use fread and fwrite to read and write
2660484Sobrien   integers.  It does no swapping.  */
2760484Sobrien
28218822Sdim#include "sysdep.h"
2960484Sobrien#include "bfd.h"
3060484Sobrien#include "bucomm.h"
3160484Sobrien#include "libiberty.h"
3260484Sobrien#include "windres.h"
3360484Sobrien
3460484Sobrien#include <assert.h>
3560484Sobrien#include <time.h>
3660484Sobrien
37218822Sdimstatic rc_uint_type write_res_directory (windres_bfd *, rc_uint_type,
38218822Sdim				    	 const rc_res_directory *, const rc_res_id *,
39218822Sdim				    	 const rc_res_id *, rc_uint_type *, int);
40218822Sdimstatic rc_uint_type write_res_resource (windres_bfd *, rc_uint_type,const rc_res_id *,
41218822Sdim				   	const rc_res_id *, const rc_res_resource *,
42218822Sdim				   	rc_uint_type *);
43218822Sdimstatic rc_uint_type write_res_bin (windres_bfd *, rc_uint_type, const rc_res_resource *,
44218822Sdim				   const rc_res_id *, const rc_res_id *,
45218822Sdim				   const rc_res_res_info *);
4660484Sobrien
47218822Sdimstatic rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
48218822Sdimstatic rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
49218822Sdimstatic rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
5060484Sobrien
51218822Sdimstatic rc_uint_type write_res_header (windres_bfd *, rc_uint_type, rc_uint_type,
52218822Sdim				      const rc_res_id *, const rc_res_id *,
53218822Sdim				      const rc_res_res_info *);
5460484Sobrien
55218822Sdimstatic int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
56218822Sdimstatic void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
57218822Sdim			   rc_uint_type);
58218822Sdimstatic void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
59218822Sdimstatic void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
60218822Sdimstatic unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
61218822Sdimstatic void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
62218822Sdimstatic int probe_binary (windres_bfd *wrbfd, rc_uint_type);
6360484Sobrien
64218822Sdimstatic unsigned long get_id_size (const rc_res_id *);
6560484Sobrien
66218822Sdimstatic void res_add_resource (rc_res_resource *, const rc_res_id *,
67218822Sdim			      const rc_res_id *, rc_uint_type, int);
6860484Sobrien
69218822Sdimstatic void res_append_resource (rc_res_directory **, rc_res_resource *,
70218822Sdim				 int, const rc_res_id *, int);
7160484Sobrien
72218822Sdimstatic rc_res_directory *resources = NULL;
7360484Sobrien
7460484Sobrienstatic const char *filename;
7560484Sobrien
7660484Sobrienextern char *program_name;
7760484Sobrien
7860484Sobrien/* Read resource file */
79218822Sdimrc_res_directory *
80218822Sdimread_res_file (const char *fn)
8160484Sobrien{
82218822Sdim  rc_uint_type off, flen;
83218822Sdim  windres_bfd wrbfd;
84218822Sdim  bfd *abfd;
85218822Sdim  asection *sec;
8660484Sobrien  filename = fn;
8760484Sobrien
88218822Sdim  flen = (rc_uint_type) get_file_size (filename);
89218822Sdim  if (! flen)
90218822Sdim    fatal ("can't open '%s' for input.", filename);
91218822Sdim  abfd = windres_open_as_binary (filename, 1);
92218822Sdim  sec = bfd_get_section_by_name (abfd, ".data");
93218822Sdim  if (sec == NULL)
94218822Sdim    bfd_fatal ("bfd_get_section_by_name");
95218822Sdim  set_windres_bfd (&wrbfd, abfd, sec,
96218822Sdim		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
97218822Sdim					: WR_KIND_BFD_BIN_L));
98218822Sdim  off = 0;
9960484Sobrien
100218822Sdim  if (! probe_binary (&wrbfd, flen))
101218822Sdim    set_windres_bfd_endianess (&wrbfd, ! target_is_bigendian);
102218822Sdim
103218822Sdim  skip_null_resource (&wrbfd, &off, flen);
104218822Sdim
105218822Sdim  while (read_resource_entry (&wrbfd, &off, flen))
10660484Sobrien    ;
10760484Sobrien
108218822Sdim  bfd_close (abfd);
10960484Sobrien
11060484Sobrien  return resources;
11160484Sobrien}
11260484Sobrien
11360484Sobrien/* Write resource file */
11460484Sobrienvoid
115218822Sdimwrite_res_file (const char *fn,const rc_res_directory *resdir)
11660484Sobrien{
117218822Sdim  asection *sec;
118218822Sdim  rc_uint_type language;
119218822Sdim  bfd *abfd;
120218822Sdim  windres_bfd wrbfd;
121218822Sdim  unsigned long sec_length = 0,sec_length_wrote;
122218822Sdim  static const bfd_byte sign[] =
12360484Sobrien  {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
12460484Sobrien   0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
12560484Sobrien   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12660484Sobrien   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
12760484Sobrien
12860484Sobrien  filename = fn;
12960484Sobrien
130218822Sdim  abfd = windres_open_as_binary (filename, 0);
131218822Sdim  sec = bfd_make_section (abfd, ".data");
132218822Sdim  if (sec == NULL)
133218822Sdim    bfd_fatal ("bfd_make_section");
134218822Sdim  if (! bfd_set_section_flags (abfd, sec,
135218822Sdim			       (SEC_HAS_CONTENTS | SEC_ALLOC
136218822Sdim			        | SEC_LOAD | SEC_DATA)))
137218822Sdim    bfd_fatal ("bfd_set_section_flags");
138218822Sdim  /* Requiring this is probably a bug in BFD.  */
139218822Sdim  sec->output_section = sec;
14060484Sobrien
141218822Sdim  set_windres_bfd (&wrbfd, abfd, sec,
142218822Sdim		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
143218822Sdim					: WR_KIND_BFD_BIN_L));
14460484Sobrien
14560484Sobrien  language = -1;
146218822Sdim  sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
147218822Sdim				    (const rc_res_id *) NULL,
148218822Sdim				    (const rc_res_id *) NULL, &language, 1);
149218822Sdim  if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3))
150218822Sdim    bfd_fatal ("bfd_set_section_size");
151218822Sdim  if ((sec_length & 3) != 0)
152218822Sdim    set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
153218822Sdim  set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
154218822Sdim  language = -1;
155218822Sdim  sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
156218822Sdim					  (const rc_res_id *) NULL,
157218822Sdim					  (const rc_res_id *) NULL,
158218822Sdim					  &language, 1);
159218822Sdim  if (sec_length != sec_length_wrote)
160218822Sdim    fatal ("res write failed with different sizes (%lu/%lu).", (long) sec_length,
161218822Sdim    	   (long) sec_length_wrote);
16260484Sobrien
163218822Sdim  bfd_close (abfd);
164218822Sdim  return;
16560484Sobrien}
16660484Sobrien
16760484Sobrien/* Read a resource entry, returns 0 when all resources are read */
16860484Sobrienstatic int
169218822Sdimread_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
17060484Sobrien{
171218822Sdim  rc_res_id type;
172218822Sdim  rc_res_id name;
173218822Sdim  rc_res_res_info resinfo;
174218822Sdim  res_hdr reshdr;
17560484Sobrien  void *buff;
17660484Sobrien
177218822Sdim  rc_res_resource *r;
178218822Sdim  struct bin_res_info l;
17960484Sobrien
180218822Sdim  off[0] = (off[0] + 3) & ~3;
18160484Sobrien
18260484Sobrien  /* Read header */
183218822Sdim  if ((off[0] + 8) > omax)
18460484Sobrien    return 0;
185218822Sdim  read_res_data_hdr (wrbfd, off, omax, &reshdr);
18660484Sobrien
18760484Sobrien  /* read resource type */
188218822Sdim  read_res_id (wrbfd, off, omax, &type);
18960484Sobrien  /* read resource id */
190218822Sdim  read_res_id (wrbfd, off, omax, &name);
19160484Sobrien
192218822Sdim  off[0] = (off[0] + 3) & ~3;
19360484Sobrien
19460484Sobrien  /* Read additional resource header */
195218822Sdim  read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
196218822Sdim  resinfo.version = windres_get_32 (wrbfd, l.version, 4);
197218822Sdim  resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
198218822Sdim  resinfo.language = windres_get_16 (wrbfd, l.language, 2);
199218822Sdim  /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
200218822Sdim  resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
20160484Sobrien
202218822Sdim  off[0] = (off[0] + 3) & ~3;
20360484Sobrien
20460484Sobrien  /* Allocate buffer for data */
20560484Sobrien  buff = res_alloc (reshdr.data_size);
20660484Sobrien  /* Read data */
207218822Sdim  read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
20860484Sobrien  /* Convert binary data to resource */
209218822Sdim  r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
21060484Sobrien  r->res_info = resinfo;
21160484Sobrien  /* Add resource to resource directory */
21260484Sobrien  res_add_resource (r, &type, &name, resinfo.language, 0);
21360484Sobrien
21460484Sobrien  return 1;
21560484Sobrien}
21660484Sobrien
21760484Sobrien/* write resource directory to binary resource file */
218218822Sdimstatic rc_uint_type
219218822Sdimwrite_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
220218822Sdim		     const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
221218822Sdim		     int level)
22260484Sobrien{
223218822Sdim  const rc_res_entry *re;
22460484Sobrien
22560484Sobrien  for (re = rd->entries; re != NULL; re = re->next)
22660484Sobrien    {
22760484Sobrien      switch (level)
22860484Sobrien	{
22960484Sobrien	case 1:
23060484Sobrien	  /* If we're at level 1, the key of this resource is the
23160484Sobrien	     type.  This normally duplicates the information we have
23260484Sobrien	     stored with the resource itself, but we need to remember
23360484Sobrien	     the type if this is a user define resource type.  */
23460484Sobrien	  type = &re->id;
23560484Sobrien	  break;
23660484Sobrien
23760484Sobrien	case 2:
23860484Sobrien	  /* If we're at level 2, the key of this resource is the name
239104834Sobrien	     we are going to use in the rc printout.  */
24060484Sobrien	  name = &re->id;
24160484Sobrien	  break;
24260484Sobrien
24360484Sobrien	case 3:
24460484Sobrien	  /* If we're at level 3, then this key represents a language.
24560484Sobrien	     Use it to update the current language.  */
246218822Sdim	  if (! re->id.named
24760484Sobrien	      && re->id.u.id != (unsigned long) *language
24860484Sobrien	      && (re->id.u.id & 0xffff) == re->id.u.id)
24960484Sobrien	    {
25060484Sobrien	      *language = re->id.u.id;
25160484Sobrien	    }
25260484Sobrien	  break;
25360484Sobrien
25460484Sobrien	default:
25560484Sobrien	  break;
25660484Sobrien	}
25760484Sobrien
25860484Sobrien      if (re->subdir)
259218822Sdim	off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
260218822Sdim				   level + 1);
26160484Sobrien      else
26260484Sobrien	{
26360484Sobrien	  if (level == 3)
26460484Sobrien	    {
26560484Sobrien	      /* This is the normal case: the three levels are
26660484Sobrien	         TYPE/NAME/LANGUAGE.  NAME will have been set at level
26760484Sobrien	         2, and represents the name to use.  We probably just
26860484Sobrien	         set LANGUAGE, and it will probably match what the
26960484Sobrien	         resource itself records if anything.  */
270218822Sdim	      off = write_res_resource (wrbfd, off, type, name, re->u.res,
271218822Sdim	      				language);
27260484Sobrien	    }
27360484Sobrien	  else
27460484Sobrien	    {
27560484Sobrien	      fprintf (stderr, "// Resource at unexpected level %d\n", level);
276218822Sdim	      off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
277218822Sdim	      				re->u.res, language);
27860484Sobrien	    }
27960484Sobrien	}
28060484Sobrien    }
28160484Sobrien
282218822Sdim  return off;
28360484Sobrien}
28460484Sobrien
285218822Sdimstatic rc_uint_type
286218822Sdimwrite_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
287218822Sdim		    const rc_res_id *name, const rc_res_resource *res,
288218822Sdim		    rc_uint_type *language ATTRIBUTE_UNUSED)
28960484Sobrien{
29060484Sobrien  int rt;
29160484Sobrien
29260484Sobrien  switch (res->type)
29360484Sobrien    {
29460484Sobrien    default:
29560484Sobrien      abort ();
29660484Sobrien
29760484Sobrien    case RES_TYPE_ACCELERATOR:
29860484Sobrien      rt = RT_ACCELERATOR;
29960484Sobrien      break;
30060484Sobrien
30160484Sobrien    case RES_TYPE_BITMAP:
30260484Sobrien      rt = RT_BITMAP;
30360484Sobrien      break;
30460484Sobrien
30560484Sobrien    case RES_TYPE_CURSOR:
30660484Sobrien      rt = RT_CURSOR;
30760484Sobrien      break;
30860484Sobrien
30960484Sobrien    case RES_TYPE_GROUP_CURSOR:
31060484Sobrien      rt = RT_GROUP_CURSOR;
31160484Sobrien      break;
31260484Sobrien
31360484Sobrien    case RES_TYPE_DIALOG:
31460484Sobrien      rt = RT_DIALOG;
31560484Sobrien      break;
31660484Sobrien
31760484Sobrien    case RES_TYPE_FONT:
31860484Sobrien      rt = RT_FONT;
31960484Sobrien      break;
32060484Sobrien
32160484Sobrien    case RES_TYPE_FONTDIR:
32260484Sobrien      rt = RT_FONTDIR;
32360484Sobrien      break;
32460484Sobrien
32560484Sobrien    case RES_TYPE_ICON:
32660484Sobrien      rt = RT_ICON;
32760484Sobrien      break;
32860484Sobrien
32960484Sobrien    case RES_TYPE_GROUP_ICON:
33060484Sobrien      rt = RT_GROUP_ICON;
33160484Sobrien      break;
33260484Sobrien
33360484Sobrien    case RES_TYPE_MENU:
33460484Sobrien      rt = RT_MENU;
33560484Sobrien      break;
33660484Sobrien
33760484Sobrien    case RES_TYPE_MESSAGETABLE:
33860484Sobrien      rt = RT_MESSAGETABLE;
33960484Sobrien      break;
34060484Sobrien
34160484Sobrien    case RES_TYPE_RCDATA:
34260484Sobrien      rt = RT_RCDATA;
34360484Sobrien      break;
34460484Sobrien
34560484Sobrien    case RES_TYPE_STRINGTABLE:
34660484Sobrien      rt = RT_STRING;
34760484Sobrien      break;
34860484Sobrien
34960484Sobrien    case RES_TYPE_USERDATA:
35060484Sobrien      rt = 0;
35160484Sobrien      break;
35260484Sobrien
35360484Sobrien    case RES_TYPE_VERSIONINFO:
35460484Sobrien      rt = RT_VERSION;
35560484Sobrien      break;
356218822Sdim
357218822Sdim    case RES_TYPE_TOOLBAR:
358218822Sdim      rt = RT_TOOLBAR;
359218822Sdim      break;
36060484Sobrien    }
36160484Sobrien
36260484Sobrien  if (rt != 0
36360484Sobrien      && type != NULL
36460484Sobrien      && (type->named || type->u.id != (unsigned long) rt))
36560484Sobrien    {
36660484Sobrien      fprintf (stderr, "// Unexpected resource type mismatch: ");
36760484Sobrien      res_id_print (stderr, *type, 1);
36860484Sobrien      fprintf (stderr, " != %d", rt);
36960484Sobrien      abort ();
37060484Sobrien    }
37160484Sobrien
372218822Sdim  return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
37360484Sobrien}
37460484Sobrien
37560484Sobrien/* Write a resource in binary resource format */
376218822Sdimstatic rc_uint_type
377218822Sdimwrite_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
378218822Sdim	       const rc_res_id *type, const rc_res_id *name,
379218822Sdim	       const rc_res_res_info *resinfo)
38060484Sobrien{
381218822Sdim  rc_uint_type noff;
382218822Sdim  rc_uint_type datasize = 0;
38360484Sobrien
384218822Sdim  noff = res_to_bin ((windres_bfd *) NULL, off, res);
385218822Sdim  datasize = noff - off;
38660484Sobrien
387218822Sdim  off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
388218822Sdim  return res_to_bin (wrbfd, off, res);
38960484Sobrien}
39060484Sobrien
39160484Sobrien/* Get number of bytes needed to store an id in binary format */
39260484Sobrienstatic unsigned long
39360484Sobrienget_id_size (id)
394218822Sdim     const rc_res_id *id;
39560484Sobrien{
39660484Sobrien  if (id->named)
39760484Sobrien    return sizeof (unichar) * (id->u.n.length + 1);
39860484Sobrien  else
39960484Sobrien    return sizeof (unichar) * 2;
40060484Sobrien}
40160484Sobrien
40260484Sobrien/* Write a resource header */
403218822Sdimstatic rc_uint_type
404218822Sdimwrite_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
405218822Sdim		  const rc_res_id *type, const rc_res_id *name,
406218822Sdim		  const rc_res_res_info *resinfo)
40760484Sobrien{
408218822Sdim  res_hdr reshdr;
40960484Sobrien  reshdr.data_size = datasize;
41060484Sobrien  reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
41160484Sobrien
41289857Sobrien  reshdr.header_size = (reshdr.header_size + 3) & ~3;
41389857Sobrien
414218822Sdim  off = (off + 3) & ~3;
41560484Sobrien
416218822Sdim  off = write_res_data_hdr (wrbfd, off, &reshdr);
417218822Sdim  off = write_res_id (wrbfd, off, type);
418218822Sdim  off = write_res_id (wrbfd, off, name);
41960484Sobrien
420218822Sdim  off = (off + 3) & ~3;
421218822Sdim
422218822Sdim  off = write_res_info (wrbfd, off, resinfo);
423218822Sdim  off = (off + 3) & ~3;
424218822Sdim  return off;
42560484Sobrien}
42660484Sobrien
427218822Sdimstatic rc_uint_type
428218822Sdimwrite_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
429218822Sdim{
430218822Sdim  if (wrbfd)
431218822Sdim    {
432218822Sdim      struct bin_res_hdr brh;
433218822Sdim      windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
434218822Sdim      windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
435218822Sdim      set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
436218822Sdim    }
437218822Sdim  return off + BIN_RES_HDR_SIZE;
438218822Sdim}
43960484Sobrien
44060484Sobrienstatic void
441218822Sdimread_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
442218822Sdim		   res_hdr *reshdr)
44360484Sobrien{
444218822Sdim  struct bin_res_hdr brh;
445218822Sdim
446218822Sdim  if ((off[0] + BIN_RES_HDR_SIZE) > omax)
447218822Sdim    fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
448218822Sdim
449218822Sdim  get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
450218822Sdim  reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
451218822Sdim  reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
452218822Sdim  off[0] += BIN_RES_HDR_SIZE;
45360484Sobrien}
45460484Sobrien
45560484Sobrien/* Read data from file, abort on failure */
45660484Sobrienstatic void
457218822Sdimread_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
458218822Sdim	       rc_uint_type size)
45960484Sobrien{
460218822Sdim  if ((off[0] + size) > omax)
461218822Sdim    fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
462218822Sdim    	   (long) omax, (long) size);
463218822Sdim  get_windres_bfd_content (wrbfd, data, off[0], size);
464218822Sdim  off[0] += size;
46560484Sobrien}
46660484Sobrien
46760484Sobrien/* Write a resource id */
468218822Sdimstatic rc_uint_type
469218822Sdimwrite_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
47060484Sobrien{
47160484Sobrien  if (id->named)
47260484Sobrien    {
473218822Sdim      rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
474218822Sdim      if (wrbfd)
475218822Sdim	{
476218822Sdim	  rc_uint_type i;
477218822Sdim	  bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
478218822Sdim	  for (i = 0; i < (len - 1); i++)
479218822Sdim	    windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
480218822Sdim	  windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
481218822Sdim	  set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
482218822Sdim	}
483218822Sdim      off += (len * sizeof (unichar));
48460484Sobrien    }
48560484Sobrien  else
48660484Sobrien    {
487218822Sdim      if (wrbfd)
488218822Sdim	{
489218822Sdim	  struct bin_res_id bid;
490218822Sdim	  windres_put_16 (wrbfd, bid.sig, 0xffff);
491218822Sdim	  windres_put_16 (wrbfd, bid.id, id->u.id);
492218822Sdim	  set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
493218822Sdim	}
494218822Sdim      off += BIN_RES_ID;
49560484Sobrien    }
496218822Sdim  return off;
49760484Sobrien}
49860484Sobrien
49960484Sobrien/* Write resource info */
500218822Sdimstatic rc_uint_type
501218822Sdimwrite_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
50260484Sobrien{
503218822Sdim  if (wrbfd)
504218822Sdim    {
505218822Sdim      struct bin_res_info l;
506218822Sdim
507218822Sdim      windres_put_32 (wrbfd, l.version, info->version);
508218822Sdim      windres_put_16 (wrbfd, l.memflags, info->memflags);
509218822Sdim      windres_put_16 (wrbfd, l.language, info->language);
510218822Sdim      windres_put_32 (wrbfd, l.version2, info->version);
511218822Sdim      windres_put_32 (wrbfd, l.characteristics, info->characteristics);
512218822Sdim      set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
513218822Sdim    }
514218822Sdim  return off + BIN_RES_INFO_SIZE;
51560484Sobrien}
51660484Sobrien
51760484Sobrien/* read a resource identifier */
518218822Sdimstatic void
519218822Sdimread_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
52060484Sobrien{
521218822Sdim  struct bin_res_id bid;
52260484Sobrien  unsigned short ord;
52360484Sobrien  unichar *id_s = NULL;
524218822Sdim  rc_uint_type len;
52560484Sobrien
526218822Sdim  read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
527218822Sdim  ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
52860484Sobrien  if (ord == 0xFFFF)		/* an ordinal id */
52960484Sobrien    {
530218822Sdim      read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
53160484Sobrien      id->named = 0;
532218822Sdim      id->u.id = windres_get_16 (wrbfd, bid.id, 2);
53360484Sobrien    }
53460484Sobrien  else
53560484Sobrien    /* named id */
53660484Sobrien    {
537218822Sdim      off[0] -= 2;
538218822Sdim      id_s = read_unistring (wrbfd, off, omax, &len);
53960484Sobrien      id->named = 1;
54060484Sobrien      id->u.n.length = len;
54160484Sobrien      id->u.n.name = id_s;
54260484Sobrien    }
54360484Sobrien}
54460484Sobrien
54560484Sobrien/* Read a null terminated UNICODE string */
54660484Sobrienstatic unichar *
547218822Sdimread_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
548218822Sdim		rc_uint_type *len)
54960484Sobrien{
55060484Sobrien  unichar *s;
551218822Sdim  bfd_byte d[2];
55260484Sobrien  unichar c;
55360484Sobrien  unichar *p;
554218822Sdim  rc_uint_type l;
555218822Sdim  rc_uint_type soff = off[0];
55660484Sobrien
557218822Sdim  do
558218822Sdim    {
559218822Sdim      read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
560218822Sdim      c = windres_get_16 (wrbfd, d, 2);
561218822Sdim    }
562218822Sdim  while (c != 0);
563218822Sdim  l = ((soff - off[0]) / sizeof (unichar));
56460484Sobrien
565218822Sdim  /* there are hardly any names longer than 256 characters, but anyway. */
566218822Sdim  p = s = (unichar *) xmalloc (sizeof (unichar) * l);
56760484Sobrien  do
56860484Sobrien    {
569218822Sdim      read_res_data (wrbfd, off, omax, d, sizeof (unichar));
570218822Sdim      c = windres_get_16 (wrbfd, d, 2);
57160484Sobrien      *p++ = c;
57260484Sobrien    }
57360484Sobrien  while (c != 0);
574218822Sdim  *len = l - 1;
57560484Sobrien  return s;
57660484Sobrien}
57760484Sobrien
578218822Sdimstatic int
579218822Sdimprobe_binary (windres_bfd *wrbfd, rc_uint_type omax)
58060484Sobrien{
581218822Sdim  rc_uint_type off;
582218822Sdim  res_hdr reshdr;
583218822Sdim
584218822Sdim  off = 0;
585218822Sdim  read_res_data_hdr (wrbfd, &off, omax, &reshdr);
586218822Sdim  if (reshdr.data_size != 0)
587218822Sdim    return 1;
588218822Sdim  if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
589218822Sdim      || (reshdr.header_size != 0x20000000 && target_is_bigendian))
590218822Sdim    return 1;
591218822Sdim
592218822Sdim  /* Subtract size of HeaderSize. DataSize has to be zero. */
593218822Sdim  off += 0x20 - BIN_RES_HDR_SIZE;
594218822Sdim  if ((off + BIN_RES_HDR_SIZE) >= omax)
595218822Sdim    return 1;
596218822Sdim  read_res_data_hdr (wrbfd, &off, omax, &reshdr);
597218822Sdim  /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
598218822Sdim     which is part of reshdr.header_size. We shouldn't take it
599218822Sdim     into account twice.  */
600218822Sdim  if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
601218822Sdim    return 0;
602218822Sdim  return 1;
60360484Sobrien}
60460484Sobrien
60560484Sobrien/* Check if file is a win32 binary resource file, if so
60660484Sobrien   skip past the null resource. Returns 0 if successful, -1 on
60760484Sobrien   error.
60860484Sobrien */
60960484Sobrienstatic void
610218822Sdimskip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
61160484Sobrien{
612218822Sdim  res_hdr reshdr;
613218822Sdim  read_res_data_hdr (wrbfd, off, omax, &reshdr);
614218822Sdim  if (reshdr.data_size != 0)
61560484Sobrien    goto skip_err;
616218822Sdim  if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
617218822Sdim    || (reshdr.header_size != 0x20000000 && target_is_bigendian))
618218822Sdim    goto skip_err;
61960484Sobrien
620218822Sdim  /* Subtract size of HeaderSize. DataSize has to be zero. */
621218822Sdim  off[0] += 0x20 - BIN_RES_HDR_SIZE;
622218822Sdim  if (off[0] >= omax)
62360484Sobrien    goto skip_err;
62460484Sobrien
62560484Sobrien  return;
62660484Sobrien
62760484Sobrienskip_err:
62860484Sobrien  fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
62960484Sobrien	   filename);
63060484Sobrien  xexit (1);
63160484Sobrien}
63260484Sobrien
63360484Sobrien/* Add a resource to resource directory */
634218822Sdimstatic void
635218822Sdimres_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
636218822Sdim		  rc_uint_type language, int dupok)
63760484Sobrien{
638218822Sdim  rc_res_id a[3];
63960484Sobrien
64060484Sobrien  a[0] = *type;
64160484Sobrien  a[1] = *id;
64260484Sobrien  a[2].named = 0;
64360484Sobrien  a[2].u.id = language;
64460484Sobrien  res_append_resource (&resources, r, 3, a, dupok);
64560484Sobrien}
64660484Sobrien
64760484Sobrien/* Append a resource to resource directory.
64860484Sobrien   This is just copied from define_resource
64960484Sobrien   and modified to add an existing resource.
65060484Sobrien */
651218822Sdimstatic void
652218822Sdimres_append_resource (rc_res_directory **resources, rc_res_resource *resource,
653218822Sdim		     int cids, const rc_res_id *ids, int dupok)
65460484Sobrien{
655218822Sdim  rc_res_entry *re = NULL;
65660484Sobrien  int i;
65760484Sobrien
65860484Sobrien  assert (cids > 0);
65960484Sobrien  for (i = 0; i < cids; i++)
66060484Sobrien    {
661218822Sdim      rc_res_entry **pp;
66260484Sobrien
66360484Sobrien      if (*resources == NULL)
66460484Sobrien	{
66560484Sobrien	  static unsigned long timeval;
66660484Sobrien
66760484Sobrien	  /* Use the same timestamp for every resource created in a
66860484Sobrien	     single run.  */
66960484Sobrien	  if (timeval == 0)
67060484Sobrien	    timeval = time (NULL);
67160484Sobrien
672218822Sdim	  *resources = ((rc_res_directory *)
673218822Sdim			res_alloc (sizeof (rc_res_directory)));
67460484Sobrien	  (*resources)->characteristics = 0;
67560484Sobrien	  (*resources)->time = timeval;
67660484Sobrien	  (*resources)->major = 0;
67760484Sobrien	  (*resources)->minor = 0;
67860484Sobrien	  (*resources)->entries = NULL;
67960484Sobrien	}
68060484Sobrien
68160484Sobrien      for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
68260484Sobrien	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
68360484Sobrien	  break;
68460484Sobrien
68560484Sobrien      if (*pp != NULL)
68660484Sobrien	re = *pp;
68760484Sobrien      else
68860484Sobrien	{
689218822Sdim	  re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
69060484Sobrien	  re->next = NULL;
69160484Sobrien	  re->id = ids[i];
69260484Sobrien	  if ((i + 1) < cids)
69360484Sobrien	    {
69460484Sobrien	      re->subdir = 1;
69560484Sobrien	      re->u.dir = NULL;
69660484Sobrien	    }
69760484Sobrien	  else
69860484Sobrien	    {
69960484Sobrien	      re->subdir = 0;
70060484Sobrien	      re->u.res = NULL;
70160484Sobrien	    }
70260484Sobrien
70360484Sobrien	  *pp = re;
70460484Sobrien	}
70560484Sobrien
70660484Sobrien      if ((i + 1) < cids)
70760484Sobrien	{
708218822Sdim	  if (! re->subdir)
70960484Sobrien	    {
71060484Sobrien	      fprintf (stderr, "%s: ", program_name);
71160484Sobrien	      res_ids_print (stderr, i, ids);
71260484Sobrien	      fprintf (stderr, ": expected to be a directory\n");
71360484Sobrien	      xexit (1);
71460484Sobrien	    }
71560484Sobrien
71660484Sobrien	  resources = &re->u.dir;
71760484Sobrien	}
71860484Sobrien    }
71960484Sobrien
72060484Sobrien  if (re->subdir)
72160484Sobrien    {
72260484Sobrien      fprintf (stderr, "%s: ", program_name);
72360484Sobrien      res_ids_print (stderr, cids, ids);
72460484Sobrien      fprintf (stderr, ": expected to be a leaf\n");
72560484Sobrien      xexit (1);
72660484Sobrien    }
72760484Sobrien
72860484Sobrien  if (re->u.res != NULL)
72960484Sobrien    {
73060484Sobrien      if (dupok)
73160484Sobrien	return;
73260484Sobrien
73360484Sobrien      fprintf (stderr, "%s: warning: ", program_name);
73460484Sobrien      res_ids_print (stderr, cids, ids);
73560484Sobrien      fprintf (stderr, ": duplicate value\n");
73660484Sobrien    }
73760484Sobrien
73860484Sobrien  re->u.res = resource;
73960484Sobrien}
740