Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

resource_he.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/scumm/he/resource_he.cpp $
 * $Id: resource_he.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */


#include "scumm/scumm.h"
#include "scumm/file.h"
#include "scumm/he/intern_he.h"
#include "scumm/resource.h"
#include "scumm/he/resource_he.h"
#include "scumm/he/sound_he.h"

#include "sound/wave.h"
#include "graphics/cursorman.h"

#include "common/stream.h"
#include "common/system.h"

namespace Scumm {

#define LE16(x)      ((x) = TO_LE_16(x))
#define LE32(x)      ((x) = TO_LE_32(x))


ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
      : _vm(scumm) {

      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 %lu", id, (long)(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 %lu", id, (long)(cc - _cursorCache));
            free(cursorRes);
            cc->valid = true;
            cc->id = id;
            cc->last_used = g_system->getMillis();
      }

      if (cc->palette)
            CursorMan.replaceCursorPalette(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 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.empty()) { // We are running for the first time
            _fileName = _vm->generateFilename(-3);
      }


      /* get file size */
      fi.file->open(_fileName);
      if (!fi.file->isOpen()) {
            error("Cannot open file %s", _fileName.c_str());
      }

      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;
      }

      /* errors will be printed by the callback */
      ressize = do_resources(&fi, arg_type, arg_name, arg_language, 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;
}

/* return the resource id quoted if it's a string, otherwise just return it */
char *Win32ResExtractor::WinResource::get_resource_id_quoted() {
      // FIXME: Using a static var here is EVIL and in fact, broken when
      // used multiple times in a row, e.g. in a single call to printf()
      // or debug()... which is in fact how we use this function... :-)
      static char tmp[WINRES_ID_MAXLEN+2];

      if (numeric_id || id[0] == '\0')
            return id;

      sprintf(tmp, "'%s'", id);
      return tmp;
}

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]",
        name_wr->get_resource_id_quoted(),
        (lang_wr->id[0] != '\0' ? " language: " : ""),
        lang_wr->get_resource_id_quoted(), 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 < FROM_LE_16(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,
                  FROM_LE_32(icondir->entries[c].bytes_in_res),
                  (is_icon ? icondir->entries[c].res_info.icon.width : FROM_LE_16(icondir->entries[c].res_info.cursor.width)),
                  (is_icon ? icondir->entries[c].res_info.icon.height : FROM_LE_16(icondir->entries[c].res_info.cursor.height)),
                  FROM_LE_16(icondir->entries[c].plane_count),
                  FROM_LE_16(icondir->entries[c].bit_count));*/

            /* find the corresponding icon resource */
            snprintf(name, sizeof(name)/sizeof(char), "-%d", FROM_LE_16(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 != FROM_LE_32(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, FROM_LE_32(icondir->entries[c].bytes_in_res));
                }
                size += iconsize; /* size += FROM_LE_32(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) + (FROM_LE_16(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 = TO_LE_16(FROM_LE_16(icondir->count) - skipped);

      /* transfer each cursor/icon: Win32CursorIconDirEntry and data */
      skipped = 0;
      for (c = 0 ; c < FROM_LE_16(icondir->count) ; c++) {
            int level;
            char name[14];
            WinResource *fwr;
            byte *data;

            /* find the corresponding icon resource */
            snprintf(name, sizeof(name)/sizeof(char), "-%d", FROM_LE_16(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 = TO_LE_16(FROM_LE_16(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 = TO_LE_32(offset);

            /* transfer resource into file memory */
            if (is_icon) {
                  memcpy(&memory[offset], data, FROM_LE_32(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,
                           FROM_LE_32(icondir->entries[c].bytes_in_res)-sizeof(uint16)*2);
                  offset -= sizeof(uint16)*2;
            }

            /* increase the offset pointer */
            offset += FROM_LE_32(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, 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, 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, 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, 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;
}

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 = FROM_LE_16(mem[0]);
            RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(uint16) * len);

            len = MIN(FROM_LE_16(mem[0]), (uint16)WINRES_ID_MAXLEN);
            for (c = 0 ; c < len ; c++)
                  wr->id[c] = FROM_LE_16(mem[c+1]) & 0x00FF;
            wr->id[len] = '\0';
            wr->numeric_id = false;
      } else {                            /* Unicode string id */
            /* translate id into a string */
            snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value);
            wr->numeric_id = true;
      }

      return true;
}

byte *Win32ResExtractor::get_resource_entry(WinLibrary *fi, WinResource *wr, int *size) {
      byte *result;

      Win32ImageResourceDataEntry *dataent;

      dataent = (Win32ImageResourceDataEntry *) wr->children;
      RETURN_IF_BAD_POINTER(NULL, *dataent);
      *size = FROM_LE_32(dataent->size);

      result = fi->memory + FROM_LE_32(dataent->offset_to_data);

      RETURN_IF_BAD_OFFSET(NULL, result, *size);

      return result;
}

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 = FROM_LE_16(pe_res->number_of_named_entries) + FROM_LE_16(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 = ((FROM_LE_32(dirent[c].offset_to_data) & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != 0);
            wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY);

            /* fill in wr->id, wr->numeric_id */
            if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name)))
                  return NULL;
      }

      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;

      return list_pe_resources(fi, (Win32ImageResourceDirectory *)
                   (res == NULL ? fi->first_resource : res->children),
                   (res == NULL ? 0 : res->level+1),
                   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 (FROM_LE_16(MZ_HEADER(fi->memory)->magic) == IMAGE_DOS_SIGNATURE) {
            DOSImageHeader *mz_header = MZ_HEADER(fi->memory);

            RETURN_IF_BAD_POINTER(false, mz_header->lfanew);

            // Apply endian fix (currently only lfanew is used from the DOSImageHeader,
            // so we don't bother to 'fix' the rest).
            LE32(mz_header->lfanew);

            if (mz_header->lfanew < sizeof(DOSImageHeader)) {
                  error("%s: not a Windows library", fi->file->name());
                  return false;
            }
      }

      /* check for NT header signature `PE' */
      RETURN_IF_BAD_POINTER(false, PE_HEADER(fi->memory)->signature);
      if (FROM_LE_32(PE_HEADER(fi->memory)->signature) == IMAGE_NT_SIGNATURE) {
            Win32ImageNTHeaders *pe_header;
            int d;

            // Fix image header endianess
            fix_win32_image_header_endian(PE_HEADER(fi->memory));

            /* 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--) {
                  Win32ImageSectionHeader *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 (FROM_LE_32(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]);
            Win32ImageDataDirectory *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;
            }

            fix_win32_image_data_directory(dir);

            fi->first_resource = fi->memory + dir->virtual_address;
            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);
            fix_win32_image_section_header(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);

                                    // We set up cursor palette for default cursor, so use it
                                    if (!simple_vec(mask_data, x + mmod, 1)) {
                                          if (color) {
                                                cursor[0][width * d + x] = 254; // white
                                          } else {
                                                cursor[0][width * d + x] = 253; // black
                                          }
                                    } 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;
                  }
                  assert(min_offset != 0x7fffffff);
                  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;
}

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);
}

/* 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);

      // FIXME: Does this assert ever trigger? If so, we should modify this function
      // to properly deal with it.
      assert(obj->file_header.size_of_optional_header >= sizeof(obj->optional_header));
    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.empty()) { // We are running for the first time
            _fileName = _vm->generateFilename(-3);

            // Some programs write it as .bin. Try that too
            if (!in.open(_fileName)) {
                  Common::String tmp(_fileName);

                  _fileName += ".bin";

                  if (!in.open(_fileName)) {
                        // And finally check if we have dumped resource fork
                        _fileName = tmp;
                        _fileName += ".bin";
                        if (!in.open(_fileName)) {
                              error("Cannot open file any of files '%s', '%s.bin', '%s.rsrc",
                                      tmp.c_str(), tmp.c_str(), tmp.c_str());
                        }
                  }
            }
      } else
            in.open(_fileName);

      if (!in.isOpen()) {
            error("Cannot open file %s", _fileName.c_str());
      }

      // we haven't calculated it
      if (_resOffset == -1) {
            if (!init(in))
                  error("Resource fork is missing in file '%s'", _fileName.c_str());
            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);
      assert(_objectStateTable);
      assert(_objectOwnerTable);

      _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
}

#ifndef DISABLE_HE
void ScummEngine_v99he::readMAXS(int blockSize) {
      if (blockSize == 52) {
            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;
      } else
            ScummEngine_v90he::readMAXS(blockSize);
}

void ScummEngine_v90he::readMAXS(int blockSize) {
      if (blockSize == 46) {
            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 (_game.features & GF_HE_985)
                  _numGlobalScripts = 2048;
            else
                  _numGlobalScripts = 200;
      } else
            ScummEngine_v72he::readMAXS(blockSize);
}

void ScummEngine_v72he::readMAXS(int blockSize) {
      if (blockSize == 40) {
            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;
      } else
            ScummEngine_v6::readMAXS(blockSize);
}

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 (!((SoundHE *)_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_BE_UINT32(ptr) == MKID_BE('RIFF')) {
                  byte flags;
                  int rate;

                  size = READ_BE_UINT32(ptr + 4);
                  Common::MemoryReadStream stream(ptr, size);

                  if (!Audio::loadWAVFromStream(stream, size, rate, flags)) {
                        error("getSoundResourceSize: Not a valid WAV file");
                  }
            } else {
                  ptr += 8 + READ_BE_UINT32(ptr + 12);
                  if (READ_BE_UINT32(ptr) == MKID_BE('SBNG')) {
                        ptr += READ_BE_UINT32(ptr + 4);
                  }

                  assert(READ_BE_UINT32(ptr) == MKID_BE('SDAT'));
                  size = READ_BE_UINT32(ptr + 4) - 8;
            }
      }

      return size;
}

void ScummEngine_v80he::createSound(int snd1id, int 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(((SoundHE *)_sound)->_heChannel); i++) {
            if (((SoundHE *)_sound)->_heChannel[i].sound == snd1id)
                  chan =  i;
      }

      sbng1Ptr = heFindResource(MKID_BE('SBNG'), snd1Ptr);
      sbng2Ptr = heFindResource(MKID_BE('SBNG'), snd2Ptr);

      if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
            if (chan != -1 && ((SoundHE *)_sound)->_heChannel[chan].codeOffs > 0) {
                  int curOffs = ((SoundHE *)_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;
            }

            ((SoundHE *)_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_BE('SDAT'), snd1Ptr);
      assert(sdat1Ptr);
      sdat2Ptr = heFindResource(MKID_BE('SDAT'), snd2Ptr);
      assert(sdat2Ptr);

      sdat1size = READ_BE_UINT32(sdat1Ptr + 4) - 8 - _sndPtrOffs;
      sdat2size = READ_BE_UINT32(sdat2Ptr + 4) - 8;

      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;
      }
}
#endif

} // End of namespace Scumm

Generated by  Doxygen 1.6.0   Back to index