Home | History | Annotate | Download | only in binutils
      1 /* resres.c: read_res_file and write_res_file implementation for windres.
      2    Copyright (C) 1998-2016 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 (const rc_res_id *id)
    390 {
    391   if (id->named)
    392     return sizeof (unichar) * (id->u.n.length + 1);
    393   else
    394     return sizeof (unichar) * 2;
    395 }
    396 
    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)
    402 {
    403   res_hdr reshdr;
    404   reshdr.data_size = datasize;
    405   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
    406 
    407   reshdr.header_size = (reshdr.header_size + 3) & ~3;
    408 
    409   off = (off + 3) & ~3;
    410 
    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);
    414 
    415   off = (off + 3) & ~3;
    416 
    417   off = write_res_info (wrbfd, off, resinfo);
    418   off = (off + 3) & ~3;
    419   return off;
    420 }
    421 
    422 static rc_uint_type
    423 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
    424 {
    425   if (wrbfd)
    426     {
    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);
    431     }
    432   return off + BIN_RES_HDR_SIZE;
    433 }
    434 
    435 static void
    436 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
    437 		   res_hdr *reshdr)
    438 {
    439   struct bin_res_hdr brh;
    440 
    441   if ((off[0] + BIN_RES_HDR_SIZE) > omax)
    442     fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
    443 
    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;
    448 }
    449 
    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)
    454 {
    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;
    460 }
    461 
    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)
    465 {
    466   if (id->named)
    467     {
    468       rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
    469       if (wrbfd)
    470 	{
    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)));
    477 	}
    478       off += (len * sizeof (unichar));
    479     }
    480   else
    481     {
    482       if (wrbfd)
    483 	{
    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);
    488 	}
    489       off += BIN_RES_ID;
    490     }
    491   return off;
    492 }
    493 
    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)
    497 {
    498   if (wrbfd)
    499     {
    500       struct bin_res_info l;
    501 
    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);
    508     }
    509   return off + BIN_RES_INFO_SIZE;
    510 }
    511 
    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)
    515 {
    516   struct bin_res_id bid;
    517   unsigned short ord;
    518   unichar *id_s = NULL;
    519   rc_uint_type len;
    520 
    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 */
    524     {
    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);
    528     }
    529   else
    530     /* named id */
    531     {
    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;
    537     }
    538 }
    539 
    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)
    544 {
    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];
    551 
    552   do
    553     {
    554       read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
    555       c = windres_get_16 (wrbfd, d, 2);
    556     }
    557   while (c != 0);
    558   l = ((soff - off[0]) / sizeof (unichar));
    559 
    560   /* there are hardly any names longer than 256 characters, but anyway. */
    561   p = s = (unichar *) xmalloc (sizeof (unichar) * l);
    562   do
    563     {
    564       read_res_data (wrbfd, off, omax, d, sizeof (unichar));
    565       c = windres_get_16 (wrbfd, d, 2);
    566       *p++ = c;
    567     }
    568   while (c != 0);
    569   *len = l - 1;
    570   return s;
    571 }
    572 
    573 static int
    574 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
    575 {
    576   rc_uint_type off;
    577   res_hdr reshdr;
    578 
    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;
    586 
    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;
    598 }
    599 
    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.
    603  */
    604 static void
    605 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
    606 {
    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;
    614 
    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;
    619 
    620   return;
    621 
    622 skip_err:
    623   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
    624 	   filename);
    625   xexit (1);
    626 }
    627 
    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)
    632 {
    633   rc_res_id a[3];
    634 
    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);
    640 }
    641 
    642 /* Append a resource to resource directory.
    643    This is just copied from define_resource
    644    and modified to add an existing resource.
    645  */
    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)
    649 {
    650   rc_res_entry *re = NULL;
    651   int i;
    652 
    653   assert (cids > 0);
    654   for (i = 0; i < cids; i++)
    655     {
    656       rc_res_entry **pp;
    657 
    658       if (*res_dirs == NULL)
    659 	{
    660 	  *res_dirs = ((rc_res_directory *)
    661 			res_alloc (sizeof (rc_res_directory)));
    662 
    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;
    670 	}
    671 
    672       for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next)
    673 	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
    674 	  break;
    675 
    676       if (*pp != NULL)
    677 	re = *pp;
    678       else
    679 	{
    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)
    684 	    {
    685 	      re->subdir = 1;
    686 	      re->u.dir = NULL;
    687 	    }
    688 	  else
    689 	    {
    690 	      re->subdir = 0;
    691 	      re->u.res = NULL;
    692 	    }
    693 
    694 	  *pp = re;
    695 	}
    696 
    697       if ((i + 1) < cids)
    698 	{
    699 	  if (! re->subdir)
    700 	    {
    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);
    705 	    }
    706 
    707 	  res_dirs = &re->u.dir;
    708 	}
    709     }
    710 
    711   if (re->subdir)
    712     {
    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);
    717     }
    718 
    719   if (re->u.res != NULL)
    720     {
    721       if (dupok)
    722 	return;
    723 
    724       fprintf (stderr, "%s: warning: ", program_name);
    725       res_ids_print (stderr, cids, ids);
    726       fprintf (stderr, ": duplicate value\n");
    727     }
    728 
    729   re->u.res = resource;
    730 }
    731