| /* resbin.c -- manipulate the Windows binary resource format. |
| Copyright 1997, 1998 Free Software Foundation, Inc. |
| Written by Ian Lance Taylor, Cygnus Support. |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. */ |
| |
| /* This file contains functions to convert between the binary resource |
| format and the internal structures that we want to use. The same |
| binary resource format is used in both res and COFF files. */ |
| |
| #include "bfd.h" |
| #include "bucomm.h" |
| #include "libiberty.h" |
| #include "windres.h" |
| |
| /* Macros to swap in values. */ |
| |
| #define get_16(be, s) ((be) ? bfd_getb16 (s) : bfd_getl16 (s)) |
| #define get_32(be, s) ((be) ? bfd_getb32 (s) : bfd_getl32 (s)) |
| |
| /* Local functions. */ |
| |
| static void toosmall PARAMS ((const char *)); |
| static unichar *get_unicode |
| PARAMS ((const unsigned char *, unsigned long, int, int *)); |
| static int get_resid |
| PARAMS ((struct res_id *, const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_generic |
| PARAMS ((enum res_type, const unsigned char *, unsigned long)); |
| static struct res_resource *bin_to_res_cursor |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_menu |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct menuitem *bin_to_res_menuitems |
| PARAMS ((const unsigned char *, unsigned long, int, int *)); |
| static struct menuitem *bin_to_res_menuexitems |
| PARAMS ((const unsigned char *, unsigned long, int, int *)); |
| static struct res_resource *bin_to_res_dialog |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_string |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_fontdir |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_accelerators |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_rcdata |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_group_cursor |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_group_icon |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_version |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| static struct res_resource *bin_to_res_userdata |
| PARAMS ((const unsigned char *, unsigned long, int)); |
| |
| /* Given a resource type ID, a pointer to data, a length, return a |
| res_resource structure which represents that resource. The caller |
| is responsible for initializing the res_info and coff_info fields |
| of the returned structure. */ |
| |
| struct res_resource * |
| bin_to_res (type, data, length, big_endian) |
| struct res_id type; |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| if (type.named) |
| return bin_to_res_userdata (data, length, big_endian); |
| else |
| { |
| switch (type.u.id) |
| { |
| default: |
| return bin_to_res_userdata (data, length, big_endian); |
| case RT_CURSOR: |
| return bin_to_res_cursor (data, length, big_endian); |
| case RT_BITMAP: |
| return bin_to_res_generic (RES_TYPE_BITMAP, data, length); |
| case RT_ICON: |
| return bin_to_res_generic (RES_TYPE_ICON, data, length); |
| case RT_MENU: |
| return bin_to_res_menu (data, length, big_endian); |
| case RT_DIALOG: |
| return bin_to_res_dialog (data, length, big_endian); |
| case RT_STRING: |
| return bin_to_res_string (data, length, big_endian); |
| case RT_FONTDIR: |
| return bin_to_res_fontdir (data, length, big_endian); |
| case RT_FONT: |
| return bin_to_res_generic (RES_TYPE_FONT, data, length); |
| case RT_ACCELERATOR: |
| return bin_to_res_accelerators (data, length, big_endian); |
| case RT_RCDATA: |
| return bin_to_res_rcdata (data, length, big_endian); |
| case RT_MESSAGETABLE: |
| return bin_to_res_generic (RES_TYPE_MESSAGETABLE, data, length); |
| case RT_GROUP_CURSOR: |
| return bin_to_res_group_cursor (data, length, big_endian); |
| case RT_GROUP_ICON: |
| return bin_to_res_group_icon (data, length, big_endian); |
| case RT_VERSION: |
| return bin_to_res_version (data, length, big_endian); |
| } |
| } |
| } |
| |
| /* Give an error if the binary data is too small. */ |
| |
| static void |
| toosmall (msg) |
| const char *msg; |
| { |
| fatal (_("%s: not enough binary data"), msg); |
| } |
| |
| /* Swap in a NULL terminated unicode string. */ |
| |
| static unichar * |
| get_unicode (data, length, big_endian, retlen) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| int *retlen; |
| { |
| int c, i; |
| unichar *ret; |
| |
| c = 0; |
| while (1) |
| { |
| if (length < (unsigned long) c * 2 + 2) |
| toosmall (_("null terminated unicode string")); |
| if (get_16 (big_endian, data + c * 2) == 0) |
| break; |
| ++c; |
| } |
| |
| ret = (unichar *) res_alloc ((c + 1) * sizeof (unichar)); |
| |
| for (i = 0; i < c; i++) |
| ret[i] = get_16 (big_endian, data + i * 2); |
| ret[i] = 0; |
| |
| if (retlen != NULL) |
| *retlen = c; |
| |
| return ret; |
| } |
| |
| /* Get a resource identifier. This returns the number of bytes used. */ |
| |
| static int |
| get_resid (id, data, length, big_endian) |
| struct res_id *id; |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| int first; |
| |
| if (length < 2) |
| toosmall (_("resource ID")); |
| |
| first = get_16 (big_endian, data); |
| if (first == 0xffff) |
| { |
| if (length < 4) |
| toosmall (_("resource ID")); |
| id->named = 0; |
| id->u.id = get_16 (big_endian, data + 2); |
| return 4; |
| } |
| else |
| { |
| id->named = 1; |
| id->u.n.name = get_unicode (data, length, big_endian, &id->u.n.length); |
| return id->u.n.length * 2 + 2; |
| } |
| } |
| |
| /* Convert a resource which just stores uninterpreted data from |
| binary. */ |
| |
| struct res_resource * |
| bin_to_res_generic (type, data, length) |
| enum res_type type; |
| const unsigned char *data; |
| unsigned long length; |
| { |
| struct res_resource *r; |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = type; |
| r->u.data.data = data; |
| r->u.data.length = length; |
| |
| return r; |
| } |
| |
| /* Convert a cursor resource from binary. */ |
| |
| struct res_resource * |
| bin_to_res_cursor (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| struct cursor *c; |
| struct res_resource *r; |
| |
| if (length < 4) |
| toosmall (_("cursor")); |
| |
| c = (struct cursor *) res_alloc (sizeof *c); |
| c->xhotspot = get_16 (big_endian, data); |
| c->yhotspot = get_16 (big_endian, data + 2); |
| c->length = length - 4; |
| c->data = data + 4; |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_CURSOR; |
| r->u.cursor = c; |
| |
| return r; |
| } |
| |
| /* Convert a menu resource from binary. */ |
| |
| struct res_resource * |
| bin_to_res_menu (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| struct res_resource *r; |
| struct menu *m; |
| int version, read; |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_MENU; |
| |
| m = (struct menu *) res_alloc (sizeof *m); |
| r->u.menu = m; |
| |
| if (length < 2) |
| toosmall (_("menu header")); |
| |
| version = get_16 (big_endian, data); |
| |
| if (version == 0) |
| { |
| if (length < 4) |
| toosmall (_("menu header")); |
| m->help = 0; |
| m->items = bin_to_res_menuitems (data + 4, length - 4, big_endian, |
| &read); |
| } |
| else if (version == 1) |
| { |
| unsigned int offset; |
| |
| if (length < 8) |
| toosmall (_("menuex header")); |
| m->help = get_32 (big_endian, data + 4); |
| offset = get_16 (big_endian, data + 2); |
| if (offset + 4 >= length) |
| toosmall (_("menuex offset")); |
| m->items = bin_to_res_menuexitems (data + 4 + offset, |
| length - (4 + offset), |
| big_endian, |
| &read); |
| } |
| else |
| fatal (_("unsupported menu version %d"), version); |
| |
| return r; |
| } |
| |
| /* Convert menu items from binary. */ |
| |
| static struct menuitem * |
| bin_to_res_menuitems (data, length, big_endian, read) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| int *read; |
| { |
| struct menuitem *first, **pp; |
| |
| first = NULL; |
| pp = &first; |
| |
| *read = 0; |
| |
| while (length > 0) |
| { |
| int flags, slen, itemlen; |
| unsigned int stroff; |
| struct menuitem *mi; |
| |
| if (length < 4) |
| toosmall (_("menuitem header")); |
| |
| mi = (struct menuitem *) res_alloc (sizeof *mi); |
| mi->state = 0; |
| mi->help = 0; |
| |
| flags = get_16 (big_endian, data); |
| mi->type = flags &~ (MENUITEM_POPUP | MENUITEM_ENDMENU); |
| |
| if ((flags & MENUITEM_POPUP) == 0) |
| stroff = 4; |
| else |
| stroff = 2; |
| |
| if (length < stroff + 2) |
| toosmall (_("menuitem header")); |
| |
| if (get_16 (big_endian, data + stroff) == 0) |
| { |
| slen = 0; |
| mi->text = NULL; |
| } |
| else |
| mi->text = get_unicode (data + stroff, length - stroff, big_endian, |
| &slen); |
| |
| itemlen = stroff + slen * 2 + 2; |
| |
| if ((flags & MENUITEM_POPUP) == 0) |
| { |
| mi->popup = NULL; |
| mi->id = get_16 (big_endian, data + 2); |
| } |
| else |
| { |
| int subread; |
| |
| mi->id = 0; |
| mi->popup = bin_to_res_menuitems (data + itemlen, length - itemlen, |
| big_endian, &subread); |
| itemlen += subread; |
| } |
| |
| mi->next = NULL; |
| *pp = mi; |
| pp = &mi->next; |
| |
| data += itemlen; |
| length -= itemlen; |
| *read += itemlen; |
| |
| if ((flags & MENUITEM_ENDMENU) != 0) |
| return first; |
| } |
| |
| return first; |
| } |
| |
| /* Convert menuex items from binary. */ |
| |
| static struct menuitem * |
| bin_to_res_menuexitems (data, length, big_endian, read) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| int *read; |
| { |
| struct menuitem *first, **pp; |
| |
| first = NULL; |
| pp = &first; |
| |
| *read = 0; |
| |
| while (length > 0) |
| { |
| int flags, slen; |
| unsigned int itemlen; |
| struct menuitem *mi; |
| |
| if (length < 14) |
| toosmall (_("menuitem header")); |
| |
| mi = (struct menuitem *) res_alloc (sizeof *mi); |
| mi->type = get_32 (big_endian, data); |
| mi->state = get_32 (big_endian, data + 4); |
| mi->id = get_16 (big_endian, data + 8); |
| |
| flags = get_16 (big_endian, data + 10); |
| |
| if (get_16 (big_endian, data + 12) == 0) |
| { |
| slen = 0; |
| mi->text = NULL; |
| } |
| else |
| mi->text = get_unicode (data + 12, length - 12, big_endian, &slen); |
| |
| itemlen = 12 + slen * 2 + 2; |
| itemlen = (itemlen + 3) &~ 3; |
| |
| if ((flags & 1) == 0) |
| { |
| mi->popup = NULL; |
| mi->help = 0; |
| } |
| else |
| { |
| int subread; |
| |
| if (length < itemlen + 4) |
| toosmall (_("menuitem")); |
| mi->help = get_32 (big_endian, data + itemlen); |
| itemlen += 4; |
| |
| mi->popup = bin_to_res_menuexitems (data + itemlen, |
| length - itemlen, |
| big_endian, &subread); |
| itemlen += subread; |
| } |
| |
| mi->next = NULL; |
| *pp = mi; |
| pp = &mi->next; |
| |
| data += itemlen; |
| length -= itemlen; |
| *read += itemlen; |
| |
| if ((flags & 0x80) != 0) |
| return first; |
| } |
| |
| return first; |
| } |
| |
| /* Convert a dialog resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_dialog (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| int version; |
| struct dialog *d; |
| int c, sublen, i; |
| unsigned int off; |
| struct dialog_control **pp; |
| struct res_resource *r; |
| |
| if (length < 18) |
| toosmall (_("dialog header")); |
| |
| d = (struct dialog *) res_alloc (sizeof *d); |
| |
| version = get_16 (big_endian, data); |
| if (version != 1) |
| { |
| d->ex = NULL; |
| d->style = get_32 (big_endian, data); |
| d->exstyle = get_32 (big_endian, data + 4); |
| off = 8; |
| } |
| else |
| { |
| int signature; |
| |
| signature = get_16 (big_endian, data + 2); |
| if (signature != 0xffff) |
| fatal (_("unexpected dialog signature %d"), signature); |
| |
| d->ex = (struct dialog_ex *) res_alloc (sizeof (struct dialog_ex)); |
| d->ex->help = get_32 (big_endian, data + 4); |
| d->exstyle = get_32 (big_endian, data + 8); |
| d->style = get_32 (big_endian, data + 12); |
| off = 16; |
| } |
| |
| if (length < off + 10) |
| toosmall (_("dialog header")); |
| |
| c = get_16 (big_endian, data + off); |
| d->x = get_16 (big_endian, data + off + 2); |
| d->y = get_16 (big_endian, data + off + 4); |
| d->width = get_16 (big_endian, data + off + 6); |
| d->height = get_16 (big_endian, data + off + 8); |
| |
| off += 10; |
| |
| sublen = get_resid (&d->menu, data + off, length - off, big_endian); |
| off += sublen; |
| |
| sublen = get_resid (&d->class, data + off, length - off, big_endian); |
| off += sublen; |
| |
| d->caption = get_unicode (data + off, length - off, big_endian, &sublen); |
| off += sublen * 2 + 2; |
| |
| if ((d->style & DS_SETFONT) == 0) |
| { |
| d->pointsize = 0; |
| d->font = NULL; |
| if (d->ex != NULL) |
| { |
| d->ex->weight = 0; |
| d->ex->italic = 0; |
| } |
| } |
| else |
| { |
| if (length < off + 2) |
| toosmall (_("dialog font point size")); |
| |
| d->pointsize = get_16 (big_endian, data + off); |
| off += 2; |
| |
| if (d->ex != NULL) |
| { |
| if (length < off + 4) |
| toosmall (_("dialogex font information")); |
| d->ex->weight = get_16 (big_endian, data + off); |
| d->ex->italic = get_16 (big_endian, data + off + 2); |
| off += 4; |
| } |
| |
| d->font = get_unicode (data + off, length - off, big_endian, &sublen); |
| off += sublen * 2 + 2; |
| } |
| |
| d->controls = NULL; |
| pp = &d->controls; |
| |
| for (i = 0; i < c; i++) |
| { |
| struct dialog_control *dc; |
| int datalen; |
| |
| off = (off + 3) &~ 3; |
| |
| dc = (struct dialog_control *) res_alloc (sizeof *dc); |
| |
| if (d->ex == NULL) |
| { |
| if (length < off + 8) |
| toosmall (_("dialog control")); |
| |
| dc->style = get_32 (big_endian, data + off); |
| dc->exstyle = get_32 (big_endian, data + off + 4); |
| dc->help = 0; |
| off += 8; |
| } |
| else |
| { |
| if (length < off + 12) |
| toosmall (_("dialogex control")); |
| dc->help = get_32 (big_endian, data + off); |
| dc->exstyle = get_32 (big_endian, data + off + 4); |
| dc->style = get_32 (big_endian, data + off + 8); |
| off += 12; |
| } |
| |
| if (length < off + 10) |
| toosmall (_("dialog control")); |
| |
| dc->x = get_16 (big_endian, data + off); |
| dc->y = get_16 (big_endian, data + off + 2); |
| dc->width = get_16 (big_endian, data + off + 4); |
| dc->height = get_16 (big_endian, data + off + 6); |
| |
| if (d->ex != NULL) |
| dc->id = get_32 (big_endian, data + off + 8); |
| else |
| dc->id = get_16 (big_endian, data + off + 8); |
| |
| off += 10 + (d->ex != NULL ? 2 : 0); |
| |
| sublen = get_resid (&dc->class, data + off, length - off, big_endian); |
| off += sublen; |
| |
| sublen = get_resid (&dc->text, data + off, length - off, big_endian); |
| off += sublen; |
| |
| if (length < off + 2) |
| toosmall (_("dialog control end")); |
| |
| datalen = get_16 (big_endian, data + off); |
| off += 2; |
| |
| if (datalen == 0) |
| dc->data = NULL; |
| else |
| { |
| off = (off + 3) &~ 3; |
| |
| if (length < off + datalen) |
| toosmall (_("dialog control data")); |
| |
| dc->data = ((struct rcdata_item *) |
| res_alloc (sizeof (struct rcdata_item))); |
| dc->data->next = NULL; |
| dc->data->type = RCDATA_BUFFER; |
| dc->data->u.buffer.length = datalen; |
| dc->data->u.buffer.data = data + off; |
| |
| off += datalen; |
| } |
| |
| dc->next = NULL; |
| *pp = dc; |
| pp = &dc->next; |
| } |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_DIALOG; |
| r->u.dialog = d; |
| |
| return r; |
| } |
| |
| /* Convert a stringtable resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_string (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| struct stringtable *st; |
| int i; |
| struct res_resource *r; |
| |
| st = (struct stringtable *) res_alloc (sizeof *st); |
| |
| for (i = 0; i < 16; i++) |
| { |
| unsigned int slen; |
| |
| if (length < 2) |
| toosmall (_("stringtable string length")); |
| slen = get_16 (big_endian, data); |
| st->strings[i].length = slen; |
| |
| if (slen > 0) |
| { |
| unichar *s; |
| unsigned int j; |
| |
| if (length < 2 + 2 * slen) |
| toosmall (_("stringtable string")); |
| |
| s = (unichar *) res_alloc (slen * sizeof (unichar)); |
| st->strings[i].string = s; |
| |
| for (j = 0; j < slen; j++) |
| s[j] = get_16 (big_endian, data + 2 + j * 2); |
| } |
| |
| data += 2 + 2 * slen; |
| length -= 2 + 2 * slen; |
| } |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_STRINGTABLE; |
| r->u.stringtable = st; |
| |
| return r; |
| } |
| |
| /* Convert a fontdir resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_fontdir (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| int c, i; |
| struct fontdir *first, **pp; |
| struct res_resource *r; |
| |
| if (length < 2) |
| toosmall (_("fontdir header")); |
| |
| c = get_16 (big_endian, data); |
| |
| first = NULL; |
| pp = &first; |
| |
| for (i = 0; i < c; i++) |
| { |
| struct fontdir *fd; |
| unsigned int off; |
| |
| if (length < 56) |
| toosmall (_("fontdir")); |
| |
| fd = (struct fontdir *) res_alloc (sizeof *fd); |
| fd->index = get_16 (big_endian, data); |
| |
| /* To work out the length of the fontdir data, we must get the |
| length of the device name and face name strings, even though |
| we don't store them in the fontdir structure. The |
| documentation says that these are NULL terminated char |
| strings, not Unicode strings. */ |
| |
| off = 56; |
| |
| while (off < length && data[off] != '\0') |
| ++off; |
| if (off >= length) |
| toosmall (_("fontdir device name")); |
| ++off; |
| |
| while (off < length && data[off] != '\0') |
| ++off; |
| if (off >= length) |
| toosmall (_("fontdir face name")); |
| ++off; |
| |
| fd->length = off; |
| fd->data = data; |
| |
| fd->next = NULL; |
| *pp = fd; |
| pp = &fd->next; |
| |
| /* The documentation does not indicate that any rounding is |
| required. */ |
| |
| data += off; |
| length -= off; |
| } |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_FONTDIR; |
| r->u.fontdir = first; |
| |
| return r; |
| } |
| |
| /* Convert an accelerators resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_accelerators (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| struct accelerator *first, **pp; |
| struct res_resource *r; |
| |
| first = NULL; |
| pp = &first; |
| |
| while (1) |
| { |
| struct accelerator *a; |
| |
| if (length < 8) |
| toosmall (_("accelerator")); |
| |
| a = (struct accelerator *) res_alloc (sizeof *a); |
| |
| a->flags = get_16 (big_endian, data); |
| a->key = get_16 (big_endian, data + 2); |
| a->id = get_16 (big_endian, data + 4); |
| |
| a->next = NULL; |
| *pp = a; |
| pp = &a->next; |
| |
| if ((a->flags & ACC_LAST) != 0) |
| break; |
| |
| data += 8; |
| length -= 8; |
| } |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_ACCELERATOR; |
| r->u.acc = first; |
| |
| return r; |
| } |
| |
| /* Convert an rcdata resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_rcdata (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| struct rcdata_item *ri; |
| struct res_resource *r; |
| |
| ri = (struct rcdata_item *) res_alloc (sizeof *ri); |
| |
| ri->next = NULL; |
| ri->type = RCDATA_BUFFER; |
| ri->u.buffer.length = length; |
| ri->u.buffer.data = data; |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_RCDATA; |
| r->u.rcdata = ri; |
| |
| return r; |
| } |
| |
| /* Convert a group cursor resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_group_cursor (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| int type, c, i; |
| struct group_cursor *first, **pp; |
| struct res_resource *r; |
| |
| if (length < 6) |
| toosmall (_("group cursor header")); |
| |
| type = get_16 (big_endian, data + 2); |
| if (type != 2) |
| fatal (_("unexpected group cursor type %d"), type); |
| |
| c = get_16 (big_endian, data + 4); |
| |
| data += 6; |
| length -= 6; |
| |
| first = NULL; |
| pp = &first; |
| |
| for (i = 0; i < c; i++) |
| { |
| struct group_cursor *gc; |
| |
| if (length < 14) |
| toosmall (_("group cursor")); |
| |
| gc = (struct group_cursor *) res_alloc (sizeof *gc); |
| |
| gc->width = get_16 (big_endian, data); |
| gc->height = get_16 (big_endian, data + 2); |
| gc->planes = get_16 (big_endian, data + 4); |
| gc->bits = get_16 (big_endian, data + 6); |
| gc->bytes = get_32 (big_endian, data + 8); |
| gc->index = get_16 (big_endian, data + 12); |
| |
| gc->next = NULL; |
| *pp = gc; |
| pp = &gc->next; |
| |
| data += 14; |
| length -= 14; |
| } |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_GROUP_CURSOR; |
| r->u.group_cursor = first; |
| |
| return r; |
| } |
| |
| /* Convert a group icon resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_group_icon (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| int type, c, i; |
| struct group_icon *first, **pp; |
| struct res_resource *r; |
| |
| if (length < 6) |
| toosmall (_("group icon header")); |
| |
| type = get_16 (big_endian, data + 2); |
| if (type != 1) |
| fatal (_("unexpected group icon type %d"), type); |
| |
| c = get_16 (big_endian, data + 4); |
| |
| data += 6; |
| length -= 6; |
| |
| first = NULL; |
| pp = &first; |
| |
| for (i = 0; i < c; i++) |
| { |
| struct group_icon *gi; |
| |
| if (length < 14) |
| toosmall (_("group icon")); |
| |
| gi = (struct group_icon *) res_alloc (sizeof *gi); |
| |
| gi->width = data[0]; |
| gi->height = data[1]; |
| gi->colors = data[2]; |
| gi->planes = get_16 (big_endian, data + 4); |
| gi->bits = get_16 (big_endian, data + 6); |
| gi->bytes = get_32 (big_endian, data + 8); |
| gi->index = get_16 (big_endian, data + 12); |
| |
| gi->next = NULL; |
| *pp = gi; |
| pp = &gi->next; |
| |
| data += 14; |
| length -= 14; |
| } |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_GROUP_ICON; |
| r->u.group_icon = first; |
| |
| return r; |
| } |
| |
| /* Extract data from a version header. If KEY is not NULL, then the |
| key must be KEY; otherwise, the key is returned in *PKEY. This |
| sets *LEN to the total length, *VALLEN to the value length, *TYPE |
| to the type, and *OFF to the offset to the children. */ |
| |
| static void |
| get_version_header (data, length, big_endian, key, pkey, len, vallen, type, |
| off) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| const char *key; |
| unichar **pkey; |
| int *len; |
| int *vallen; |
| int *type; |
| int *off; |
| { |
| if (length < 8) |
| toosmall (key); |
| |
| *len = get_16 (big_endian, data); |
| *vallen = get_16 (big_endian, data + 2); |
| *type = get_16 (big_endian, data + 4); |
| |
| *off = 6; |
| |
| length -= 6; |
| data += 6; |
| |
| if (key == NULL) |
| { |
| int sublen; |
| |
| *pkey = get_unicode (data, length, big_endian, &sublen); |
| *off += sublen * 2 + 2; |
| } |
| else |
| { |
| while (1) |
| { |
| if (length < 2) |
| toosmall (key); |
| if (get_16 (big_endian, data) != (unsigned char) *key) |
| fatal (_("unexpected version string")); |
| |
| *off += 2; |
| length -= 2; |
| data += 2; |
| |
| if (*key == '\0') |
| break; |
| |
| ++key; |
| } |
| } |
| |
| *off = (*off + 3) &~ 3; |
| } |
| |
| /* Convert a version resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_version (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| int verlen, vallen, type, off; |
| struct fixed_versioninfo *fi; |
| struct ver_info *first, **pp; |
| struct versioninfo *v; |
| struct res_resource *r; |
| |
| get_version_header (data, length, big_endian, "VS_VERSION_INFO", |
| (unichar *) NULL, &verlen, &vallen, &type, &off); |
| |
| if ((unsigned int) verlen != length) |
| fatal (_("version length %d does not match resource length %lu"), |
| verlen, length); |
| |
| if (type != 0) |
| fatal (_("unexpected version type %d"), type); |
| |
| data += off; |
| length -= off; |
| |
| if (vallen == 0) |
| fi = NULL; |
| else |
| { |
| unsigned long signature, fiv; |
| |
| if (vallen != 52) |
| fatal (_("unexpected fixed version information length %d"), vallen); |
| |
| if (length < 52) |
| toosmall (_("fixed version info")); |
| |
| signature = get_32 (big_endian, data); |
| if (signature != 0xfeef04bd) |
| fatal (_("unexpected fixed version signature %lu"), signature); |
| |
| fiv = get_32 (big_endian, data + 4); |
| if (fiv != 0 && fiv != 0x10000) |
| fatal (_("unexpected fixed version info version %lu"), fiv); |
| |
| fi = (struct fixed_versioninfo *) res_alloc (sizeof *fi); |
| |
| fi->file_version_ms = get_32 (big_endian, data + 8); |
| fi->file_version_ls = get_32 (big_endian, data + 12); |
| fi->product_version_ms = get_32 (big_endian, data + 16); |
| fi->product_version_ls = get_32 (big_endian, data + 20); |
| fi->file_flags_mask = get_32 (big_endian, data + 24); |
| fi->file_flags = get_32 (big_endian, data + 28); |
| fi->file_os = get_32 (big_endian, data + 32); |
| fi->file_type = get_32 (big_endian, data + 36); |
| fi->file_subtype = get_32 (big_endian, data + 40); |
| fi->file_date_ms = get_32 (big_endian, data + 44); |
| fi->file_date_ls = get_32 (big_endian, data + 48); |
| |
| data += 52; |
| length -= 52; |
| } |
| |
| first = NULL; |
| pp = &first; |
| |
| while (length > 0) |
| { |
| struct ver_info *vi; |
| int ch; |
| |
| if (length < 8) |
| toosmall (_("version var info")); |
| |
| vi = (struct ver_info *) res_alloc (sizeof *vi); |
| |
| ch = get_16 (big_endian, data + 6); |
| |
| if (ch == 'S') |
| { |
| struct ver_stringinfo **ppvs; |
| |
| vi->type = VERINFO_STRING; |
| |
| get_version_header (data, length, big_endian, "StringFileInfo", |
| (unichar *) NULL, &verlen, &vallen, &type, |
| &off); |
| |
| if (vallen != 0) |
| fatal (_("unexpected stringfileinfo value length %d"), vallen); |
| |
| data += off; |
| length -= off; |
| |
| get_version_header (data, length, big_endian, (const char *) NULL, |
| &vi->u.string.language, &verlen, &vallen, |
| &type, &off); |
| |
| if (vallen != 0) |
| fatal (_("unexpected version stringtable value length %d"), vallen); |
| |
| data += off; |
| length -= off; |
| verlen -= off; |
| |
| vi->u.string.strings = NULL; |
| ppvs = &vi->u.string.strings; |
| |
| /* It's convenient to round verlen to a 4 byte alignment, |
| since we round the subvariables in the loop. */ |
| verlen = (verlen + 3) &~ 3; |
| |
| while (verlen > 0) |
| { |
| struct ver_stringinfo *vs; |
| int subverlen, vslen, valoff; |
| |
| vs = (struct ver_stringinfo *) res_alloc (sizeof *vs); |
| |
| get_version_header (data, length, big_endian, |
| (const char *) NULL, &vs->key, &subverlen, |
| &vallen, &type, &off); |
| |
| subverlen = (subverlen + 3) &~ 3; |
| |
| data += off; |
| length -= off; |
| |
| vs->value = get_unicode (data, length, big_endian, &vslen); |
| valoff = vslen * 2 + 2; |
| valoff = (valoff + 3) &~ 3; |
| |
| if (off + valoff != subverlen) |
| fatal (_("unexpected version string length %d != %d + %d"), |
| subverlen, off, valoff); |
| |
| vs->next = NULL; |
| *ppvs = vs; |
| ppvs = &vs->next; |
| |
| data += valoff; |
| length -= valoff; |
| |
| if (verlen < subverlen) |
| fatal (_("unexpected version string length %d < %d"), |
| verlen, subverlen); |
| |
| verlen -= subverlen; |
| } |
| } |
| else if (ch == 'V') |
| { |
| struct ver_varinfo **ppvv; |
| |
| vi->type = VERINFO_VAR; |
| |
| get_version_header (data, length, big_endian, "VarFileInfo", |
| (unichar *) NULL, &verlen, &vallen, &type, |
| &off); |
| |
| if (vallen != 0) |
| fatal (_("unexpected varfileinfo value length %d"), vallen); |
| |
| data += off; |
| length -= off; |
| |
| get_version_header (data, length, big_endian, (const char *) NULL, |
| &vi->u.var.key, &verlen, &vallen, &type, &off); |
| |
| data += off; |
| length -= off; |
| |
| vi->u.var.var = NULL; |
| ppvv = &vi->u.var.var; |
| |
| while (vallen > 0) |
| { |
| struct ver_varinfo *vv; |
| |
| if (length < 4) |
| toosmall (_("version varfileinfo")); |
| |
| vv = (struct ver_varinfo *) res_alloc (sizeof *vv); |
| |
| vv->language = get_16 (big_endian, data); |
| vv->charset = get_16 (big_endian, data + 2); |
| |
| vv->next = NULL; |
| *ppvv = vv; |
| ppvv = &vv->next; |
| |
| data += 4; |
| length -= 4; |
| |
| if (vallen < 4) |
| fatal (_("unexpected version value length %d"), vallen); |
| |
| vallen -= 4; |
| } |
| } |
| else |
| fatal (_("unexpected version string")); |
| |
| vi->next = NULL; |
| *pp = vi; |
| pp = &vi->next; |
| } |
| |
| v = (struct versioninfo *) res_alloc (sizeof *v); |
| v->fixed = fi; |
| v->var = first; |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_VERSIONINFO; |
| r->u.versioninfo = v; |
| |
| return r; |
| } |
| |
| /* Convert an arbitrary user defined resource from binary. */ |
| |
| static struct res_resource * |
| bin_to_res_userdata (data, length, big_endian) |
| const unsigned char *data; |
| unsigned long length; |
| int big_endian; |
| { |
| struct rcdata_item *ri; |
| struct res_resource *r; |
| |
| ri = (struct rcdata_item *) res_alloc (sizeof *ri); |
| |
| ri->next = NULL; |
| ri->type = RCDATA_BUFFER; |
| ri->u.buffer.length = length; |
| ri->u.buffer.data = data; |
| |
| r = (struct res_resource *) res_alloc (sizeof *r); |
| r->type = RES_TYPE_USERDATA; |
| r->u.rcdata = ri; |
| |
| return r; |
| } |
| |
| /* Macros to swap out values. */ |
| |
| #define put_16(be, v, s) ((be) ? bfd_putb16 ((v), (s)) : bfd_putl16 ((v), (s))) |
| #define put_32(be, v, s) ((be) ? bfd_putb32 ((v), (s)) : bfd_putl32 ((v), (s))) |
| |
| /* Local functions used to convert resources to binary format. */ |
| |
| static void dword_align_bin PARAMS ((struct bindata ***, unsigned long *)); |
| static struct bindata *resid_to_bin PARAMS ((struct res_id, int)); |
| static struct bindata *unicode_to_bin PARAMS ((const unichar *, int)); |
| static struct bindata *res_to_bin_accelerator |
| PARAMS ((const struct accelerator *, int)); |
| static struct bindata *res_to_bin_cursor |
| PARAMS ((const struct cursor *, int)); |
| static struct bindata *res_to_bin_group_cursor |
| PARAMS ((const struct group_cursor *, int)); |
| static struct bindata *res_to_bin_dialog |
| PARAMS ((const struct dialog *, int)); |
| static struct bindata *res_to_bin_fontdir |
| PARAMS ((const struct fontdir *, int)); |
| static struct bindata *res_to_bin_group_icon |
| PARAMS ((const struct group_icon *, int)); |
| static struct bindata *res_to_bin_menu |
| PARAMS ((const struct menu *, int)); |
| static struct bindata *res_to_bin_menuitems |
| PARAMS ((const struct menuitem *, int)); |
| static struct bindata *res_to_bin_menuexitems |
| PARAMS ((const struct menuitem *, int)); |
| static struct bindata *res_to_bin_rcdata |
| PARAMS ((const struct rcdata_item *, int)); |
| static struct bindata *res_to_bin_stringtable |
| PARAMS ((const struct stringtable *, int)); |
| static struct bindata *string_to_unicode_bin PARAMS ((const char *, int)); |
| static struct bindata *res_to_bin_versioninfo |
| PARAMS ((const struct versioninfo *, int)); |
| static struct bindata *res_to_bin_generic |
| PARAMS ((unsigned long, const unsigned char *)); |
| |
| /* Convert a resource to binary. */ |
| |
| struct bindata * |
| res_to_bin (res, big_endian) |
| const struct res_resource *res; |
| int big_endian; |
| { |
| switch (res->type) |
| { |
| default: |
| abort (); |
| case RES_TYPE_BITMAP: |
| case RES_TYPE_FONT: |
| case RES_TYPE_ICON: |
| case RES_TYPE_MESSAGETABLE: |
| return res_to_bin_generic (res->u.data.length, res->u.data.data); |
| case RES_TYPE_ACCELERATOR: |
| return res_to_bin_accelerator (res->u.acc, big_endian); |
| case RES_TYPE_CURSOR: |
| return res_to_bin_cursor (res->u.cursor, big_endian); |
| case RES_TYPE_GROUP_CURSOR: |
| return res_to_bin_group_cursor (res->u.group_cursor, big_endian); |
| case RES_TYPE_DIALOG: |
| return res_to_bin_dialog (res->u.dialog, big_endian); |
| case RES_TYPE_FONTDIR: |
| return res_to_bin_fontdir (res->u.fontdir, big_endian); |
| case RES_TYPE_GROUP_ICON: |
| return res_to_bin_group_icon (res->u.group_icon, big_endian); |
| case RES_TYPE_MENU: |
| return res_to_bin_menu (res->u.menu, big_endian); |
| case RES_TYPE_RCDATA: |
| return res_to_bin_rcdata (res->u.rcdata, big_endian); |
| case RES_TYPE_STRINGTABLE: |
| return res_to_bin_stringtable (res->u.stringtable, big_endian); |
| case RES_TYPE_USERDATA: |
| return res_to_bin_rcdata (res->u.rcdata, big_endian); |
| case RES_TYPE_VERSIONINFO: |
| return res_to_bin_versioninfo (res->u.versioninfo, big_endian); |
| } |
| } |
| |
| /* Align to a 32 bit boundary. PPP points to the of a list of bindata |
| structures. LENGTH points to the length of the structures. If |
| necessary, this adds a new bindata to bring length up to a 32 bit |
| boundary. It updates *PPP and *LENGTH. */ |
| |
| static void |
| dword_align_bin (ppp, length) |
| struct bindata ***ppp; |
| unsigned long *length; |
| { |
| int add; |
| struct bindata *d; |
| |
| if ((*length & 3) == 0) |
| return; |
| |
| add = 4 - (*length & 3); |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = add; |
| d->data = (unsigned char *) reswr_alloc (add); |
| memset (d->data, 0, add); |
| |
| d->next = NULL; |
| **ppp = d; |
| *ppp = &(**ppp)->next; |
| |
| *length += add; |
| } |
| |
| /* Convert a resource ID to binary. This always returns exactly one |
| bindata structure. */ |
| |
| static struct bindata * |
| resid_to_bin (id, big_endian) |
| struct res_id id; |
| int big_endian; |
| { |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| |
| if (! id.named) |
| { |
| d->length = 4; |
| d->data = (unsigned char *) reswr_alloc (4); |
| put_16 (big_endian, 0xffff, d->data); |
| put_16 (big_endian, id.u.id, d->data + 2); |
| } |
| else |
| { |
| int i; |
| |
| d->length = id.u.n.length * 2 + 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| for (i = 0; i < id.u.n.length; i++) |
| put_16 (big_endian, id.u.n.name[i], d->data + i * 2); |
| put_16 (big_endian, 0, d->data + i * 2); |
| } |
| |
| d->next = NULL; |
| |
| return d; |
| } |
| |
| /* Convert a null terminated unicode string to binary. This always |
| returns exactly one bindata structure. */ |
| |
| static struct bindata * |
| unicode_to_bin (str, big_endian) |
| const unichar *str; |
| int big_endian; |
| { |
| int len; |
| struct bindata *d; |
| |
| len = 0; |
| if (str != NULL) |
| { |
| const unichar *s; |
| |
| for (s = str; *s != 0; s++) |
| ++len; |
| } |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = len * 2 + 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| if (str == NULL) |
| put_16 (big_endian, 0, d->data); |
| else |
| { |
| const unichar *s; |
| int i; |
| |
| for (s = str, i = 0; *s != 0; s++, i++) |
| put_16 (big_endian, *s, d->data + i * 2); |
| put_16 (big_endian, 0, d->data + i * 2); |
| } |
| |
| d->next = NULL; |
| |
| return d; |
| } |
| |
| /* Convert an accelerator resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_accelerator (accelerators, big_endian) |
| const struct accelerator *accelerators; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| const struct accelerator *a; |
| |
| first = NULL; |
| pp = &first; |
| |
| for (a = accelerators; a != NULL; a = a->next) |
| { |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 8; |
| d->data = (unsigned char *) reswr_alloc (8); |
| |
| put_16 (big_endian, |
| a->flags | (a->next != NULL ? 0 : ACC_LAST), |
| d->data); |
| put_16 (big_endian, a->key, d->data + 2); |
| put_16 (big_endian, a->id, d->data + 4); |
| put_16 (big_endian, 0, d->data + 8); |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| return first; |
| } |
| |
| /* Convert a cursor resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_cursor (c, big_endian) |
| const struct cursor *c; |
| int big_endian; |
| { |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 4; |
| d->data = (unsigned char *) reswr_alloc (4); |
| |
| put_16 (big_endian, c->xhotspot, d->data); |
| put_16 (big_endian, c->yhotspot, d->data + 2); |
| |
| d->next = (struct bindata *) reswr_alloc (sizeof *d); |
| d->next->length = c->length; |
| d->next->data = (unsigned char *) c->data; |
| d->next->next = NULL; |
| |
| return d; |
| } |
| |
| /* Convert a group cursor resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_group_cursor (group_cursors, big_endian) |
| const struct group_cursor *group_cursors; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| int c; |
| const struct group_cursor *gc; |
| |
| first = (struct bindata *) reswr_alloc (sizeof *first); |
| first->length = 6; |
| first->data = (unsigned char *) reswr_alloc (6); |
| |
| put_16 (big_endian, 0, first->data); |
| put_16 (big_endian, 2, first->data + 2); |
| |
| first->next = NULL; |
| pp = &first->next; |
| |
| c = 0; |
| for (gc = group_cursors; gc != NULL; gc = gc->next) |
| { |
| struct bindata *d; |
| |
| ++c; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 14; |
| d->data = (unsigned char *) reswr_alloc (14); |
| |
| put_16 (big_endian, gc->width, d->data); |
| put_16 (big_endian, gc->height, d->data + 2); |
| put_16 (big_endian, gc->planes, d->data + 4); |
| put_16 (big_endian, gc->bits, d->data + 6); |
| put_32 (big_endian, gc->bytes, d->data + 8); |
| put_16 (big_endian, gc->index, d->data + 12); |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| put_16 (big_endian, c, first->data + 4); |
| |
| return first; |
| } |
| |
| /* Convert a dialog resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_dialog (dialog, big_endian) |
| const struct dialog *dialog; |
| int big_endian; |
| { |
| int dialogex; |
| struct bindata *first, **pp; |
| unsigned long length; |
| int off, c; |
| struct dialog_control *dc; |
| |
| dialogex = extended_dialog (dialog); |
| |
| first = (struct bindata *) reswr_alloc (sizeof *first); |
| first->length = dialogex ? 26 : 18; |
| first->data = (unsigned char *) reswr_alloc (first->length); |
| |
| length = first->length; |
| |
| if (! dialogex) |
| { |
| put_32 (big_endian, dialog->style, first->data); |
| put_32 (big_endian, dialog->exstyle, first->data + 4); |
| off = 8; |
| } |
| else |
| { |
| put_16 (big_endian, 1, first->data); |
| put_16 (big_endian, 0xffff, first->data + 2); |
| |
| if (dialog->ex == NULL) |
| put_32 (big_endian, 0, first->data + 4); |
| else |
| put_32 (big_endian, dialog->ex->help, first->data + 4); |
| put_32 (big_endian, dialog->exstyle, first->data + 8); |
| put_32 (big_endian, dialog->style, first->data + 12); |
| off = 16; |
| } |
| |
| put_16 (big_endian, dialog->x, first->data + off + 2); |
| put_16 (big_endian, dialog->y, first->data + off + 4); |
| put_16 (big_endian, dialog->width, first->data + off + 6); |
| put_16 (big_endian, dialog->height, first->data + off + 8); |
| |
| pp = &first->next; |
| |
| *pp = resid_to_bin (dialog->menu, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| *pp = resid_to_bin (dialog->class, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| *pp = unicode_to_bin (dialog->caption, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| if ((dialog->style & DS_SETFONT) != 0) |
| { |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = dialogex ? 6 : 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| length += d->length; |
| |
| put_16 (big_endian, dialog->pointsize, d->data); |
| |
| if (dialogex) |
| { |
| if (dialog->ex == NULL) |
| { |
| put_16 (big_endian, 0, d->data + 2); |
| put_16 (big_endian, 0, d->data + 4); |
| } |
| else |
| { |
| put_16 (big_endian, dialog->ex->weight, d->data + 2); |
| put_16 (big_endian, dialog->ex->italic, d->data + 4); |
| } |
| } |
| |
| *pp = d; |
| pp = &d->next; |
| |
| *pp = unicode_to_bin (dialog->font, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| } |
| |
| c = 0; |
| for (dc = dialog->controls; dc != NULL; dc = dc->next) |
| { |
| struct bindata *d; |
| int dcoff; |
| |
| ++c; |
| |
| dword_align_bin (&pp, &length); |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = dialogex ? 24 : 18; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| length += d->length; |
| |
| if (! dialogex) |
| { |
| put_32 (big_endian, dc->style, d->data); |
| put_32 (big_endian, dc->exstyle, d->data + 4); |
| dcoff = 8; |
| } |
| else |
| { |
| put_32 (big_endian, dc->help, d->data); |
| put_32 (big_endian, dc->exstyle, d->data + 4); |
| put_32 (big_endian, dc->style, d->data + 8); |
| dcoff = 12; |
| } |
| |
| put_16 (big_endian, dc->x, d->data + dcoff); |
| put_16 (big_endian, dc->y, d->data + dcoff + 2); |
| put_16 (big_endian, dc->width, d->data + dcoff + 4); |
| put_16 (big_endian, dc->height, d->data + dcoff + 6); |
| |
| if (dialogex) |
| put_32 (big_endian, dc->id, d->data + dcoff + 8); |
| else |
| put_16 (big_endian, dc->id, d->data + dcoff + 8); |
| |
| *pp = d; |
| pp = &d->next; |
| |
| *pp = resid_to_bin (dc->class, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| *pp = resid_to_bin (dc->text, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 2; |
| d->data = (unsigned char *) reswr_alloc (2); |
| |
| length += 2; |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| |
| if (dc->data == NULL) |
| put_16 (big_endian, 0, d->data); |
| else |
| { |
| unsigned long sublen; |
| |
| dword_align_bin (&pp, &length); |
| |
| *pp = res_to_bin_rcdata (dc->data, big_endian); |
| sublen = 0; |
| while (*pp != NULL) |
| { |
| sublen += (*pp)->length; |
| pp = &(*pp)->next; |
| } |
| |
| put_16 (big_endian, sublen, d->data); |
| |
| length += sublen; |
| } |
| } |
| put_16 (big_endian, c, first->data + off); |
| |
| return first; |
| } |
| |
| /* Convert a fontdir resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_fontdir (fontdirs, big_endian) |
| const struct fontdir *fontdirs; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| int c; |
| const struct fontdir *fd; |
| |
| first = (struct bindata *) reswr_alloc (sizeof *first); |
| first->length = 2; |
| first->data = (unsigned char *) reswr_alloc (2); |
| |
| first->next = NULL; |
| pp = &first->next; |
| |
| c = 0; |
| for (fd = fontdirs; fd != NULL; fd = fd->next) |
| { |
| struct bindata *d; |
| |
| ++c; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 2; |
| d->data = (unsigned char *) reswr_alloc (2); |
| |
| put_16 (big_endian, fd->index, d->data); |
| |
| *pp = d; |
| pp = &d->next; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = fd->length; |
| d->data = (unsigned char *) fd->data; |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| put_16 (big_endian, c, first->data); |
| |
| return first; |
| } |
| |
| /* Convert a group icon resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_group_icon (group_icons, big_endian) |
| const struct group_icon *group_icons; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| int c; |
| const struct group_icon *gi; |
| |
| first = (struct bindata *) reswr_alloc (sizeof *first); |
| first->length = 6; |
| first->data = (unsigned char *) reswr_alloc (6); |
| |
| put_16 (big_endian, 0, first->data); |
| put_16 (big_endian, 1, first->data + 2); |
| |
| first->next = NULL; |
| pp = &first->next; |
| |
| c = 0; |
| for (gi = group_icons; gi != NULL; gi = gi->next) |
| { |
| struct bindata *d; |
| |
| ++c; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 14; |
| d->data = (unsigned char *) reswr_alloc (14); |
| |
| d->data[0] = gi->width; |
| d->data[1] = gi->height; |
| d->data[2] = gi->colors; |
| d->data[3] = 0; |
| put_16 (big_endian, gi->planes, d->data + 4); |
| put_16 (big_endian, gi->bits, d->data + 6); |
| put_32 (big_endian, gi->bytes, d->data + 8); |
| put_16 (big_endian, gi->index, d->data + 12); |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| put_16 (big_endian, c, first->data + 4); |
| |
| return first; |
| } |
| |
| /* Convert a menu resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_menu (menu, big_endian) |
| const struct menu *menu; |
| int big_endian; |
| { |
| int menuex; |
| struct bindata *d; |
| |
| menuex = extended_menu (menu); |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = menuex ? 8 : 4; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| if (! menuex) |
| { |
| put_16 (big_endian, 0, d->data); |
| put_16 (big_endian, 0, d->data + 2); |
| |
| d->next = res_to_bin_menuitems (menu->items, big_endian); |
| } |
| else |
| { |
| put_16 (big_endian, 1, d->data); |
| put_16 (big_endian, 4, d->data + 2); |
| put_32 (big_endian, menu->help, d->data + 4); |
| |
| d->next = res_to_bin_menuexitems (menu->items, big_endian); |
| } |
| |
| return d; |
| } |
| |
| /* Convert menu items to binary. */ |
| |
| static struct bindata * |
| res_to_bin_menuitems (items, big_endian) |
| const struct menuitem *items; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| const struct menuitem *mi; |
| |
| first = NULL; |
| pp = &first; |
| |
| for (mi = items; mi != NULL; mi = mi->next) |
| { |
| struct bindata *d; |
| int flags; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = mi->popup == NULL ? 4 : 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| flags = mi->type; |
| if (mi->next == NULL) |
| flags |= MENUITEM_ENDMENU; |
| if (mi->popup != NULL) |
| flags |= MENUITEM_POPUP; |
| |
| put_16 (big_endian, flags, d->data); |
| |
| if (mi->popup == NULL) |
| put_16 (big_endian, mi->id, d->data + 2); |
| |
| *pp = d; |
| pp = &d->next; |
| |
| *pp = unicode_to_bin (mi->text, big_endian); |
| pp = &(*pp)->next; |
| |
| if (mi->popup != NULL) |
| { |
| *pp = res_to_bin_menuitems (mi->popup, big_endian); |
| while (*pp != NULL) |
| pp = &(*pp)->next; |
| } |
| } |
| |
| return first; |
| } |
| |
| /* Convert menuex items to binary. */ |
| |
| static struct bindata * |
| res_to_bin_menuexitems (items, big_endian) |
| const struct menuitem *items; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| unsigned long length; |
| const struct menuitem *mi; |
| |
| first = NULL; |
| pp = &first; |
| |
| length = 0; |
| |
| for (mi = items; mi != NULL; mi = mi->next) |
| { |
| struct bindata *d; |
| int flags; |
| |
| dword_align_bin (&pp, &length); |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 12; |
| d->data = (unsigned char *) reswr_alloc (12); |
| |
| length += 12; |
| |
| put_32 (big_endian, mi->type, d->data); |
| put_32 (big_endian, mi->state, d->data + 4); |
| put_16 (big_endian, mi->id, d->data + 8); |
| |
| flags = 0; |
| if (mi->next == NULL) |
| flags |= 0x80; |
| if (mi->popup != NULL) |
| flags |= 1; |
| put_16 (big_endian, flags, d->data + 10); |
| |
| *pp = d; |
| pp = &d->next; |
| |
| *pp = unicode_to_bin (mi->text, big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| if (mi->popup != NULL) |
| { |
| dword_align_bin (&pp, &length); |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 4; |
| d->data = (unsigned char *) reswr_alloc (4); |
| |
| put_32 (big_endian, mi->help, d->data); |
| |
| *pp = d; |
| pp = &d->next; |
| |
| *pp = res_to_bin_menuexitems (mi->popup, big_endian); |
| while (*pp != NULL) |
| { |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| } |
| } |
| } |
| |
| return first; |
| } |
| |
| /* Convert an rcdata resource to binary. This is also used to convert |
| other information which happens to be stored in rcdata_item lists |
| to binary. */ |
| |
| static struct bindata * |
| res_to_bin_rcdata (items, big_endian) |
| const struct rcdata_item *items; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| const struct rcdata_item *ri; |
| |
| first = NULL; |
| pp = &first; |
| |
| for (ri = items; ri != NULL; ri = ri->next) |
| { |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| |
| switch (ri->type) |
| { |
| default: |
| abort (); |
| |
| case RCDATA_WORD: |
| d->length = 2; |
| d->data = (unsigned char *) reswr_alloc (2); |
| put_16 (big_endian, ri->u.word, d->data); |
| break; |
| |
| case RCDATA_DWORD: |
| d->length = 4; |
| d->data = (unsigned char *) reswr_alloc (4); |
| put_32 (big_endian, ri->u.dword, d->data); |
| break; |
| |
| case RCDATA_STRING: |
| d->length = ri->u.string.length; |
| d->data = (unsigned char *) ri->u.string.s; |
| break; |
| |
| case RCDATA_WSTRING: |
| { |
| unsigned long i; |
| |
| d->length = ri->u.wstring.length * 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| for (i = 0; i < ri->u.wstring.length; i++) |
| put_16 (big_endian, ri->u.wstring.w[i], d->data + i * 2); |
| break; |
| } |
| |
| case RCDATA_BUFFER: |
| d->length = ri->u.buffer.length; |
| d->data = (unsigned char *) ri->u.buffer.data; |
| break; |
| } |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| return first; |
| } |
| |
| /* Convert a stringtable resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_stringtable (st, big_endian) |
| const struct stringtable *st; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| int i; |
| |
| first = NULL; |
| pp = &first; |
| |
| for (i = 0; i < 16; i++) |
| { |
| int slen, j; |
| struct bindata *d; |
| unichar *s; |
| |
| slen = st->strings[i].length; |
| s = st->strings[i].string; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 2 + slen * 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| put_16 (big_endian, slen, d->data); |
| |
| for (j = 0; j < slen; j++) |
| put_16 (big_endian, s[j], d->data + 2 + j * 2); |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| return first; |
| } |
| |
| /* Convert an ASCII string to a unicode binary string. This always |
| returns exactly one bindata structure. */ |
| |
| static struct bindata * |
| string_to_unicode_bin (s, big_endian) |
| const char *s; |
| int big_endian; |
| { |
| size_t len, i; |
| struct bindata *d; |
| |
| len = strlen (s); |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = len * 2 + 2; |
| d->data = (unsigned char *) reswr_alloc (d->length); |
| |
| for (i = 0; i < len; i++) |
| put_16 (big_endian, s[i], d->data + i * 2); |
| put_16 (big_endian, 0, d->data + i * 2); |
| |
| d->next = NULL; |
| |
| return d; |
| } |
| |
| /* Convert a versioninfo resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_versioninfo (versioninfo, big_endian) |
| const struct versioninfo *versioninfo; |
| int big_endian; |
| { |
| struct bindata *first, **pp; |
| unsigned long length; |
| struct ver_info *vi; |
| |
| first = (struct bindata *) reswr_alloc (sizeof *first); |
| first->length = 6; |
| first->data = (unsigned char *) reswr_alloc (6); |
| |
| length = 6; |
| |
| if (versioninfo->fixed == NULL) |
| put_16 (big_endian, 0, first->data + 2); |
| else |
| put_16 (big_endian, 52, first->data + 2); |
| |
| put_16 (big_endian, 0, first->data + 4); |
| |
| pp = &first->next; |
| |
| *pp = string_to_unicode_bin ("VS_VERSION_INFO", big_endian); |
| length += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| dword_align_bin (&pp, &length); |
| |
| if (versioninfo->fixed != NULL) |
| { |
| const struct fixed_versioninfo *fi; |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = 52; |
| d->data = (unsigned char *) reswr_alloc (52); |
| |
| length += 52; |
| |
| fi = versioninfo->fixed; |
| |
| put_32 (big_endian, 0xfeef04bd, d->data); |
| put_32 (big_endian, 0x10000, d->data + 4); |
| put_32 (big_endian, fi->file_version_ms, d->data + 8); |
| put_32 (big_endian, fi->file_version_ls, d->data + 12); |
| put_32 (big_endian, fi->product_version_ms, d->data + 16); |
| put_32 (big_endian, fi->product_version_ls, d->data + 20); |
| put_32 (big_endian, fi->file_flags_mask, d->data + 24); |
| put_32 (big_endian, fi->file_flags, d->data + 28); |
| put_32 (big_endian, fi->file_os, d->data + 32); |
| put_32 (big_endian, fi->file_type, d->data + 36); |
| put_32 (big_endian, fi->file_subtype, d->data + 40); |
| put_32 (big_endian, fi->file_date_ms, d->data + 44); |
| put_32 (big_endian, fi->file_date_ls, d->data + 48); |
| |
| d->next = NULL; |
| *pp = d; |
| pp = &d->next; |
| } |
| |
| for (vi = versioninfo->var; vi != NULL; vi = vi->next) |
| { |
| struct bindata *vid; |
| unsigned long vilen; |
| |
| dword_align_bin (&pp, &length); |
| |
| vid = (struct bindata *) reswr_alloc (sizeof *vid); |
| vid->length = 6; |
| vid->data = (unsigned char *) reswr_alloc (6); |
| |
| length += 6; |
| vilen = 6; |
| |
| put_16 (big_endian, 0, vid->data + 2); |
| put_16 (big_endian, 0, vid->data + 4); |
| |
| *pp = vid; |
| pp = &vid->next; |
| |
| switch (vi->type) |
| { |
| default: |
| abort (); |
| |
| case VERINFO_STRING: |
| { |
| unsigned long hold, vslen; |
| struct bindata *vsd; |
| const struct ver_stringinfo *vs; |
| |
| *pp = string_to_unicode_bin ("StringFileInfo", big_endian); |
| length += (*pp)->length; |
| vilen += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| hold = length; |
| dword_align_bin (&pp, &length); |
| vilen += length - hold; |
| |
| vsd = (struct bindata *) reswr_alloc (sizeof *vsd); |
| vsd->length = 6; |
| vsd->data = (unsigned char *) reswr_alloc (6); |
| |
| length += 6; |
| vilen += 6; |
| vslen = 6; |
| |
| put_16 (big_endian, 0, vsd->data + 2); |
| put_16 (big_endian, 0, vsd->data + 4); |
| |
| *pp = vsd; |
| pp = &vsd->next; |
| |
| *pp = unicode_to_bin (vi->u.string.language, big_endian); |
| length += (*pp)->length; |
| vilen += (*pp)->length; |
| vslen += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| for (vs = vi->u.string.strings; vs != NULL; vs = vs->next) |
| { |
| struct bindata *vssd; |
| unsigned long vsslen; |
| |
| hold = length; |
| dword_align_bin (&pp, &length); |
| vilen += length - hold; |
| vslen += length - hold; |
| |
| vssd = (struct bindata *) reswr_alloc (sizeof *vssd); |
| vssd->length = 6; |
| vssd->data = (unsigned char *) reswr_alloc (6); |
| |
| length += 6; |
| vilen += 6; |
| vslen += 6; |
| vsslen = 6; |
| |
| put_16 (big_endian, 1, vssd->data + 4); |
| |
| *pp = vssd; |
| pp = &vssd->next; |
| |
| *pp = unicode_to_bin (vs->key, big_endian); |
| length += (*pp)->length; |
| vilen += (*pp)->length; |
| vslen += (*pp)->length; |
| vsslen += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| hold = length; |
| dword_align_bin (&pp, &length); |
| vilen += length - hold; |
| vslen += length - hold; |
| vsslen += length - hold; |
| |
| *pp = unicode_to_bin (vs->value, big_endian); |
| put_16 (big_endian, (*pp)->length / 2, vssd->data + 2); |
| length += (*pp)->length; |
| vilen += (*pp)->length; |
| vslen += (*pp)->length; |
| vsslen += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| put_16 (big_endian, vsslen, vssd->data); |
| } |
| |
| put_16 (big_endian, vslen, vsd->data); |
| |
| break; |
| } |
| |
| case VERINFO_VAR: |
| { |
| unsigned long hold, vvlen, vvvlen; |
| struct bindata *vvd; |
| const struct ver_varinfo *vv; |
| |
| *pp = string_to_unicode_bin ("VarFileInfo", big_endian); |
| length += (*pp)->length; |
| vilen += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| hold = length; |
| dword_align_bin (&pp, &length); |
| vilen += length - hold; |
| |
| vvd = (struct bindata *) reswr_alloc (sizeof *vvd); |
| vvd->length = 6; |
| vvd->data = (unsigned char *) reswr_alloc (6); |
| |
| length += 6; |
| vilen += 6; |
| vvlen = 6; |
| |
| put_16 (big_endian, 0, vvd->data + 4); |
| |
| *pp = vvd; |
| pp = &vvd->next; |
| |
| *pp = unicode_to_bin (vi->u.var.key, big_endian); |
| length += (*pp)->length; |
| vilen += (*pp)->length; |
| vvlen += (*pp)->length; |
| pp = &(*pp)->next; |
| |
| hold = length; |
| dword_align_bin (&pp, &length); |
| vilen += length - hold; |
| vvlen += length - hold; |
| |
| vvvlen = 0; |
| |
| for (vv = vi->u.var.var; vv != NULL; vv = vv->next) |
| { |
| struct bindata *vvsd; |
| |
| vvsd = (struct bindata *) reswr_alloc (sizeof *vvsd); |
| vvsd->length = 4; |
| vvsd->data = (unsigned char *) reswr_alloc (4); |
| |
| length += 4; |
| vilen += 4; |
| vvlen += 4; |
| vvvlen += 4; |
| |
| put_16 (big_endian, vv->language, vvsd->data); |
| put_16 (big_endian, vv->charset, vvsd->data + 2); |
| |
| vvsd->next = NULL; |
| *pp = vvsd; |
| pp = &vvsd->next; |
| } |
| |
| put_16 (big_endian, vvlen, vvd->data); |
| put_16 (big_endian, vvvlen, vvd->data + 2); |
| |
| break; |
| } |
| } |
| |
| put_16 (big_endian, vilen, vid->data); |
| } |
| |
| put_16 (big_endian, length, first->data); |
| |
| return first; |
| } |
| |
| /* Convert a generic resource to binary. */ |
| |
| static struct bindata * |
| res_to_bin_generic (length, data) |
| unsigned long length; |
| const unsigned char *data; |
| { |
| struct bindata *d; |
| |
| d = (struct bindata *) reswr_alloc (sizeof *d); |
| d->length = length; |
| d->data = (unsigned char *) data; |
| |
| d->next = NULL; |
| |
| return d; |
| } |