| /* |
| * Read a .res file and create a resource-tree |
| * |
| * Copyright 1998 Bertho A. Stultiens |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "wrc.h" |
| #include "readres.h" |
| #include "newstruc.h" |
| #include "utils.h" |
| #include "genres.h" |
| |
| static const struct resheader32 { |
| DWORD ressize; /* 0 */ |
| DWORD hdrsize; /* 0x20 */ |
| WORD restype1; /* 0xffff */ |
| WORD restype2; /* 0 */ |
| WORD resname1; /* 0xffff */ |
| WORD resname2; /* 0 */ |
| DWORD dversion; /* 0 */ |
| WORD memopt; /* 0 */ |
| WORD language; /* 0 */ |
| DWORD version; /* 0 */ |
| DWORD characts; /* 0 */ |
| } emptyheader = {0, 0x20, 0xffff, 0, 0xffff, 0, 0, 0, 0, 0, 0}, |
| emptyheaderSWAPPED = {0, BYTESWAP_DWORD(0x20), 0xffff, 0, 0xffff, 0, 0, 0, 0, 0, 0}; |
| |
| /* |
| ***************************************************************************** |
| * Function : |
| * Syntax : |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| /* |
| ***************************************************************************** |
| * Function : |
| * Syntax : |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| /* |
| ***************************************************************************** |
| * Function : |
| * Syntax : |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| static int read_data(FILE *fp, size_t size, void *buf) |
| { |
| unsigned int r; |
| int pos = ftell(fp); |
| r = fread(buf, 1, size, fp); |
| if(r == size) |
| return 0; |
| if(r == 0 && ftell(fp) - pos > 0) |
| return 1; |
| else |
| return -1; |
| } |
| |
| /* |
| ***************************************************************************** |
| * Function : |
| * Syntax : |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| static enum res_e res_type_from_id(const name_id_t *nid) |
| { |
| if(nid->type == name_str) |
| return res_usr; |
| |
| if(nid->type != name_ord) |
| internal_error(__FILE__, __LINE__, "Invalid name_id descriptor %d\n", nid->type); |
| |
| switch(nid->name.i_name) |
| { |
| case WRC_RT_CURSOR: return res_cur; |
| case WRC_RT_BITMAP: return res_bmp; |
| case WRC_RT_ICON: return res_ico; |
| case WRC_RT_MENU: return res_men; |
| case WRC_RT_DIALOG: return res_dlg; |
| case WRC_RT_STRING: return res_stt; |
| case WRC_RT_FONTDIR: return res_fntdir; |
| case WRC_RT_FONT: return res_fnt; |
| case WRC_RT_ACCELERATOR: return res_acc; |
| case WRC_RT_RCDATA: return res_rdt; |
| case WRC_RT_MESSAGETABLE: return res_msg; |
| case WRC_RT_GROUP_CURSOR: return res_curg; |
| case WRC_RT_GROUP_ICON: return res_icog; |
| case WRC_RT_VERSION: return res_ver; |
| case WRC_RT_TOOLBAR: return res_toolbar; |
| |
| default: |
| case WRC_RT_DLGINCLUDE: |
| case WRC_RT_PLUGPLAY: |
| case WRC_RT_VXD: |
| case WRC_RT_ANICURSOR: |
| case WRC_RT_ANIICON: |
| warning("Cannot be sure of resource type, using usertype settings\n"); |
| return res_usr; |
| } |
| } |
| |
| /* |
| ***************************************************************************** |
| * Function : |
| * Syntax : |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| #define get_word(idx) (*((WORD *)(&res->data[idx]))) |
| #define get_dword(idx) (*((DWORD *)(&res->data[idx]))) |
| |
| static resource_t *read_res32(FILE *fp) |
| { |
| static const char wrong_format[] = "Wrong resfile format (32bit)"; |
| DWORD ressize; |
| DWORD hdrsize; |
| DWORD totsize; |
| WORD memopt; |
| WORD language; |
| int err; |
| res_t *res; |
| resource_t *rsc; |
| resource_t *tail = NULL; |
| resource_t *list = NULL; |
| name_id_t *type = NULL; |
| name_id_t *name = NULL; |
| int idx; |
| enum res_e res_type; |
| user_t *usrres; |
| |
| while(1) |
| { |
| /* Get headersize and resource size */ |
| err = read_data(fp, sizeof(ressize), &ressize); |
| if(err < 0) |
| break; |
| else if(err > 0) |
| error(wrong_format); |
| err = read_data(fp, sizeof(hdrsize), &hdrsize); |
| if(err) |
| error(wrong_format); |
| |
| /* Align sizes and compute total size */ |
| totsize = hdrsize; |
| if(hdrsize & 3) |
| { |
| warning("Hu? .res header needed alignment (anything can happen now)\n"); |
| totsize += 4 - (hdrsize & 3); |
| } |
| totsize += ressize; |
| if(ressize & 3) |
| totsize += 4 - (ressize & 3); |
| |
| /* Read in entire data-block */ |
| fseek(fp, -8, SEEK_CUR); |
| res = new_res(); |
| if(res->allocsize < totsize) |
| grow_res(res, totsize - res->allocsize + 8); |
| err = read_data(fp, totsize, res->data); |
| if(err) |
| error(wrong_format); |
| |
| res->dataidx = hdrsize; |
| res->size = hdrsize + ressize; |
| |
| /* Analyse the content of the header */ |
| idx = 8; |
| /* Get restype */ |
| if(get_word(idx) == 0xffff) |
| { |
| idx += sizeof(WORD); |
| type = new_name_id(); |
| type->type = name_ord; |
| type->name.i_name = get_word(idx); |
| idx += sizeof(WORD); |
| } |
| else if(get_word(idx) == 0) |
| { |
| error("ResType name has zero length (32 bit)\n"); |
| } |
| else |
| { |
| int tag = idx; |
| string_t *str; |
| while(1) |
| { |
| idx += sizeof(WORD); |
| if(!get_word(idx)) |
| break; |
| } |
| idx += sizeof(WORD); |
| str = new_string(); |
| str->type = str_unicode; |
| str->size = (idx - tag) / 2; |
| str->str.wstr = xmalloc(idx-tag+2); |
| memcpy(str->str.wstr, &res->data[tag], idx-tag); |
| str->str.wstr[str->size] = 0; |
| type = new_name_id(); |
| type->type = name_str; |
| type->name.s_name = str; |
| } |
| /* Get resname */ |
| if(get_word(idx) == 0xffff) |
| { |
| idx += sizeof(WORD); |
| name = new_name_id(); |
| name->type = name_ord; |
| name->name.i_name = get_word(idx); |
| idx += sizeof(WORD); |
| } |
| else if(get_word(idx) == 0) |
| { |
| error("ResName name has zero length (32 bit)\n"); |
| } |
| else |
| { |
| int tag = idx; |
| string_t *str; |
| while(1) |
| { |
| idx += sizeof(WORD); |
| if(!get_word(idx)) |
| break; |
| } |
| idx += sizeof(WORD); |
| str = new_string(); |
| str->type = str_unicode; |
| str->size = (idx - tag) / 2; |
| str->str.wstr = xmalloc(idx-tag+2); |
| memcpy(str->str.wstr, &res->data[tag], idx-tag); |
| str->str.wstr[str->size] = 0; |
| name = new_name_id(); |
| name->type = name_str; |
| name->name.s_name = str; |
| } |
| |
| /* align */ |
| if(idx & 0x3) |
| idx += 4 - (idx & 3); |
| |
| idx += sizeof(DWORD); /* Skip DataVersion */ |
| memopt = get_word(idx); |
| idx += sizeof(WORD); |
| language = get_word(idx); |
| |
| /* Build a resource_t list */ |
| res_type = res_type_from_id(type); |
| if(res_type == res_usr) |
| { |
| /* User-type has custom ResType for .[s|h] generation */ |
| usrres = new_user(type, NULL, new_int(memopt)); |
| } |
| else |
| { |
| free (type); |
| usrres = NULL; |
| } |
| rsc = new_resource(res_type, |
| usrres, |
| memopt, |
| new_language(PRIMARYLANGID(language), |
| SUBLANGID(language))); |
| rsc->binres = res; |
| rsc->name = name; |
| rsc->c_name = make_c_name(get_c_typename(res_type), name, rsc->lan); |
| if(!list) |
| { |
| list = rsc; |
| tail = rsc; |
| } |
| else |
| { |
| rsc->prev = tail; |
| tail->next = rsc; |
| tail = rsc; |
| } |
| } |
| return list; |
| } |
| |
| /* |
| ***************************************************************************** |
| * Function : |
| * Syntax : |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| static resource_t *read_res16(FILE *fp) |
| { |
| internal_error(__FILE__, __LINE__, "Can't yet read 16 bit .res files\n"); |
| return NULL; |
| } |
| |
| /* |
| ***************************************************************************** |
| * Function : read_resfile |
| * Syntax : resource_t *read_resfile(char *inname) |
| * Input : |
| * Output : |
| * Description : |
| * Remarks : |
| ***************************************************************************** |
| */ |
| resource_t *read_resfile(char *inname) |
| { |
| FILE *fp; |
| struct resheader32 rh; |
| int is32bit = 1; |
| resource_t *top; |
| |
| fp = fopen(inname, "rb"); |
| if(!fp) |
| error("Could not open inputfile %s\n", inname); |
| |
| /* Determine 16 or 32 bit .res file */ |
| if(fread(&rh, 1, sizeof(rh), fp) != sizeof(rh)) |
| is32bit = 0; |
| else |
| { |
| if(!memcmp(&emptyheader, &rh, sizeof(rh))) |
| is32bit = 1; |
| else if(!memcmp(&emptyheaderSWAPPED, &rh, sizeof(rh))) |
| error("Binary .res-file has its byteorder swapped\n"); |
| else |
| is32bit = 0; |
| } |
| |
| if(is32bit && !win32) |
| error("Cannot convert 32-bit .res-file into 16-bit resources (and will, hopefully never, implement it)\n"); |
| |
| if(!is32bit && win32) |
| error("Cannot (yet) convert 16-bit .res-file into 32-bit resources\n"); |
| |
| if(!is32bit) |
| { |
| fseek(fp, 0, SEEK_SET); |
| top = read_res16(fp); |
| } |
| else |
| { |
| top = read_res32(fp); |
| } |
| |
| fclose(fp); |
| |
| return top; |
| } |