aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/resource_v7he.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm/resource_v7he.cpp')
-rw-r--r--engines/scumm/resource_v7he.cpp1912
1 files changed, 1912 insertions, 0 deletions
diff --git a/engines/scumm/resource_v7he.cpp b/engines/scumm/resource_v7he.cpp
new file mode 100644
index 0000000000..6ce8446a9a
--- /dev/null
+++ b/engines/scumm/resource_v7he.cpp
@@ -0,0 +1,1912 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * Parts of code heavily based on:
+ * icoutils - A set of programs dealing with MS Windows icons and cursors.
+ * Copyright (C) 1998-2001 Oskar Liljeblad
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "sound/wave.h"
+
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace Scumm {
+
+ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
+ : _vm(scumm) {
+
+ _fileName[0] = 0;
+ memset(_cursorCache, 0, sizeof(_cursorCache));
+}
+
+ResExtractor::~ResExtractor() {
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (cc->valid) {
+ free(cc->bitmap);
+ free(cc->palette);
+ }
+ }
+ memset(_cursorCache, 0, sizeof(_cursorCache));
+}
+
+ResExtractor::CachedCursor *ResExtractor::findCachedCursor(int id) {
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (cc->valid && cc->id == id) {
+ return cc;
+ }
+ }
+ return NULL;
+}
+
+ResExtractor::CachedCursor *ResExtractor::getCachedCursorSlot() {
+ uint32 min_last_used = 0;
+ CachedCursor *r = NULL;
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (!cc->valid) {
+ return cc;
+ } else {
+ if (min_last_used == 0 || cc->last_used < min_last_used) {
+ min_last_used = cc->last_used;
+ r = cc;
+ }
+ }
+ }
+ assert(r);
+ free(r->bitmap);
+ free(r->palette);
+ memset(r, 0, sizeof(CachedCursor));
+ return r;
+}
+
+void ResExtractor::setCursor(int id) {
+ byte *cursorRes = 0;
+ int cursorsize;
+ int keycolor = 0;
+ CachedCursor *cc = findCachedCursor(id);
+ if (cc != NULL) {
+ debug(7, "Found cursor %d in cache slot %d", id, cc - _cursorCache);
+ } else {
+ cc = getCachedCursorSlot();
+ assert(cc && !cc->valid);
+ cursorsize = extractResource(id, &cursorRes);
+ convertIcons(cursorRes, cursorsize, &cc->bitmap, &cc->w, &cc->h, &cc->hotspot_x, &cc->hotspot_y, &keycolor, &cc->palette, &cc->palSize);
+ debug(7, "Adding cursor %d to cache slot %d", id, cc - _cursorCache);
+ free(cursorRes);
+ cc->valid = true;
+ cc->id = id;
+ cc->last_used = g_system->getMillis();
+ }
+
+ if (_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette) && cc->palette)
+ _vm->_system->setCursorPalette(cc->palette, 0, cc->palSize);
+
+ _vm->setCursorHotspot(cc->hotspot_x, cc->hotspot_y);
+ _vm->setCursorFromBuffer(cc->bitmap, cc->w, cc->h, cc->w);
+}
+
+
+/*
+ * Static variables
+ */
+const char *res_types[] = {
+ /* 0x01: */
+ "cursor", "bitmap", "icon", "menu", "dialog", "string",
+ "fontdir", "font", "accelerator", "rcdata", "messagelist",
+ "group_cursor", NULL, "group_icon", NULL,
+ /* the following are not defined in winbase.h, but found in wrc. */
+ /* 0x10: */
+ "version", "dlginclude", NULL, "plugplay", "vxd",
+ "anicursor", "aniicon"
+};
+#define RES_TYPE_COUNT (sizeof(res_types)/sizeof(char *))
+
+Win32ResExtractor::Win32ResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
+}
+
+int Win32ResExtractor::extractResource(int resId, byte **data) {
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%d", resId);
+
+ return extractResource_("group_cursor", buf, data);
+}
+
+int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte **data) {
+ char *arg_language = NULL;
+ const char *arg_type = resType;
+ char *arg_name = resName;
+ int arg_action = ACTION_LIST;
+ int ressize = 0;
+
+ _arg_raw = false;
+
+ /* translate --type option from resource type string to integer */
+ arg_type = res_type_string_to_id(arg_type);
+
+ WinLibrary fi;
+
+ /* initiate stuff */
+ fi.memory = NULL;
+ fi.file = new Common::File;
+
+ if (!_fileName[0]) { // We are running for the first time
+ snprintf(_fileName, 256, "%s.he3", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ char buf1[128];
+
+ _vm->generateSubstResFileName(_fileName, buf1, sizeof(buf1));
+ strcpy(_fileName, buf1);
+ }
+ }
+
+
+ /* get file size */
+ fi.file->open(_fileName);
+ if (!fi.file->isOpen()) {
+ error("Cannot open file %s", _fileName);
+ }
+
+ fi.total_size = fi.file->size();
+ if (fi.total_size == -1) {
+ error("Cannot get size of file %s", fi.file->name());
+ goto cleanup;
+ }
+ if (fi.total_size == 0) {
+ error("%s: file has a size of 0", fi.file->name());
+ goto cleanup;
+ }
+
+ /* read all of file */
+ fi.memory = (byte *)malloc(fi.total_size);
+ if (fi.file->read(fi.memory, fi.total_size) == 0) {
+ error("Cannot read from file %s", fi.file->name());
+ goto cleanup;
+ }
+
+ /* identify file and find resource table */
+ if (!read_library(&fi)) {
+ /* error reported by read_library */
+ goto cleanup;
+ }
+
+ // verbose_printf("file is a %s\n",
+ // fi.is_PE_binary ? "Windows NT `PE' binary" : "Windows 3.1 `NE' binary");
+
+ /* errors will be printed by the callback */
+ ressize = do_resources(&fi, arg_type, arg_name, arg_language, arg_action, data);
+
+ /* free stuff and close file */
+ cleanup:
+ if (fi.file != NULL)
+ fi.file->close();
+ if (fi.memory != NULL)
+ free(fi.memory);
+
+ return ressize;
+}
+
+
+/* res_type_id_to_string:
+ * Translate a numeric resource type to it's corresponding string type.
+ * (For informative-ness.)
+ */
+const char *Win32ResExtractor::res_type_id_to_string(int id) {
+ if (id == 241)
+ return "toolbar";
+ if (id > 0 && id <= (int)RES_TYPE_COUNT)
+ return res_types[id-1];
+ return NULL;
+}
+
+/* res_type_string_to_id:
+ * Translate a resource type string to integer.
+ * (Used to convert the --type option.)
+ */
+const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
+ static const char *res_type_ids[] = {
+ "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", "-10",
+ "-11", "-12", NULL, "-14", NULL, "-16", "-17", NULL, "-19",
+ "-20", "-21", "-22"
+ };
+ int c;
+
+ if (type == NULL)
+ return NULL;
+
+ for (c = 0 ; c < (int)RES_TYPE_COUNT ; c++) {
+ if (res_types[c] != NULL && !scumm_stricmp(type, res_types[c]))
+ return res_type_ids[c];
+ }
+
+ return type;
+}
+
+int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
+ WinResource *type_wr, WinResource *name_wr,
+ WinResource *lang_wr, byte **data) {
+ int size;
+ bool free_it;
+ const char *type;
+ int32 id;
+
+ if (*data) {
+ error("Win32ResExtractor::extract_resources() more than one cursor");
+ return 0;
+ }
+
+ *data = extract_resource(fi, wr, &size, &free_it, type_wr->id, (lang_wr == NULL ? NULL : lang_wr->id), _arg_raw);
+
+ if (data == NULL) {
+ error("Win32ResExtractor::extract_resources() problem with resource extraction");
+ return 0;
+ }
+
+ /* get named resource type if possible */
+ type = NULL;
+ if ((id = strtol(type_wr->id, 0, 10)) != 0)
+ type = res_type_id_to_string(id);
+
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s%s%s [size=%d]",
+ get_resource_id_quoted(name_wr),
+ (lang_wr->id[0] != '\0' ? " language: " : ""),
+ get_resource_id_quoted(lang_wr), size);
+
+ return size;
+}
+
+/* extract_resource:
+ * Extract a resource, returning pointer to data.
+ */
+byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size,
+ bool *free_it, char *type, char *lang, bool raw) {
+ char *str;
+ int32 intval;
+
+ /* just return pointer to data if raw */
+ if (raw) {
+ *free_it = false;
+ /* get_resource_entry will print possible error */
+ return get_resource_entry(fi, wr, size);
+ }
+
+ /* find out how to extract */
+ str = type;
+ if (str != NULL && (intval = strtol(STRIP_RES_ID_FORMAT(str), 0, 10))) {
+ if (intval == (int)RT_GROUP_ICON) {
+ *free_it = true;
+ return extract_group_icon_cursor_resource(fi, wr, lang, size, true);
+ }
+ if (intval == (int)RT_GROUP_CURSOR) {
+ *free_it = true;
+ return extract_group_icon_cursor_resource(fi, wr, lang, size, false);
+ }
+ }
+
+ return NULL;
+}
+
+/* extract_group_icon_resource:
+ * Create a complete RT_GROUP_ICON resource, that can be written to
+ * an `.ico' file without modifications. Returns an allocated
+ * memory block that should be freed with free() once used.
+ *
+ * `root' is the offset in file that specifies the resource.
+ * `base' is the offset that string pointers are calculated from.
+ * `ressize' should point to an integer variable where the size of
+ * the returned memory block will be placed.
+ * `is_icon' indicates whether resource to be extracted is icon
+ * or cursor group.
+ */
+byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang,
+ int *ressize, bool is_icon) {
+ Win32CursorIconDir *icondir;
+ Win32CursorIconFileDir *fileicondir;
+ byte *memory;
+ int c, offset, skipped;
+ int size;
+
+ /* get resource data and size */
+ icondir = (Win32CursorIconDir *)get_resource_entry(fi, wr, &size);
+ if (icondir == NULL) {
+ /* get_resource_entry will print error */
+ return NULL;
+ }
+
+ /* calculate total size of output file */
+ RETURN_IF_BAD_POINTER(NULL, icondir->count);
+ skipped = 0;
+ for (c = 0 ; c < icondir->count ; c++) {
+ int level;
+ int iconsize;
+ char name[14];
+ WinResource *fwr;
+
+ RETURN_IF_BAD_POINTER(NULL, icondir->entries[c]);
+ /*printf("%d. bytes_in_res=%d width=%d height=%d planes=%d bit_count=%d\n", c,
+ icondir->entries[c].bytes_in_res,
+ (is_icon ? icondir->entries[c].res_info.icon.width : icondir->entries[c].res_info.cursor.width),
+ (is_icon ? icondir->entries[c].res_info.icon.height : icondir->entries[c].res_info.cursor.height),
+ icondir->entries[c].plane_count,
+ icondir->entries[c].bit_count);*/
+
+ /* find the corresponding icon resource */
+ snprintf(name, sizeof(name)/sizeof(char), "-%d", icondir->entries[c].res_id);
+ fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level);
+ if (fwr == NULL) {
+ error("%s: could not find `%s' in `%s' resource.",
+ fi->file->name(), &name[1], (is_icon ? "group_icon" : "group_cursor"));
+ return NULL;
+ }
+
+ if (get_resource_entry(fi, fwr, &iconsize) != NULL) {
+ if (iconsize == 0) {
+ debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", fi->file->name(), name);
+ skipped++;
+ continue;
+ }
+ if ((uint32)iconsize != icondir->entries[c].bytes_in_res) {
+ debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)",
+ fi->file->name(), name, iconsize, icondir->entries[c].bytes_in_res);
+ }
+ size += iconsize; /* size += icondir->entries[c].bytes_in_res; */
+
+ /* cursor resources have two additional WORDs that contain
+ * hotspot info */
+ if (!is_icon)
+ size -= sizeof(uint16)*2;
+ }
+ }
+ offset = sizeof(Win32CursorIconFileDir) + (icondir->count-skipped) * sizeof(Win32CursorIconFileDirEntry);
+ size += offset;
+ *ressize = size;
+
+ /* allocate that much memory */
+ memory = (byte *)malloc(size);
+ fileicondir = (Win32CursorIconFileDir *)memory;
+
+ /* transfer Win32CursorIconDir structure members */
+ fileicondir->reserved = icondir->reserved;
+ fileicondir->type = icondir->type;
+ fileicondir->count = icondir->count - skipped;
+
+ /* transfer each cursor/icon: Win32CursorIconDirEntry and data */
+ skipped = 0;
+ for (c = 0 ; c < icondir->count ; c++) {
+ int level;
+ char name[14];
+ WinResource *fwr;
+ byte *data;
+
+ /* find the corresponding icon resource */
+ snprintf(name, sizeof(name)/sizeof(char), "-%d", icondir->entries[c].res_id);
+ fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level);
+ if (fwr == NULL) {
+ error("%s: could not find `%s' in `%s' resource.",
+ fi->file->name(), &name[1], (is_icon ? "group_icon" : "group_cursor"));
+ return NULL;
+ }
+
+ /* get data and size of that resource */
+ data = (byte *)get_resource_entry(fi, fwr, &size);
+ if (data == NULL) {
+ /* get_resource_entry has printed error */
+ return NULL;
+ }
+ if (size == 0) {
+ skipped++;
+ continue;
+ }
+
+ /* copy ICONDIRENTRY (not including last dwImageOffset) */
+ memcpy(&fileicondir->entries[c-skipped], &icondir->entries[c],
+ sizeof(Win32CursorIconFileDirEntry)-sizeof(uint32));
+
+ /* special treatment for cursors */
+ if (!is_icon) {
+ fileicondir->entries[c-skipped].width = icondir->entries[c].res_info.cursor.width;
+ fileicondir->entries[c-skipped].height = icondir->entries[c].res_info.cursor.height / 2;
+ fileicondir->entries[c-skipped].color_count = 0;
+ fileicondir->entries[c-skipped].reserved = 0;
+ }
+
+ /* set image offset and increase it */
+ fileicondir->entries[c-skipped].dib_offset = offset;
+
+ /* transfer resource into file memory */
+ if (is_icon) {
+ memcpy(&memory[offset], data, icondir->entries[c].bytes_in_res);
+ } else {
+ fileicondir->entries[c-skipped].hotspot_x = ((uint16 *) data)[0];
+ fileicondir->entries[c-skipped].hotspot_y = ((uint16 *) data)[1];
+ memcpy(&memory[offset], data+sizeof(uint16)*2,
+ icondir->entries[c].bytes_in_res-sizeof(uint16)*2);
+ offset -= sizeof(uint16)*2;
+ }
+
+ /* increase the offset pointer */
+ offset += icondir->entries[c].bytes_in_res;
+ }
+
+ return memory;
+}
+
+/* check_offset:
+ * Check if a chunk of data (determined by offset and size)
+ * is within the bounds of the WinLibrary file.
+ * Usually not called directly.
+ */
+bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) {
+ int need_size = (int)((byte *)offset - memory + size);
+
+ debugC(DEBUG_RESOURCE, "check_offset: size=%x vs %x offset=%x size=%x",
+ need_size, total_size, (byte *)offset - memory, size);
+
+ if (need_size < 0 || need_size > total_size) {
+ error("%s: premature end", name);
+ return false;
+ }
+
+ return true;
+}
+
+
+/* do_resources:
+ * Do something for each resource matching type, name and lang.
+ */
+int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, int action, byte **data) {
+ WinResource *type_wr;
+ WinResource *name_wr;
+ WinResource *lang_wr;
+ int size;
+
+ type_wr = (WinResource *)calloc(sizeof(WinResource)*3, 1);
+ name_wr = type_wr + 1;
+ lang_wr = type_wr + 2;
+
+ size = do_resources_recurs(fi, NULL, type_wr, name_wr, lang_wr, type, name, lang, action, data);
+
+ free(type_wr);
+
+ return size;
+}
+
+/* what is each entry in this directory level for? type, name or language? */
+#define WINRESOURCE_BY_LEVEL(x) ((x)==0 ? type_wr : ((x)==1 ? name_wr : lang_wr))
+
+/* does the id of this entry match the specified id? */
+#define LEVEL_MATCHES(x) (x == NULL || x ## _wr->id[0] == '\0' || compare_resource_id(x ## _wr, x))
+
+int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base,
+ WinResource *type_wr, WinResource *name_wr, WinResource *lang_wr,
+ const char *type, char *name, char *lang, int action, byte **data) {
+ int c, rescnt;
+ WinResource *wr;
+ uint32 size = 0;
+
+ /* get a list of all resources at this level */
+ wr = list_resources(fi, base, &rescnt);
+ if (wr == NULL)
+ if (size != 0)
+ return size;
+ else
+ return 0;
+
+ /* process each resource listed */
+ for (c = 0 ; c < rescnt ; c++) {
+ /* (over)write the corresponding WinResource holder with the current */
+ memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource));
+
+ /* go deeper unless there is something that does NOT match */
+ if (LEVEL_MATCHES(type) && LEVEL_MATCHES(name) && LEVEL_MATCHES(lang)) {
+ if (wr->is_directory)
+ size = do_resources_recurs(fi, wr+c, type_wr, name_wr, lang_wr, type, name, lang, action, data);
+ else
+ size = extract_resources(fi, wr+c, type_wr, name_wr, lang_wr, data);
+ }
+ }
+
+ /* since we're moving back one level after this, unset the
+ * WinResource holder used on this level */
+ memset(WINRESOURCE_BY_LEVEL(wr[0].level), 0, sizeof(WinResource));
+
+ return size;
+}
+
+/* return the resource id quoted if it's a string, otherwise just return it */
+char *Win32ResExtractor::get_resource_id_quoted(WinResource *wr) {
+ static char tmp[WINRES_ID_MAXLEN+2];
+
+ if (wr->numeric_id || wr->id[0] == '\0')
+ return wr->id;
+
+ sprintf(tmp, "'%s'", wr->id);
+ return tmp;
+}
+
+bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) {
+ if (wr->numeric_id) {
+ int32 cmp1, cmp2;
+ if (id[0] == '+')
+ return false;
+ if (id[0] == '-')
+ id++;
+ if (!(cmp1 = strtol(wr->id, 0, 10)) || !(cmp2 = strtol(id, 0, 10)) || cmp1 != cmp2)
+ return false;
+ } else {
+ if (id[0] == '-')
+ return false;
+ if (id[0] == '+')
+ id++;
+ if (strcmp(wr->id, id))
+ return false;
+ }
+
+ return true;
+}
+
+bool Win32ResExtractor::decode_pe_resource_id(WinLibrary *fi, WinResource *wr, uint32 value) {
+ if (value & IMAGE_RESOURCE_NAME_IS_STRING) { /* numeric id */
+ int c, len;
+ uint16 *mem = (uint16 *)
+ (fi->first_resource + (value & ~IMAGE_RESOURCE_NAME_IS_STRING));
+
+ /* copy each char of the string, and terminate it */
+ RETURN_IF_BAD_POINTER(false, *mem);
+ len = mem[0];
+ RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(uint16) * len);
+
+ len = MIN(mem[0], (uint16)WINRES_ID_MAXLEN);
+ for (c = 0 ; c < len ; c++)
+ wr->id[c] = mem[c+1] & 0x00FF;
+ wr->id[len] = '\0';
+ } else { /* Unicode string id */
+ /* translate id into a string */
+ snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value);
+ }
+
+ wr->numeric_id = (value & IMAGE_RESOURCE_NAME_IS_STRING ? false:true);
+ return true;
+}
+
+byte *Win32ResExtractor::get_resource_entry(WinLibrary *fi, WinResource *wr, int *size) {
+ if (fi->is_PE_binary) {
+ Win32ImageResourceDataEntry *dataent;
+
+ dataent = (Win32ImageResourceDataEntry *) wr->children;
+ RETURN_IF_BAD_POINTER(NULL, *dataent);
+ *size = dataent->size;
+ RETURN_IF_BAD_OFFSET(NULL, fi->memory + dataent->offset_to_data, *size);
+
+ return fi->memory + dataent->offset_to_data;
+ } else {
+ Win16NENameInfo *nameinfo;
+ int sizeshift;
+
+ nameinfo = (Win16NENameInfo *) wr->children;
+ sizeshift = *((uint16 *) fi->first_resource - 1);
+ *size = nameinfo->length << sizeshift;
+ RETURN_IF_BAD_OFFSET(NULL, fi->memory + (nameinfo->offset << sizeshift), *size);
+
+ return fi->memory + (nameinfo->offset << sizeshift);
+ }
+}
+
+bool Win32ResExtractor::decode_ne_resource_id(WinLibrary *fi, WinResource *wr, uint16 value) {
+ if (value & NE_RESOURCE_NAME_IS_NUMERIC) { /* numeric id */
+ /* translate id into a string */
+ snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value & ~NE_RESOURCE_NAME_IS_NUMERIC);
+ } else { /* ASCII string id */
+ int len;
+ char *mem = (char *)NE_HEADER(fi->memory)
+ + NE_HEADER(fi->memory)->rsrctab
+ + value;
+
+ /* copy each char of the string, and terminate it */
+ RETURN_IF_BAD_POINTER(false, *mem);
+ len = mem[0];
+ RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(char) * len);
+ memcpy(wr->id, &mem[1], len);
+ wr->id[len] = '\0';
+ }
+
+ wr->numeric_id = (value & NE_RESOURCE_NAME_IS_NUMERIC ? true:false);
+ return true;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary *fi, Win32ImageResourceDirectory *pe_res, int level, int *count) {
+ WinResource *wr;
+ int c, rescnt;
+ Win32ImageResourceDirectoryEntry *dirent
+ = (Win32ImageResourceDirectoryEntry *)(pe_res + 1);
+
+ /* count number of `type' resources */
+ RETURN_IF_BAD_POINTER(NULL, *dirent);
+ rescnt = pe_res->number_of_named_entries + pe_res->number_of_id_entries;
+ *count = rescnt;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ for (c = 0 ; c < rescnt ; c++) {
+ RETURN_IF_BAD_POINTER(NULL, dirent[c]);
+ wr[c].this_ = pe_res;
+ wr[c].level = level;
+ wr[c].is_directory = (dirent[c].u2.s.data_is_directory);
+ wr[c].children = fi->first_resource + dirent[c].u2.s.offset_to_directory;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_pe_resource_id (fi, wr + c, dirent[c].u1.name))
+ return NULL;
+ }
+
+ return wr;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_ne_name_resources(WinLibrary *fi, WinResource *typeres, int *count) {
+ int c, rescnt;
+ WinResource *wr;
+ Win16NETypeInfo *typeinfo = (Win16NETypeInfo *) typeres->this_;
+ Win16NENameInfo *nameinfo = (Win16NENameInfo *) typeres->children;
+
+ /* count number of `type' resources */
+ RETURN_IF_BAD_POINTER(NULL, typeinfo->count);
+ *count = rescnt = typeinfo->count;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ for (c = 0 ; c < rescnt ; c++) {
+ RETURN_IF_BAD_POINTER(NULL, nameinfo[c]);
+ wr[c].this_ = nameinfo+c;
+ wr[c].is_directory = false;
+ wr[c].children = nameinfo+c;
+ wr[c].level = 1;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_ne_resource_id(fi, wr + c, (nameinfo+c)->id))
+ return NULL;
+ }
+
+ return wr;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_ne_type_resources(WinLibrary *fi, int *count) {
+ int c, rescnt;
+ WinResource *wr;
+ Win16NETypeInfo *typeinfo;
+
+ /* count number of `type' resources */
+ typeinfo = (Win16NETypeInfo *) fi->first_resource;
+ RETURN_IF_BAD_POINTER(NULL, *typeinfo);
+ for (rescnt = 0 ; typeinfo->type_id != 0 ; rescnt++) {
+ typeinfo = NE_TYPEINFO_NEXT(typeinfo);
+ RETURN_IF_BAD_POINTER(NULL, *typeinfo);
+ }
+ *count = rescnt;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ typeinfo = (Win16NETypeInfo *) fi->first_resource;
+ for (c = 0 ; c < rescnt ; c++) {
+ wr[c].this_ = typeinfo;
+ wr[c].is_directory = (typeinfo->count != 0);
+ wr[c].children = typeinfo+1;
+ wr[c].level = 0;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_ne_resource_id(fi, wr + c, typeinfo->type_id))
+ return NULL;
+
+ typeinfo = NE_TYPEINFO_NEXT(typeinfo);
+ }
+
+ return wr;
+}
+
+/* list_resources:
+ * Return an array of WinResource's in the current
+ * resource level specified by res.
+ */
+Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) {
+ if (res != NULL && !res->is_directory)
+ return NULL;
+
+ if (fi->is_PE_binary) {
+ return list_pe_resources(fi, (Win32ImageResourceDirectory *)
+ (res == NULL ? fi->first_resource : res->children),
+ (res == NULL ? 0 : res->level+1),
+ count);
+ } else {
+ return (res == NULL
+ ? list_ne_type_resources(fi, count)
+ : list_ne_name_resources(fi, res, count));
+ }
+}
+
+/* read_library:
+ * Read header and get resource directory offset in a Windows library
+ * (AKA module).
+ *
+ */
+bool Win32ResExtractor::read_library(WinLibrary *fi) {
+ /* check for DOS header signature `MZ' */
+ RETURN_IF_BAD_POINTER(false, MZ_HEADER(fi->memory)->magic);
+ if (MZ_HEADER(fi->memory)->magic == IMAGE_DOS_SIGNATURE) {
+ DOSImageHeader *mz_header = MZ_HEADER(fi->memory);
+
+ RETURN_IF_BAD_POINTER(false, mz_header->lfanew);
+ if (mz_header->lfanew < sizeof(DOSImageHeader)) {
+ error("%s: not a Windows library", fi->file->name());
+ return false;
+ }
+ }
+
+ /* check for OS2 (Win16) header signature `NE' */
+ RETURN_IF_BAD_POINTER(false, NE_HEADER(fi->memory)->magic);
+ if (NE_HEADER(fi->memory)->magic == IMAGE_OS2_SIGNATURE) {
+ OS2ImageHeader *header = NE_HEADER(fi->memory);
+
+ RETURN_IF_BAD_POINTER(false, header->rsrctab);
+ RETURN_IF_BAD_POINTER(false, header->restab);
+ if (header->rsrctab >= header->restab) {
+ error("%s: no resource directory found", fi->file->name());
+ return false;
+ }
+
+ fi->is_PE_binary = false;
+ fi->first_resource = (byte *) NE_HEADER(fi->memory)
+ + header->rsrctab + sizeof(uint16);
+ RETURN_IF_BAD_POINTER(false, *(Win16NETypeInfo *) fi->first_resource);
+
+ return true;
+ }
+
+ /* check for NT header signature `PE' */
+ RETURN_IF_BAD_POINTER(false, PE_HEADER(fi->memory)->signature);
+ if (PE_HEADER(fi->memory)->signature == IMAGE_NT_SIGNATURE) {
+ Win32ImageSectionHeader *pe_sec;
+ Win32ImageDataDirectory *dir;
+ Win32ImageNTHeaders *pe_header;
+ int d;
+
+ /* allocate new memory */
+ fi->total_size = calc_vma_size(fi);
+ if (fi->total_size == 0) {
+ /* calc_vma_size has reported error */
+ return false;
+ }
+ fi->memory = (byte *)realloc(fi->memory, fi->total_size);
+
+ /* relocate memory, start from last section */
+ pe_header = PE_HEADER(fi->memory);
+ RETURN_IF_BAD_POINTER(false, pe_header->file_header.number_of_sections);
+
+ /* we don't need to do OFFSET checking for the sections.
+ * calc_vma_size has already done that */
+ for (d = pe_header->file_header.number_of_sections - 1; d >= 0 ; d--) {
+ pe_sec = PE_SECTIONS(fi->memory) + d;
+
+ if (pe_sec->characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
+ continue;
+
+ //if (pe_sec->virtual_address + pe_sec->size_of_raw_data > fi->total_size)
+
+ RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->virtual_address, pe_sec->size_of_raw_data);
+ RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->pointer_to_raw_data, pe_sec->size_of_raw_data);
+ if (pe_sec->virtual_address != pe_sec->pointer_to_raw_data) {
+ memmove(fi->memory + pe_sec->virtual_address,
+ fi->memory + pe_sec->pointer_to_raw_data,
+ pe_sec->size_of_raw_data);
+ }
+ }
+
+ /* find resource directory */
+ RETURN_IF_BAD_POINTER(false, pe_header->optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
+ dir = pe_header->optional_header.data_directory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
+ if (dir->size == 0) {
+ error("%s: file contains no resources", fi->file->name());
+ return false;
+ }
+
+ fi->first_resource = fi->memory + dir->virtual_address;
+ fi->is_PE_binary = true;
+ return true;
+ }
+
+ /* other (unknown) header signature was found */
+ error("%s: not a Windows library", fi->file->name());
+ return false;
+}
+
+/* calc_vma_size:
+ * Calculate the total amount of memory needed for a 32-bit Windows
+ * module. Returns -1 if file was too small.
+ */
+int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
+ Win32ImageSectionHeader *seg;
+ int c, segcount, size;
+
+ size = 0;
+ RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections);
+ segcount = PE_HEADER(fi->memory)->file_header.number_of_sections;
+
+ /* If there are no segments, just process file like it is.
+ * This is (probably) not the right thing to do, but problems
+ * will be delt with later anyway.
+ */
+ if (segcount == 0)
+ return fi->total_size;
+
+ seg = PE_SECTIONS(fi->memory);
+ RETURN_IF_BAD_POINTER(-1, *seg);
+ for (c = 0 ; c < segcount ; c++) {
+ RETURN_IF_BAD_POINTER(0, *seg);
+
+ size = MAX((uint32)size, seg->virtual_address + seg->size_of_raw_data);
+ /* I have no idea what misc.virtual_size is for... */
+ size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size);
+ seg++;
+ }
+
+ return size;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) {
+ int c, rescnt;
+ WinResource *return_wr;
+
+ wr = list_resources(fi, wr, &rescnt);
+ if (wr == NULL)
+ return NULL;
+
+ for (c = 0 ; c < rescnt ; c++) {
+ if (compare_resource_id(&wr[c], id)) {
+ /* duplicate WinResource and return it */
+ return_wr = (WinResource *)malloc(sizeof(WinResource));
+ memcpy(return_wr, &wr[c], sizeof(WinResource));
+
+ /* free old WinResource */
+ free(wr);
+ return return_wr;
+ }
+ }
+
+ return NULL;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::find_resource(WinLibrary *fi, const char *type, const char *name, const char *language, int *level) {
+ WinResource *wr;
+
+ *level = 0;
+ if (type == NULL)
+ return NULL;
+ wr = find_with_resource_array(fi, NULL, type);
+ if (wr == NULL || !wr->is_directory)
+ return wr;
+
+ *level = 1;
+ if (name == NULL)
+ return wr;
+ wr = find_with_resource_array(fi, wr, name);
+ if (wr == NULL || !wr->is_directory)
+ return wr;
+
+ *level = 2;
+ if (language == NULL)
+ return wr;
+ wr = find_with_resource_array(fi, wr, language);
+ return wr;
+}
+
+#define ROW_BYTES(bits) ((((bits) + 31) >> 5) << 2)
+
+
+int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **pal, int *palSize) {
+ Win32CursorIconFileDir dir;
+ Win32CursorIconFileDirEntry *entries = NULL;
+ uint32 offset;
+ uint32 c, d;
+ int completed;
+ int matched = 0;
+ MemoryReadStream *in = new MemoryReadStream(data, datasize);
+
+ if (!in->read(&dir, sizeof(Win32CursorIconFileDir)- sizeof(Win32CursorIconFileDirEntry)))
+ goto cleanup;
+ fix_win32_cursor_icon_file_dir_endian(&dir);
+
+ if (dir.reserved != 0) {
+ error("not an icon or cursor file (reserved non-zero)");
+ goto cleanup;
+ }
+ if (dir.type != 1 && dir.type != 2) {
+ error("not an icon or cursor file (wrong type)");
+ goto cleanup;
+ }
+
+ entries = (Win32CursorIconFileDirEntry *)malloc(dir.count * sizeof(Win32CursorIconFileDirEntry));
+ for (c = 0; c < dir.count; c++) {
+ if (!in->read(&entries[c], sizeof(Win32CursorIconFileDirEntry)))
+ goto cleanup;
+ fix_win32_cursor_icon_file_dir_entry_endian(&entries[c]);
+ if (entries[c].reserved != 0)
+ error("reserved is not zero");
+ }
+
+ offset = sizeof(Win32CursorIconFileDir) + (dir.count - 1) * (sizeof(Win32CursorIconFileDirEntry));
+
+ for (completed = 0; completed < dir.count; ) {
+ uint32 min_offset = 0x7fffffff;
+ int previous = completed;
+
+ for (c = 0; c < dir.count; c++) {
+ if (entries[c].dib_offset == offset) {
+ Win32BitmapInfoHeader bitmap;
+ Win32RGBQuad *palette = NULL;
+ uint32 palette_count = 0;
+ uint32 image_size, mask_size;
+ uint32 width, height;
+ byte *image_data = NULL, *mask_data = NULL;
+ byte *row = NULL;
+
+ if (!in->read(&bitmap, sizeof(Win32BitmapInfoHeader)))
+ goto local_cleanup;
+
+ fix_win32_bitmap_info_header_endian(&bitmap);
+ if (bitmap.size < sizeof(Win32BitmapInfoHeader)) {
+ error("bitmap header is too short");
+ goto local_cleanup;
+ }
+ if (bitmap.compression != 0) {
+ error("compressed image data not supported");
+ goto local_cleanup;
+ }
+ if (bitmap.x_pels_per_meter != 0)
+ error("x_pels_per_meter field in bitmap should be zero");
+ if (bitmap.y_pels_per_meter != 0)
+ error("y_pels_per_meter field in bitmap should be zero");
+ if (bitmap.clr_important != 0)
+ error("clr_important field in bitmap should be zero");
+ if (bitmap.planes != 1)
+ error("planes field in bitmap should be one");
+ if (bitmap.size != sizeof(Win32BitmapInfoHeader)) {
+ uint32 skip = bitmap.size - sizeof(Win32BitmapInfoHeader);
+ error("skipping %d bytes of extended bitmap header", skip);
+ in->seek(skip, SEEK_CUR);
+ }
+ offset += bitmap.size;
+
+ if (bitmap.clr_used != 0 || bitmap.bit_count < 24) {
+ palette_count = (bitmap.clr_used != 0 ? bitmap.clr_used : 1 << bitmap.bit_count);
+ palette = (Win32RGBQuad *)malloc(sizeof(Win32RGBQuad) * palette_count);
+ if (!in->read(palette, sizeof(Win32RGBQuad) * palette_count))
+ goto local_cleanup;
+ offset += sizeof(Win32RGBQuad) * palette_count;
+ }
+
+ width = bitmap.width;
+ height = ABS(bitmap.height)/2;
+
+ image_size = height * ROW_BYTES(width * bitmap.bit_count);
+ mask_size = height * ROW_BYTES(width);
+
+ if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
+ debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)",
+ entries[c].dib_size,
+ bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)
+ );
+
+ image_data = (byte *)malloc(image_size);
+ if (!in->read(image_data, image_size))
+ goto local_cleanup;
+
+ mask_data = (byte *)malloc(mask_size);
+ if (!in->read(mask_data, mask_size))
+ goto local_cleanup;
+
+ offset += image_size;
+ offset += mask_size;
+ completed++;
+ matched++;
+
+ *hotspot_x = entries[c].hotspot_x;
+ *hotspot_y = entries[c].hotspot_y;
+ *w = width;
+ *h = height;
+ *keycolor = 0;
+ *cursor = (byte *)malloc(width * height);
+
+ row = (byte *)malloc(width * 4);
+
+ for (d = 0; d < height; d++) {
+ uint32 x;
+ uint32 y = (bitmap.height < 0 ? d : height - d - 1);
+ uint32 imod = y * (image_size / height) * 8 / bitmap.bit_count;
+ //uint32 mmod = y * (mask_size / height) * 8;
+
+ for (x = 0; x < width; x++) {
+
+ uint32 color = simple_vec(image_data, x + imod, bitmap.bit_count);
+
+ // FIXME?: This works only with b/w cursors and white index may be
+ // different. But now it's enough.
+ if (color) {
+ cursor[0][width * d + x] = 15; // white in SCUMM
+ } else {
+ cursor[0][width * d + x] = 255; // transparent
+ }
+ /*
+
+ if (bitmap.bit_count <= 16) {
+ if (color >= palette_count) {
+ error("color out of range in image data");
+ goto local_cleanup;
+ }
+ row[4*x+0] = palette[color].red;
+ row[4*x+1] = palette[color].green;
+ row[4*x+2] = palette[color].blue;
+
+ } else {
+ row[4*x+0] = (color >> 16) & 0xFF;
+ row[4*x+1] = (color >> 8) & 0xFF;
+ row[4*x+2] = (color >> 0) & 0xFF;
+ }
+ if (bitmap.bit_count == 32)
+ row[4*x+3] = (color >> 24) & 0xFF;
+ else
+ row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
+ */
+ }
+
+ }
+
+ if (row != NULL)
+ free(row);
+ if (palette != NULL)
+ free(palette);
+ if (image_data != NULL) {
+ free(image_data);
+ free(mask_data);
+ }
+ continue;
+
+ local_cleanup:
+
+ if (row != NULL)
+ free(row);
+ if (palette != NULL)
+ free(palette);
+ if (image_data != NULL) {
+ free(image_data);
+ free(mask_data);
+ }
+ goto cleanup;
+ } else {
+ if (entries[c].dib_offset > offset)
+ min_offset = MIN(min_offset, entries[c].dib_offset);
+ }
+ }
+
+ if (previous == completed) {
+ if (min_offset < offset) {
+ error("offset of bitmap header incorrect (too low)");
+ goto cleanup;
+ }
+ debugC(DEBUG_RESOURCE, "skipping %d bytes of garbage at %d", min_offset-offset, offset);
+ in->seek(min_offset - offset, SEEK_CUR);
+ offset = min_offset;
+ }
+ }
+
+ free(entries);
+ return matched;
+
+cleanup:
+
+ free(entries);
+ return -1;
+}
+
+uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) {
+ switch (size) {
+ case 1:
+ return (data[ofs/8] >> (7 - ofs%8)) & 1;
+ case 2:
+ return (data[ofs/4] >> ((3 - ofs%4) << 1)) & 3;
+ case 4:
+ return (data[ofs/2] >> ((1 - ofs%2) << 2)) & 15;
+ case 8:
+ return data[ofs];
+ case 16:
+ return data[2*ofs] | data[2*ofs+1] << 8;
+ case 24:
+ return data[3*ofs] | data[3*ofs+1] << 8 | data[3*ofs+2] << 16;
+ case 32:
+ return data[4*ofs] | data[4*ofs+1] << 8 | data[4*ofs+2] << 16 | data[4*ofs+3] << 24;
+ }
+
+ return 0;
+}
+
+#define LE16(x) ((x) = TO_LE_16(x))
+#define LE32(x) ((x) = TO_LE_32(x))
+
+void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) {
+ LE16(obj->reserved);
+ LE16(obj->type);
+ LE16(obj->count);
+}
+
+void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) {
+ LE32(obj->size);
+ LE32(obj->width);
+ LE32(obj->height);
+ LE16(obj->planes);
+ LE16(obj->bit_count);
+ LE32(obj->compression);
+ LE32(obj->size_image);
+ LE32(obj->x_pels_per_meter);
+ LE32(obj->y_pels_per_meter);
+ LE32(obj->clr_used);
+ LE32(obj->clr_important);
+}
+
+void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) {
+ LE16(obj->hotspot_x);
+ LE16(obj->hotspot_y);
+ LE32(obj->dib_size);
+ LE32(obj->dib_offset);
+}
+
+void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) {
+ LE32(obj->misc.physical_address);
+ LE32(obj->virtual_address);
+ LE32(obj->size_of_raw_data);
+ LE32(obj->pointer_to_raw_data);
+ LE32(obj->pointer_to_relocations);
+ LE32(obj->pointer_to_linenumbers);
+ LE16(obj->number_of_relocations);
+ LE16(obj->number_of_linenumbers);
+ LE32(obj->characteristics);
+}
+
+void Win32ResExtractor::fix_os2_image_header_endian(OS2ImageHeader *obj) {
+ LE16(obj->magic);
+ LE16(obj->enttab);
+ LE16(obj->cbenttab);
+ LE32(obj->crc);
+ LE16(obj->flags);
+ LE16(obj->autodata);
+ LE16(obj->heap);
+ LE16(obj->stack);
+ LE32(obj->csip);
+ LE32(obj->sssp);
+ LE16(obj->cseg);
+ LE16(obj->cmod);
+ LE16(obj->cbnrestab);
+ LE16(obj->segtab);
+ LE16(obj->rsrctab);
+ LE16(obj->restab);
+ LE16(obj->modtab);
+ LE16(obj->imptab);
+ LE32(obj->nrestab);
+ LE16(obj->cmovent);
+ LE16(obj->align);
+ LE16(obj->cres);
+ LE16(obj->fastload_offset);
+ LE16(obj->fastload_length);
+ LE16(obj->swaparea);
+ LE16(obj->expver);
+}
+
+/* fix_win32_image_header_endian:
+ * NOTE: This assumes that the optional header is always available.
+ */
+void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) {
+ LE32(obj->signature);
+ LE16(obj->file_header.machine);
+ LE16(obj->file_header.number_of_sections);
+ LE32(obj->file_header.time_date_stamp);
+ LE32(obj->file_header.pointer_to_symbol_table);
+ LE32(obj->file_header.number_of_symbols);
+ LE16(obj->file_header.size_of_optional_header);
+ LE16(obj->file_header.characteristics);
+ LE16(obj->optional_header.magic);
+ LE32(obj->optional_header.size_of_code);
+ LE32(obj->optional_header.size_of_initialized_data);
+ LE32(obj->optional_header.size_of_uninitialized_data);
+ LE32(obj->optional_header.address_of_entry_point);
+ LE32(obj->optional_header.base_of_code);
+ LE32(obj->optional_header.base_of_data);
+ LE32(obj->optional_header.image_base);
+ LE32(obj->optional_header.section_alignment);
+ LE32(obj->optional_header.file_alignment);
+ LE16(obj->optional_header.major_operating_system_version);
+ LE16(obj->optional_header.minor_operating_system_version);
+ LE16(obj->optional_header.major_image_version);
+ LE16(obj->optional_header.minor_image_version);
+ LE16(obj->optional_header.major_subsystem_version);
+ LE16(obj->optional_header.minor_subsystem_version);
+ LE32(obj->optional_header.win32_version_value);
+ LE32(obj->optional_header.size_of_image);
+ LE32(obj->optional_header.size_of_headers);
+ LE32(obj->optional_header.checksum);
+ LE16(obj->optional_header.subsystem);
+ LE16(obj->optional_header.dll_characteristics);
+ LE32(obj->optional_header.size_of_stack_reserve);
+ LE32(obj->optional_header.size_of_stack_commit);
+ LE32(obj->optional_header.size_of_heap_reserve);
+ LE32(obj->optional_header.size_of_heap_commit);
+ LE32(obj->optional_header.loader_flags);
+ LE32(obj->optional_header.number_of_rva_and_sizes);
+}
+
+void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) {
+ LE32(obj->virtual_address);
+ LE32(obj->size);
+}
+
+
+MacResExtractor::MacResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
+ _resOffset = -1;
+}
+
+int MacResExtractor::extractResource(int id, byte **buf) {
+ Common::File in;
+ int size;
+
+ if (!_fileName[0]) // We are running for the first time
+ if (_vm->_substResFileNameIndex > 0) {
+ char buf1[128];
+
+ snprintf(buf1, 128, "%s.he3", _vm->getBaseName());
+ _vm->generateSubstResFileName(buf1, _fileName, sizeof(buf1));
+
+ // Some programs write it as .bin. Try that too
+ if (!in.exists(_fileName)) {
+ strcpy(buf1, _fileName);
+ snprintf(_fileName, 128, "%s.bin", buf1);
+
+ if (!in.exists(_fileName)) {
+ // And finally check if we have dumped resource fork
+ snprintf(_fileName, 128, "%s.rsrc", buf1);
+ if (!in.exists(_fileName)) {
+ error("Cannot open file any of files '%s', '%s.bin', '%s.rsrc",
+ buf1, buf1, buf1);
+ }
+ }
+ }
+ }
+
+ in.open(_fileName);
+ if (!in.isOpen()) {
+ error("Cannot open file %s", _fileName);
+ }
+
+ // we haven't calculated it
+ if (_resOffset == -1) {
+ if (!init(in))
+ error("Resource fork is missing in file '%s'", _fileName);
+ in.close();
+ in.open(_fileName);
+ }
+
+ *buf = getResource(in, "crsr", 1000 + id, &size);
+
+ in.close();
+
+ if (*buf == NULL)
+ error("There is no cursor ID #%d", 1000 + id);
+
+ return size;
+}
+
+#define MBI_INFOHDR 128
+#define MBI_ZERO1 0
+#define MBI_NAMELEN 1
+#define MBI_ZERO2 74
+#define MBI_ZERO3 82
+#define MBI_DFLEN 83
+#define MBI_RFLEN 87
+#define MAXNAMELEN 63
+
+bool MacResExtractor::init(Common::File in) {
+ byte infoHeader[MBI_INFOHDR];
+ int32 data_size, rsrc_size;
+ int32 data_size_pad, rsrc_size_pad;
+ int filelen;
+
+ filelen = in.size();
+ in.read(infoHeader, MBI_INFOHDR);
+
+ // Maybe we have MacBinary?
+ if (infoHeader[MBI_ZERO1] == 0 && infoHeader[MBI_ZERO2] == 0 &&
+ infoHeader[MBI_ZERO3] == 0 && infoHeader[MBI_NAMELEN] <= MAXNAMELEN) {
+
+ // Pull out fork lengths
+ data_size = READ_BE_UINT32(infoHeader + MBI_DFLEN);
+ rsrc_size = READ_BE_UINT32(infoHeader + MBI_RFLEN);
+
+ data_size_pad = (((data_size + 127) >> 7) << 7);
+ rsrc_size_pad = (((rsrc_size + 127) >> 7) << 7);
+
+ // Length check
+ int sumlen = MBI_INFOHDR + data_size_pad + rsrc_size_pad;
+
+ if (sumlen == filelen)
+ _resOffset = MBI_INFOHDR + data_size_pad;
+ }
+
+ if (_resOffset == -1) // MacBinary check is failed
+ _resOffset = 0; // Maybe we have dumped fork?
+
+ in.seek(_resOffset);
+
+ _dataOffset = in.readUint32BE() + _resOffset;
+ _mapOffset = in.readUint32BE() + _resOffset;
+ _dataLength = in.readUint32BE();
+ _mapLength = in.readUint32BE();
+
+ // do sanity check
+ if (_dataOffset >= filelen || _mapOffset >= filelen ||
+ _dataLength + _mapLength > filelen) {
+ _resOffset = -1;
+ return false;
+ }
+
+ debug(7, "got header: data %d [%d] map %d [%d]",
+ _dataOffset, _dataLength, _mapOffset, _mapLength);
+
+ readMap(in);
+
+ return true;
+}
+
+byte *MacResExtractor::getResource(Common::File in, const char *typeID, int16 resID, int *size) {
+ int i;
+ int typeNum = -1;
+ int resNum = -1;
+ byte *buf;
+ int len;
+
+ for (i = 0; i < _resMap.numTypes; i++)
+ if (strcmp(_resTypes[i].id, typeID) == 0) {
+ typeNum = i;
+ break;
+ }
+
+ if (typeNum == -1)
+ return NULL;
+
+ for (i = 0; i < _resTypes[typeNum].items; i++)
+ if (_resLists[typeNum][i].id == resID) {
+ resNum = i;
+ break;
+ }
+
+ if (resNum == -1)
+ return NULL;
+
+ in.seek(_dataOffset + _resLists[typeNum][resNum].dataOffset);
+
+ len = in.readUint32BE();
+ buf = (byte *)malloc(len);
+
+ in.read(buf, len);
+
+ *size = len;
+
+ return buf;
+}
+
+void MacResExtractor::readMap(Common::File in) {
+ int i, j, len;
+
+ in.seek(_mapOffset + 22);
+
+ _resMap.resAttr = in.readUint16BE();
+ _resMap.typeOffset = in.readUint16BE();
+ _resMap.nameOffset = in.readUint16BE();
+ _resMap.numTypes = in.readUint16BE();
+ _resMap.numTypes++;
+
+ in.seek(_mapOffset + _resMap.typeOffset + 2);
+ _resTypes = new ResType[_resMap.numTypes];
+
+ for (i = 0; i < _resMap.numTypes; i++) {
+ in.read(_resTypes[i].id, 4);
+ _resTypes[i].id[4] = 0;
+ _resTypes[i].items = in.readUint16BE();
+ _resTypes[i].offset = in.readUint16BE();
+ _resTypes[i].items++;
+ }
+
+ _resLists = new ResPtr[_resMap.numTypes];
+
+ for (i = 0; i < _resMap.numTypes; i++) {
+ _resLists[i] = new Resource[_resTypes[i].items];
+ in.seek(_resTypes[i].offset + _mapOffset + _resMap.typeOffset);
+
+ for (j = 0; j < _resTypes[i].items; j++) {
+ ResPtr resPtr = _resLists[i] + j;
+
+ resPtr->id = in.readUint16BE();
+ resPtr->nameOffset = in.readUint16BE();
+ resPtr->dataOffset = in.readUint32BE();
+ in.readUint32BE();
+ resPtr->name = 0;
+
+ resPtr->attr = resPtr->dataOffset >> 24;
+ resPtr->dataOffset &= 0xFFFFFF;
+ }
+
+ for (j = 0; j < _resTypes[i].items; j++) {
+ if (_resLists[i][j].nameOffset != -1) {
+ in.seek(_resLists[i][j].nameOffset + _mapOffset + _resMap.nameOffset);
+
+ len = in.readByte();
+ _resLists[i][j].name = new byte[len + 1];
+ _resLists[i][j].name[len] = 0;
+ in.read(_resLists[i][j].name, len);
+ }
+ }
+ }
+}
+
+int MacResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize) {
+ Common::MemoryReadStream dis(data, datasize);
+ int i, b;
+ byte imageByte;
+ byte *iconData;
+ int numBytes;
+ int pixelsPerByte, bpp;
+ int ctSize;
+ byte bitmask;
+ int iconRowBytes, iconBounds[4];
+ int ignored;
+ int iconDataSize;
+
+ dis.readUint16BE(); // type
+ dis.readUint32BE(); // offset to pixel map
+ dis.readUint32BE(); // offset to pixel data
+ dis.readUint32BE(); // expanded cursor data
+ dis.readUint16BE(); // expanded data depth
+ dis.readUint32BE(); // reserved
+
+ // Grab B/W icon data
+ *cursor = (byte *)malloc(16 * 16);
+ for (i = 0; i < 32; i++) {
+ imageByte = dis.readByte();
+ for (b = 0; b < 8; b++)
+ cursor[0][i*8+b] = (byte)((imageByte &
+ (0x80 >> b)) > 0? 0x0F: 0x00);
+ }
+
+ // Apply mask data
+ for (i = 0; i < 32; i++) {
+ imageByte = dis.readByte();
+ for (b = 0; b < 8; b++)
+ if ((imageByte & (0x80 >> b)) == 0)
+ cursor[0][i*8+b] = 0xff;
+ }
+
+ *hotspot_y = dis.readUint16BE();
+ *hotspot_x = dis.readUint16BE();
+ *w = *h = 16;
+
+ // Use b/w cursor on backends which don't support cursor palettes
+ if (!_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette))
+ return 1;
+
+ dis.readUint32BE(); // reserved
+ dis.readUint32BE(); // cursorID
+
+ // Color version of cursor
+ dis.readUint32BE(); // baseAddr
+
+ // Keep only lowbyte for now
+ dis.readByte();
+ iconRowBytes = dis.readByte();
+
+ if (!iconRowBytes)
+ return 1;
+
+ iconBounds[0] = dis.readUint16BE();
+ iconBounds[1] = dis.readUint16BE();
+ iconBounds[2] = dis.readUint16BE();
+ iconBounds[3] = dis.readUint16BE();
+
+ dis.readUint16BE(); // pmVersion
+ dis.readUint16BE(); // packType
+ dis.readUint32BE(); // packSize
+
+ dis.readUint32BE(); // hRes
+ dis.readUint32BE(); // vRes
+
+ dis.readUint16BE(); // pixelType
+ dis.readUint16BE(); // pixelSize
+ dis.readUint16BE(); // cmpCount
+ dis.readUint16BE(); // cmpSize
+
+ dis.readUint32BE(); // planeByte
+ dis.readUint32BE(); // pmTable
+ dis.readUint32BE(); // reserved
+
+ // Pixel data for cursor
+ iconDataSize = iconRowBytes * (iconBounds[3] - iconBounds[1]);
+ iconData = (byte *)malloc(iconDataSize);
+ dis.read(iconData, iconDataSize);
+
+ // Color table
+ dis.readUint32BE(); // ctSeed
+ dis.readUint16BE(); // ctFlag
+ ctSize = dis.readUint16BE() + 1;
+
+ *palette = (byte *)malloc(ctSize * 4);
+
+ // Read just high byte of 16-bit color
+ for (int c = 0; c < ctSize; c++) {
+ // We just use indices 0..ctSize, so ignore color ID
+ dis.readUint16BE(); // colorID[c]
+
+ palette[0][c * 4 + 0] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 1] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 2] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 3] = 0;
+ }
+
+ *palSize = ctSize;
+
+ numBytes =
+ (iconBounds[2] - iconBounds[0]) *
+ (iconBounds[3] - iconBounds[1]);
+
+ pixelsPerByte = (iconBounds[2] - iconBounds[0]) / iconRowBytes;
+ bpp = 8 / pixelsPerByte;
+
+ // build a mask to make sure the pixels are properly shifted out
+ bitmask = 0;
+ for (int m = 0; m < bpp; m++) {
+ bitmask <<= 1;
+ bitmask |= 1;
+ }
+
+ // Extract pixels from bytes
+ for (int j = 0; j < iconDataSize; j++)
+ for (b = 0; b < pixelsPerByte; b++) {
+ int idx = j * pixelsPerByte + (pixelsPerByte - 1 - b);
+
+ if (cursor[0][idx] != 0xff) // if mask is not there
+ cursor[0][idx] = (byte)((iconData[j] >> (b * bpp)) & bitmask);
+ }
+
+ free(iconData);
+
+ assert(datasize - dis.pos() == 0);
+
+ return 1;
+}
+
+
+
+void ScummEngine_v70he::readRoomsOffsets() {
+ int num, i;
+ byte *ptr;
+
+ debug(9, "readRoomOffsets()");
+
+ num = READ_LE_UINT16(_heV7RoomOffsets);
+ ptr = _heV7RoomOffsets + 2;
+ for (i = 0; i < num; i++) {
+ res.roomoffs[rtRoom][i] = READ_LE_UINT32(ptr);
+ ptr += 4;
+ }
+}
+
+void ScummEngine_v70he::readGlobalObjects() {
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ _fileHandle->read(_objectStateTable, num);
+ _fileHandle->read(_objectOwnerTable, num);
+ _fileHandle->read(_objectRoomTable, num);
+
+ _fileHandle->read(_classData, num * sizeof(uint32));
+
+#if defined(SCUMM_BIG_ENDIAN)
+ // Correct the endianess if necessary
+ for (int i = 0; i != num; i++)
+ _classData[i] = FROM_LE_32(_classData[i]);
+#endif
+}
+
+void ScummEngine_v99he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v99he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numSprites = _fileHandle->readUint16LE();
+ _numLocalScripts = _fileHandle->readUint16LE();
+ _HEHeapSize = _fileHandle->readUint16LE();
+ _numPalettes = _fileHandle->readUint16LE();
+ _numUnk = _fileHandle->readUint16LE();
+ _numTalkies = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 2048;
+}
+
+void ScummEngine_v90he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v90he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numSprites = _fileHandle->readUint16LE();
+ _numLocalScripts = _fileHandle->readUint16LE();
+ _HEHeapSize = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ if (_features & GF_HE_985)
+ _numGlobalScripts = 2048;
+ else
+ _numGlobalScripts = 200;
+}
+
+void ScummEngine_v72he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v72he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numBitVariables = _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numVerbs = _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 200;
+}
+
+byte *ScummEngine_v72he::getStringAddress(int i) {
+ byte *addr = getResourceAddress(rtString, i);
+ if (addr == NULL)
+ return NULL;
+ return ((ScummEngine_v72he::ArrayHeader *)addr)->data;
+}
+
+int ScummEngine_v72he::getSoundResourceSize(int id) {
+ const byte *ptr;
+ int offs, size;
+
+ if (id > _numSounds) {
+ if (!_sound->getHEMusicDetails(id, offs, size)) {
+ debug(0, "getSoundResourceSize: musicID %d not found", id);
+ return 0;
+ }
+ } else {
+ ptr = getResourceAddress(rtSound, id);
+ if (!ptr)
+ return 0;
+
+ if (READ_UINT32(ptr) == MKID('RIFF')) {
+ byte flags;
+ int rate;
+
+ size = READ_BE_UINT32(ptr + 4);
+ Common::MemoryReadStream stream(ptr, size);
+
+ if (!loadWAVFromStream(stream, size, rate, flags)) {
+ error("getSoundResourceSize: Not a valid WAV file");
+ }
+ } else {
+ ptr += 8 + READ_BE_UINT32(ptr + 12);
+ if (READ_UINT32(ptr) == MKID('SBNG')) {
+ ptr += READ_BE_UINT32(ptr + 4);
+ }
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ }
+ }
+
+ return size;
+}
+
+void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
+ debug(0, "createSound: snd1id %d snd2id %d", snd1id, snd2id);
+
+ byte *snd1Ptr, *snd2Ptr;
+ byte *sbng1Ptr, *sbng2Ptr;
+ byte *sdat1Ptr, *sdat2Ptr;
+ byte *src, *dst, *tmp;
+ int len, offs, size;
+ int sdat1size, sdat2size;
+
+ if (snd2id == -1) {
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+ return;
+ }
+
+ if (snd1id != _curSndId) {
+ _curSndId = snd1id;
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+ }
+
+ snd1Ptr = getResourceAddress(rtSound, snd1id);
+ assert(snd1Ptr);
+ snd2Ptr = getResourceAddress(rtSound, snd2id);
+ assert(snd2Ptr);
+
+ int i;
+ int chan = -1;
+ for (i = 0; i < ARRAYSIZE(_sound->_heChannel); i++) {
+ if (_sound->_heChannel[i].sound == snd1id)
+ chan = i;
+ }
+
+ sbng1Ptr = heFindResource(MKID('SBNG'), snd1Ptr);
+ sbng2Ptr = heFindResource(MKID('SBNG'), snd2Ptr);
+
+ if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
+ if (chan != -1 && _sound->_heChannel[chan].codeOffs > 0) {
+ int curOffs = _sound->_heChannel[chan].codeOffs;
+
+ src = snd1Ptr + curOffs;
+ dst = sbng1Ptr + 8;
+ size = READ_BE_UINT32(sbng1Ptr + 4);
+ len = sbng1Ptr - snd1Ptr + size - curOffs;
+
+ byte *data = (byte *)malloc(len);
+ memcpy(data, src, len);
+ memcpy(dst, data, len);
+ free(data);
+
+ dst = sbng1Ptr + 8;
+ while ((size = READ_LE_UINT16(dst)) != 0)
+ dst += size;
+ } else {
+ dst = sbng1Ptr + 8;
+ }
+
+ _sound->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8;
+
+ tmp = sbng2Ptr + 8;
+ while ((offs = READ_LE_UINT16(tmp)) != 0) {
+ tmp += offs;
+ }
+
+ src = sbng2Ptr + 8;
+ len = tmp - sbng2Ptr - 6;
+ memcpy(dst, src, len);
+
+ int32 time;
+ while ((size = READ_LE_UINT16(dst)) != 0) {
+ time = READ_LE_UINT32(dst + 2);
+ time += _sndTmrOffs;
+ WRITE_LE_UINT32(dst + 2, time);
+ dst += size;
+ }
+ }
+
+ sdat1Ptr = heFindResource(MKID('SDAT'), snd1Ptr);
+ assert(sdat1Ptr);
+ sdat2Ptr = heFindResource(MKID('SDAT'), snd2Ptr);
+ assert(sdat2Ptr);
+
+ sdat1size = READ_BE_UINT32(sdat1Ptr + 4) - 8 - _sndPtrOffs;
+ sdat2size = READ_BE_UINT32(sdat2Ptr + 4) - 8;
+
+ debug(0, "SDAT size1 %d size2 %d", sdat1size, sdat2size);
+ if (sdat2size < sdat1size) {
+ src = sdat2Ptr + 8;
+ dst = sdat1Ptr + 8 + _sndPtrOffs;
+ len = sdat2size;
+
+ memcpy(dst, src, len);
+
+ _sndPtrOffs += sdat2size;
+ _sndTmrOffs += sdat2size;
+ } else {
+ src = sdat2Ptr + 8;
+ dst = sdat1Ptr + 8 + _sndPtrOffs;
+ len = sdat1size;
+
+ memcpy(dst, src, len);
+
+ if (sdat2size != sdat1size) {
+ src = sdat2Ptr + 8 + sdat1size;
+ dst = sdat1Ptr + 8;
+ len = sdat2size - sdat1size;
+
+ memcpy(dst, src, len);
+ }
+
+ _sndPtrOffs = sdat2size - sdat1size;
+ _sndTmrOffs += sdat2size;
+ }
+}
+
+} // End of namespace Scumm