Home | History | Annotate | Download | only in binutils
      1 /* resres.c: read_res_file and write_res_file implementation for windres.
      2    Copyright (C) 1998-2014 Free Software Foundation, Inc.
      3    Written by Anders Norlander <anorland (at) hem2.passagen.se>.
      4    Rewritten by Kai Tietz, Onevision.
      5 
      6    This file is part of GNU Binutils.
      7 
      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.
     12 
     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.
     17 
     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.  */
     22 
     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.  */
     26 
     27 #include "sysdep.h"
     28 #include "bfd.h"
     29 #include "bucomm.h"
     30 #include "libiberty.h"
     31 #include "windres.h"
     32 
     33 #include <assert.h>
     34 
     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 *);
     44 
     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 *);
     48 
     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 *);
     52 
     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);
     61 
     62 static unsigned long get_id_size (const rc_res_id *);
     63 
     64 static void res_add_resource (rc_res_resource *, const rc_res_id *,
     65 			      const rc_res_id *, rc_uint_type, int);
     66 
     67 static void res_append_resource (rc_res_directory **, rc_res_resource *,
     68 				 int, const rc_res_id *, int);
     69 
     70 static rc_res_directory *resources = NULL;
     71 
     72 static const char *filename;
     73 
     74 extern char *program_name;
     75 
     76 /* Read resource file */
     77 rc_res_directory *
     78 read_res_file (const char *fn)
     79 {
     80   rc_uint_type off, flen;
     81   windres_bfd wrbfd;
     82   bfd *abfd;
     83   asection *sec;
     84   filename = fn;
     85 
     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;
     97 
     98   if (! probe_binary (&wrbfd, flen))
     99     set_windres_bfd_endianness (&wrbfd, ! target_is_bigendian);
    100 
    101   skip_null_resource (&wrbfd, &off, flen);
    102 
    103   while (read_resource_entry (&wrbfd, &off, flen))
    104     ;
    105 
    106   bfd_close (abfd);
    107 
    108   return resources;
    109 }
    110 
    111 /* Write resource file */
    112 void
    113 write_res_file (const char *fn,const rc_res_directory *resdir)
    114 {
    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};
    125 
    126   filename = fn;
    127 
    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;
    136 
    137   set_windres_bfd (&wrbfd, abfd, sec,
    138 		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
    139 					: WR_KIND_BFD_BIN_L));
    140 
    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 (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));
    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);
    158 
    159   bfd_close (abfd);
    160   return;
    161 }
    162 
    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)
    166 {
    167   rc_res_id type;
    168   rc_res_id name;
    169   rc_res_res_info resinfo;
    170   res_hdr reshdr;
    171   void *buff;
    172 
    173   rc_res_resource *r;
    174   struct bin_res_info l;
    175 
    176   off[0] = (off[0] + 3) & ~3;
    177 
    178   /* Read header */
    179   if ((off[0] + 8) > omax)
    180     return 0;
    181   read_res_data_hdr (wrbfd, off, omax, &reshdr);
    182 
    183   /* read resource type */
    184   read_res_id (wrbfd, off, omax, &type);
    185   /* read resource id */
    186   read_res_id (wrbfd, off, omax, &name);
    187 
    188   off[0] = (off[0] + 3) & ~3;
    189 
    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);
    197 
    198   off[0] = (off[0] + 3) & ~3;
    199 
    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);
    209 
    210   return 1;
    211 }
    212 
    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)
    218 {
    219   const rc_res_entry *re;
    220 
    221   for (re = rd->entries; re != NULL; re = re->next)
    222     {
    223       switch (level)
    224 	{
    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;
    232 
    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;
    238 
    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)
    245 	    {
    246 	      *language = re->id.u.id;
    247 	    }
    248 	  break;
    249 
    250 	default:
    251 	  break;
    252 	}
    253 
    254       if (re->subdir)
    255 	off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
    256 				   level + 1);
    257       else
    258 	{
    259 	  if (level == 3)
    260 	    {
    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);
    268 	    }
    269 	  else
    270 	    {
    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);
    274 	    }
    275 	}
    276     }
    277 
    278   return off;
    279 }
    280 
    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)
    285 {
    286   int rt;
    287 
    288   switch (res->type)
    289     {
    290     default:
    291       abort ();
    292 
    293     case RES_TYPE_ACCELERATOR:
    294       rt = RT_ACCELERATOR;
    295       break;
    296 
    297     case RES_TYPE_BITMAP:
    298       rt = RT_BITMAP;
    299       break;
    300 
    301     case RES_TYPE_CURSOR:
    302       rt = RT_CURSOR;
    303       break;
    304 
    305     case RES_TYPE_GROUP_CURSOR:
    306       rt = RT_GROUP_CURSOR;
    307       break;
    308 
    309     case RES_TYPE_DIALOG:
    310       rt = RT_DIALOG;
    311       break;
    312 
    313     case RES_TYPE_FONT:
    314       rt = RT_FONT;
    315       break;
    316 
    317     case RES_TYPE_FONTDIR:
    318       rt = RT_FONTDIR;
    319       break;
    320 
    321     case RES_TYPE_ICON:
    322       rt = RT_ICON;
    323       break;
    324 
    325     case RES_TYPE_GROUP_ICON:
    326       rt = RT_GROUP_ICON;
    327       break;
    328 
    329     case RES_TYPE_MENU:
    330       rt = RT_MENU;
    331       break;
    332 
    333     case RES_TYPE_MESSAGETABLE:
    334       rt = RT_MESSAGETABLE;
    335       break;
    336 
    337     case RES_TYPE_RCDATA:
    338       rt = RT_RCDATA;
    339       break;
    340 
    341     case RES_TYPE_STRINGTABLE:
    342       rt = RT_STRING;
    343       break;
    344 
    345     case RES_TYPE_USERDATA:
    346       rt = 0;
    347       break;
    348 
    349     case RES_TYPE_VERSIONINFO:
    350       rt = RT_VERSION;
    351       break;
    352 
    353     case RES_TYPE_TOOLBAR:
    354       rt = RT_TOOLBAR;
    355       break;
    356     }
    357 
    358   if (rt != 0
    359       && type != NULL
    360       && (type->named || type->u.id != (unsigned long) rt))
    361     {
    362       fprintf (stderr, "// Unexpected resource type mismatch: ");
    363       res_id_print (stderr, *type, 1);
    364       fprintf (stderr, " != %d", rt);
    365       abort ();
    366     }
    367 
    368   return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
    369 }
    370 
    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)
    376 {
    377   rc_uint_type noff;
    378   rc_uint_type datasize = 0;
    379 
    380   noff = res_to_bin ((windres_bfd *) NULL, off, res);
    381   datasize = noff - off;
    382 
    383   off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
    384   return res_to_bin (wrbfd, off, res);
    385 }
    386 
    387 /* Get number of bytes needed to store an id in binary format */
    388 static unsigned long
    389 get_id_size (id)
    390      const rc_res_id *id;
    391 {
    392   if (id->named)
    393     return sizeof (unichar) * (id->u.n.length + 1);
    394   else
    395     return sizeof (unichar) * 2;
    396 }
    397 
    398 /* Write a resource header */
    399 static rc_uint_type
    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)
    403 {
    404   res_hdr reshdr;
    405   reshdr.data_size = datasize;
    406   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
    407 
    408   reshdr.header_size = (reshdr.header_size + 3) & ~3;
    409 
    410   off = (off + 3) & ~3;
    411 
    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);
    415 
    416   off = (off + 3) & ~3;
    417 
    418   off = write_res_info (wrbfd, off, resinfo);
    419   off = (off + 3) & ~3;
    420   return off;
    421 }
    422 
    423 static rc_uint_type
    424 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
    425 {
    426   if (wrbfd)
    427     {
    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);
    432     }
    433   return off + BIN_RES_HDR_SIZE;
    434 }
    435 
    436 static void
    437 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
    438 		   res_hdr *reshdr)
    439 {
    440   struct bin_res_hdr brh;
    441 
    442   if ((off[0] + BIN_RES_HDR_SIZE) > omax)
    443     fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
    444 
    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;
    449 }
    450 
    451 /* Read data from file, abort on failure */
    452 static void
    453 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
    454 	       rc_uint_type size)
    455 {
    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);
    460   off[0] += size;
    461 }
    462 
    463 /* Write a resource id */
    464 static rc_uint_type
    465 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
    466 {
    467   if (id->named)
    468     {
    469       rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
    470       if (wrbfd)
    471 	{
    472 	  rc_uint_type i;
    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)));
    478 	}
    479       off += (len * sizeof (unichar));
    480     }
    481   else
    482     {
    483       if (wrbfd)
    484 	{
    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);
    489 	}
    490       off += BIN_RES_ID;
    491     }
    492   return off;
    493 }
    494 
    495 /* Write resource info */
    496 static rc_uint_type
    497 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
    498 {
    499   if (wrbfd)
    500     {
    501       struct bin_res_info l;
    502 
    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);
    509     }
    510   return off + BIN_RES_INFO_SIZE;
    511 }
    512 
    513 /* read a resource identifier */
    514 static void
    515 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
    516 {
    517   struct bin_res_id bid;
    518   unsigned short ord;
    519   unichar *id_s = NULL;
    520   rc_uint_type len;
    521 
    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 */
    525     {
    526       read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
    527       id->named = 0;
    528       id->u.id = windres_get_16 (wrbfd, bid.id, 2);
    529     }
    530   else
    531     /* named id */
    532     {
    533       off[0] -= 2;
    534       id_s = read_unistring (wrbfd, off, omax, &len);
    535       id->named = 1;
    536       id->u.n.length = len;
    537       id->u.n.name = id_s;
    538     }
    539 }
    540 
    541 /* Read a null terminated UNICODE string */
    542 static unichar *
    543 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
    544 		rc_uint_type *len)
    545 {
    546   unichar *s;
    547   bfd_byte d[2];
    548   unichar c;
    549   unichar *p;
    550   rc_uint_type l;
    551   rc_uint_type soff = off[0];
    552 
    553   do
    554     {
    555       read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
    556       c = windres_get_16 (wrbfd, d, 2);
    557     }
    558   while (c != 0);
    559   l = ((soff - off[0]) / sizeof (unichar));
    560 
    561   /* there are hardly any names longer than 256 characters, but anyway. */
    562   p = s = (unichar *) xmalloc (sizeof (unichar) * l);
    563   do
    564     {
    565       read_res_data (wrbfd, off, omax, d, sizeof (unichar));
    566       c = windres_get_16 (wrbfd, d, 2);
    567       *p++ = c;
    568     }
    569   while (c != 0);
    570   *len = l - 1;
    571   return s;
    572 }
    573 
    574 static int
    575 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
    576 {
    577   rc_uint_type off;
    578   res_hdr reshdr;
    579 
    580   off = 0;
    581   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
    582   if (reshdr.data_size != 0)
    583     return 1;
    584   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
    585       || (reshdr.header_size != 0x20000000 && target_is_bigendian))
    586     return 1;
    587 
    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)
    591     return 1;
    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)
    597     return 0;
    598   return 1;
    599 }
    600 
    601 /* Check if file is a win32 binary resource file, if so
    602    skip past the null resource. Returns 0 if successful, -1 on
    603    error.
    604  */
    605 static void
    606 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
    607 {
    608   res_hdr reshdr;
    609   read_res_data_hdr (wrbfd, off, omax, &reshdr);
    610   if (reshdr.data_size != 0)
    611     goto skip_err;
    612   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
    613     || (reshdr.header_size != 0x20000000 && target_is_bigendian))
    614     goto skip_err;
    615 
    616   /* Subtract size of HeaderSize. DataSize has to be zero. */
    617   off[0] += 0x20 - BIN_RES_HDR_SIZE;
    618   if (off[0] >= omax)
    619     goto skip_err;
    620 
    621   return;
    622 
    623 skip_err:
    624   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
    625 	   filename);
    626   xexit (1);
    627 }
    628 
    629 /* Add a resource to resource directory */
    630 static void
    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)
    633 {
    634   rc_res_id a[3];
    635 
    636   a[0] = *type;
    637   a[1] = *id;
    638   a[2].named = 0;
    639   a[2].u.id = language;
    640   res_append_resource (&resources, r, 3, a, dupok);
    641 }
    642 
    643 /* Append a resource to resource directory.
    644    This is just copied from define_resource
    645    and modified to add an existing resource.
    646  */
    647 static void
    648 res_append_resource (rc_res_directory **res_dirs, rc_res_resource *resource,
    649 		     int cids, const rc_res_id *ids, int dupok)
    650 {
    651   rc_res_entry *re = NULL;
    652   int i;
    653 
    654   assert (cids > 0);
    655   for (i = 0; i < cids; i++)
    656     {
    657       rc_res_entry **pp;
    658 
    659       if (*res_dirs == NULL)
    660 	{
    661 	  *res_dirs = ((rc_res_directory *)
    662 			res_alloc (sizeof (rc_res_directory)));
    663 
    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;
    671 	}
    672 
    673       for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next)
    674 	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
    675 	  break;
    676 
    677       if (*pp != NULL)
    678 	re = *pp;
    679       else
    680 	{
    681 	  re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
    682 	  re->next = NULL;
    683 	  re->id = ids[i];
    684 	  if ((i + 1) < cids)
    685 	    {
    686 	      re->subdir = 1;
    687 	      re->u.dir = NULL;
    688 	    }
    689 	  else
    690 	    {
    691 	      re->subdir = 0;
    692 	      re->u.res = NULL;
    693 	    }
    694 
    695 	  *pp = re;
    696 	}
    697 
    698       if ((i + 1) < cids)
    699 	{
    700 	  if (! re->subdir)
    701 	    {
    702 	      fprintf (stderr, "%s: ", program_name);
    703 	      res_ids_print (stderr, i, ids);
    704 	      fprintf (stderr, ": expected to be a directory\n");
    705 	      xexit (1);
    706 	    }
    707 
    708 	  res_dirs = &re->u.dir;
    709 	}
    710     }
    711 
    712   if (re->subdir)
    713     {
    714       fprintf (stderr, "%s: ", program_name);
    715       res_ids_print (stderr, cids, ids);
    716       fprintf (stderr, ": expected to be a leaf\n");
    717       xexit (1);
    718     }
    719 
    720   if (re->u.res != NULL)
    721     {
    722       if (dupok)
    723 	return;
    724 
    725       fprintf (stderr, "%s: warning: ", program_name);
    726       res_ids_print (stderr, cids, ids);
    727       fprintf (stderr, ": duplicate value\n");
    728     }
    729 
    730   re->u.res = resource;
    731 }
    732