1 /* resres.c: read_res_file and write_res_file implementation for windres. 2 Copyright 1998, 1999, 2001, 2002 Free Software Foundation, Inc. 3 Written by Anders Norlander <anorland@hem2.passagen.se>. 4 5 This file is part of GNU Binutils. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 20 02110-1301, USA. */ 21 22 /* FIXME: This file does not work correctly in a cross configuration. 23 It assumes that it can use fread and fwrite to read and write 24 integers. It does no swapping. */ 25 26 #include "bfd.h" 27 #include "bucomm.h" 28 #include "libiberty.h" 29 #include "windres.h" 30 31 #include <assert.h> 32 #include <time.h> 33 34 struct res_hdr 35 { 36 unsigned long data_size; 37 unsigned long header_size; 38 }; 39 40 static void write_res_directory 41 PARAMS ((const struct res_directory *, 42 const struct res_id *, const struct res_id *, 43 int *, int)); 44 static void write_res_resource 45 PARAMS ((const struct res_id *, const struct res_id *, 46 const struct res_resource *, int *)); 47 static void write_res_bin 48 PARAMS ((const struct res_resource *, const struct res_id *, 49 const struct res_id *, const struct res_res_info *)); 50 51 static void write_res_id PARAMS ((const struct res_id *)); 52 static void write_res_info PARAMS ((const struct res_res_info *)); 53 static void write_res_data PARAMS ((const void *, size_t, int)); 54 static void write_res_header 55 PARAMS ((unsigned long, const struct res_id *, const struct res_id *, 56 const struct res_res_info *)); 57 58 static int read_resource_entry PARAMS ((void)); 59 static void read_res_data PARAMS ((void *, size_t, int)); 60 static void read_res_id PARAMS ((struct res_id *)); 61 static unichar *read_unistring PARAMS ((int *)); 62 static void skip_null_resource PARAMS ((void)); 63 64 static unsigned long get_id_size PARAMS ((const struct res_id *)); 65 static void res_align_file PARAMS ((void)); 66 67 static void 68 res_add_resource 69 PARAMS ((struct res_resource *, const struct res_id *, 70 const struct res_id *, int, int)); 71 72 void 73 res_append_resource 74 PARAMS ((struct res_directory **, struct res_resource *, 75 int, const struct res_id *, int)); 76 77 static struct res_directory *resources = NULL; 78 79 static FILE *fres; 80 static const char *filename; 81 82 extern char *program_name; 83 84 /* Read resource file */ 85 struct res_directory * 86 read_res_file (fn) 87 const char *fn; 88 { 89 filename = fn; 90 fres = fopen (filename, "rb"); 91 if (fres == NULL) 92 fatal ("can't open `%s' for output: %s", filename, strerror (errno)); 93 94 skip_null_resource (); 95 96 while (read_resource_entry ()) 97 ; 98 99 fclose (fres); 100 101 return resources; 102 } 103 104 /* Write resource file */ 105 void 106 write_res_file (fn, resdir) 107 const char *fn; 108 const struct res_directory *resdir; 109 { 110 int language; 111 static const unsigned char sign[] = 112 {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 113 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 116 long fpos; 117 118 filename = fn; 119 120 fres = fopen (filename, "wb"); 121 if (fres == NULL) 122 fatal ("can't open `%s' for output: %s", filename, strerror (errno)); 123 124 /* Write 32 bit resource signature */ 125 write_res_data (sign, sizeof (sign), 1); 126 127 /* write resources */ 128 129 language = -1; 130 write_res_directory (resdir, (const struct res_id *) NULL, 131 (const struct res_id *) NULL, &language, 1); 132 133 /* end file on DWORD boundary */ 134 fpos = ftell (fres); 135 if (fpos % 4) 136 write_res_data (sign, fpos % 4, 1); 137 138 fclose (fres); 139 } 140 141 /* Read a resource entry, returns 0 when all resources are read */ 142 static int 143 read_resource_entry (void) 144 { 145 struct res_id type; 146 struct res_id name; 147 struct res_res_info resinfo; 148 struct res_hdr reshdr; 149 long version; 150 void *buff; 151 152 struct res_resource *r; 153 154 res_align_file (); 155 156 /* Read header */ 157 if (fread (&reshdr, sizeof (reshdr), 1, fres) != 1) 158 return 0; 159 160 /* read resource type */ 161 read_res_id (&type); 162 /* read resource id */ 163 read_res_id (&name); 164 165 res_align_file (); 166 167 /* Read additional resource header */ 168 read_res_data (&resinfo.version, sizeof (resinfo.version), 1); 169 read_res_data (&resinfo.memflags, sizeof (resinfo.memflags), 1); 170 read_res_data (&resinfo.language, sizeof (resinfo.language), 1); 171 read_res_data (&version, sizeof (version), 1); 172 read_res_data (&resinfo.characteristics, sizeof (resinfo.characteristics), 1); 173 174 res_align_file (); 175 176 /* Allocate buffer for data */ 177 buff = res_alloc (reshdr.data_size); 178 /* Read data */ 179 read_res_data (buff, reshdr.data_size, 1); 180 /* Convert binary data to resource */ 181 r = bin_to_res (type, buff, reshdr.data_size, 0); 182 r->res_info = resinfo; 183 /* Add resource to resource directory */ 184 res_add_resource (r, &type, &name, resinfo.language, 0); 185 186 return 1; 187 } 188 189 /* write resource directory to binary resource file */ 190 static void 191 write_res_directory (rd, type, name, language, level) 192 const struct res_directory *rd; 193 const struct res_id *type; 194 const struct res_id *name; 195 int *language; 196 int level; 197 { 198 const struct res_entry *re; 199 200 for (re = rd->entries; re != NULL; re = re->next) 201 { 202 switch (level) 203 { 204 case 1: 205 /* If we're at level 1, the key of this resource is the 206 type. This normally duplicates the information we have 207 stored with the resource itself, but we need to remember 208 the type if this is a user define resource type. */ 209 type = &re->id; 210 break; 211 212 case 2: 213 /* If we're at level 2, the key of this resource is the name 214 we are going to use in the rc printout. */ 215 name = &re->id; 216 break; 217 218 case 3: 219 /* If we're at level 3, then this key represents a language. 220 Use it to update the current language. */ 221 if (!re->id.named 222 && re->id.u.id != (unsigned long) *language 223 && (re->id.u.id & 0xffff) == re->id.u.id) 224 { 225 *language = re->id.u.id; 226 } 227 break; 228 229 default: 230 break; 231 } 232 233 if (re->subdir) 234 write_res_directory (re->u.dir, type, name, language, level + 1); 235 else 236 { 237 if (level == 3) 238 { 239 /* This is the normal case: the three levels are 240 TYPE/NAME/LANGUAGE. NAME will have been set at level 241 2, and represents the name to use. We probably just 242 set LANGUAGE, and it will probably match what the 243 resource itself records if anything. */ 244 write_res_resource (type, name, re->u.res, language); 245 } 246 else 247 { 248 fprintf (stderr, "// Resource at unexpected level %d\n", level); 249 write_res_resource (type, (struct res_id *) NULL, re->u.res, 250 language); 251 } 252 } 253 } 254 255 } 256 257 static void 258 write_res_resource (type, name, res, language) 259 const struct res_id *type; 260 const struct res_id *name; 261 const struct res_resource *res; 262 int *language ATTRIBUTE_UNUSED; 263 { 264 int rt; 265 266 switch (res->type) 267 { 268 default: 269 abort (); 270 271 case RES_TYPE_ACCELERATOR: 272 rt = RT_ACCELERATOR; 273 break; 274 275 case RES_TYPE_BITMAP: 276 rt = RT_BITMAP; 277 break; 278 279 case RES_TYPE_CURSOR: 280 rt = RT_CURSOR; 281 break; 282 283 case RES_TYPE_GROUP_CURSOR: 284 rt = RT_GROUP_CURSOR; 285 break; 286 287 case RES_TYPE_DIALOG: 288 rt = RT_DIALOG; 289 break; 290 291 case RES_TYPE_FONT: 292 rt = RT_FONT; 293 break; 294 295 case RES_TYPE_FONTDIR: 296 rt = RT_FONTDIR; 297 break; 298 299 case RES_TYPE_ICON: 300 rt = RT_ICON; 301 break; 302 303 case RES_TYPE_GROUP_ICON: 304 rt = RT_GROUP_ICON; 305 break; 306 307 case RES_TYPE_MENU: 308 rt = RT_MENU; 309 break; 310 311 case RES_TYPE_MESSAGETABLE: 312 rt = RT_MESSAGETABLE; 313 break; 314 315 case RES_TYPE_RCDATA: 316 rt = RT_RCDATA; 317 break; 318 319 case RES_TYPE_STRINGTABLE: 320 rt = RT_STRING; 321 break; 322 323 case RES_TYPE_USERDATA: 324 rt = 0; 325 break; 326 327 case RES_TYPE_VERSIONINFO: 328 rt = RT_VERSION; 329 break; 330 } 331 332 if (rt != 0 333 && type != NULL 334 && (type->named || type->u.id != (unsigned long) rt)) 335 { 336 fprintf (stderr, "// Unexpected resource type mismatch: "); 337 res_id_print (stderr, *type, 1); 338 fprintf (stderr, " != %d", rt); 339 abort (); 340 } 341 342 write_res_bin (res, type, name, &res->res_info); 343 return; 344 } 345 346 /* Write a resource in binary resource format */ 347 static void 348 write_res_bin (res, type, name, resinfo) 349 const struct res_resource *res; 350 const struct res_id *type; 351 const struct res_id *name; 352 const struct res_res_info *resinfo; 353 { 354 unsigned long datasize = 0; 355 const struct bindata *bin_rep, *data; 356 357 bin_rep = res_to_bin (res, 0); 358 for (data = bin_rep; data != NULL; data = data->next) 359 datasize += data->length; 360 361 write_res_header (datasize, type, name, resinfo); 362 363 for (data = bin_rep; data != NULL; data = data->next) 364 write_res_data (data->data, data->length, 1); 365 } 366 367 /* Get number of bytes needed to store an id in binary format */ 368 static unsigned long 369 get_id_size (id) 370 const struct res_id *id; 371 { 372 if (id->named) 373 return sizeof (unichar) * (id->u.n.length + 1); 374 else 375 return sizeof (unichar) * 2; 376 } 377 378 /* Write a resource header */ 379 static void 380 write_res_header (datasize, type, name, resinfo) 381 unsigned long datasize; 382 const struct res_id *type; 383 const struct res_id *name; 384 const struct res_res_info *resinfo; 385 { 386 struct res_hdr reshdr; 387 reshdr.data_size = datasize; 388 reshdr.header_size = 24 + get_id_size (type) + get_id_size (name); 389 390 reshdr.header_size = (reshdr.header_size + 3) & ~3; 391 392 res_align_file (); 393 write_res_data (&reshdr, sizeof (reshdr), 1); 394 write_res_id (type); 395 write_res_id (name); 396 397 res_align_file (); 398 399 write_res_info (resinfo); 400 res_align_file (); 401 } 402 403 404 /* Write data to file, abort on failure */ 405 static void 406 write_res_data (data, size, count) 407 const void *data; 408 size_t size; 409 int count; 410 { 411 if ((size_t) fwrite (data, size, count, fres) != (size_t) count) 412 fatal ("%s: could not write to file", filename); 413 } 414 415 /* Read data from file, abort on failure */ 416 static void 417 read_res_data (data, size, count) 418 void *data; 419 size_t size; 420 int count; 421 { 422 if (fread (data, size, count, fres) != (size_t) count) 423 fatal ("%s: unexpected end of file", filename); 424 } 425 426 /* Write a resource id */ 427 static void 428 write_res_id (id) 429 const struct res_id *id; 430 { 431 if (id->named) 432 { 433 unsigned long len = id->u.n.length; 434 unichar null_term = 0; 435 write_res_data (id->u.n.name, len * sizeof (unichar), 1); 436 write_res_data (&null_term, sizeof (null_term), 1); 437 } 438 else 439 { 440 unsigned short i = 0xFFFF; 441 write_res_data (&i, sizeof (i), 1); 442 i = id->u.id; 443 write_res_data (&i, sizeof (i), 1); 444 } 445 } 446 447 /* Write resource info */ 448 static void 449 write_res_info (info) 450 const struct res_res_info *info; 451 { 452 write_res_data (&info->version, sizeof (info->version), 1); 453 write_res_data (&info->memflags, sizeof (info->memflags), 1); 454 write_res_data (&info->language, sizeof (info->language), 1); 455 write_res_data (&info->version, sizeof (info->version), 1); 456 write_res_data (&info->characteristics, sizeof (info->characteristics), 1); 457 } 458 459 /* read a resource identifier */ 460 void 461 read_res_id (id) 462 struct res_id *id; 463 { 464 unsigned short ord; 465 unichar *id_s = NULL; 466 int len; 467 468 read_res_data (&ord, sizeof (ord), 1); 469 if (ord == 0xFFFF) /* an ordinal id */ 470 { 471 read_res_data (&ord, sizeof (ord), 1); 472 id->named = 0; 473 id->u.id = ord; 474 } 475 else 476 /* named id */ 477 { 478 if (fseek (fres, -sizeof (ord), SEEK_CUR) != 0) 479 fatal ("%s: %s: could not seek in file", program_name, filename); 480 id_s = read_unistring (&len); 481 id->named = 1; 482 id->u.n.length = len; 483 id->u.n.name = id_s; 484 } 485 } 486 487 /* Read a null terminated UNICODE string */ 488 static unichar * 489 read_unistring (len) 490 int *len; 491 { 492 unichar *s; 493 unichar c; 494 unichar *p; 495 int l; 496 497 *len = 0; 498 l = 0; 499 500 /* there are hardly any names longer than 256 characters */ 501 p = s = (unichar *) xmalloc (sizeof (unichar) * 256); 502 do 503 { 504 read_res_data (&c, sizeof (c), 1); 505 *p++ = c; 506 if (c != 0) 507 l++; 508 } 509 while (c != 0); 510 *len = l; 511 return s; 512 } 513 514 /* align file on DWORD boundary */ 515 static void 516 res_align_file (void) 517 { 518 int pos = ftell (fres); 519 int skip = ((pos + 3) & ~3) - pos; 520 if (fseek (fres, skip, SEEK_CUR) != 0) 521 fatal ("%s: %s: unable to align file", program_name, filename); 522 } 523 524 /* Check if file is a win32 binary resource file, if so 525 skip past the null resource. Returns 0 if successful, -1 on 526 error. 527 */ 528 static void 529 skip_null_resource (void) 530 { 531 struct res_hdr reshdr = 532 {0, 0}; 533 read_res_data (&reshdr, sizeof (reshdr), 1); 534 if ((reshdr.data_size != 0) || (reshdr.header_size != 0x20)) 535 goto skip_err; 536 537 /* Subtract size of HeaderSize and DataSize */ 538 if (fseek (fres, reshdr.header_size - 8, SEEK_CUR) != 0) 539 goto skip_err; 540 541 return; 542 543 skip_err: 544 fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name, 545 filename); 546 xexit (1); 547 } 548 549 /* Add a resource to resource directory */ 550 void 551 res_add_resource (r, type, id, language, dupok) 552 struct res_resource *r; 553 const struct res_id *type; 554 const struct res_id *id; 555 int language; 556 int dupok; 557 { 558 struct res_id a[3]; 559 560 a[0] = *type; 561 a[1] = *id; 562 a[2].named = 0; 563 a[2].u.id = language; 564 res_append_resource (&resources, r, 3, a, dupok); 565 } 566 567 /* Append a resource to resource directory. 568 This is just copied from define_resource 569 and modified to add an existing resource. 570 */ 571 void 572 res_append_resource (resources, resource, cids, ids, dupok) 573 struct res_directory **resources; 574 struct res_resource *resource; 575 int cids; 576 const struct res_id *ids; 577 int dupok; 578 { 579 struct res_entry *re = NULL; 580 int i; 581 582 assert (cids > 0); 583 for (i = 0; i < cids; i++) 584 { 585 struct res_entry **pp; 586 587 if (*resources == NULL) 588 { 589 static unsigned long timeval; 590 591 /* Use the same timestamp for every resource created in a 592 single run. */ 593 if (timeval == 0) 594 timeval = time (NULL); 595 596 *resources = ((struct res_directory *) 597 res_alloc (sizeof **resources)); 598 (*resources)->characteristics = 0; 599 (*resources)->time = timeval; 600 (*resources)->major = 0; 601 (*resources)->minor = 0; 602 (*resources)->entries = NULL; 603 } 604 605 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next) 606 if (res_id_cmp ((*pp)->id, ids[i]) == 0) 607 break; 608 609 if (*pp != NULL) 610 re = *pp; 611 else 612 { 613 re = (struct res_entry *) res_alloc (sizeof *re); 614 re->next = NULL; 615 re->id = ids[i]; 616 if ((i + 1) < cids) 617 { 618 re->subdir = 1; 619 re->u.dir = NULL; 620 } 621 else 622 { 623 re->subdir = 0; 624 re->u.res = NULL; 625 } 626 627 *pp = re; 628 } 629 630 if ((i + 1) < cids) 631 { 632 if (!re->subdir) 633 { 634 fprintf (stderr, "%s: ", program_name); 635 res_ids_print (stderr, i, ids); 636 fprintf (stderr, ": expected to be a directory\n"); 637 xexit (1); 638 } 639 640 resources = &re->u.dir; 641 } 642 } 643 644 if (re->subdir) 645 { 646 fprintf (stderr, "%s: ", program_name); 647 res_ids_print (stderr, cids, ids); 648 fprintf (stderr, ": expected to be a leaf\n"); 649 xexit (1); 650 } 651 652 if (re->u.res != NULL) 653 { 654 if (dupok) 655 return; 656 657 fprintf (stderr, "%s: warning: ", program_name); 658 res_ids_print (stderr, cids, ids); 659 fprintf (stderr, ": duplicate value\n"); 660 } 661 662 re->u.res = resource; 663 } 664