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

compact.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/sky/compact.cpp $
 * $Id: compact.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */


#include "common/endian.h"
#include "common/util.h"
#include "common/file.h"
#include "sky/compact.h"
#include "gui/message.h"

extern int gDebugLevel;

namespace Sky {

#define     SKY_CPT_SIZE      419427

#define OFFS(type,item) (((long)(&((type*)0)->item)))
#define MK32(type,item) OFFS(type, item),0,0,0
#define MK16(type,item) OFFS(type, item),0
#define MK32_A5(type, item) MK32(type, item[0]), MK32(type, item[1]), \
      MK32(type, item[2]), MK32(type, item[3]), MK32(type, item[4])

static const uint32 compactOffsets[] = {
      MK16(Compact, logic),
      MK16(Compact, status),
      MK16(Compact, sync),
      MK16(Compact, screen),
      MK16(Compact, place),
      MK32(Compact, getToTableId),
      MK16(Compact, xcood),
      MK16(Compact, ycood),
      MK16(Compact, frame),
      MK16(Compact, cursorText),
      MK16(Compact, mouseOn),
      MK16(Compact, mouseOff),
      MK16(Compact, mouseClick),
      MK16(Compact, mouseRelX),
      MK16(Compact, mouseRelY),
      MK16(Compact, mouseSizeX),
      MK16(Compact, mouseSizeY),
      MK16(Compact, actionScript),
      MK16(Compact, upFlag),
      MK16(Compact, downFlag),
      MK16(Compact, getToFlag),
      MK16(Compact, flag),
      MK16(Compact, mood),
      MK32(Compact, grafixProgId),
      MK16(Compact, offset),
      MK16(Compact, mode),
      MK16(Compact, baseSub),
      MK16(Compact, baseSub_off),
      MK16(Compact, actionSub),
      MK16(Compact, actionSub_off),
      MK16(Compact, getToSub),
      MK16(Compact, getToSub_off),
      MK16(Compact, extraSub),
      MK16(Compact, extraSub_off),
      MK16(Compact, dir),
      MK16(Compact, stopScript),
      MK16(Compact, miniBump),
      MK16(Compact, leaving),
      MK16(Compact, atWatch),
      MK16(Compact, atWas),
      MK16(Compact, alt),
      MK16(Compact, request),
      MK16(Compact, spWidth_xx),
      MK16(Compact, spColour),
      MK16(Compact, spTextId),
      MK16(Compact, spTime),
      MK16(Compact, arAnimIndex),
      MK32(Compact, turnProgId),
      MK16(Compact, waitingFor),
      MK16(Compact, arTargetX),
      MK16(Compact, arTargetY),
      MK32(Compact, animScratchId),
      MK16(Compact, megaSet),
};

static const uint32 megaSetOffsets[] = {
      MK16(MegaSet, gridWidth),
      MK16(MegaSet, colOffset),
      MK16(MegaSet, colWidth),
      MK16(MegaSet, lastChr),
      MK32(MegaSet, animUpId),
      MK32(MegaSet, animDownId),
      MK32(MegaSet, animLeftId),
      MK32(MegaSet, animRightId),
      MK32(MegaSet, standUpId),
      MK32(MegaSet, standDownId),
      MK32(MegaSet, standLeftId),
      MK32(MegaSet, standRightId),
      MK32(MegaSet, standTalkId),
};

static const uint32 turnTableOffsets[] = {
      MK32_A5(TurnTable, turnTableUp),
      MK32_A5(TurnTable, turnTableDown),
      MK32_A5(TurnTable, turnTableLeft),
      MK32_A5(TurnTable, turnTableRight),
      MK32_A5(TurnTable, turnTableTalk),
};

#define COMPACT_SIZE (sizeof(compactOffsets)/sizeof(uint32))
#define MEGASET_SIZE (sizeof(megaSetOffsets)/sizeof(uint32))
#define TURNTABLE_SIZE (sizeof(turnTableOffsets)/sizeof(uint32))

SkyCompact::SkyCompact(void) {
      _cptFile = new Common::File();
      if (!_cptFile->open("sky.cpt")) {
            GUI::MessageDialog dialog("Unable to find \"sky.cpt\" file!\n"
                                                  "Please download it from www.scummvm.org", "OK", NULL);
            dialog.runModal();
            error("Unable to find \"sky.cpt\" file\nPlease download it from www.scummvm.org");
      }

      uint16 fileVersion = _cptFile->readUint16LE();
      if (fileVersion != 0)
            error("unknown \"sky.cpt\" version");

      if (SKY_CPT_SIZE != _cptFile->size()) {
            GUI::MessageDialog dialog("The \"sky.cpt\" file has an incorrect size.\nPlease (re)download it from www.scummvm.org", "OK", NULL);
            dialog.runModal();
            error("Incorrect sky.cpt size (%d, expected: %d)", _cptFile->size(), SKY_CPT_SIZE);
      }

      // set the necessary data structs up...
      _numDataLists = _cptFile->readUint16LE();
      _cptNames     = (char***)malloc(_numDataLists * sizeof(char**));
      _dataListLen  = (uint16 *)malloc(_numDataLists * sizeof(uint16));
      _cptSizes     = (uint16 **)malloc(_numDataLists * sizeof(uint16*));
      _cptTypes     = (uint16 **)malloc(_numDataLists * sizeof(uint16*));
      _compacts     = (Compact***)malloc(_numDataLists * sizeof(Compact**));

      for (int i = 0; i < _numDataLists; i++) {
            _dataListLen[i] = _cptFile->readUint16LE();
            _cptNames[i] = (char**)malloc(_dataListLen[i] * sizeof(char*));
            _cptSizes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
            _cptTypes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
            _compacts[i] = (Compact**)malloc(_dataListLen[i] * sizeof(Compact*));
      }

      uint32 rawSize = _cptFile->readUint32LE() * sizeof(uint16);
      uint16 *rawPos = _rawBuf = (uint16*)malloc(rawSize);

      uint32 srcSize = _cptFile->readUint32LE() * sizeof(uint16);
      uint16 *srcBuf = (uint16*)malloc(srcSize);
      uint16 *srcPos = srcBuf;
      _cptFile->read(srcBuf, srcSize);

      uint32 asciiSize = _cptFile->readUint32LE();
      char *asciiPos = _asciiBuf = (char*)malloc(asciiSize);
      _cptFile->read(_asciiBuf, asciiSize);

      // and fill them with the compact data
      for (uint32 lcnt = 0; lcnt < _numDataLists; lcnt++) {
            for (uint32 ecnt = 0; ecnt < _dataListLen[lcnt]; ecnt++) {
                  _cptSizes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
                  if (_cptSizes[lcnt][ecnt]) {
                        _cptTypes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
                        _compacts[lcnt][ecnt] = (Compact*)rawPos;
                        _cptNames[lcnt][ecnt] = asciiPos;
                        asciiPos += strlen(asciiPos) + 1;

                        for (uint16 elemCnt = 0; elemCnt < _cptSizes[lcnt][ecnt]; elemCnt++)
                              *rawPos++ = READ_LE_UINT16(srcPos++);
                  } else {
                        _cptTypes[lcnt][ecnt] = 0;
                        _compacts[lcnt][ecnt] = NULL;
                        _cptNames[lcnt][ecnt] = NULL;
                  }
            }
      }
      free(srcBuf);

      uint16 numDlincs = _cptFile->readUint16LE();
      uint16 *dlincBuf = (uint16*)malloc(numDlincs * 2 * sizeof(uint16));
      uint16 *dlincPos = dlincBuf;
      _cptFile->read(dlincBuf, numDlincs * 2 * sizeof(uint16));
      // these compacts don't actually exist but only point to other ones...
      uint16 cnt;
      for (cnt = 0; cnt < numDlincs; cnt++) {
            uint16 dlincId = READ_LE_UINT16(dlincPos++);
            uint16 destId = READ_LE_UINT16(dlincPos++);
            assert(((dlincId >> 12) < _numDataLists) && ((dlincId & 0xFFF) < _dataListLen[dlincId >> 12]) && (_compacts[dlincId >> 12][dlincId & 0xFFF] == NULL));
            _compacts[dlincId >> 12][dlincId & 0xFFF] = _compacts[destId >> 12][destId & 0xFFF];

            assert(_cptNames[dlincId >> 12][dlincId & 0xFFF] == NULL);
            _cptNames[dlincId >> 12][dlincId & 0xFFF] = asciiPos;
            asciiPos += strlen(asciiPos) + 1;
      }
      free(dlincBuf);

      // if this is v0.0288, parse this diff data
      uint16 numDiffs = _cptFile->readUint16LE();
      uint16 diffSize = _cptFile->readUint16LE();
      uint16 *diffBuf = (uint16*)malloc(diffSize * sizeof(uint16));
      _cptFile->read(diffBuf, diffSize * sizeof(uint16));
      if (SkyEngine::_systemVars.gameVersion == 288) {
            uint16 *diffPos = diffBuf;
            for (cnt = 0; cnt < numDiffs; cnt++) {
                  uint16 cptId = READ_LE_UINT16(diffPos++);
                  uint16 *rawCpt = (uint16*)fetchCpt(cptId);
                  rawCpt += READ_LE_UINT16(diffPos++);
                  uint16 len = READ_LE_UINT16(diffPos++);
                  for (uint16 elemCnt = 0; elemCnt < len; elemCnt++)
                        rawCpt[elemCnt] = READ_LE_UINT16(diffPos++);
            }
            assert(diffPos == (diffBuf + diffSize));
      }
      free(diffBuf);

      // these are the IDs that have to be saved into savegame files.
      _numSaveIds = _cptFile->readUint16LE();
      _saveIds = (uint16*)malloc(_numSaveIds * sizeof(uint16));
      _cptFile->read(_saveIds, _numSaveIds * sizeof(uint16));
      for (cnt = 0; cnt < _numSaveIds; cnt++)
            _saveIds[cnt] = FROM_LE_16(_saveIds[cnt]);
      _resetDataPos = _cptFile->pos();
}

SkyCompact::~SkyCompact(void) {
      free(_rawBuf);
      free(_asciiBuf);
      free(_saveIds);
      for (int i = 0; i < _numDataLists; i++) {
            free(_cptNames[i]);
            free(_cptSizes[i]);
            free(_cptTypes[i]);
            free(_compacts[i]);
      }
      free(_cptNames);
      free(_dataListLen);
      free(_cptSizes);
      free(_cptTypes);
      free(_compacts);
      _cptFile->close();
      delete _cptFile;
}

// needed for some workaround where the engine has to check if it's currently processing joey, for example
bool SkyCompact::cptIsId(Compact *cpt, uint16 id) {
      return (cpt == fetchCpt(id));
}

Compact *SkyCompact::fetchCpt(uint16 cptId) {
      if (cptId == 0xFFFF) // is this really still necessary?
            return NULL;
      assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));

      if (gDebugLevel >= 8) {
            debug(8, "Loading Compact %s [%s] (%04X=%d,%d)", _cptNames[cptId >> 12][cptId & 0xFFF], nameForType(_cptTypes[cptId >> 12][cptId & 0xFFF]), cptId, cptId >> 12, cptId & 0xFFF);
      }

      return _compacts[cptId >> 12][cptId & 0xFFF];
}

Compact *SkyCompact::fetchCptInfo(uint16 cptId, uint16 *elems, uint16 *type, char *name) {
      assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
      if (elems)
            *elems = _cptSizes[cptId >> 12][cptId & 0xFFF];
      if (type)
            *type  = _cptTypes[cptId >> 12][cptId & 0xFFF];
      if (name) {
            if (_cptNames[cptId >> 12][cptId & 0xFFF] != NULL)
                  strcpy(name, _cptNames[cptId >> 12][cptId & 0xFFF]);
            else
                  strcpy(name, "(null)");
      }
      return fetchCpt(cptId);
}

const char *SkyCompact::nameForType(uint16 type) {
      if (type >= NUM_CPT_TYPES)
            return "unknown";
      else
            return _typeNames[type];
}

uint16 *SkyCompact::getSub(Compact *cpt, uint16 mode) {
      switch (mode) {
      case 0:
            return &(cpt->baseSub);
      case 2:
            return &(cpt->baseSub_off);
      case 4:
            return &(cpt->actionSub);
      case 6:
            return &(cpt->actionSub_off);
      case 8:
            return &(cpt->getToSub);
      case 10:
            return &(cpt->getToSub_off);
      case 12:
            return &(cpt->extraSub);
      case 14:
            return &(cpt->extraSub_off);
      default:
            error("Invalid Mode (%d)", mode);
      }
}

uint16 *SkyCompact::getGrafixPtr(Compact *cpt) {
      uint16 *gfxBase = (uint16*)fetchCpt(cpt->grafixProgId);
      if (gfxBase == NULL)
            return NULL;

      return gfxBase + cpt->grafixProgPos;
}

/**
 * Returns the n'th mega set specified by \a megaSet from Compact \a cpt.
 */
MegaSet *SkyCompact::getMegaSet(Compact *cpt) {
      switch (cpt->megaSet) {
      case 0:
            return &cpt->megaSet0;
      case NEXT_MEGA_SET:
            return &cpt->megaSet1;
      case NEXT_MEGA_SET*2:
            return &cpt->megaSet2;
      case NEXT_MEGA_SET*3:
            return &cpt->megaSet3;
      default:
            error("Invalid MegaSet (%d)", cpt->megaSet);
      }
}

/**
 \brief Returns the turn table for direction \a dir
      from Compact \a cpt in \a megaSet.

 Functionally equivalent to:
 \verbatim
 clear eax
 mov al,20
 mul (cpt[esi]).c_dir
 add ax,(cpt[esi]).c_mega_set
 lea eax,(cpt[esi+eax]).c_turn_table_up
 \endverbatim
*/
uint16 *SkyCompact::getTurnTable(Compact *cpt, uint16 dir) {
      MegaSet *m = getMegaSet(cpt);
      TurnTable *turnTable = (TurnTable*)fetchCpt(m->turnTableId);
      switch (dir) {
      case 0:
            return turnTable->turnTableUp;
      case 1:
            return turnTable->turnTableDown;
      case 2:
            return turnTable->turnTableLeft;
      case 3:
            return turnTable->turnTableRight;
      case 4:
            return turnTable->turnTableTalk;
      default:
            error("No TurnTable (%d) in MegaSet (%d)", dir, cpt->megaSet);
      }
}

void *SkyCompact::getCompactElem(Compact *cpt, uint16 off) {
      if (off < COMPACT_SIZE)
            return((uint8 *)cpt + compactOffsets[off]);
      off -= COMPACT_SIZE;

      if (off < MEGASET_SIZE)
            return((uint8 *)&(cpt->megaSet0) + megaSetOffsets[off]);

      off -= MEGASET_SIZE;
      if (off < TURNTABLE_SIZE)
            return ((uint8 *)fetchCpt(cpt->megaSet0.turnTableId) + turnTableOffsets[off]);

      off -= TURNTABLE_SIZE;
      if (off < MEGASET_SIZE)
            return((uint8 *)&(cpt->megaSet1) + megaSetOffsets[off]);

      off -= MEGASET_SIZE;
      if (off < TURNTABLE_SIZE)
            return ((uint8 *)fetchCpt(cpt->megaSet1.turnTableId) + turnTableOffsets[off]);

      off -= TURNTABLE_SIZE;
      if (off < MEGASET_SIZE)
            return((uint8 *)&(cpt->megaSet2) + megaSetOffsets[off]);

      off -= MEGASET_SIZE;
      if (off < TURNTABLE_SIZE)
            return ((uint8 *)fetchCpt(cpt->megaSet2.turnTableId) + turnTableOffsets[off]);

      off -= TURNTABLE_SIZE;
      if (off < MEGASET_SIZE)
            return((uint8 *)&(cpt->megaSet3) + megaSetOffsets[off]);

      off -= MEGASET_SIZE;
      if (off < TURNTABLE_SIZE)
            return ((uint8 *)fetchCpt(cpt->megaSet3.turnTableId) + turnTableOffsets[off]);
      off -= TURNTABLE_SIZE;

      error("Offset %X out of bounds of compact", (int)(off + COMPACT_SIZE + 4 * MEGASET_SIZE + 4 * TURNTABLE_SIZE));
}

uint8 *SkyCompact::createResetData(uint16 gameVersion) {
      _cptFile->seek(_resetDataPos);
      uint32 dataSize = _cptFile->readUint16LE() * sizeof(uint16);
      uint16 *resetBuf = (uint16*)malloc(dataSize);
      _cptFile->read(resetBuf, dataSize);
      uint16 numDiffs = _cptFile->readUint16LE();
      for (uint16 cnt = 0; cnt < numDiffs; cnt++) {
            uint16 version = _cptFile->readUint16LE();
            uint16 diffFields = _cptFile->readUint16LE();
            if (version == gameVersion) {
                  for (uint16 diffCnt = 0; diffCnt < diffFields; diffCnt++) {
                        uint16 pos = _cptFile->readUint16LE();
                        resetBuf[pos] = TO_LE_16(_cptFile->readUint16LE());
                  }
                  return (uint8*)resetBuf;
            } else
                  _cptFile->seek(diffFields * 2 * sizeof(uint16), SEEK_CUR);
      }
      free(resetBuf);
      error("Unable to find reset data for Beneath a Steel Sky Version 0.0%03d", gameVersion);
}

// - debugging functions

uint16 SkyCompact::findCptId(void *cpt) {
      for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
            for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
                  if (_compacts[listCnt][elemCnt] == cpt)
                        return (listCnt << 12) | elemCnt;
      // not found
      debug(1, "Id for Compact %p wasn't found", cpt);
      return 0;
}

uint16 SkyCompact::findCptId(const char *cptName) {
      for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
            for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
                  if (_cptNames[listCnt][elemCnt] != 0)
                        if (scumm_stricmp(cptName, _cptNames[listCnt][elemCnt]) == 0)
                              return (listCnt << 12) | elemCnt;
      // not found
      debug(1, "Id for Compact %s wasn't found", cptName);
      return 0;
}

uint16 SkyCompact::giveNumDataLists(void) {
      return _numDataLists;
}

uint16 SkyCompact::giveDataListLen(uint16 listNum) {
      if (listNum >= _numDataLists) // list doesn't exist
            return 0;
      else
            return _dataListLen[listNum];
}

const char *SkyCompact::_typeNames[NUM_CPT_TYPES] = {
      "null",
      "COMPACT",
      "TURNTABLE",
      "ANIM SEQ",
      "UNKNOWN",
      "GETTOTABLE",
      "AR BUFFER",
      "MAIN LIST"
};

} // End of namespace Sky

Generated by  Doxygen 1.6.0   Back to index