Files
scummvm/scumm/scummvm.cpp
T
James Brown 63ee97b1ee Fix using MAD -without- Vorbis, and remove COMPRESSED_SOUND_FILE define.
Please keep in mind that if you put an 'else' statement in an if clause, keep in mind that if the code it's supposed to
'else' is in another define.... and that isn't set... the NEXT statement of code will be used as the 'if..else'
condition.

svn-id: r5344
2002-10-28 12:56:42 +00:00

1568 lines
34 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* wCopyright (C) 2001/2002 The ScummVM project
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "stdafx.h"
#include "scumm.h"
#include "sound/mixer.h"
#include "sound/mididrv.h"
#include "scumm/sound.h"
#include "scumm/imuse.h"
#include "scumm/bundle.h"
#include "actor.h"
#include "debug.h"
#include "dialogs.h"
#include "gameDetector.h"
#include "gui/newgui.h"
#include "gui/message.h"
#include "object.h"
#include "resource.h"
#include "string.h"
#ifdef _WIN32_WCE
extern void GraphicsOff(void);
#endif
// Use g_scumm from error() ONLY
Scumm *g_scumm = 0;
extern NewGui *g_gui;
void autosave(void * engine)
{
g_scumm->_doAutosave = true;
}
void Scumm::initRandSeeds()
{
_randSeed1 = 0xA943DE33;
_randSeed2 = 0x37A9ED29;
}
uint Scumm::getRandomNumber(uint max)
{
/* TODO: my own random number generator */
_randSeed1 = 0xDEADBF03 * (_randSeed1 + 1);
_randSeed1 = (_randSeed1 >> 13) | (_randSeed1 << 19);
return _randSeed1 % (max + 1);
}
uint Scumm::getRandomNumberRng(uint min, uint max)
{
return getRandomNumber(max - min) + min;
}
Scumm::Scumm (GameDetector *detector, OSystem *syst)
: Engine(detector, syst), _pauseDialog(0), _optionsDialog(0), _saveLoadDialog(0)
{
OSystem::Property prop;
// Use g_scumm from error() ONLY
g_scumm = this;
_debugMode = detector->_debugMode;
_bootParam = detector->_bootParam;
_exe_name = (char*)detector->_gameRealName.c_str();
_game_name = (char*)detector->_gameFileName.c_str();
_gameId = detector->_gameId;
_features = detector->_features;
_soundCardType = detector->_soundCardType;
_noSubtitles = detector->_noSubtitles;
_defaultTalkDelay = detector->_talkSpeed;
_use_adlib = detector->_use_adlib;
if (_gameId == GID_ZAK256) { // FmTowns is 320x240
_realWidth = 320;
_realHeight = 240;
} else if (_gameId == GID_CMI) {
_realWidth = 640;
_realHeight = 480;
} else {
_realWidth = 320;
_realHeight = 200;
}
gdi._numStrips = _realWidth / 8;
_newgui = g_gui;
_bundle = new Bundle();
_sound = new Sound(this);
_timer = Engine::_timer;
_sound->_sound_volume_master = 0;
_sound->_sound_volume_sfx = detector->_sfx_volume;
_sound->_sound_volume_music = detector->_music_volume;
/* Initialize backend */
syst->init_size(_realWidth, _realHeight);
prop.cd_num = detector->_cdrom;
if (prop.cd_num >= 0 && (_features & GF_AUDIOTRACKS))
syst->property(OSystem::PROP_OPEN_CD, &prop);
/* Bind the mixer to the system => mixer will be invoked
* automatically when samples need to be generated */
_silentDigitalImuse = false;
if (!_mixer->bindToSystem(syst)) {
warning("Sound initialization failed");
if (detector->_use_adlib) {
_use_adlib = false;
detector->_use_adlib = false;
detector->_midi_driver = MD_NULL;
warning("Adlib music was selected, switching to midi null driver");
}
_silentDigitalImuse = true;
}
_mixer->setVolume(kDefaultSFXVolume);
_mixer->setMusicVolume(kDefaultMusicVolume);
// Init iMuse
if (_features & GF_AFTER_V7) {
_imuseDigital = new IMuseDigital(this);
_imuse = NULL;
} else {
if (detector->_use_adlib) {
_imuse = IMuse::create_adlib(syst, _mixer);
} else {
void *midiTemp = detector->createMidi();
if (!midiTemp) { // Fallback to adlib/midiemu
_imuse = IMuse::create_adlib(syst, _mixer);
} else {
_imuse = IMuse::create_midi(syst, detector->createMidi());
}
}
_imuseDigital = NULL;
if (detector->_gameTempo != 0)
_imuse->property(IMuse::PROP_TEMPO_BASE, detector->_gameTempo);
_imuse->set_music_volume(_sound->_sound_volume_music);
}
// Load game from specified slot, if any
if (detector->_save_slot != -1) {
_saveLoadSlot = detector->_save_slot;
_saveLoadFlag = 2;
_saveLoadCompatible = false;
}
loadLanguageBundle();
_audioNames = NULL;
}
Scumm::~Scumm ()
{
delete [] _actors;
if (_pauseDialog)
delete _pauseDialog;
if (_optionsDialog)
delete _optionsDialog;
if (_saveLoadDialog)
delete _saveLoadDialog;
delete _bundle;
delete _sound;
if (_imuse)
delete _imuse;
if (_imuseDigital)
delete _imuseDigital;
if (_existLanguageFile)
delete _languageBuffer;
if (_audioNames)
delete _audioNames;
}
void Scumm::scummInit()
{
int i;
Actor *a;
tempMusic = 0;
debug(9, "scummInit");
if (_features & GF_OLD_BUNDLE)
_resourceHeaderSize = 2; // FIXME - to be rechecked
else if (_features & GF_SMALL_HEADER)
_resourceHeaderSize = 6;
else
_resourceHeaderSize = 8;
memset(_charsetData, 0, sizeof(_charsetData));
if (!(_features & GF_SMALL_NAMES))
loadCharset(1);
if (_features & GF_AFTER_V7) {
initScreens(0, 0, _realWidth, _realHeight);
} else {
initScreens(0, 16, _realWidth, 144);
}
setShake(0);
setupCursor();
/* Allocate and initilise actors */
_actors = new Actor[MAX_ACTORS];
for (i = 1, a = getFirstActor(); ++a, i < NUM_ACTORS; i++) {
a->number = i;
a->initActorClass(this);
a->initActor(1);
}
_vars[VAR_CHARINC] = 4;
_numNestedScripts = 0;
vm.cutSceneStackPointer = 0;
memset(vm.cutScenePtr, 0, sizeof(vm.cutScenePtr));
memset(vm.cutSceneData, 0, sizeof(vm.cutSceneData));
for (i = 0; i < _maxVerbs; i++) {
_verbs[i].verbid = 0;
_verbs[i].right = _realWidth - 1;
_verbs[i].oldleft = -1;
_verbs[i].type = 0;
_verbs[i].color = 2;
_verbs[i].hicolor = 0;
_verbs[i].charset_nr = 1;
_verbs[i].curmode = 0;
_verbs[i].saveid = 0;
_verbs[i].center = 0;
_verbs[i].key = 0;
}
if (!(_features & GF_AFTER_V7)) {
camera._leftTrigger = 10;
camera._rightTrigger = 30;
camera._mode = 0;
}
camera._follows = 0;
virtscr[0].xstart = 0;
if (!(_features & GF_AFTER_V7)) {
_vars[VAR_V5_DRAWFLAGS] = 11;
_vars[VAR_59] = 3;
// Setup light
_vars[VAR_CURRENT_LIGHTS] = LIGHTMODE_actor_base | LIGHTMODE_actor_color | LIGHTMODE_screen;
_flashlightXStrips = 7;
_flashlightYStrips = 7;
}
mouse.x = 104;
mouse.y = 56;
_ENCD_offs = 0;
_EXCD_offs = 0;
_currentScript = 0xFF;
_sentenceNum = 0;
_currentRoom = 0;
_numObjectsInRoom = 0;
_actorToPrintStrFor = 0;
charset._bufPos = 0;
_haveMsg = 0;
_varwatch = -1;
_screenStartStrip = 0;
_vars[VAR_TALK_ACTOR] = 0;
_talkDelay = 0;
_keepText = false;
gdi._currentCursor = 0;
_cursorState = 0;
_userPut = 0;
_newEffect = 129;
_fullRedraw = true;
clearDrawObjectQueue();
for (i = 0; i < 6; i++) {
if (_features & GF_OLD256) {
_string[i].t_xpos = 0;
_string[i].t_ypos = 0;
} else {
_string[i].t_xpos = 2;
_string[i].t_ypos = 5;
}
_string[i].t_right = _realWidth - 1;
_string[i].t_color = 0xF;
_string[i].t_center = 0;
_string[i].t_charset = 0;
}
_numInMsgStack = 0;
createResource(rtTemp, 6, 500);
initScummVars();
if (!(_features & GF_AFTER_V6))
_vars[VAR_V5_TALK_STRING_Y] = -0x50;
getGraphicsPerformance();
_sound->_current_cache = 0;
_timer->installProcedure(&autosave, 5 * 60 * 1000);
}
void Scumm::initScummVars()
{
if (!(_features & GF_AFTER_V7)) {
_vars[VAR_CURRENTDRIVE] = _currentDrive;
_vars[VAR_FIXEDDISK] = checkFixedDisk();
_vars[VAR_SOUNDCARD] = _soundCardType;
_vars[VAR_VIDEOMODE] = 0x13;
_vars[VAR_HEAPSPACE] = 1400;
_vars[VAR_MOUSEPRESENT] = _mousePresent;
_vars[VAR_SOUNDPARAM] = _soundParam;
_vars[VAR_SOUNDPARAM2] = _soundParam2;
_vars[VAR_SOUNDPARAM3] = _soundParam3;
if (_features & GF_AFTER_V6)
_vars[VAR_V6_EMSSPACE] = 10000;
}
}
void Scumm::checkRange(int max, int min, int no, const char *str)
{
if (no < min || no > max) {
error("Value %d is out of bounds (%d,%d) int script(%d) msg %s", no, min,
max, vm.slot[_curExecScript].number, str);
}
}
int Scumm::scummLoop(int delta)
{
static int counter = 0;
#ifndef _WIN32_WCE
if (_debugger)
_debugger->on_frame();
#endif
// Randomize the PRNG by calling it at regular intervals. This ensures
// that it will be in a different state each time you run the program.
getRandomNumber(2);
_vars[VAR_TMR_1] += delta;
_vars[VAR_TMR_2] += delta;
_vars[VAR_TMR_3] += delta;
_vars[VAR_TMR_4] += delta;
if (delta > 15)
delta = 15;
decreaseScriptDelay(delta);
// If _talkDelay is -1, that means the text should never time out.
// This is used for drawing verb texts, e.g. the Full Throttle
// dialogue choices.
if (_talkDelay != -1) {
_talkDelay -= delta;
if (_talkDelay < 0)
_talkDelay = 0;
}
processKbd();
if (_features & GF_AFTER_V7) {
_vars[VAR_CAMERA_POS_X] = camera._cur.x;
_vars[VAR_CAMERA_POS_Y] = camera._cur.y;
} else {
_vars[VAR_CAMERA_POS_X] = camera._cur.x;
}
_vars[VAR_HAVE_MSG] = (_haveMsg == 0xFE) ? 0xFF : _haveMsg;
_vars[VAR_VIRT_MOUSE_X] = _virtual_mouse_x;
_vars[VAR_VIRT_MOUSE_Y] = _virtual_mouse_y;
_vars[VAR_MOUSE_X] = mouse.x;
_vars[VAR_MOUSE_Y] = mouse.y;
_vars[VAR_DEBUGMODE] = _debugMode;
if (_gameId == GID_MONKEY_VGA) {
// FIXME: Is all this really necessary now?
if (delta == 1)
_vars[VAR_MI1_TIMER]++;
else if (++counter != 2)
_vars[VAR_MI1_TIMER] += 5;
else {
counter = 0;
_vars[VAR_MI1_TIMER] += 6;
}
} else if (_features & GF_AUDIOTRACKS) {
_vars[VAR_MI1_TIMER] = _sound->readCDTimer();
} else if (_features & GF_OLD256) {
if(tempMusic == 3) {
tempMusic = 0;
_vars[VAR_MUSIC_FLAG]++;
} else {
tempMusic++;
}
}
if (_saveLoadFlag) {
if (_saveLoadFlag == 1) {
saveState(_saveLoadSlot, _saveLoadCompatible);
// Ender: Disabled for small_header games, as
// can overwrite game variables (eg, Zak256 cashcards)
if (_saveLoadCompatible && !(_features & GF_SMALL_HEADER))
_vars[VAR_GAME_LOADED] = 201;
} else {
loadState(_saveLoadSlot, _saveLoadCompatible);
// Ender: Disabled for small_header games, as
// can overwrite game variables (eg, Zak256 cashcards)
if (_saveLoadCompatible && !(_features & GF_SMALL_HEADER))
_vars[VAR_GAME_LOADED] = 203;
}
_saveLoadFlag = 0;
}
if (_doAutosave) {
_saveLoadSlot = 0;
sprintf(_saveLoadName, "Autosave %d", _saveLoadSlot);
_saveLoadFlag = 1;
_saveLoadCompatible = false;
_doAutosave = false;
}
if (_completeScreenRedraw) {
_completeScreenRedraw = false;
gdi.clearUpperMask();
charset._hasMask = false;
redrawVerbs();
_fullRedraw = true;
}
runAllScripts();
checkExecVerbs();
checkAndRunVar33();
if (_currentRoom == 0) {
gdi._cursorActive = 0;
CHARSET_1();
drawDirtyScreenParts();
_sound->processSoundQues();
camera._last = camera._cur;
} else {
walkActors();
moveCamera();
fixObjectFlags();
CHARSET_1();
if (camera._cur.x != camera._last.x || _BgNeedsRedraw || _fullRedraw
|| (_features & GF_AFTER_V7 && camera._cur.y != camera._last.y)) {
redrawBGAreas();
}
processDrawQue();
if (_verbRedraw)
redrawVerbs();
setActorRedrawFlags(true, true);
resetActorBgs();
if (!(_vars[VAR_CURRENT_LIGHTS] & LIGHTMODE_screen) &&
_vars[VAR_CURRENT_LIGHTS] & LIGHTMODE_flashlight) {
drawFlashlight();
setActorRedrawFlags(true, false);
}
processActors();
_fullRedraw = false;
cyclePalette();
palManipulate();
if (_doEffect) {
_doEffect = false;
fadeIn(_newEffect);
clearClickedStatus();
}
if (_cursorState > 0) {
verbMouseOver(checkMouseOver(mouse.x, mouse.y));
}
drawBlastObjects();
drawDirtyScreenParts();
removeBlastObjects();
if (!(_features & GF_AFTER_V6))
playActorSounds();
_sound->processSoundQues();
camera._last = camera._cur;
}
if (!(++_expire_counter)) {
increaseResourceCounter();
}
animateCursor();
/* show or hide mouse */
_system->show_mouse(_cursorState > 0);
_vars[VAR_TIMER] = 0;
return _vars[VAR_TIMER_NEXT];
}
void Scumm::startScene(int room, Actor * a, int objectNr)
{
int i, where;
Actor *at;
CHECK_HEAP debug(1, "Loading room %d", room);
clearMsgQueue();
fadeOut(_switchRoomEffect2);
_newEffect = _switchRoomEffect;
if (_currentScript != 0xFF) {
if (vm.slot[_currentScript].where == WIO_ROOM ||
vm.slot[_currentScript].where == WIO_FLOBJECT) {
if (vm.slot[_currentScript].cutsceneOverride != 0)
error("Object %d stopped with active cutscene/override in exit",
vm.slot[_currentScript].number);
_currentScript = 0xFF;
} else if (vm.slot[_currentScript].where == WIO_LOCAL) {
if (vm.slot[_currentScript].cutsceneOverride != 0)
error("Script %d stopped with active cutscene/override in exit",
vm.slot[_currentScript].number);
_currentScript = 0xFF;
}
}
if (!(_features & GF_SMALL_HEADER)) // Disable for SH games. Overwrites
_vars[VAR_NEW_ROOM] = room; // gamevars, eg Zak cashcards
runExitScript();
killScriptsAndResources();
clearEnqueue();
stopCycle(0);
for (i = 1, at = getFirstActor(); ++at, i < NUM_ACTORS; i++) {
at->hideActor();
}
if (!(_features & GF_AFTER_V7)) {
for (i = 0; i < 0x100; i++)
_shadowPalette[i] = i;
}
clearDrawObjectQueue();
_vars[VAR_ROOM] = room;
_fullRedraw = true;
increaseResourceCounter();
_currentRoom = room;
_vars[VAR_ROOM] = room;
if (room >= 0x80 && !(_features & GF_AFTER_V7))
_roomResource = _resourceMapper[room & 0x7F];
else
_roomResource = room;
_vars[VAR_ROOM_RESOURCE] = _roomResource;
if (room != 0)
ensureResourceLoaded(rtRoom, room);
if (_currentRoom == 0) {
_ENCD_offs = _EXCD_offs = 0;
_numObjectsInRoom = 0;
return;
}
initRoomSubBlocks();
if (_features & GF_SMALL_HEADER)
loadRoomObjectsSmall();
else
loadRoomObjects();
if (!(_features & GF_AFTER_V7)) {
camera._mode = CM_NORMAL;
camera._cur.x = camera._dest.x = _realWidth / 2;
camera._cur.y = camera._dest.y = _realHeight / 2;
}
if (_features & GF_AFTER_V6) {
_vars[VAR_V6_SCREEN_WIDTH] = _scrWidth;
_vars[VAR_V6_SCREEN_HEIGHT] = _scrHeight;
}
_vars[VAR_CAMERA_MIN_X] = _realWidth / 2;
_vars[VAR_CAMERA_MAX_X] = _scrWidth - (_realWidth / 2);
if (_features & GF_AFTER_V7) {
_vars[VAR_CAMERA_MIN_Y] = _realHeight / 2;
_vars[VAR_CAMERA_MAX_Y] = _scrHeight - (_realHeight / 2);
setCameraAt(_realWidth / 2, _realHeight / 2);
}
if (_roomResource == 0)
return;
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
if (a) {
where = whereIsObject(objectNr);
if (where != WIO_ROOM && where != WIO_FLOBJECT)
error("startScene: Object %d is not in room %d", objectNr,
_currentRoom);
int x, y, dir;
getObjectXYPos(objectNr, x, y, dir);
a->putActor(x, y, _currentRoom);
a->setDirection(dir + 180);
a->moving = 0;
}
showActors();
_egoPositioned = false;
runEntryScript();
if (!(_features & GF_AFTER_V7)) {
if (a && !_egoPositioned) {
int x, y;
getObjectXYPos(objectNr, x, y);
a->putActor(x, y, _currentRoom);
a->moving = 0;
}
} else {
if (camera._follows) {
a = derefActorSafe(camera._follows, "startScene: follows");
setCameraAt(a->x, a->y);
}
}
_doEffect = true;
CHECK_HEAP;
}
void Scumm::initRoomSubBlocks()
{
int i, offs;
byte *ptr;
byte *roomptr, *searchptr;
RoomHeader *rmhd;
_ENCD_offs = 0;
_EXCD_offs = 0;
_CLUT_offs = 0;
_PALS_offs = 0;
nukeResource(rtMatrix, 1);
nukeResource(rtMatrix, 2);
for (i = 1; i < _maxScaleTable; i++)
nukeResource(rtScaleTable, i);
roomptr = getResourceAddress(rtRoom, _roomResource);
rmhd = (RoomHeader *)findResourceData(MKID('RMHD'), roomptr);
if (_features & GF_AFTER_V7) {
_scrWidth = READ_LE_UINT16(&(rmhd->v7.width));
_scrHeight = READ_LE_UINT16(&(rmhd->v7.height));
} else {
_scrWidth = READ_LE_UINT16(&(rmhd->old.width));
_scrHeight = READ_LE_UINT16(&(rmhd->old.height));
}
if (_features & GF_SMALL_HEADER)
_IM00_offs = findResourceData(MKID('IM00'), roomptr) - roomptr;
else
_IM00_offs =
findResource(MKID('IM00'),
findResource(MKID('RMIM'), roomptr)) - roomptr;
ptr = findResourceData(MKID('EXCD'), roomptr);
if (ptr) {
_EXCD_offs = ptr - roomptr;
#ifdef DUMP_SCRIPTS
dumpResource("exit-", _roomResource, ptr - 8);
#endif
}
ptr = findResourceData(MKID('ENCD'), roomptr);
if (ptr) {
_ENCD_offs = ptr - roomptr;
#ifdef DUMP_SCRIPTS
dumpResource("entry-", _roomResource, ptr - 8);
#endif
}
if (_features & GF_SMALL_HEADER) {
ptr = findResourceData(MKID('BOXD'), roomptr);
if (ptr) {
byte numOfBoxes = *(ptr);
int size;
if (_features & GF_OLD256)
size = numOfBoxes * (SIZEOF_BOX - 2) + 1;
else
size = numOfBoxes * SIZEOF_BOX + 1;
createResource(rtMatrix, 2, size);
memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
ptr += size;
size = getResourceDataSize(ptr - size - 6) - size;
if (size >= 0) { // do this :)
createResource(rtMatrix, 1, size);
memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
}
}
} else {
ptr = findResourceData(MKID('BOXD'), roomptr);
if (ptr) {
int size = getResourceDataSize(ptr);
createResource(rtMatrix, 2, size);
roomptr = getResourceAddress(rtRoom, _roomResource);
ptr = findResourceData(MKID('BOXD'), roomptr);
memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
}
ptr = findResourceData(MKID('BOXM'), roomptr);
if (ptr) {
int size = getResourceDataSize(ptr);
createResource(rtMatrix, 1, size);
roomptr = getResourceAddress(rtRoom, _roomResource);
ptr = findResourceData(MKID('BOXM'), roomptr);
memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
}
}
ptr = findResourceData(MKID('SCAL'), roomptr);
if (ptr) {
offs = ptr - roomptr;
for (i = 1; i < _maxScaleTable; i++, offs += 8) {
int a = READ_LE_UINT16(roomptr + offs);
int b = READ_LE_UINT16(roomptr + offs + 2);
int c = READ_LE_UINT16(roomptr + offs + 4);
int d = READ_LE_UINT16(roomptr + offs + 6);
if (a || b || c || d) {
setScaleItem(i, b, a, d, c);
roomptr = getResourceAddress(rtRoom, _roomResource);
}
}
}
memset(_localScriptList, 0, sizeof(_localScriptList));
searchptr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_features & GF_SMALL_HEADER) {
while ((ptr = findResourceSmall(MKID('LSCR'), searchptr)) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
id = ptr[0];
#ifdef DUMP_SCRIPTS
do {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
dumpResource(buf, id, ptr - 6);
} while (0);
#endif
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomptr;
searchptr = NULL;
}
} else {
while ((ptr = findResource(MKID('LSCR'), searchptr)) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
if (_features & GF_AFTER_V7) {
id = READ_LE_UINT16(ptr);
checkRange(NUM_LOCALSCRIPT + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
_localScriptList[id - _numGlobalScripts] = ptr + 2 - roomptr;
} else {
id = ptr[0];
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomptr;
}
#ifdef DUMP_SCRIPTS
do {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
dumpResource(buf, id, ptr - 8);
} while (0);
#endif
searchptr = NULL;
}
}
// FIXME - we could simply always call findResourceData here, it will
// do the right thing even if GF_SMALL_HEADER is set. But then, we have
// to change setPaletteFromPtr() (easy). The problematic bit is save game
// compatibility - _CLUT_offs is stored in the save game after all.
// Of course we could just decide to not use _CLUT_offs anymore, and change
// setPaletteFromRes() to invoke findResourceData() each time
// (and also getPalettePtr()).
if (_features & GF_SMALL_HEADER)
ptr = findResourceSmall(MKID('CLUT'), roomptr);
else
ptr = findResourceData(MKID('CLUT'), roomptr);
if (ptr) {
_CLUT_offs = ptr - roomptr;
setPaletteFromRes();
}
if (_features & GF_AFTER_V6) {
ptr = findResource(MKID('PALS'), roomptr);
if (ptr) {
_PALS_offs = ptr - roomptr;
setPalette(0);
}
}
ptr = findResourceData(MKID('CYCL'), roomptr);
if (ptr)
initCycl(ptr);
ptr = findResourceData(MKID('TRNS'), roomptr);
if (ptr)
gdi._transparency = ptr[0];
else
gdi._transparency = 255;
initBGBuffers(_scrHeight);
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
}
void Scumm::setScaleItem(int slot, int a, int b, int c, int d)
{
byte *ptr;
int cur, amounttoadd, i, tmp;
ptr = createResource(rtScaleTable, slot, 200);
if (a == c)
return;
cur = (b - d) * a;
amounttoadd = d - b;
for (i = 200; i > 0; i--) {
tmp = cur / (c - a) + b;
if (tmp < 1)
tmp = 1;
if (tmp > 255)
tmp = 255;
*ptr++ = tmp;
cur += amounttoadd;
}
}
void Scumm::dumpResource(char *tag, int idx, byte *ptr)
{
char buf[256];
File out;
uint32 size;
if (_features & GF_SMALL_HEADER)
size = READ_LE_UINT32(ptr);
else
size = READ_BE_UINT32_UNALIGNED(ptr + 4);
#if defined(MACOS_CARBON)
sprintf(buf, ":dumps:%s%d.dmp", tag, idx);
#else
sprintf(buf, "dumps/%s%d.dmp", tag, idx);
#endif
out.open(buf, "", 1);
if (out.isOpen() == false) {
out.open(buf, "", 2);
if (out.isOpen() == false)
return;
out.write(ptr, size);
}
out.close();
}
void Scumm::clearClickedStatus()
{
checkKeyHit();
_mouseButStat = 0;
_leftBtnPressed = 0;
_rightBtnPressed = 0;
}
int Scumm::checkKeyHit()
{
int a = _keyPressed;
_keyPressed = 0;
return a;
}
void Scumm::pauseGame(bool user)
{
pauseDialog();
}
void Scumm::setOptions()
{
//_newgui->optionsDialog();
}
void Scumm::runDialog(Dialog *dialog)
{
// Pause sound put
bool old_soundsPaused = _sound->_soundsPaused;
_sound->pauseSounds(true);
// Open & run the dialog
dialog->runModal();
// Restore old cursor
updateCursor();
// Resume sound output
_sound->pauseSounds(old_soundsPaused);
}
void Scumm::pauseDialog()
{
if (!_pauseDialog) {
_pauseDialog = new PauseDialog(_newgui, this);
}
runDialog(_pauseDialog);
}
void Scumm::saveloadDialog()
{
if (!_saveLoadDialog)
_saveLoadDialog = new SaveLoadDialog(_newgui, this);
runDialog(_saveLoadDialog);
}
void Scumm::optionsDialog()
{
if (!_optionsDialog)
_optionsDialog = new OptionsDialog(_newgui, this);
runDialog(_optionsDialog);
}
void Scumm::shutDown(int i)
{
/* TODO: implement this */
warning("shutDown: not implemented");
}
void Scumm::processKbd()
{
int saveloadkey;
getKeyInput(0);
if (_features & GF_OLD256) /* FIXME: Support ingame screen */
saveloadkey = 319;
else
saveloadkey = _vars[VAR_SAVELOADDIALOG_KEY];
_virtual_mouse_x = mouse.x + virtscr[0].xstart;
if(_features & GF_AFTER_V7)
_virtual_mouse_y = mouse.y + camera._cur.y - (_realHeight / 2);
else
_virtual_mouse_y = mouse.y;
if (!(_features & GF_OLD256))
_virtual_mouse_y += virtscr[0].topline;
else
_virtual_mouse_y -= 16;
if (_virtual_mouse_y < 0)
_virtual_mouse_y = -1;
if (_features & GF_OLD256) {
if (_virtual_mouse_y >= virtscr[0].height + virtscr[0].topline)
_virtual_mouse_y = -1;
} else {
if (_virtual_mouse_y >= virtscr[0].height)
_virtual_mouse_y = -1;
}
if (!_lastKeyHit)
return;
if (_lastKeyHit == KEY_SET_OPTIONS) {
setOptions();
return;
}
if (_lastKeyHit == _vars[VAR_RESTART_KEY]) {
warning("Restart not implemented");
// pauseGame(true);
return;
}
if (_lastKeyHit == _vars[VAR_PAUSE_KEY]) {
pauseGame(true);
/* pause */
return;
}
if (_lastKeyHit == _vars[VAR_CUTSCENEEXIT_KEY]) {
if (_insaneState) {
_videoFinished = true;
} else
exitCutscene();
} else if (_lastKeyHit == saveloadkey && _currentRoom != 0) {
if (_features & GF_AFTER_V7)
runScript(_vars[VAR_UNK_SCRIPT], 0, 0, 0);
saveloadDialog(); // Display NewGui
if (_features & GF_AFTER_V7)
runScript(_vars[VAR_UNK_SCRIPT_2], 0, 0, 0);
} else if (_lastKeyHit == _vars[VAR_TALKSTOP_KEY]) {
_talkDelay = 0;
if (_sound->_sfxMode & 2)
stopTalk();
return;
} else if (_lastKeyHit == '[') { // [ volume down
_sound->_sound_volume_master-=5;
if (_sound->_sound_volume_master < 0)
_sound->_sound_volume_master = 0;
if (_imuse) {
_imuse->set_master_volume(_sound->_sound_volume_master);
}
} else if (_lastKeyHit == ']') { // ] volume down
_sound->_sound_volume_master+=5;
if (_sound->_sound_volume_master > 128)
_sound->_sound_volume_master = 128;
if (_imuse) {
_imuse->set_master_volume(_sound->_sound_volume_master);
}
} else if (_lastKeyHit == '-') { // - text speed down
_defaultTalkDelay+=5;
if (_defaultTalkDelay > 90)
_defaultTalkDelay = 90;
_vars[VAR_CHARINC] = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '+') { // + text speed up
_defaultTalkDelay-=5;
if (_defaultTalkDelay < 5)
_defaultTalkDelay = 5;
_vars[VAR_CHARINC] = _defaultTalkDelay / 20;
}
// FIXME: Preparing to remove
// else if (_lastKeyHit == 321) { // F7, display new GUI
// saveloadDialog();
// }
_mouseButStat = _lastKeyHit;
}
int Scumm::getKeyInput(int a)
{
_mouseButStat = 0;
_lastKeyHit = checkKeyHit();
if (a == 0)
convertKeysToClicks();
if (mouse.x < 0)
mouse.x = 0;
if (mouse.x > _realWidth-1)
mouse.x = _realWidth-1;
if (mouse.y < 0)
mouse.y = 0;
if (mouse.y > _realHeight-1)
mouse.y = _realHeight-1;
if (_leftBtnPressed & msClicked && _rightBtnPressed & msClicked) {
_mouseButStat = 0;
_lastKeyHit = _vars[VAR_CUTSCENEEXIT_KEY];
} else if (_leftBtnPressed & msClicked) {
_mouseButStat = MBS_LEFT_CLICK;
} else if (_rightBtnPressed & msClicked) {
_mouseButStat = MBS_RIGHT_CLICK;
}
if (_features & GF_AFTER_V7) {
// _vars[VAR_LEFTBTN_DOWN] = (_leftBtnPressed&msClicked) != 0;
_vars[VAR_LEFTBTN_HOLD] = (_leftBtnPressed & msDown) != 0;
// _vars[VAR_RIGHTBTN_DOWN] = (_rightBtnPressed&msClicked) != 0;
_vars[VAR_RIGHTBTN_HOLD] = (_rightBtnPressed & msDown) != 0;
}
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
return _lastKeyHit;
}
void Scumm::convertKeysToClicks()
{
if (_lastKeyHit && _cursorState > 0) {
if (_lastKeyHit == 9) {
_mouseButStat = MBS_RIGHT_CLICK;
} else if (_lastKeyHit == 13) {
_mouseButStat = MBS_LEFT_CLICK;
} else
return;
_lastKeyHit = 0;
}
}
Actor *Scumm::derefActor(int id)
{
return &_actors[id];
}
Actor *Scumm::derefActorSafe(int id, const char *errmsg)
{
if (id < 1 || id >= NUM_ACTORS) {
if (_debugMode)
warning
("Invalid actor %d in %s (script %d, opcode 0x%x) - This is potentially a BIG problem.",
id, errmsg, vm.slot[_curExecScript].number, _opcode);
return NULL;
}
return derefActor(id);
}
void Scumm::setStringVars(int slot)
{
StringTab *st = &_string[slot];
st->xpos = st->t_xpos;
st->ypos = st->t_ypos;
st->center = st->t_center;
st->overhead = st->t_overhead;
st->no_talk_anim = st->t_no_talk_anim;
st->right = st->t_right;
st->color = st->t_color;
st->charset = st->t_charset;
}
void Scumm::unkMiscOp9()
{
warning("stub unkMiscOp9()");
}
void Scumm::startManiac()
{
warning("stub startManiac()");
}
void Scumm::destroy()
{
freeResources();
free(_objectStateTable);
free(_objectRoomTable);
free(_objectOwnerTable);
free(_inventory);
free(_arrays);
free(_verbs);
free(_objs);
free(_vars);
free(_bitVars);
free(_newNames);
free(_classData);
}
//
// Convert an old style direction to a new style one (angle),
//
int newDirToOldDir(int dir)
{
if (dir >= 71 && dir <= 109)
return 1;
if (dir >= 109 && dir <= 251)
return 2;
if (dir >= 251 && dir <= 289)
return 0;
return 3;
}
//
// Convert an new style (angle) direction to an old style one.
//
int oldDirToNewDir(int dir)
{
const int new_dir_table[4] = { 270, 90, 180, 0 };
return new_dir_table[dir];
}
//
// Convert an angle to a simple direction.
//
int toSimpleDir(int dirType, int dir)
{
const int16 many_direction_tab[16] = {
71, 109, 251, 289, -1, -1, -1, -1,
22, 72, 107, 157, 202, 252, 287, 337
};
int num = dirType ? 8 : 4;
const int16 *dirtab = &many_direction_tab[dirType * 8];
for (int i = 1; i < num; i++, dirtab++) {
if (dir >= dirtab[0] && dir <= dirtab[1])
return i;
}
return 0;
}
//
// Convert a simple direction to an angle
//
int fromSimpleDir(int dirType, int dir)
{
if (dirType)
return dir * 45;
else
return dir * 90;
}
//
// Normalize the given angle - that means, ensure it is positive, and
// change it to the closest multiple of 45 degree by abusing toSimpleDir.
//
int normalizeAngle(int angle)
{
int temp;
temp = (angle + 360) % 360;
return toSimpleDir(1, temp) * 45;
}
void NORETURN CDECL error(const char *s, ...)
{
char buf[1024];
#if defined( USE_WINDBG ) || defined ( _WIN32_WCE )
char buf2[1024];
#if defined( _WIN32_WCE )
TCHAR buf2w[2048];
#endif
#endif
va_list va;
va_start(va, s);
vsprintf(buf, s, va);
va_end(va);
if (g_scumm && g_scumm->_currentScript != 0xFF) {
ScriptSlot *ss = &g_scumm->vm.slot[g_scumm->_currentScript];
fprintf(stderr, "Error(%d:%d:0x%X): %s!\n",
g_scumm->_roomResource,
ss->number,
g_scumm->_scriptPointer - g_scumm->_scriptOrgPointer, buf);
#if defined( USE_WINDBG ) || defined( _WIN32_WCE )
sprintf(buf2, "Error(%d:%d:0x%X): %s!\n",
g_scumm->_roomResource,
ss->number,
g_scumm->_scriptPointer - g_scumm->_scriptOrgPointer,
buf);
#if defined ( _WIN32_WCE )
/*
MultiByteToWideChar(CP_ACP, 0, buf2, strlen(buf2) + 1, buf2w, sizeof(buf2w));
GraphicsOff();
MessageBox(NULL, buf2w, TEXT("ScummVM error"), MB_OK);
*/
#else
OutputDebugString(buf2);
#endif
#endif
} else {
fprintf(stderr, "Error: %s!\n", buf);
#if defined( USE_WINDBG ) || defined( _WIN32_WCE )
sprintf(&buf[strlen(buf)], "\n");
#if defined ( _WIN32_WCE )
/*
MultiByteToWideChar(CP_ACP, 0, buf, strlen(buf) + 1, buf2w, sizeof(buf2w));
GraphicsOff();
MessageBox(NULL, buf2w, TEXT("ScummVM error"), MB_OK);
*/
#else
OutputDebugString(buf);
#endif
#endif
}
// Finally exit. quit() will terminate the program if g_system iss present
if (g_system)
g_system->quit();
exit(1);
}
ScummDebugger g_debugger;
void Scumm::waitForTimer(int msec_delay) {
OSystem::Event event;
uint32 start_time;
if (_fastMode&2)
msec_delay = 0;
else if (_fastMode&1)
msec_delay = 10;
start_time = _system->get_msecs();
for(;;) {
while (_system->poll_event(&event)) {
switch(event.event_code) {
case OSystem::EVENT_KEYDOWN:
if (event.kbd.keycode >= '0' && event.kbd.keycode<='9'
&& (event.kbd.flags == OSystem::KBD_SHIFT ||
event.kbd.flags == OSystem::KBD_CTRL)) {
_saveLoadSlot = event.kbd.keycode - '0';
sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot);
_saveLoadFlag = (event.kbd.flags == OSystem::KBD_SHIFT) ? 1 : 2;
_saveLoadCompatible = false;
} else if (event.kbd.flags==OSystem::KBD_CTRL) {
if (event.kbd.keycode=='f')
_fastMode ^= 1;
else if (event.kbd.keycode=='g')
_fastMode ^= 2;
else if ((event.kbd.keycode=='d') && (!_system->property(OSystem::PROP_GET_FULLSCREEN, 0)))
g_debugger.attach(this);
else if (event.kbd.keycode=='s')
resourceStats();
} else
_keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
break;
case OSystem::EVENT_MOUSEMOVE:
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
_system->set_mouse_pos(event.mouse.x, event.mouse.y);
_system->update_screen();
break;
case OSystem::EVENT_LBUTTONDOWN:
_leftBtnPressed |= msClicked|msDown;
#ifdef _WIN32_WCE
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
#endif
break;
case OSystem::EVENT_RBUTTONDOWN:
_rightBtnPressed |= msClicked|msDown;
#ifdef _WIN32_WCE
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
#endif
break;
case OSystem::EVENT_LBUTTONUP:
_leftBtnPressed &= ~msDown;
break;
case OSystem::EVENT_RBUTTONUP:
_rightBtnPressed &= ~msDown;
break;
}
}
_sound->updateCD(); // Loop CD Audio if needed
if (_system->get_msecs() >= start_time + msec_delay)
break;
_system->delay_msecs(10);
}
}
void Scumm::updatePalette() {
if (_palDirtyMax == -1)
return;
bool noir_mode = (_gameId == GID_SAMNMAX && readVar(0x8000));
int first = _palDirtyMin;
int num = _palDirtyMax - first + 1;
int i;
byte palette_colors[1024],*p = palette_colors;
for (i = _palDirtyMin; i <= _palDirtyMax; i++) {
byte *data;
if (_features & GF_SMALL_HEADER)
data = _currentPalette + _shadowPalette[i] * 3;
else
data = _currentPalette + i * 3;
// Sam & Max film noir mode. Convert the colours to grayscale
// before uploading them to the backend.
if (noir_mode) {
double r, g, b;
double brightness;
r = (double) data[0];
g = (double) data[1];
b = (double) data[2];
brightness = (0.299 * r + 0.587 * g + 0.114 * b) + 0.5;
*p++ = (byte) brightness;
*p++ = (byte) brightness;
*p++ = (byte) brightness;
*p++ = 0;
} else {
*p++ = data[0];
*p++ = data[1];
*p++ = data[2];
*p++ = 0;
}
}
_system->set_palette(palette_colors, first, num);
_palDirtyMax = -1;
_palDirtyMin = 256;
}
void Scumm::mainRun()
{
int delta = 0;
int last_time = _system->get_msecs();
int new_time;
for(;;) {
updatePalette();
_system->update_screen();
new_time = _system->get_msecs();
waitForTimer(delta * 15 + last_time - new_time);
last_time = _system->get_msecs();
delta = scummLoop(delta);
if (delta < 1) // Ensure we don't get into a loop
delta = 1; // by not decreasing sleepers.
}
}
void Scumm::launch()
{
charset._vm = this;
gdi._vm = this;
_maxHeapThreshold = 450000;
_minHeapThreshold = 400000;
_verbRedraw = false;
allocResTypeData(rtBuffer, MKID('NONE'), 10, "buffer", 0);
initVirtScreen(0, 0, 0, _realWidth, _realHeight, false, false);
if (_features & GF_AFTER_V7)
setupScummVarsNew();
else
setupScummVarsOld();
if ((_features & GF_AFTER_V7) || (_gameId == GID_SAMNMAX))
NUM_ACTORS = 30;
else
NUM_ACTORS = 13;
if (_features & GF_AFTER_V7)
OF_OWNER_ROOM = 0xFF;
else
OF_OWNER_ROOM = 0x0F;
// if (_gameId==GID_MONKEY2 && _bootParam == 0)
// _bootParam = 10001;
if (_gameId == GID_INDY4 && _bootParam == 0) {
_bootParam = -7873;
}
readIndexFile();
initRandSeeds();
if (_features & GF_NEW_OPCODES)
setupOpcodes2();
else
setupOpcodes();
scummInit();
if (!(_features & GF_AFTER_V7))
_vars[VAR_VERSION] = 21;
_vars[VAR_DEBUGMODE] = _debugMode;
if (_gameId == GID_MONKEY)
_vars[74] = 1225;
_sound->setupSound();
runScript(1, 0, 0, &_bootParam);
}
void Scumm::go() {
launch();
mainRun();
}
byte Scumm::getDefaultGUIColor(int color)
{
/* FIXME: strange IF line? */
if ((_features & GF_AFTER_V7) || (_features & GF_SMALL_HEADER))
return 0;
if (_features & GF_AFTER_V6) {
if (color == 8)
color = 1;
return readArray(110, 0, color);
} else {
return getStringAddress(21)[color];
}
}
bool Scumm::checkFixedDisk() {
return true;
}