xref: /netbsd-src/external/gpl3/binutils.old/dist/binutils/resres.c (revision e992f068c547fd6e84b3f104dc2340adcc955732)
175fd0b74Schristos /* resres.c: read_res_file and write_res_file implementation for windres.
2*e992f068Schristos    Copyright (C) 1998-2022 Free Software Foundation, Inc.
375fd0b74Schristos    Written by Anders Norlander <anorland@hem2.passagen.se>.
475fd0b74Schristos    Rewritten by Kai Tietz, Onevision.
575fd0b74Schristos 
675fd0b74Schristos    This file is part of GNU Binutils.
775fd0b74Schristos 
875fd0b74Schristos    This program is free software; you can redistribute it and/or modify
975fd0b74Schristos    it under the terms of the GNU General Public License as published by
1075fd0b74Schristos    the Free Software Foundation; either version 3 of the License, or
1175fd0b74Schristos    (at your option) any later version.
1275fd0b74Schristos 
1375fd0b74Schristos    This program is distributed in the hope that it will be useful,
1475fd0b74Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
1575fd0b74Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1675fd0b74Schristos    GNU General Public License for more details.
1775fd0b74Schristos 
1875fd0b74Schristos    You should have received a copy of the GNU General Public License
1975fd0b74Schristos    along with this program; if not, write to the Free Software
2075fd0b74Schristos    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
2175fd0b74Schristos    02110-1301, USA.  */
2275fd0b74Schristos 
2375fd0b74Schristos /* FIXME: This file does not work correctly in a cross configuration.
2475fd0b74Schristos    It assumes that it can use fread and fwrite to read and write
2575fd0b74Schristos    integers.  It does no swapping.  */
2675fd0b74Schristos 
2775fd0b74Schristos #include "sysdep.h"
2875fd0b74Schristos #include "bfd.h"
2975fd0b74Schristos #include "bucomm.h"
3075fd0b74Schristos #include "libiberty.h"
3175fd0b74Schristos #include "windres.h"
3275fd0b74Schristos 
3375fd0b74Schristos #include <assert.h>
3475fd0b74Schristos 
3575fd0b74Schristos static rc_uint_type write_res_directory (windres_bfd *, rc_uint_type,
3675fd0b74Schristos 				    	 const rc_res_directory *, const rc_res_id *,
3775fd0b74Schristos 				    	 const rc_res_id *, rc_uint_type *, int);
3875fd0b74Schristos static rc_uint_type write_res_resource (windres_bfd *, rc_uint_type,const rc_res_id *,
3975fd0b74Schristos 				   	const rc_res_id *, const rc_res_resource *,
4075fd0b74Schristos 				   	rc_uint_type *);
4175fd0b74Schristos static rc_uint_type write_res_bin (windres_bfd *, rc_uint_type, const rc_res_resource *,
4275fd0b74Schristos 				   const rc_res_id *, const rc_res_id *,
4375fd0b74Schristos 				   const rc_res_res_info *);
4475fd0b74Schristos 
4575fd0b74Schristos static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
4675fd0b74Schristos static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
4775fd0b74Schristos static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
4875fd0b74Schristos 
4975fd0b74Schristos static rc_uint_type write_res_header (windres_bfd *, rc_uint_type, rc_uint_type,
5075fd0b74Schristos 				      const rc_res_id *, const rc_res_id *,
5175fd0b74Schristos 				      const rc_res_res_info *);
5275fd0b74Schristos 
5375fd0b74Schristos static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
5475fd0b74Schristos static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
5575fd0b74Schristos 			   rc_uint_type);
5675fd0b74Schristos static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
5775fd0b74Schristos static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
5875fd0b74Schristos static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
5975fd0b74Schristos static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
6075fd0b74Schristos static int probe_binary (windres_bfd *wrbfd, rc_uint_type);
6175fd0b74Schristos 
6275fd0b74Schristos static unsigned long get_id_size (const rc_res_id *);
6375fd0b74Schristos 
6475fd0b74Schristos static void res_add_resource (rc_res_resource *, const rc_res_id *,
6575fd0b74Schristos 			      const rc_res_id *, rc_uint_type, int);
6675fd0b74Schristos 
6775fd0b74Schristos static void res_append_resource (rc_res_directory **, rc_res_resource *,
6875fd0b74Schristos 				 int, const rc_res_id *, int);
6975fd0b74Schristos 
7075fd0b74Schristos static rc_res_directory *resources = NULL;
7175fd0b74Schristos 
7275fd0b74Schristos static const char *filename;
7375fd0b74Schristos 
7475fd0b74Schristos extern char *program_name;
7575fd0b74Schristos 
7675fd0b74Schristos /* Read resource file */
7775fd0b74Schristos rc_res_directory *
read_res_file(const char * fn)7875fd0b74Schristos read_res_file (const char *fn)
7975fd0b74Schristos {
8075fd0b74Schristos   rc_uint_type off, flen;
8175fd0b74Schristos   windres_bfd wrbfd;
8275fd0b74Schristos   bfd *abfd;
8375fd0b74Schristos   asection *sec;
8475fd0b74Schristos   filename = fn;
8575fd0b74Schristos 
8675fd0b74Schristos   flen = (rc_uint_type) get_file_size (filename);
8775fd0b74Schristos   if (! flen)
8875fd0b74Schristos     fatal ("can't open '%s' for input.", filename);
8975fd0b74Schristos   abfd = windres_open_as_binary (filename, 1);
9075fd0b74Schristos   sec = bfd_get_section_by_name (abfd, ".data");
9175fd0b74Schristos   if (sec == NULL)
9275fd0b74Schristos     bfd_fatal ("bfd_get_section_by_name");
9375fd0b74Schristos   set_windres_bfd (&wrbfd, abfd, sec,
9475fd0b74Schristos 		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
9575fd0b74Schristos 					: WR_KIND_BFD_BIN_L));
9675fd0b74Schristos   off = 0;
9775fd0b74Schristos 
9875fd0b74Schristos   if (! probe_binary (&wrbfd, flen))
9975fd0b74Schristos     set_windres_bfd_endianness (&wrbfd, ! target_is_bigendian);
10075fd0b74Schristos 
10175fd0b74Schristos   skip_null_resource (&wrbfd, &off, flen);
10275fd0b74Schristos 
10375fd0b74Schristos   while (read_resource_entry (&wrbfd, &off, flen))
10475fd0b74Schristos     ;
10575fd0b74Schristos 
10675fd0b74Schristos   bfd_close (abfd);
10775fd0b74Schristos 
10875fd0b74Schristos   return resources;
10975fd0b74Schristos }
11075fd0b74Schristos 
11175fd0b74Schristos /* Write resource file */
11275fd0b74Schristos void
write_res_file(const char * fn,const rc_res_directory * resdir)11375fd0b74Schristos write_res_file (const char *fn,const rc_res_directory *resdir)
11475fd0b74Schristos {
11575fd0b74Schristos   asection *sec;
11675fd0b74Schristos   rc_uint_type language;
11775fd0b74Schristos   bfd *abfd;
11875fd0b74Schristos   windres_bfd wrbfd;
11975fd0b74Schristos   unsigned long sec_length = 0,sec_length_wrote;
12075fd0b74Schristos   static const bfd_byte sign[] =
12175fd0b74Schristos   {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
12275fd0b74Schristos    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
12375fd0b74Schristos    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12475fd0b74Schristos    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
12575fd0b74Schristos 
12675fd0b74Schristos   filename = fn;
12775fd0b74Schristos 
12875fd0b74Schristos   abfd = windres_open_as_binary (filename, 0);
12975fd0b74Schristos   sec = bfd_make_section_with_flags (abfd, ".data",
13075fd0b74Schristos 				     (SEC_HAS_CONTENTS | SEC_ALLOC
13175fd0b74Schristos 				      | SEC_LOAD | SEC_DATA));
13275fd0b74Schristos   if (sec == NULL)
13375fd0b74Schristos     bfd_fatal ("bfd_make_section");
13475fd0b74Schristos   /* Requiring this is probably a bug in BFD.  */
13575fd0b74Schristos   sec->output_section = sec;
13675fd0b74Schristos 
13775fd0b74Schristos   set_windres_bfd (&wrbfd, abfd, sec,
13875fd0b74Schristos 		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
13975fd0b74Schristos 					: WR_KIND_BFD_BIN_L));
14075fd0b74Schristos 
14175fd0b74Schristos   language = -1;
14275fd0b74Schristos   sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
14375fd0b74Schristos 				    (const rc_res_id *) NULL,
14475fd0b74Schristos 				    (const rc_res_id *) NULL, &language, 1);
145012573ebSchristos   if (!bfd_set_section_size (sec, (sec_length + 3) & ~3))
14675fd0b74Schristos     bfd_fatal ("bfd_set_section_size");
14775fd0b74Schristos   if ((sec_length & 3) != 0)
14875fd0b74Schristos     set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
14975fd0b74Schristos   set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
15075fd0b74Schristos   language = -1;
15175fd0b74Schristos   sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
15275fd0b74Schristos 					  (const rc_res_id *) NULL,
15375fd0b74Schristos 					  (const rc_res_id *) NULL,
15475fd0b74Schristos 					  &language, 1);
15575fd0b74Schristos   if (sec_length != sec_length_wrote)
15675fd0b74Schristos     fatal ("res write failed with different sizes (%lu/%lu).",
15775fd0b74Schristos 	   (unsigned long) sec_length, (unsigned long) sec_length_wrote);
15875fd0b74Schristos 
15975fd0b74Schristos   bfd_close (abfd);
16075fd0b74Schristos   return;
16175fd0b74Schristos }
16275fd0b74Schristos 
16375fd0b74Schristos /* Read a resource entry, returns 0 when all resources are read */
16475fd0b74Schristos static int
read_resource_entry(windres_bfd * wrbfd,rc_uint_type * off,rc_uint_type omax)16575fd0b74Schristos read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
16675fd0b74Schristos {
16775fd0b74Schristos   rc_res_id type;
16875fd0b74Schristos   rc_res_id name;
16975fd0b74Schristos   rc_res_res_info resinfo;
17075fd0b74Schristos   res_hdr reshdr;
17175fd0b74Schristos   void *buff;
17275fd0b74Schristos 
17375fd0b74Schristos   rc_res_resource *r;
17475fd0b74Schristos   struct bin_res_info l;
17575fd0b74Schristos 
17675fd0b74Schristos   off[0] = (off[0] + 3) & ~3;
17775fd0b74Schristos 
17875fd0b74Schristos   /* Read header */
17975fd0b74Schristos   if ((off[0] + 8) > omax)
18075fd0b74Schristos     return 0;
18175fd0b74Schristos   read_res_data_hdr (wrbfd, off, omax, &reshdr);
18275fd0b74Schristos 
18375fd0b74Schristos   /* read resource type */
18475fd0b74Schristos   read_res_id (wrbfd, off, omax, &type);
18575fd0b74Schristos   /* read resource id */
18675fd0b74Schristos   read_res_id (wrbfd, off, omax, &name);
18775fd0b74Schristos 
18875fd0b74Schristos   off[0] = (off[0] + 3) & ~3;
18975fd0b74Schristos 
19075fd0b74Schristos   /* Read additional resource header */
19175fd0b74Schristos   read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
19275fd0b74Schristos   resinfo.version = windres_get_32 (wrbfd, l.version, 4);
19375fd0b74Schristos   resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
19475fd0b74Schristos   resinfo.language = windres_get_16 (wrbfd, l.language, 2);
19575fd0b74Schristos   /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
19675fd0b74Schristos   resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
19775fd0b74Schristos 
19875fd0b74Schristos   off[0] = (off[0] + 3) & ~3;
19975fd0b74Schristos 
20075fd0b74Schristos   /* Allocate buffer for data */
20175fd0b74Schristos   buff = res_alloc (reshdr.data_size);
20275fd0b74Schristos   /* Read data */
20375fd0b74Schristos   read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
20475fd0b74Schristos   /* Convert binary data to resource */
20575fd0b74Schristos   r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
20675fd0b74Schristos   r->res_info = resinfo;
20775fd0b74Schristos   /* Add resource to resource directory */
20875fd0b74Schristos   res_add_resource (r, &type, &name, resinfo.language, 0);
20975fd0b74Schristos 
21075fd0b74Schristos   return 1;
21175fd0b74Schristos }
21275fd0b74Schristos 
21375fd0b74Schristos /* write resource directory to binary resource file */
21475fd0b74Schristos static rc_uint_type
write_res_directory(windres_bfd * wrbfd,rc_uint_type off,const rc_res_directory * rd,const rc_res_id * type,const rc_res_id * name,rc_uint_type * language,int level)21575fd0b74Schristos write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
21675fd0b74Schristos 		     const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
21775fd0b74Schristos 		     int level)
21875fd0b74Schristos {
21975fd0b74Schristos   const rc_res_entry *re;
22075fd0b74Schristos 
22175fd0b74Schristos   for (re = rd->entries; re != NULL; re = re->next)
22275fd0b74Schristos     {
22375fd0b74Schristos       switch (level)
22475fd0b74Schristos 	{
22575fd0b74Schristos 	case 1:
22675fd0b74Schristos 	  /* If we're at level 1, the key of this resource is the
22775fd0b74Schristos 	     type.  This normally duplicates the information we have
22875fd0b74Schristos 	     stored with the resource itself, but we need to remember
22975fd0b74Schristos 	     the type if this is a user define resource type.  */
23075fd0b74Schristos 	  type = &re->id;
23175fd0b74Schristos 	  break;
23275fd0b74Schristos 
23375fd0b74Schristos 	case 2:
23475fd0b74Schristos 	  /* If we're at level 2, the key of this resource is the name
23575fd0b74Schristos 	     we are going to use in the rc printout.  */
23675fd0b74Schristos 	  name = &re->id;
23775fd0b74Schristos 	  break;
23875fd0b74Schristos 
23975fd0b74Schristos 	case 3:
24075fd0b74Schristos 	  /* If we're at level 3, then this key represents a language.
24175fd0b74Schristos 	     Use it to update the current language.  */
24275fd0b74Schristos 	  if (! re->id.named
24375fd0b74Schristos 	      && re->id.u.id != (unsigned long) *language
24475fd0b74Schristos 	      && (re->id.u.id & 0xffff) == re->id.u.id)
24575fd0b74Schristos 	    {
24675fd0b74Schristos 	      *language = re->id.u.id;
24775fd0b74Schristos 	    }
24875fd0b74Schristos 	  break;
24975fd0b74Schristos 
25075fd0b74Schristos 	default:
25175fd0b74Schristos 	  break;
25275fd0b74Schristos 	}
25375fd0b74Schristos 
25475fd0b74Schristos       if (re->subdir)
25575fd0b74Schristos 	off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
25675fd0b74Schristos 				   level + 1);
25775fd0b74Schristos       else
25875fd0b74Schristos 	{
25975fd0b74Schristos 	  if (level == 3)
26075fd0b74Schristos 	    {
26175fd0b74Schristos 	      /* This is the normal case: the three levels are
26275fd0b74Schristos 	         TYPE/NAME/LANGUAGE.  NAME will have been set at level
26375fd0b74Schristos 	         2, and represents the name to use.  We probably just
26475fd0b74Schristos 	         set LANGUAGE, and it will probably match what the
26575fd0b74Schristos 	         resource itself records if anything.  */
26675fd0b74Schristos 	      off = write_res_resource (wrbfd, off, type, name, re->u.res,
26775fd0b74Schristos 	      				language);
26875fd0b74Schristos 	    }
26975fd0b74Schristos 	  else
27075fd0b74Schristos 	    {
27175fd0b74Schristos 	      fprintf (stderr, "// Resource at unexpected level %d\n", level);
27275fd0b74Schristos 	      off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
27375fd0b74Schristos 	      				re->u.res, language);
27475fd0b74Schristos 	    }
27575fd0b74Schristos 	}
27675fd0b74Schristos     }
27775fd0b74Schristos 
27875fd0b74Schristos   return off;
27975fd0b74Schristos }
28075fd0b74Schristos 
28175fd0b74Schristos static rc_uint_type
write_res_resource(windres_bfd * wrbfd,rc_uint_type off,const rc_res_id * type,const rc_res_id * name,const rc_res_resource * res,rc_uint_type * language ATTRIBUTE_UNUSED)28275fd0b74Schristos write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
28375fd0b74Schristos 		    const rc_res_id *name, const rc_res_resource *res,
28475fd0b74Schristos 		    rc_uint_type *language ATTRIBUTE_UNUSED)
28575fd0b74Schristos {
28675fd0b74Schristos   int rt;
28775fd0b74Schristos 
28875fd0b74Schristos   switch (res->type)
28975fd0b74Schristos     {
29075fd0b74Schristos     default:
29175fd0b74Schristos       abort ();
29275fd0b74Schristos 
29375fd0b74Schristos     case RES_TYPE_ACCELERATOR:
29475fd0b74Schristos       rt = RT_ACCELERATOR;
29575fd0b74Schristos       break;
29675fd0b74Schristos 
29775fd0b74Schristos     case RES_TYPE_BITMAP:
29875fd0b74Schristos       rt = RT_BITMAP;
29975fd0b74Schristos       break;
30075fd0b74Schristos 
30175fd0b74Schristos     case RES_TYPE_CURSOR:
30275fd0b74Schristos       rt = RT_CURSOR;
30375fd0b74Schristos       break;
30475fd0b74Schristos 
30575fd0b74Schristos     case RES_TYPE_GROUP_CURSOR:
30675fd0b74Schristos       rt = RT_GROUP_CURSOR;
30775fd0b74Schristos       break;
30875fd0b74Schristos 
30975fd0b74Schristos     case RES_TYPE_DIALOG:
31075fd0b74Schristos       rt = RT_DIALOG;
31175fd0b74Schristos       break;
31275fd0b74Schristos 
31375fd0b74Schristos     case RES_TYPE_FONT:
31475fd0b74Schristos       rt = RT_FONT;
31575fd0b74Schristos       break;
31675fd0b74Schristos 
31775fd0b74Schristos     case RES_TYPE_FONTDIR:
31875fd0b74Schristos       rt = RT_FONTDIR;
31975fd0b74Schristos       break;
32075fd0b74Schristos 
32175fd0b74Schristos     case RES_TYPE_ICON:
32275fd0b74Schristos       rt = RT_ICON;
32375fd0b74Schristos       break;
32475fd0b74Schristos 
32575fd0b74Schristos     case RES_TYPE_GROUP_ICON:
32675fd0b74Schristos       rt = RT_GROUP_ICON;
32775fd0b74Schristos       break;
32875fd0b74Schristos 
32975fd0b74Schristos     case RES_TYPE_MENU:
33075fd0b74Schristos       rt = RT_MENU;
33175fd0b74Schristos       break;
33275fd0b74Schristos 
33375fd0b74Schristos     case RES_TYPE_MESSAGETABLE:
33475fd0b74Schristos       rt = RT_MESSAGETABLE;
33575fd0b74Schristos       break;
33675fd0b74Schristos 
33775fd0b74Schristos     case RES_TYPE_RCDATA:
33875fd0b74Schristos       rt = RT_RCDATA;
33975fd0b74Schristos       break;
34075fd0b74Schristos 
34175fd0b74Schristos     case RES_TYPE_STRINGTABLE:
34275fd0b74Schristos       rt = RT_STRING;
34375fd0b74Schristos       break;
34475fd0b74Schristos 
34575fd0b74Schristos     case RES_TYPE_USERDATA:
34675fd0b74Schristos       rt = 0;
34775fd0b74Schristos       break;
34875fd0b74Schristos 
34975fd0b74Schristos     case RES_TYPE_VERSIONINFO:
35075fd0b74Schristos       rt = RT_VERSION;
35175fd0b74Schristos       break;
35275fd0b74Schristos 
35375fd0b74Schristos     case RES_TYPE_TOOLBAR:
35475fd0b74Schristos       rt = RT_TOOLBAR;
35575fd0b74Schristos       break;
35675fd0b74Schristos     }
35775fd0b74Schristos 
35875fd0b74Schristos   if (rt != 0
35975fd0b74Schristos       && type != NULL
36075fd0b74Schristos       && (type->named || type->u.id != (unsigned long) rt))
36175fd0b74Schristos     {
36275fd0b74Schristos       fprintf (stderr, "// Unexpected resource type mismatch: ");
36375fd0b74Schristos       res_id_print (stderr, *type, 1);
36475fd0b74Schristos       fprintf (stderr, " != %d", rt);
36575fd0b74Schristos       abort ();
36675fd0b74Schristos     }
36775fd0b74Schristos 
36875fd0b74Schristos   return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
36975fd0b74Schristos }
37075fd0b74Schristos 
37175fd0b74Schristos /* Write a resource in binary resource format */
37275fd0b74Schristos static rc_uint_type
write_res_bin(windres_bfd * wrbfd,rc_uint_type off,const rc_res_resource * res,const rc_res_id * type,const rc_res_id * name,const rc_res_res_info * resinfo)37375fd0b74Schristos write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
37475fd0b74Schristos 	       const rc_res_id *type, const rc_res_id *name,
37575fd0b74Schristos 	       const rc_res_res_info *resinfo)
37675fd0b74Schristos {
37775fd0b74Schristos   rc_uint_type noff;
37875fd0b74Schristos   rc_uint_type datasize = 0;
37975fd0b74Schristos 
38075fd0b74Schristos   noff = res_to_bin ((windres_bfd *) NULL, off, res);
38175fd0b74Schristos   datasize = noff - off;
38275fd0b74Schristos 
38375fd0b74Schristos   off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
38475fd0b74Schristos   return res_to_bin (wrbfd, off, res);
38575fd0b74Schristos }
38675fd0b74Schristos 
38775fd0b74Schristos /* Get number of bytes needed to store an id in binary format */
38875fd0b74Schristos static unsigned long
get_id_size(const rc_res_id * id)38975fd0b74Schristos get_id_size (const rc_res_id *id)
39075fd0b74Schristos {
39175fd0b74Schristos   if (id->named)
39275fd0b74Schristos     return sizeof (unichar) * (id->u.n.length + 1);
39375fd0b74Schristos   else
39475fd0b74Schristos     return sizeof (unichar) * 2;
39575fd0b74Schristos }
39675fd0b74Schristos 
39775fd0b74Schristos /* Write a resource header */
39875fd0b74Schristos static rc_uint_type
write_res_header(windres_bfd * wrbfd,rc_uint_type off,rc_uint_type datasize,const rc_res_id * type,const rc_res_id * name,const rc_res_res_info * resinfo)39975fd0b74Schristos write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
40075fd0b74Schristos 		  const rc_res_id *type, const rc_res_id *name,
40175fd0b74Schristos 		  const rc_res_res_info *resinfo)
40275fd0b74Schristos {
40375fd0b74Schristos   res_hdr reshdr;
40475fd0b74Schristos   reshdr.data_size = datasize;
40575fd0b74Schristos   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
40675fd0b74Schristos 
40775fd0b74Schristos   reshdr.header_size = (reshdr.header_size + 3) & ~3;
40875fd0b74Schristos 
40975fd0b74Schristos   off = (off + 3) & ~3;
41075fd0b74Schristos 
41175fd0b74Schristos   off = write_res_data_hdr (wrbfd, off, &reshdr);
41275fd0b74Schristos   off = write_res_id (wrbfd, off, type);
41375fd0b74Schristos   off = write_res_id (wrbfd, off, name);
41475fd0b74Schristos 
41575fd0b74Schristos   off = (off + 3) & ~3;
41675fd0b74Schristos 
41775fd0b74Schristos   off = write_res_info (wrbfd, off, resinfo);
41875fd0b74Schristos   off = (off + 3) & ~3;
41975fd0b74Schristos   return off;
42075fd0b74Schristos }
42175fd0b74Schristos 
42275fd0b74Schristos static rc_uint_type
write_res_data_hdr(windres_bfd * wrbfd,rc_uint_type off,res_hdr * hdr)42375fd0b74Schristos write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
42475fd0b74Schristos {
42575fd0b74Schristos   if (wrbfd)
42675fd0b74Schristos     {
42775fd0b74Schristos       struct bin_res_hdr brh;
42875fd0b74Schristos       windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
42975fd0b74Schristos       windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
43075fd0b74Schristos       set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
43175fd0b74Schristos     }
43275fd0b74Schristos   return off + BIN_RES_HDR_SIZE;
43375fd0b74Schristos }
43475fd0b74Schristos 
43575fd0b74Schristos static void
read_res_data_hdr(windres_bfd * wrbfd,rc_uint_type * off,rc_uint_type omax,res_hdr * reshdr)43675fd0b74Schristos read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
43775fd0b74Schristos 		   res_hdr *reshdr)
43875fd0b74Schristos {
43975fd0b74Schristos   struct bin_res_hdr brh;
44075fd0b74Schristos 
44175fd0b74Schristos   if ((off[0] + BIN_RES_HDR_SIZE) > omax)
44275fd0b74Schristos     fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
44375fd0b74Schristos 
44475fd0b74Schristos   get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
44575fd0b74Schristos   reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
44675fd0b74Schristos   reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
44775fd0b74Schristos   off[0] += BIN_RES_HDR_SIZE;
44875fd0b74Schristos }
44975fd0b74Schristos 
45075fd0b74Schristos /* Read data from file, abort on failure */
45175fd0b74Schristos static void
read_res_data(windres_bfd * wrbfd,rc_uint_type * off,rc_uint_type omax,void * data,rc_uint_type size)45275fd0b74Schristos read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
45375fd0b74Schristos 	       rc_uint_type size)
45475fd0b74Schristos {
45575fd0b74Schristos   if ((off[0] + size) > omax)
45675fd0b74Schristos     fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
45775fd0b74Schristos     	   (long) omax, (long) size);
45875fd0b74Schristos   get_windres_bfd_content (wrbfd, data, off[0], size);
45975fd0b74Schristos   off[0] += size;
46075fd0b74Schristos }
46175fd0b74Schristos 
46275fd0b74Schristos /* Write a resource id */
46375fd0b74Schristos static rc_uint_type
write_res_id(windres_bfd * wrbfd,rc_uint_type off,const rc_res_id * id)46475fd0b74Schristos write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
46575fd0b74Schristos {
46675fd0b74Schristos   if (id->named)
46775fd0b74Schristos     {
46875fd0b74Schristos       rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
46975fd0b74Schristos       if (wrbfd)
47075fd0b74Schristos 	{
47175fd0b74Schristos 	  rc_uint_type i;
47275fd0b74Schristos 	  bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
47375fd0b74Schristos 	  for (i = 0; i < (len - 1); i++)
47475fd0b74Schristos 	    windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
47575fd0b74Schristos 	  windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
47675fd0b74Schristos 	  set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
47775fd0b74Schristos 	}
47875fd0b74Schristos       off += (len * sizeof (unichar));
47975fd0b74Schristos     }
48075fd0b74Schristos   else
48175fd0b74Schristos     {
48275fd0b74Schristos       if (wrbfd)
48375fd0b74Schristos 	{
48475fd0b74Schristos 	  struct bin_res_id bid;
48575fd0b74Schristos 	  windres_put_16 (wrbfd, bid.sig, 0xffff);
48675fd0b74Schristos 	  windres_put_16 (wrbfd, bid.id, id->u.id);
48775fd0b74Schristos 	  set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
48875fd0b74Schristos 	}
48975fd0b74Schristos       off += BIN_RES_ID;
49075fd0b74Schristos     }
49175fd0b74Schristos   return off;
49275fd0b74Schristos }
49375fd0b74Schristos 
49475fd0b74Schristos /* Write resource info */
49575fd0b74Schristos static rc_uint_type
write_res_info(windres_bfd * wrbfd,rc_uint_type off,const rc_res_res_info * info)49675fd0b74Schristos write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
49775fd0b74Schristos {
49875fd0b74Schristos   if (wrbfd)
49975fd0b74Schristos     {
50075fd0b74Schristos       struct bin_res_info l;
50175fd0b74Schristos 
50275fd0b74Schristos       windres_put_32 (wrbfd, l.version, info->version);
50375fd0b74Schristos       windres_put_16 (wrbfd, l.memflags, info->memflags);
50475fd0b74Schristos       windres_put_16 (wrbfd, l.language, info->language);
50575fd0b74Schristos       windres_put_32 (wrbfd, l.version2, info->version);
50675fd0b74Schristos       windres_put_32 (wrbfd, l.characteristics, info->characteristics);
50775fd0b74Schristos       set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
50875fd0b74Schristos     }
50975fd0b74Schristos   return off + BIN_RES_INFO_SIZE;
51075fd0b74Schristos }
51175fd0b74Schristos 
51275fd0b74Schristos /* read a resource identifier */
51375fd0b74Schristos static void
read_res_id(windres_bfd * wrbfd,rc_uint_type * off,rc_uint_type omax,rc_res_id * id)51475fd0b74Schristos read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
51575fd0b74Schristos {
51675fd0b74Schristos   struct bin_res_id bid;
51775fd0b74Schristos   unsigned short ord;
51875fd0b74Schristos   unichar *id_s = NULL;
51975fd0b74Schristos   rc_uint_type len;
52075fd0b74Schristos 
52175fd0b74Schristos   read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
52275fd0b74Schristos   ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
52375fd0b74Schristos   if (ord == 0xFFFF)		/* an ordinal id */
52475fd0b74Schristos     {
52575fd0b74Schristos       read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
52675fd0b74Schristos       id->named = 0;
52775fd0b74Schristos       id->u.id = windres_get_16 (wrbfd, bid.id, 2);
52875fd0b74Schristos     }
52975fd0b74Schristos   else
53075fd0b74Schristos     /* named id */
53175fd0b74Schristos     {
53275fd0b74Schristos       off[0] -= 2;
53375fd0b74Schristos       id_s = read_unistring (wrbfd, off, omax, &len);
53475fd0b74Schristos       id->named = 1;
53575fd0b74Schristos       id->u.n.length = len;
53675fd0b74Schristos       id->u.n.name = id_s;
53775fd0b74Schristos     }
53875fd0b74Schristos }
53975fd0b74Schristos 
54075fd0b74Schristos /* Read a null terminated UNICODE string */
54175fd0b74Schristos static unichar *
read_unistring(windres_bfd * wrbfd,rc_uint_type * off,rc_uint_type omax,rc_uint_type * len)54275fd0b74Schristos read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
54375fd0b74Schristos 		rc_uint_type *len)
54475fd0b74Schristos {
54575fd0b74Schristos   unichar *s;
54675fd0b74Schristos   bfd_byte d[2];
54775fd0b74Schristos   unichar c;
54875fd0b74Schristos   unichar *p;
54975fd0b74Schristos   rc_uint_type l;
55075fd0b74Schristos   rc_uint_type soff = off[0];
55175fd0b74Schristos 
55275fd0b74Schristos   do
55375fd0b74Schristos     {
55475fd0b74Schristos       read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
55575fd0b74Schristos       c = windres_get_16 (wrbfd, d, 2);
55675fd0b74Schristos     }
55775fd0b74Schristos   while (c != 0);
55875fd0b74Schristos   l = ((soff - off[0]) / sizeof (unichar));
55975fd0b74Schristos 
56075fd0b74Schristos   /* there are hardly any names longer than 256 characters, but anyway. */
56175fd0b74Schristos   p = s = (unichar *) xmalloc (sizeof (unichar) * l);
56275fd0b74Schristos   do
56375fd0b74Schristos     {
56475fd0b74Schristos       read_res_data (wrbfd, off, omax, d, sizeof (unichar));
56575fd0b74Schristos       c = windres_get_16 (wrbfd, d, 2);
56675fd0b74Schristos       *p++ = c;
56775fd0b74Schristos     }
56875fd0b74Schristos   while (c != 0);
56975fd0b74Schristos   *len = l - 1;
57075fd0b74Schristos   return s;
57175fd0b74Schristos }
57275fd0b74Schristos 
57375fd0b74Schristos static int
probe_binary(windres_bfd * wrbfd,rc_uint_type omax)57475fd0b74Schristos probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
57575fd0b74Schristos {
57675fd0b74Schristos   rc_uint_type off;
57775fd0b74Schristos   res_hdr reshdr;
57875fd0b74Schristos 
57975fd0b74Schristos   off = 0;
58075fd0b74Schristos   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
58175fd0b74Schristos   if (reshdr.data_size != 0)
58275fd0b74Schristos     return 1;
58375fd0b74Schristos   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
58475fd0b74Schristos       || (reshdr.header_size != 0x20000000 && target_is_bigendian))
58575fd0b74Schristos     return 1;
58675fd0b74Schristos 
58775fd0b74Schristos   /* Subtract size of HeaderSize. DataSize has to be zero. */
58875fd0b74Schristos   off += 0x20 - BIN_RES_HDR_SIZE;
58975fd0b74Schristos   if ((off + BIN_RES_HDR_SIZE) >= omax)
59075fd0b74Schristos     return 1;
59175fd0b74Schristos   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
59275fd0b74Schristos   /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
59375fd0b74Schristos      which is part of reshdr.header_size. We shouldn't take it
59475fd0b74Schristos      into account twice.  */
59575fd0b74Schristos   if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
59675fd0b74Schristos     return 0;
59775fd0b74Schristos   return 1;
59875fd0b74Schristos }
59975fd0b74Schristos 
60075fd0b74Schristos /* Check if file is a win32 binary resource file, if so
60175fd0b74Schristos    skip past the null resource. Returns 0 if successful, -1 on
60275fd0b74Schristos    error.
60375fd0b74Schristos  */
60475fd0b74Schristos static void
skip_null_resource(windres_bfd * wrbfd,rc_uint_type * off,rc_uint_type omax)60575fd0b74Schristos skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
60675fd0b74Schristos {
60775fd0b74Schristos   res_hdr reshdr;
60875fd0b74Schristos   read_res_data_hdr (wrbfd, off, omax, &reshdr);
60975fd0b74Schristos   if (reshdr.data_size != 0)
61075fd0b74Schristos     goto skip_err;
61175fd0b74Schristos   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
61275fd0b74Schristos     || (reshdr.header_size != 0x20000000 && target_is_bigendian))
61375fd0b74Schristos     goto skip_err;
61475fd0b74Schristos 
61575fd0b74Schristos   /* Subtract size of HeaderSize. DataSize has to be zero. */
61675fd0b74Schristos   off[0] += 0x20 - BIN_RES_HDR_SIZE;
61775fd0b74Schristos   if (off[0] >= omax)
61875fd0b74Schristos     goto skip_err;
61975fd0b74Schristos 
62075fd0b74Schristos   return;
62175fd0b74Schristos 
62275fd0b74Schristos  skip_err:
62375fd0b74Schristos   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
62475fd0b74Schristos 	   filename);
62575fd0b74Schristos   xexit (1);
62675fd0b74Schristos }
62775fd0b74Schristos 
62875fd0b74Schristos /* Add a resource to resource directory */
62975fd0b74Schristos static void
res_add_resource(rc_res_resource * r,const rc_res_id * type,const rc_res_id * id,rc_uint_type language,int dupok)63075fd0b74Schristos res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
63175fd0b74Schristos 		  rc_uint_type language, int dupok)
63275fd0b74Schristos {
63375fd0b74Schristos   rc_res_id a[3];
63475fd0b74Schristos 
63575fd0b74Schristos   a[0] = *type;
63675fd0b74Schristos   a[1] = *id;
63775fd0b74Schristos   a[2].named = 0;
63875fd0b74Schristos   a[2].u.id = language;
63975fd0b74Schristos   res_append_resource (&resources, r, 3, a, dupok);
64075fd0b74Schristos }
64175fd0b74Schristos 
64275fd0b74Schristos /* Append a resource to resource directory.
64375fd0b74Schristos    This is just copied from define_resource
64475fd0b74Schristos    and modified to add an existing resource.
64575fd0b74Schristos  */
64675fd0b74Schristos static void
res_append_resource(rc_res_directory ** res_dirs,rc_res_resource * resource,int cids,const rc_res_id * ids,int dupok)64775fd0b74Schristos res_append_resource (rc_res_directory **res_dirs, rc_res_resource *resource,
64875fd0b74Schristos 		     int cids, const rc_res_id *ids, int dupok)
64975fd0b74Schristos {
65075fd0b74Schristos   rc_res_entry *re = NULL;
65175fd0b74Schristos   int i;
65275fd0b74Schristos 
65375fd0b74Schristos   assert (cids > 0);
65475fd0b74Schristos   for (i = 0; i < cids; i++)
65575fd0b74Schristos     {
65675fd0b74Schristos       rc_res_entry **pp;
65775fd0b74Schristos 
65875fd0b74Schristos       if (*res_dirs == NULL)
65975fd0b74Schristos 	{
66075fd0b74Schristos 	  *res_dirs = ((rc_res_directory *)
66175fd0b74Schristos 			res_alloc (sizeof (rc_res_directory)));
66275fd0b74Schristos 
66375fd0b74Schristos 	  (*res_dirs)->characteristics = 0;
66475fd0b74Schristos 	  /* Using a real timestamp only serves to create non-deterministic
66575fd0b74Schristos 	     results.  Use zero instead.  */
66675fd0b74Schristos 	  (*res_dirs)->time = 0;
66775fd0b74Schristos 	  (*res_dirs)->major = 0;
66875fd0b74Schristos 	  (*res_dirs)->minor = 0;
66975fd0b74Schristos 	  (*res_dirs)->entries = NULL;
67075fd0b74Schristos 	}
67175fd0b74Schristos 
67275fd0b74Schristos       for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next)
67375fd0b74Schristos 	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
67475fd0b74Schristos 	  break;
67575fd0b74Schristos 
67675fd0b74Schristos       if (*pp != NULL)
67775fd0b74Schristos 	re = *pp;
67875fd0b74Schristos       else
67975fd0b74Schristos 	{
68075fd0b74Schristos 	  re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
68175fd0b74Schristos 	  re->next = NULL;
68275fd0b74Schristos 	  re->id = ids[i];
68375fd0b74Schristos 	  if ((i + 1) < cids)
68475fd0b74Schristos 	    {
68575fd0b74Schristos 	      re->subdir = 1;
68675fd0b74Schristos 	      re->u.dir = NULL;
68775fd0b74Schristos 	    }
68875fd0b74Schristos 	  else
68975fd0b74Schristos 	    {
69075fd0b74Schristos 	      re->subdir = 0;
69175fd0b74Schristos 	      re->u.res = NULL;
69275fd0b74Schristos 	    }
69375fd0b74Schristos 
69475fd0b74Schristos 	  *pp = re;
69575fd0b74Schristos 	}
69675fd0b74Schristos 
69775fd0b74Schristos       if ((i + 1) < cids)
69875fd0b74Schristos 	{
69975fd0b74Schristos 	  if (! re->subdir)
70075fd0b74Schristos 	    {
70175fd0b74Schristos 	      fprintf (stderr, "%s: ", program_name);
70275fd0b74Schristos 	      res_ids_print (stderr, i, ids);
70375fd0b74Schristos 	      fprintf (stderr, ": expected to be a directory\n");
70475fd0b74Schristos 	      xexit (1);
70575fd0b74Schristos 	    }
70675fd0b74Schristos 
70775fd0b74Schristos 	  res_dirs = &re->u.dir;
70875fd0b74Schristos 	}
70975fd0b74Schristos     }
71075fd0b74Schristos 
71175fd0b74Schristos   if (re->subdir)
71275fd0b74Schristos     {
71375fd0b74Schristos       fprintf (stderr, "%s: ", program_name);
71475fd0b74Schristos       res_ids_print (stderr, cids, ids);
71575fd0b74Schristos       fprintf (stderr, ": expected to be a leaf\n");
71675fd0b74Schristos       xexit (1);
71775fd0b74Schristos     }
71875fd0b74Schristos 
71975fd0b74Schristos   if (re->u.res != NULL)
72075fd0b74Schristos     {
72175fd0b74Schristos       if (dupok)
72275fd0b74Schristos 	return;
72375fd0b74Schristos 
72475fd0b74Schristos       fprintf (stderr, "%s: warning: ", program_name);
72575fd0b74Schristos       res_ids_print (stderr, cids, ids);
72675fd0b74Schristos       fprintf (stderr, ": duplicate value\n");
72775fd0b74Schristos     }
72875fd0b74Schristos 
72975fd0b74Schristos   re->u.res = resource;
73075fd0b74Schristos }
731