scummvm/scumm/scummvm.cpp

2699 lines
76 KiB
C++
Raw Normal View History

2001-10-09 14:30:12 +00:00
/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2003 The ScummVM project
2001-10-09 14:30:12 +00:00
*
* 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$
2001-10-09 14:30:12 +00:00
*
*/
#include "stdafx.h"
2003-09-11 10:32:15 +00:00
#include "base/gameDetector.h"
#include "base/plugins.h"
#include "common/config-file.h"
2003-09-11 10:32:15 +00:00
#include "gui/console.h"
#include "gui/message.h"
2003-09-11 10:32:15 +00:00
#include "gui/newgui.h"
2003-09-11 10:32:15 +00:00
#include "scumm/actor.h"
#include "scumm/akos.h"
#include "scumm/boxes.h"
#include "scumm/charset.h"
#include "scumm/costume.h"
#include "scumm/debugger.h"
#include "scumm/dialogs.h"
#include "scumm/imuse_digi.h"
#include "scumm/imuse.h"
#include "scumm/intern.h"
#include "scumm/object.h"
#include "scumm/player_v1.h"
#include "scumm/player_v2.h"
#include "scumm/player_v2a.h"
2003-09-11 10:32:15 +00:00
#include "scumm/player_v3a.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/verbs.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
#ifdef MACOSX
#include <sys/types.h>
#include <sys/stat.h>
#endif
2001-10-09 14:30:12 +00:00
#ifdef _WIN32_WCE
extern bool isSmartphone(void);
#endif
extern NewGui *g_gui;
extern uint16 _debugLevel;
extern uint16 _demo_mode;
namespace Scumm {
2003-09-11 10:32:15 +00:00
enum MouseButtonStatus {
msDown = 1,
msClicked = 2
};
// Use g_scumm from error() ONLY
ScummEngine *g_scumm = 0;
2003-04-30 13:23:31 +00:00
ScummDebugger *g_debugger;
static const TargetSettings scumm_settings[] = {
/* Scumm Version 1 */
/* Scumm Version 2 */
{"maniac", "Maniac Mansion", GID_MANIAC, 2, MDT_PCSPK,
GF_SMALL_HEADER | GF_USE_KEY | GF_SMALL_NAMES | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, "00.LFL"},
{"maniacnes", "Maniac Mansion (NES)", GID_MANIAC, 2, MDT_NONE,
GF_SMALL_HEADER | GF_USE_KEY | GF_SMALL_NAMES | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING | GF_NES, "00.LFL"},
{"zak", "Zak McKracken and the Alien Mindbenders", GID_ZAK, 2, MDT_PCSPK,
GF_SMALL_HEADER | GF_USE_KEY | GF_SMALL_NAMES | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, "00.LFL"},
/* Scumm Version 3 */
{"indy3EGA", "Indiana Jones and the Last Crusade", GID_INDY3, 3, MDT_PCSPK | MDT_ADLIB,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
{"indy3Towns", "Indiana Jones and the Last Crusade (FM Towns)", GID_INDY3, 3, MDT_TOWNS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
{"indy3", "Indiana Jones and the Last Crusade (256)", GID_INDY3, 3, MDT_PCSPK | MDT_ADLIB,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, "00.LFL"},
{"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, MDT_TOWNS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
{"loom", "Loom", GID_LOOM, 3, MDT_PCSPK | MDT_ADLIB | MDT_NATIVE,
2003-08-08 11:32:40 +00:00
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
{"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_TOWNS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
/* Scumm Version 4 */
{"monkeyEGA", "Monkey Island 1 (EGA)", GID_MONKEY_EGA, 4, MDT_PCSPK | MDT_ADLIB | MDT_NATIVE,
GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, "000.LFL"},
{"pass", "Passport to Adventure", GID_PASS, 4, MDT_PCSPK | MDT_ADLIB,
GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, "000.LFL"},
/* Scumm version 5 */
{"monkeyVGA", "Monkey Island 1 (256 color Floppy version)", GID_MONKEY_VGA, 4, MDT_PCSPK | MDT_ADLIB | MDT_NATIVE,
GF_SMALL_HEADER | GF_USE_KEY, "000.LFL"},
{"loomcd", "Loom (256 color CD version)", GID_LOOM256, 4, MDT_NONE,
2003-08-08 11:32:40 +00:00
GF_SMALL_HEADER | GF_USE_KEY | GF_AUDIOTRACKS, "000.LFL"},
{"monkey", "Monkey Island 1", GID_MONKEY, 5, /*MDT_PCSPK |*/ MDT_ADLIB,
GF_USE_KEY | GF_AUDIOTRACKS, 0},
{"monkey1", "Monkey Island 1 (alt)", GID_MONKEY, 5, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY | GF_AUDIOTRACKS, 0},
{"game", "Monkey Island 1 (SegaCD version)", GID_MONKEY_SEGA, 5, MDT_NONE,
GF_USE_KEY | GF_AUDIOTRACKS, 0},
{"monkey2", "Monkey Island 2: LeChuck's revenge", GID_MONKEY2, 5, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
{"mi2demo", "Monkey Island 2: LeChuck's revenge (Demo)", GID_MONKEY2, 5, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
{"indydemo", "Indiana Jones and the Fate of Atlantis (FM Towns Demo)", GID_INDY4, 5, MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
2003-10-04 01:12:30 +00:00
{"indy4", "Indiana Jones and the Fate of Atlantis (FM Towns)", GID_INDY4, 5, MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
{"atlantis", "Indiana Jones and the Fate of Atlantis", GID_INDY4, 5, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
{"playfate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
{"fate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_USE_KEY, 0},
/* Scumm Version 6 */
{"puttputt", "Putt-Putt Joins The Parade (DOS)", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttdemo", "Putt-Putt Joins The Parade (DOS Demo)", GID_PUTTDEMO, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS, 0},
{"moondemo", "Putt-Putt Goes To The Moon (DOS Demo)", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttmoon", "Putt-Putt Goes To The Moon (DOS)", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"funpack", "Putt-Putt's Fun Pack", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbpack", "Fatty Bear's Fun Pack", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbear", "Fatty Bear's Birthday Surprise (DOS)", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbdemo", "Fatty Bear's Birthday Surprise (DOS Demo)", GID_PUTTPUTT, 6, MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"tentacle", "Day Of The Tentacle", GID_TENTACLE, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY, 0},
{"dottdemo", "Day Of The Tentacle (Demo)", GID_TENTACLE, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY, 0},
{"samnmax", "Sam & Max", GID_SAMNMAX, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER, 0},
{"samdemo", "Sam & Max (Demo)", GID_SAMNMAX, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER, 0},
{"snmdemo", "Sam & Max (Demo)", GID_SAMNMAX, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER, 0},
{"snmidemo", "Sam & Max (Interactive WIP Demo)", GID_SAMNMAX, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER, 0},
{"test", "Test demo game", GID_SAMNMAX, 6, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_NATIVE, GF_NEW_OPCODES, 0},
/* Scumm Version 7 */
{"ft", "Full Throttle", GID_FT, 7, MDT_NONE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, 0},
{"ftdemo", "Full Throttle (Mac Demo)", GID_FT, 7, MDT_NONE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, 0},
{"dig", "The Dig", GID_DIG, 7, MDT_NONE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, 0},
#ifndef __PALM_OS__ // these are SVGA games not supported under PalmOS
/* Scumm Version 8 */
{"comi", "The Curse of Monkey Island", GID_CMI, 8, MDT_NONE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEFAULT_TO_1X_SCALER, 0},
2003-09-14 22:46:47 +00:00
/* Note that both full versions of Humongous games and demos were often released for
* several interpreter versions... */
/* Humongous Entertainment Scumm Version 7 */
2003-09-28 16:26:06 +00:00
{"catalog", "Humongous Interactive Catalog", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"farm", "Let's Explore the Farm with Buzzy", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"farmdemo", "Let's Explore the Farm with Buzzy (Demo)", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-30 06:11:55 +00:00
{"airport", "Let's Explore the Airport with Buzzy", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"airdemo", "Let's Explore the Airport with Buzzy (Demo)", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-30 06:11:55 +00:00
{"jungle", "Let's Explore the Jungle with Buzzy", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttzoo", "Putt-Putt Saves the Zoo", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"zoodemo", "Putt-Putt Saves the Zoo (Demo)", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"freddi", "Freddi Fish 1: Missing Kelp Seeds", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"freddemo", "Freddi Fish 1: Missing Kelp Seeds (Demo)", GID_PUTTPUTT, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
/* Humongous Entertainment Scumm Version 8.0 ? Scummsrc.80 */
{"pajama", "Pajama Sam 1: No Need to Hide", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"pjs-demo", "Pajama Sam 1: No Need to Hide (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"ffhsdemo", "Freddi Fish 2: Haunted Schoolbus (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"ff2-demo", "Freddi Fish 2: Haunted Schoolbus (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-23 20:29:50 +00:00
{"freddi2", "Freddi Fish 2: Haunted Schoolbus", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
/* Humongous Entertainment Scumm Version 9.0 ? Scummsys.90 */
{"timedemo", "Putt-Putt Travels Through Time (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"f3-mdemo", "Freddi Fish 3: Stolen Conch Shell (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"spyfox", "Spyfox 1: Dry Cereal", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"foxdemo", "Spyfox 1: Dry Cereal (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"spydemo", "Spyfox 1: Dry Cereal (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"kinddemo", "Big Thinkers Kindergarten (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"1grademo", "Big Thinkers First Grade (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
/* Humongous Entertainment Scumm Version 9.5 ? Scummsys.95 */
{"pj2demo", "Pajama Sam 2: Thunder and Lightning (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-28 16:26:06 +00:00
2003-09-14 22:46:47 +00:00
/* Humongous Entertainment Scumm Version 9.8 ? Scummsys.98
* these and later games can easily be identified by the .(a) file instead of a .he1 */
{"racedemo", "Putt-Putt Enters the Race (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-24 05:59:32 +00:00
{"puttrace", "Putt-Putt Enters the Race", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-28 16:26:06 +00:00
{"freddi4", "Freddi Fish 4: Hogfish Rustlers of Briny Gulch", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"f4-demo", "Freddi Fish 4: Hogfish Rustlers of Briny Gulch (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"bluesabctimedemo", "Blue's ABC Time Activities (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-28 16:26:06 +00:00
{"bluesabctime", "Blue's ABC Time Activities", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-25 06:12:04 +00:00
{"blue'sbirthday-yellow", "Blue's Birthday Yellow", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
/* Humongous Entertainment Scumm Version ? engine moved to c++ */
{"pj3-demo", "Pajama Sam 3: You Are What You Eat (Demo)", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-23 20:29:50 +00:00
{"freddicove", "Freddi Fish 5: The Case of the Creature of Coral Cave", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
2003-09-14 22:46:47 +00:00
{"putttime", "Putt-Putt Travels Through Time", GID_PJSDEMO, 6, MDT_NONE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
// bunch of backyard sports games...
#endif
{NULL, NULL, 0, 0, MDT_NONE, 0, NULL}
};
ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst)
2003-03-06 17:58:13 +00:00
: Engine(detector, syst), _pauseDialog(0), _optionsDialog(0), _saveLoadDialog(0) {
2002-08-18 18:39:42 +00:00
OSystem::Property prop;
2003-04-30 13:23:31 +00:00
// Init all vars - maybe now we can get rid of our custom new/delete operators?
_imuse = NULL;
_imuseDigital = NULL;
_features = 0;
_verbs = NULL;
_objs = NULL;
_debugger = NULL;
2003-06-08 12:11:14 +00:00
_sound = NULL;
2003-04-30 13:23:31 +00:00
memset(&res, 0, sizeof(res));
memset(&vm, 0, sizeof(vm));
_smushFrameRate = 0;
2003-06-08 12:11:14 +00:00
_insaneState = false;
_videoFinished = false;
_smushPlay = false;
2003-04-30 13:23:31 +00:00
_quit = false;
_newgui = NULL;
_pauseDialog = NULL;
_optionsDialog = NULL;
_saveLoadDialog = NULL;
_confirmExitDialog = NULL;
2003-04-30 13:23:31 +00:00
_debuggerDialog = NULL;
_fastMode = 0;
_gameId = 0;
2003-06-08 12:11:14 +00:00
memset(&gdi, 0, sizeof(Gdi));
2003-04-30 13:23:31 +00:00
_actors = NULL;
_inventory = NULL;
_newNames = NULL;
_scummVars = NULL;
2003-04-30 13:23:31 +00:00
_varwatch = 0;
_bitVars = NULL;
_numVariables = 0;
_numBitVariables = 0;
_numLocalObjects = 0;
_numGlobalObjects = 0;
_numArray = 0;
_numVerbs = 0;
_numFlObject = 0;
_numInventory = 0;
_numRooms = 0;
_numScripts = 0;
_numSounds = 0;
_numCharsets = 0;
_numNewNames = 0;
_numGlobalScripts = 0;
_numActors = 0;
2003-04-30 13:23:31 +00:00
_numCostumes = 0;
_audioNames = NULL;
_numAudioNames = 0;
_curActor = 0;
_curVerb = 0;
_curVerbSlot = 0;
_curPalIndex = 0;
_currentRoom = 0;
_egoPositioned = false;
_keyPressed = 0;
_lastKeyHit = 0;
_mouseButStat = 0;
_leftBtnPressed = 0;
_rightBtnPressed = 0;
_bootParam = 0;
_dumpScripts = false;
_debugMode = 0;
_language = 0;
_objectOwnerTable = NULL;
_objectRoomTable = NULL;
_objectStateTable = NULL;
2003-06-09 01:06:29 +00:00
memset(&_objectIDMap, 0, sizeof(ObjectIDMap));
2003-04-30 13:23:31 +00:00
_numObjectsInRoom = 0;
_userPut = 0;
_userState = 0;
2003-04-30 13:23:31 +00:00
_resourceHeaderSize = 0;
_saveLoadFlag = 0;
_saveLoadSlot = 0;
_lastSaveTime = 0;
_saveLoadCompatible = false;
2003-06-09 01:06:29 +00:00
memset(_saveLoadName, 0, sizeof(_saveLoadName));
2003-04-30 13:23:31 +00:00
_maxHeapThreshold = 0;
_minHeapThreshold = 0;
2003-06-09 01:06:29 +00:00
memset(_localScriptList, 0, sizeof(_localScriptList));
2003-04-30 13:23:31 +00:00
_scriptPointer = NULL;
_scriptOrgPointer = NULL;
_opcode = 0;
2003-09-11 10:32:15 +00:00
vm.numNestedScripts = 0;
2003-04-30 13:23:31 +00:00
_currentScript = 0;
_curExecScript = 0;
_lastCodePtr = NULL;
_resultVarNumber = 0;
_scummStackPos = 0;
2003-06-09 01:06:29 +00:00
memset(_scummStack, 0, sizeof(_scummStack));
2003-04-30 13:23:31 +00:00
_keyScriptKey = 0;
_keyScriptNo = 0;
2003-06-09 01:06:29 +00:00
memset(&_fileHandle, 0, sizeof(File));
2003-04-30 13:23:31 +00:00
_fileOffset = 0;
_exe_name = NULL;
_game_name = NULL;
_dynamicRoomOffsets = false;
2003-06-09 01:06:29 +00:00
memset(_resourceMapper, 0, sizeof(_resourceMapper));
2003-04-30 13:23:31 +00:00
_allocatedSize = 0;
_expire_counter = 0;
_lastLoadedRoom = 0;
_roomResource = 0;
OF_OWNER_ROOM = 0;
_verbMouseOver = 0;
_inventoryOffset = 0;
_classData = NULL;
_actorToPrintStrFor = 0;
_sentenceNum = 0;
2003-06-09 01:06:29 +00:00
memset(_sentence, 0, sizeof(_sentence));
memset(_string, 0, sizeof(_string));
2003-04-30 13:23:31 +00:00
_screenB = 0;
_screenH = 0;
_roomHeight = 0;
_roomWidth = 0;
_screenHeight = 0;
_screenWidth = 0;
2003-06-09 01:06:29 +00:00
memset(virtscr, 0, sizeof(virtscr));
memset(&camera, 0, sizeof(CameraData));
memset(_colorCycle, 0, sizeof(_colorCycle));
2003-04-30 13:23:31 +00:00
_ENCD_offs = 0;
_EXCD_offs = 0;
_CLUT_offs = 0;
_IM00_offs = 0;
_PALS_offs = 0;
_fullRedraw = false;
_BgNeedsRedraw = false;
_verbRedraw = false;
_screenEffectFlag = false;
_completeScreenRedraw = false;
2003-06-09 01:06:29 +00:00
memset(&_cursor, 0, sizeof(_cursor));
memset(_grabbedCursor, 0, sizeof(_grabbedCursor));
2003-04-30 13:23:31 +00:00
_currentCursor = 0;
_newEffect = 0;
_switchRoomEffect2 = 0;
_switchRoomEffect = 0;
_doEffect = false;
2003-06-09 01:06:29 +00:00
memset(&_flashlight, 0, sizeof(_flashlight));
_roomStrips = 0;
2003-05-28 21:28:30 +00:00
_bompActorPalettePtr = NULL;
2003-04-30 13:23:31 +00:00
_shakeEnabled= false;
_shakeFrame = 0;
_screenStartStrip = 0;
_screenEndStrip = 0;
_screenLeft = 0;
_screenTop = 0;
2003-06-09 01:06:29 +00:00
_blastObjectQueuePos = 0;
memset(_blastObjectQueue, 0, sizeof(_blastObjectQueue));
2003-04-30 13:23:31 +00:00
_blastTextQueuePos = 0;
2003-06-09 01:06:29 +00:00
memset(_blastTextQueue, 0, sizeof(_blastTextQueue));
2003-04-30 13:23:31 +00:00
_drawObjectQueNr = 0;
2003-06-09 01:06:29 +00:00
memset(_drawObjectQue, 0, sizeof(_drawObjectQue));
2003-04-30 13:23:31 +00:00
_palManipStart = 0;
_palManipEnd = 0;
_palManipCounter = 0;
_palManipPalette = NULL;
_palManipIntermediatePal = NULL;
2003-06-09 01:06:29 +00:00
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
_roomPalette = NULL;
2003-04-30 13:23:31 +00:00
_shadowPalette = NULL;
_shadowPaletteSize = 0;
2003-06-09 01:06:29 +00:00
memset(_currentPalette, 0, sizeof(_currentPalette));
memset(_proc_special_palette, 0, sizeof(_proc_special_palette));
2003-04-30 13:23:31 +00:00
_palDirtyMin = 0;
_palDirtyMax = 0;
_haveMsg = 0;
_useTalkAnims = false;
_defaultTalkDelay = 0;
_midiDriver = MD_NULL;
2003-04-30 13:23:31 +00:00
tempMusic = 0;
_silentDigitalImuse = 0;
_noDigitalSamples = 0;
_saveSound = 0;
2003-06-09 01:06:29 +00:00
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
memset(_scaleSlots, 0, sizeof(_scaleSlots));
2003-04-30 13:23:31 +00:00
_charset = NULL;
_charsetColor = 0;
2003-06-09 01:06:29 +00:00
memset(_charsetColorMap, 0, sizeof(_charsetColorMap));
memset(_charsetData, 0, sizeof(_charsetData));
2003-04-30 13:23:31 +00:00
_charsetBufPos = 0;
2003-06-09 01:06:29 +00:00
memset(_charsetBuffer, 0, sizeof(_charsetBuffer));
_demo_mode = false;
2003-04-30 13:23:31 +00:00
_noSubtitles = false;
_confirmExit = false;
2003-04-30 13:23:31 +00:00
_numInMsgStack = 0;
_msgPtrToAdd = NULL;
_messagePtr = NULL;
_talkDelay = 0;
_keepText = false;
_existLanguageFile = false;
_languageBuffer = NULL;
_languageIndex = NULL;
2003-06-09 01:06:29 +00:00
memset(_transText, 0, sizeof(_transText));
_costumeRenderer = NULL;
2003-06-14 12:24:42 +00:00
_2byteFontPtr = 0;
2003-04-30 13:23:31 +00:00
//
// Init all VARS to 0xFF
//
VAR_LANGUAGE = 0xFF;
VAR_KEYPRESS = 0xFF;
VAR_EGO = 0xFF;
VAR_CAMERA_POS_X = 0xFF;
VAR_HAVE_MSG = 0xFF;
VAR_ROOM = 0xFF;
VAR_OVERRIDE = 0xFF;
VAR_MACHINE_SPEED = 0xFF;
VAR_ME = 0xFF;
VAR_NUM_ACTOR = 0xFF;
VAR_CURRENT_LIGHTS = 0xFF;
VAR_CURRENTDRIVE = 0xFF; // How about merging this with VAR_CURRENTDISK?
VAR_CURRENTDISK = 0xFF;
VAR_TMR_1 = 0xFF;
VAR_TMR_2 = 0xFF;
VAR_TMR_3 = 0xFF;
VAR_MUSIC_TIMER = 0xFF;
VAR_ACTOR_RANGE_MIN = 0xFF;
VAR_ACTOR_RANGE_MAX = 0xFF;
VAR_CAMERA_MIN_X = 0xFF;
VAR_CAMERA_MAX_X = 0xFF;
VAR_TIMER_NEXT = 0xFF;
VAR_VIRT_MOUSE_X = 0xFF;
VAR_VIRT_MOUSE_Y = 0xFF;
VAR_ROOM_RESOURCE = 0xFF;
VAR_LAST_SOUND = 0xFF;
VAR_CUTSCENEEXIT_KEY = 0xFF;
VAR_OPTIONS_KEY = 0xFF;
VAR_TALK_ACTOR = 0xFF;
VAR_CAMERA_FAST_X = 0xFF;
VAR_SCROLL_SCRIPT = 0xFF;
VAR_ENTRY_SCRIPT = 0xFF;
VAR_ENTRY_SCRIPT2 = 0xFF;
VAR_EXIT_SCRIPT = 0xFF;
VAR_EXIT_SCRIPT2 = 0xFF;
VAR_VERB_SCRIPT = 0xFF;
VAR_SENTENCE_SCRIPT = 0xFF;
VAR_INVENTORY_SCRIPT = 0xFF;
VAR_CUTSCENE_START_SCRIPT = 0xFF;
VAR_CUTSCENE_END_SCRIPT = 0xFF;
VAR_CHARINC = 0xFF;
2003-05-14 13:30:52 +00:00
VAR_CHARCOUNT = 0xFF;
VAR_WALKTO_OBJ = 0xFF;
VAR_DEBUGMODE = 0xFF;
VAR_HEAPSPACE = 0xFF;
VAR_RESTART_KEY = 0xFF;
VAR_PAUSE_KEY = 0xFF;
VAR_MOUSE_X = 0xFF;
VAR_MOUSE_Y = 0xFF;
VAR_TIMER = 0xFF;
VAR_TMR_4 = 0xFF;
VAR_SOUNDCARD = 0xFF;
VAR_VIDEOMODE = 0xFF;
VAR_SAVELOADDIALOG_KEY = 0xFF;
VAR_FIXEDDISK = 0xFF;
VAR_CURSORSTATE = 0xFF;
VAR_USERPUT = 0xFF;
VAR_SOUNDRESULT = 0xFF;
VAR_TALKSTOP_KEY = 0xFF;
VAR_59 = 0xFF;
VAR_NOSUBTITLES = 0xFF;
VAR_SOUNDPARAM = 0xFF;
VAR_SOUNDPARAM2 = 0xFF;
VAR_SOUNDPARAM3 = 0xFF;
VAR_MOUSEPRESENT = 0xFF;
VAR_PERFORMANCE_1 = 0xFF;
VAR_PERFORMANCE_2 = 0xFF;
VAR_ROOM_FLAG = 0xFF;
VAR_GAME_LOADED = 0xFF;
VAR_NEW_ROOM = 0xFF;
VAR_VERSION = 0xFF;
VAR_V5_TALK_STRING_Y = 0xFF;
VAR_V6_SCREEN_WIDTH = 0xFF;
VAR_V6_SCREEN_HEIGHT = 0xFF;
VAR_V6_EMSSPACE = 0xFF;
VAR_V6_RANDOM_NR = 0xFF;
VAR_STRING2DRAW = 0xFF;
VAR_CAMERA_POS_Y = 0xFF;
VAR_CAMERA_MIN_Y = 0xFF;
VAR_CAMERA_MAX_Y = 0xFF;
VAR_CAMERA_THRESHOLD_X = 0xFF;
VAR_CAMERA_THRESHOLD_Y = 0xFF;
VAR_CAMERA_SPEED_X = 0xFF;
VAR_CAMERA_SPEED_Y = 0xFF;
VAR_CAMERA_ACCEL_X = 0xFF;
VAR_CAMERA_ACCEL_Y = 0xFF;
VAR_CAMERA_DEST_X = 0xFF;
VAR_CAMERA_DEST_Y = 0xFF;
VAR_CAMERA_FOLLOWED_ACTOR = 0xFF;
VAR_LEFTBTN_DOWN = 0xFF;
VAR_RIGHTBTN_DOWN = 0xFF;
VAR_LEFTBTN_HOLD = 0xFF;
VAR_RIGHTBTN_HOLD = 0xFF;
VAR_MOUSE_BUTTONS = 0xFF;
VAR_MOUSE_HOLD = 0xFF;
VAR_SAVELOAD_SCRIPT = 0xFF;
VAR_SAVELOAD_SCRIPT2 = 0xFF;
VAR_DEFAULT_TALK_DELAY = 0xFF;
VAR_CHARSET_MASK = 0xFF;
VAR_CUSTOMSCALETABLE = 0xFF;
VAR_V6_SOUNDMODE = 0xFF;
VAR_ACTIVE_VERB = 0xFF;
VAR_ACTIVE_OBJECT1 = 0xFF;
VAR_ACTIVE_OBJECT2 = 0xFF;
VAR_VERB_ALLOWED = 0xFF;
VAR_CLICK_AREA = 0xFF;
// Use g_scumm from error() ONLY
g_scumm = this;
2003-04-30 13:23:31 +00:00
g_debugger = new ScummDebugger;
_debugMode = detector->_debugMode;
_debugLevel = detector->_debugLevel;
_dumpScripts = detector->_dumpScripts;
_bootParam = detector->_bootParam;
_exe_name = strdup(detector->_game.targetName);
_game_name = strdup(detector->_gameFileName.c_str());
_gameId = detector->_game.id;
_version = detector->_game.version;
setFeatures(detector->_game.features);
_demo_mode = detector->_demo_mode;
_noSubtitles = detector->_noSubtitles;
_confirmExit = detector->_confirmExit;
_defaultTalkDelay = detector->_talkSpeed;
_midiDriver = detector->_midi_driver;
_native_mt32 = detector->_native_mt32;
_language = detector->_language;
memset(&res, 0, sizeof(res));
2003-05-13 23:33:01 +00:00
_hexdumpScripts = false;
_showStack = false;
if (_features & GF_FMTOWNS) { // FmTowns is 320x240
_screenWidth = 320;
_screenHeight = 240;
2002-09-21 16:20:52 +00:00
} else if (_gameId == GID_CMI) {
_screenWidth = 640;
_screenHeight = 480;
} else if (_features & GF_NES) {
_screenWidth = 256;
_screenHeight = 240;
} else {
_screenWidth = 320;
_screenHeight = 200;
}
gdi._numStrips = _screenWidth / 8;
_newgui = g_gui;
2002-08-14 20:43:56 +00:00
_sound = new Sound(this);
_sound->_sound_volume_master = detector->_master_volume;
_sound->_sound_volume_sfx = detector->_sfx_volume;
_sound->_sound_volume_music = detector->_music_volume;
2002-08-18 18:39:42 +00:00
/* Initialize backend */
syst->init_size(_screenWidth, _screenHeight);
2002-08-18 18:39:42 +00:00
prop.cd_num = detector->_cdrom;
if (prop.cd_num >= 0 && (_features & GF_AUDIOTRACKS))
2002-09-28 15:19:21 +00:00
syst->property(OSystem::PROP_OPEN_CD, &prop);
2002-08-18 18:39:42 +00:00
2002-11-30 16:03:46 +00:00
#ifndef __GP32__ //ph0x FIXME, "quick dirty hack"
2002-08-18 18:39:42 +00:00
/* Bind the mixer to the system => mixer will be invoked
2003-06-09 01:06:29 +00:00
* automatically when samples need to be generated */
_silentDigitalImuse = false;
2003-06-09 01:06:29 +00:00
if (!_mixer->bindToSystem(syst)) {
warning("Sound mixer initialization failed");
if (detector->_midi_driver == MD_ADLIB ||
detector->_midi_driver == MD_PCSPK ||
detector->_midi_driver == MD_PCJR)
{
_midiDriver = detector->_midi_driver = MD_NULL;
warning("MIDI driver depends on sound mixer, switching to null MIDI driver");
2003-06-09 01:06:29 +00:00
}
_silentDigitalImuse = true;
_noDigitalSamples = true;
2003-06-09 01:06:29 +00:00
}
2002-11-19 17:18:16 +00:00
_mixer->setVolume(kDefaultSFXVolume * kDefaultMasterVolume / 255);
2002-08-24 15:58:08 +00:00
_mixer->setMusicVolume(kDefaultMusicVolume);
2002-08-18 18:39:42 +00:00
// Init iMuse
2003-08-05 17:50:06 +00:00
_imuse = NULL;
_imuseDigital = NULL;
_musicEngine = NULL;
if (_features & GF_DIGI_IMUSE) {
_musicEngine = _imuseDigital = new IMuseDigital(this);
#ifndef __PALM_OS__
} else if ((_features & GF_AMIGA) && (_version == 2)) {
_musicEngine = new Player_V2A(this);
#endif
} else if ((_features & GF_AMIGA) && (_version == 3)) {
_musicEngine = new Player_V3A(this);
} else if ((_features & GF_AMIGA) && (_version < 5)) {
_musicEngine = NULL;
} else if (((_midiDriver == MD_PCJR) || (_midiDriver == MD_PCSPK)) && ((_version > 2) && (_version < 5))) {
_musicEngine = new Player_V2(this);
} else if (_version > 2) {
MidiDriver *driver = detector->createMidi();
if (driver && detector->_native_mt32)
driver->property (MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
_musicEngine = _imuse = IMuse::create(syst, _mixer, driver);
if (_imuse) {
if (detector->_gameTempo != 0)
_imuse->property(IMuse::PROP_TEMPO_BASE, detector->_gameTempo);
_imuse->property(IMuse::PROP_OLD_ADLIB_INSTRUMENTS, (_features & GF_SMALL_HEADER) ? 1 : 0);
_imuse->property(IMuse::PROP_MULTI_MIDI, detector->_multi_midi && _midiDriver != MD_NULL);
_imuse->property(IMuse::PROP_NATIVE_MT32, detector->_native_mt32);
if (_features & GF_HUMONGOUS || _features & GF_FMTOWNS) {
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
}
if (_features & GF_FMTOWNS)
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
_imuse->set_music_volume(_sound->_sound_volume_music);
}
2002-08-18 18:39:42 +00:00
}
2002-11-30 16:03:46 +00:00
#endif // ph0x-hack
2002-08-18 18:39:42 +00:00
// Load game from specified slot, if any
if (detector->_save_slot != -1) {
_saveLoadSlot = detector->_save_slot;
_saveLoadFlag = 2;
_saveLoadCompatible = false;
}
loadLanguageBundle();
// Load CJK font
_CJKMode = false;
if ((_gameId == GID_DIG || _gameId == GID_CMI) && (_language == KO_KOR || _language == JA_JPN || _language == ZH_TWN)) {
File fp;
const char *fontFile = NULL;
switch(_language) {
case KO_KOR:
_CJKMode = true;
fontFile = "korean.fnt";
break;
case JA_JPN:
_CJKMode = true;
fontFile = (_gameId == GID_DIG) ? "kanji16.fnt" : "japanese.fnt";
break;
case ZH_TWN:
if (_gameId == GID_CMI) {
_CJKMode = true;
fontFile = "chinese.fnt";
}
break;
}
if (_CJKMode && fp.open(fontFile, getGameDataPath(), 1)) {
debug(2, "Loading CJK Font");
fp.seek(2,SEEK_CUR);
_2byteWidth = fp.readByte(); //FIXME: is this correct?
_2byteHeight = fp.readByte();
int numChar = 0;
switch(_language) {
case KO_KOR:
numChar = 2350;
break;
case JA_JPN:
numChar = (_gameId == GID_DIG) ? 1 : 1; //FIXME
break;
case ZH_TWN:
numChar = 1; //FIXME
break;
}
_2byteFontPtr = new byte[2 * _2byteHeight * numChar];
fp.read(_2byteFontPtr, 2 * _2byteHeight * numChar);
fp.close();
}
}
2002-10-11 08:35:12 +00:00
_audioNames = NULL;
2002-08-14 00:01:39 +00:00
}
ScummEngine::~ScummEngine() {
_mixer->stopAll();
2002-08-14 00:01:39 +00:00
delete [] _actors;
2002-12-25 21:42:22 +00:00
delete _2byteFontPtr;
2002-12-25 21:42:22 +00:00
delete _charset;
delete _pauseDialog;
delete _optionsDialog;
delete _saveLoadDialog;
delete _confirmExitDialog;
2002-08-14 20:43:56 +00:00
delete _sound;
if (_musicEngine) {
_musicEngine->terminate();
delete _musicEngine;
}
free(_languageBuffer);
free(_audioNames);
2003-04-30 13:23:31 +00:00
delete _costumeRenderer;
free(_roomPalette);
2003-06-15 00:33:16 +00:00
free(_shadowPalette);
2003-04-30 13:23:31 +00:00
freeResources();
free(_objectStateTable);
free(_objectRoomTable);
free(_objectOwnerTable);
free(_inventory);
free(_verbs);
free(_objs);
free(_scummVars);
free(_bitVars);
free(_newNames);
free(_classData);
free(_exe_name);
free(_game_name);
free(_roomStrips);
free(_languageIndex);
2003-04-30 13:23:31 +00:00
delete g_debugger;
2002-08-14 00:01:39 +00:00
}
void ScummEngine::go() {
launch();
mainRun();
}
#pragma mark -
#pragma mark --- Initialization ---
#pragma mark -
void ScummEngine::launch() {
gdi._vm = this;
#ifdef __PALM_OS__
2003-08-16 09:20:24 +00:00
// revert to old value (450000) and make ScummVM works again in some devices with same problem as below.
// 2500000 is too big and make ScummVM crashes : MemMove to NULL or immediate exit if try to allocate
// memory with new operator
2003-08-16 09:20:24 +00:00
_maxHeapThreshold = 450000;
#else
// Since the new costumes are very big, we increase the heap limit, to avoid having
// to constantly reload stuff from the data files.
if (_features & GF_NEW_COSTUMES)
_maxHeapThreshold = 2500000;
else
_maxHeapThreshold = 550000;
#endif
_minHeapThreshold = 400000;
_verbRedraw = false;
allocResTypeData(rtBuffer, MKID('NONE'), 10, "buffer", 0);
initVirtScreen(0, 0, 0, _screenWidth, _screenHeight, false, false);
setupScummVars();
setupOpcodes();
if (_version == 8)
_numActors = 80;
else if ((_version == 7) || (_gameId == GID_SAMNMAX))
_numActors = 30;
else if (_gameId == GID_MANIAC)
_numActors = 25;
else
_numActors = 13;
if (_version >= 7)
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;
}
if (_features & GF_OLD_BUNDLE)
_resourceHeaderSize = 4; // FIXME - to be rechecked
else if (_features & GF_SMALL_HEADER)
_resourceHeaderSize = 6;
else
_resourceHeaderSize = 8;
readIndexFile();
scummInit();
if (_version > 2) {
if (_version < 7)
VAR(VAR_VERSION) = 21;
2003-09-05 15:53:20 +00:00
if (!((_features & GF_MACINTOSH) && (_version == 3))) {
2003-09-05 15:25:31 +00:00
// This is the for the Mac version of Indy3/Loom
VAR(VAR_DEBUGMODE) = _debugMode;
}
}
if (_gameId == GID_MONKEY || _gameId == GID_MONKEY_SEGA)
_scummVars[74] = 1225;
_sound->setupSound();
// If requested, load a save game instead of running the boot script
if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveLoadCompatible)) {
if (_gameId == GID_MANIAC && _demo_mode)
runScript(9, 0, 0, &_bootParam);
else
runScript(1, 0, 0, &_bootParam);
}
_saveLoadFlag = 0;
}
void ScummEngine::setFeatures (uint32 newFeatures) {
bool newCostumes = (_features & GF_NEW_COSTUMES) != 0;
bool newNewCostumes = (newFeatures & GF_NEW_COSTUMES) != 0;
bool amigaPalette = (_features & GF_AMIGA) != 0;
bool newAmigaPalette = (newFeatures & GF_AMIGA) != 0;
_features = newFeatures;
if (!_costumeRenderer || newCostumes != newNewCostumes) {
delete _costumeRenderer;
if (newNewCostumes)
_costumeRenderer = new AkosRenderer(this);
else
_costumeRenderer = new CostumeRenderer(this);
}
if ((_features & GF_16COLOR) && amigaPalette != newAmigaPalette) {
if (_features & GF_AMIGA)
setupAmigaPalette();
else
setupEGAPalette();
}
}
void ScummEngine::scummInit() {
2001-10-09 14:30:12 +00:00
int i;
2002-08-14 20:43:56 +00:00
tempMusic = 0;
2001-10-09 14:30:12 +00:00
debug(9, "scummInit");
if ((_gameId == GID_MANIAC) && (_version == 1)) {
initScreens(0, 16, _screenWidth, 152);
} else if (_version >= 7) {
initScreens(0, 0, _screenWidth, _screenHeight);
} else {
initScreens(0, 16, _screenWidth, 144);
}
if (_version == 1) {
for (i = 0; i < 16; i++)
_roomPalette[i] = i;
if (_gameId == GID_MANIAC)
setupV1ManiacPalette();
else
setupV1ZakPalette();
} else if (_features & GF_16COLOR) {
for (i = 0; i < 16; i++) {
_roomPalette[i] = i;
_shadowPalette[i] = i;
}
if ((_features & GF_AMIGA) || (_features & GF_ATARI_ST))
setupAmigaPalette();
else
setupEGAPalette();
}
if (_version <= 2) {
initV2MouseOver();
// Seems in V2 there was only a single room effect (iris),
// so we set that here.
_switchRoomEffect2 = 1;
_switchRoomEffect = 5;
}
if (_version <= 2)
_charset = new CharsetRendererV2(this);
else if (_version == 3)
_charset = new CharsetRendererV3(this);
else if (_version == 8)
_charset = new CharsetRendererNut(this);
2002-12-25 23:18:30 +00:00
else
_charset = new CharsetRendererClassic(this);
memset(_charsetData, 0, sizeof(_charsetData));
2003-03-06 17:58:13 +00:00
if (!(_features & GF_SMALL_NAMES) && _version != 8)
loadCharset(1);
2003-06-09 01:06:29 +00:00
if (_features & GF_OLD_BUNDLE)
loadCharset(0); // FIXME - HACK ?
2001-10-09 14:30:12 +00:00
setShake(0);
setupCursor();
2002-12-29 19:54:11 +00:00
// Allocate and Initialize actors
Actor::initActorClass(this);
_actors = new Actor[_numActors];
for (i = 0; i < _numActors; i++) {
_actors[i].number = i;
_actors[i].initActor(1);
2003-06-09 01:06:29 +00:00
// this is from IDB
if (_version == 1)
2003-06-07 14:02:07 +00:00
_actors[i].setActorCostume(i);
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
2003-09-11 10:32:15 +00:00
vm.numNestedScripts = 0;
2001-10-09 14:30:12 +00:00
vm.cutSceneStackPointer = 0;
memset(vm.cutScenePtr, 0, sizeof(vm.cutScenePtr));
memset(vm.cutSceneData, 0, sizeof(vm.cutSceneData));
for (i = 0; i < _maxVerbs; i++) {
2001-10-16 10:01:48 +00:00
_verbs[i].verbid = 0;
_verbs[i].right = _screenWidth - 1;
2003-05-15 22:36:43 +00:00
_verbs[i].old.left = -1;
2001-10-16 10:01:48 +00:00
_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;
2001-10-16 10:01:48 +00:00
_verbs[i].key = 0;
2001-10-09 14:30:12 +00:00
}
if (!(_features & GF_NEW_CAMERA)) {
camera._leftTrigger = 10;
camera._rightTrigger = 30;
camera._mode = 0;
}
2001-10-09 14:30:12 +00:00
camera._follows = 0;
virtscr[0].xstart = 0;
if (!(_features & GF_NEW_OPCODES)) {
// Setup light
2003-05-15 22:48:06 +00:00
_flashlight.xStrips = 7;
_flashlight.yStrips = 7;
_flashlight.buffer = NULL;
}
2001-10-09 14:30:12 +00:00
_mouse.x = 104;
_mouse.y = 56;
2001-10-09 14:30:12 +00:00
_ENCD_offs = 0;
_EXCD_offs = 0;
_currentScript = 0xFF;
_sentenceNum = 0;
2001-10-09 14:30:12 +00:00
_currentRoom = 0;
_numObjectsInRoom = 0;
_actorToPrintStrFor = 0;
_charsetBufPos = 0;
2001-10-09 14:30:12 +00:00
_haveMsg = 0;
_varwatch = -1;
2001-10-09 14:30:12 +00:00
_screenStartStrip = 0;
_talkDelay = 0;
_keepText = false;
_currentCursor = 0;
2002-12-04 22:31:36 +00:00
_cursor.state = 0;
2001-10-09 14:30:12 +00:00
_userPut = 0;
2001-10-09 14:30:12 +00:00
_newEffect = 129;
_fullRedraw = true;
2001-10-09 14:30:12 +00:00
clearDrawObjectQueue();
for (i = 0; i < 6; i++) {
if (_version == 3) { // FIXME - what is this?
_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 = _screenWidth - 1;
_string[i].t_color = 0xF;
_string[i].t_center = 0;
_string[i].t_charset = 0;
2001-10-09 14:30:12 +00:00
}
_numInMsgStack = 0;
createResource(rtTemp, 6, 500);
2001-10-09 14:30:12 +00:00
initScummVars();
// FIXME: disabled, why we need this, it's looks completly dummy and slow down startup
// getGraphicsPerformance();
2002-12-13 03:23:17 +00:00
_lastSaveTime = _system->get_msecs();
2001-10-09 14:30:12 +00:00
}
void ScummEngine::initScummVars() {
2003-05-08 21:02:46 +00:00
// FIXME
if (_version <= 2) {
// This needs to be at least greater than 40 to get the more
// elaborate version of the EGA Zak into. I don't know where
// else it makes any difference.
VAR(VAR_MACHINE_SPEED) = 0x7FFF;
2003-05-08 21:02:46 +00:00
return;
}
2003-05-08 21:02:46 +00:00
if (_version < 6)
VAR(VAR_V5_TALK_STRING_Y) = -0x50;
if (_version == 8) { // Fixme: How do we deal with non-cd installs?
2003-05-14 09:47:53 +00:00
VAR(VAR_CURRENTDISK) = 1;
VAR(VAR_LANGUAGE) = _language;
} else if (_version >= 7) {
2003-05-14 09:47:53 +00:00
VAR(VAR_V6_EMSSPACE) = 10000;
} else {
VAR(VAR_CURRENTDRIVE) = 0;
VAR(VAR_FIXEDDISK) = true;
switch (_midiDriver) {
case MD_NULL: VAR(VAR_SOUNDCARD) = 0; break;
case MD_ADLIB: VAR(VAR_SOUNDCARD) = 3; break;
case MD_PCSPK:
case MD_PCJR: VAR(VAR_SOUNDCARD) = 1; break;
default:
if ((_gameId == GID_MONKEY_EGA || _gameId == GID_MONKEY_VGA || _gameId == GID_LOOM)
&& !(_features & GF_AMIGA || _features & GF_ATARI_ST || _features & GF_MACINTOSH)) {
if (_gameId == GID_LOOM) {
char buf[50];
uint i = 82;
File f;
2003-09-21 04:54:34 +00:00
while (i < 85) {
sprintf(buf, "%d.LFL", i);
f.open(buf, _gameDataPath);
if (f.isOpen() == false)
error("Native MIDI support requires Roland patch from LucasArts");
f.close();
i++;
}
} else if (_gameId == GID_MONKEY_EGA) {
File f;
f.open("DISK09.LEC", _gameDataPath);
if (f.isOpen() == false)
error("Native MIDI support requires Roland patch from LucasArts");
}
2003-08-15 15:10:19 +00:00
VAR(VAR_SOUNDCARD) = 4;
} else
VAR(VAR_SOUNDCARD) = 3;
}
VAR(VAR_VIDEOMODE) = 0x13;
VAR(VAR_HEAPSPACE) = 1400;
VAR(VAR_MOUSEPRESENT) = true; // FIXME - used to be 0, but that seems odd?!?
if (_features & GF_HUMONGOUS)
VAR(VAR_SOUNDPARAM) = 1; // soundblaster for music
2003-06-09 01:06:29 +00:00
else
VAR(VAR_SOUNDPARAM) = 0;
VAR(VAR_SOUNDPARAM2) = 0;
VAR(VAR_SOUNDPARAM3) = 0;
if (_version >= 6 && VAR_V6_EMSSPACE != 0xFF)
VAR(VAR_V6_EMSSPACE) = 10000;
VAR(VAR_59) = 3;
}
2003-09-05 15:25:31 +00:00
if ((_features & GF_MACINTOSH) && (_version == 3)) {
// This is the for the Mac version of Indy3/Loom
VAR(39) = 320;
}
if (!(_features & GF_NEW_OPCODES)) {
// Setup light
VAR(VAR_CURRENT_LIGHTS) = LIGHTMODE_actor_base | LIGHTMODE_actor_color | LIGHTMODE_screen;
}
VAR(VAR_CHARINC) = 4;
VAR(VAR_TALK_ACTOR) = 0;
2003-03-06 17:58:13 +00:00
}
2001-10-09 14:30:12 +00:00
#pragma mark -
#pragma mark --- Main loop ---
#pragma mark -
void ScummEngine::mainRun() {
int delta = 0;
int diff = _system->get_msecs();
while (!_quit) {
updatePalette();
_system->update_screen();
diff -= _system->get_msecs();
waitForTimer(delta * 15 + diff);
diff = _system->get_msecs();
delta = scummLoop(delta);
if (delta < 1) // Ensure we don't get into a loop
delta = 1; // by not decreasing sleepers.
if (_quit) {
// TODO: Maybe perform an autosave on exit?
// TODO: Also, we could optionally show a "Do you really want to quit?" dialog here
}
}
}
void ScummEngine::waitForTimer(int msec_delay) {
uint32 start_time;
if (_fastMode & 2)
msec_delay = 0;
else if (_fastMode & 1)
msec_delay = 10;
start_time = _system->get_msecs();
while (!_quit) {
parseEvents();
_sound->updateCD(); // Loop CD Audio if needed
if (_system->get_msecs() >= start_time + msec_delay)
break;
_system->delay_msecs(10);
2001-10-09 14:30:12 +00:00
}
}
int ScummEngine::scummLoop(int delta) {
if (_debugger)
_debugger->on_frame();
// 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.
_rnd.getRandomNumber(2);
if (_version > 2) {
VAR(VAR_TMR_1) += delta;
VAR(VAR_TMR_2) += delta;
VAR(VAR_TMR_3) += delta;
}
if (VAR_TMR_4 != 0xFF)
VAR(VAR_TMR_4) += delta;
2001-10-09 14:30:12 +00:00
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;
}
// Record the current ego actor before any scripts (including input scripts)
// get a chance to run.
int oldEgo = 0;
if (VAR_EGO != 0xFF)
oldEgo = VAR(VAR_EGO);
// In V1-V3 games, CHARSET_1 is called much earlier than in newer games.
// See also bug #770042 for a case were this makes a difference.
// FIXME: Actually I am only sure that this is correct for V1-V2 and Loom.
// We should also check Indy3 & Zak256.
if (_version <= 3)
CHARSET_1();
processKbd();
if (_features & GF_NEW_CAMERA) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
} else if (_version <= 2) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x / 8;
} else {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
}
VAR(VAR_HAVE_MSG) = (_haveMsg == 0xFE) ? 0xFF : _haveMsg;
if (_version <= 2) {
VAR(VAR_VIRT_MOUSE_X) = _virtualMouse.x / 8;
VAR(VAR_VIRT_MOUSE_Y) = _virtualMouse.y / 2;
} else {
VAR(VAR_VIRT_MOUSE_X) = _virtualMouse.x;
VAR(VAR_VIRT_MOUSE_Y) = _virtualMouse.y;
VAR(VAR_MOUSE_X) = _mouse.x;
VAR(VAR_MOUSE_Y) = _mouse.y;
2003-09-05 15:25:31 +00:00
if ((_features & GF_MACINTOSH) && (_version == 3)) {
// This is the for the Mac version of Indy3/Loom
VAR(VAR_DEBUGMODE) = _debugMode;
}
2003-05-08 20:37:06 +00:00
}
if (_features & GF_AUDIOTRACKS) {
// Covered automatically by the Sound class
} else if (_musicEngine && VAR_MUSIC_TIMER != 0xFF) {
// The music engine generates the timer data for us.
VAR(VAR_MUSIC_TIMER) = _musicEngine->getMusicTimer();
} else if (_features & GF_SMALL_HEADER) {
// Used for Money Island 1 (Amiga)
// TODO: The music delay (given in milliseconds) might have to be tuned a little
// to get it correct for all games. Without the ability to watch/listen to the
// original games, I can't do that myself.
const int MUSIC_DELAY = 350;
tempMusic += delta * 15; // Convert delta to milliseconds
if (tempMusic >= MUSIC_DELAY) {
tempMusic -= MUSIC_DELAY;
VAR(VAR_MUSIC_TIMER) += 1;
}
}
2002-12-13 03:23:17 +00:00
// Trigger autosave all 5 minutes.
if (!_saveLoadFlag && _system->get_msecs() > _lastSaveTime + 5 * 60 * 1000) {
_saveLoadSlot = 0;
sprintf(_saveLoadName, "Autosave %d", _saveLoadSlot);
_saveLoadFlag = 1;
_saveLoadCompatible = false;
}
if (VAR_GAME_LOADED != 0xFF)
VAR(VAR_GAME_LOADED) = 0;
if (_saveLoadFlag) {
load_game:
bool success;
2003-07-08 18:09:22 +00:00
const char *errMsg = 0;
char filename[256];
if (_saveLoadFlag == 1) {
success = saveState(_saveLoadSlot, _saveLoadCompatible);
if (!success)
2002-12-03 23:53:42 +00:00
errMsg = "Failed to save game state to file:\n\n%s";
// Ender: Disabled for small_header games, as can overwrite game
// variables (eg, Zak256 cashcard values). Temp disabled for V8
// because of odd timing issue with scripts and the variable reset
if (success && _saveLoadCompatible && !(_features & GF_SMALL_HEADER) && _version < 8)
VAR(VAR_GAME_LOADED) = 201;
} else {
success = loadState(_saveLoadSlot, _saveLoadCompatible);
if (!success)
2002-12-03 23:53:42 +00:00
errMsg = "Failed to load game state from file:\n\n%s";
// Ender: Disabled for small_header games, as can overwrite game
// variables (eg, Zak256 cashcard values).
2003-03-06 17:58:13 +00:00
if (success && _saveLoadCompatible && !(_features & GF_SMALL_HEADER))
VAR(VAR_GAME_LOADED) = 203;
2001-10-09 14:30:12 +00:00
}
makeSavegameName(filename, _saveLoadSlot, _saveLoadCompatible);
if (!success) {
displayError(false, errMsg, filename);
} else if (_saveLoadFlag == 1 && _saveLoadSlot != 0 && !_saveLoadCompatible) {
// Display "Save succesful" message, except for auto saves
2003-04-30 13:23:31 +00:00
#ifdef __PALM_OS__
char buf[256]; // 1024 is too big overflow the stack
#else
char buf[1024];
2003-04-30 13:23:31 +00:00
#endif
2003-07-31 18:55:29 +00:00
sprintf(buf, "Successfully saved game state in file:\n\n%s", filename);
2003-06-09 01:06:29 +00:00
2003-09-11 10:32:15 +00:00
MessageDialog dialog(_newgui, buf, 1500, false);
runDialog(dialog);
}
if (success && _saveLoadFlag != 1)
clearClickedStatus();
_saveLoadFlag = 0;
2002-12-13 03:23:17 +00:00
_lastSaveTime = _system->get_msecs();
}
if (_completeScreenRedraw) {
_completeScreenRedraw = false;
gdi.clearCharsetMask();
_charset->_hasMask = false;
// HACK as in game save stuff isn't supported currently
if (_gameId == GID_LOOM || _gameId == GID_LOOM256) {
int args = 2;
uint value = (_gameId == GID_LOOM256) ? 150 : 100;
byte restoreScript = (_features & GF_FMTOWNS) ? 17 : 18;
redrawVerbs();
// if verbs should be shown restore them
if (VAR(value) == 2)
runScript(restoreScript, 0, 0, &args);
} else if (_version > 3) {
for (int i = 0; i < _maxVerbs; i++)
drawVerb(i, 0);
} else {
redrawVerbs();
}
verbMouseOver(0);
if (_version <= 2) {
redrawV2Inventory();
checkV2MouseOver(_mouse);
}
_verbRedraw = false;
_fullRedraw = true;
}
2001-10-09 14:30:12 +00:00
runAllScripts();
checkExecVerbs();
2002-12-09 01:27:40 +00:00
checkAndRunSentenceScript();
2001-10-09 14:30:12 +00:00
if (_quit)
return 0;
// HACK: If a load was requested, immediately perform it. This avoids
// drawing the current room right after the load is request but before
2003-06-09 01:06:29 +00:00
// it is performed. That was annoying esp. if you loaded while a SMUSH
// cutscene was playing.
if (_saveLoadFlag && _saveLoadFlag != 1) {
goto load_game;
}
if (_currentRoom == 0) {
if (_version > 3)
CHARSET_1();
drawDirtyScreenParts();
} else {
2001-10-09 14:30:12 +00:00
walkActors();
moveCamera();
fixObjectFlags();
if (_version > 3)
CHARSET_1();
2002-08-20 02:13:41 +00:00
if (camera._cur.x != camera._last.x || _BgNeedsRedraw || _fullRedraw
|| ((_features & GF_NEW_CAMERA) && camera._cur.y != camera._last.y)) {
2002-08-20 02:13:41 +00:00
redrawBGAreas();
2001-10-09 14:30:12 +00:00
}
2002-08-20 02:13:41 +00:00
2001-10-09 14:30:12 +00:00
processDrawQue();
if (_verbRedraw) {
redrawVerbs();
}
2003-06-09 01:06:29 +00:00
setActorRedrawFlags();
2001-10-09 14:30:12 +00:00
resetActorBgs();
if (!(_features & GF_NEW_OPCODES) &&
!(VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_screen) &&
VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_flashlight) {
drawFlashlight();
setActorRedrawFlags();
}
2001-10-09 14:30:12 +00:00
processActors();
_fullRedraw = false;
2001-10-09 14:30:12 +00:00
cyclePalette();
palManipulate();
2001-10-26 17:34:50 +00:00
if (_doEffect) {
_doEffect = false;
fadeIn(_newEffect);
2001-10-09 14:30:12 +00:00
clearClickedStatus();
}
if (!_verbRedraw && _cursor.state > 0) {
verbMouseOver(checkMouseOver(_mouse.x, _mouse.y));
2001-10-09 14:30:12 +00:00
}
_verbRedraw = false;
2001-10-09 14:30:12 +00:00
if (_version <= 2) {
if (oldEgo != VAR(VAR_EGO)) {
// FIXME/TODO: Reset and redraw the sentence line
oldEgo = VAR(VAR_EGO);
_inventoryOffset = 0;
redrawV2Inventory();
}
checkV2MouseOver(_mouse);
}
// For the Full Throttle credits to work properly, the blast
// texts have to be drawn before the blast objects. Unless
// someone can think of a better way to achieve this effect.
if (_gameId == GID_FT) {
drawBlastTexts();
drawBlastObjects();
} else {
drawBlastObjects();
drawBlastTexts();
}
if (_version == 8)
processUpperActors();
drawDirtyScreenParts();
removeBlastTexts();
removeBlastObjects();
2001-10-09 14:30:12 +00:00
if (_version <= 5)
playActorSounds();
}
2003-07-02 00:49:03 +00:00
_sound->processSoundQues();
camera._last = camera._cur;
if (!(++_expire_counter)) {
increaseResourceCounter();
}
animateCursor();
/* show or hide mouse */
2002-12-04 22:31:36 +00:00
_system->show_mouse(_cursor.state > 0);
if (VAR_TIMER != 0xFF)
VAR(VAR_TIMER) = 0;
return VAR(VAR_TIMER_NEXT);
}
#pragma mark -
#pragma mark --- Events / Input ---
#pragma mark -
void ScummEngine::parseEvents() {
OSystem::Event event;
2001-10-09 14:30:12 +00:00
while (_system->poll_event(&event)) {
2001-10-09 14:30:12 +00:00
switch(event.event_code) {
case OSystem::EVENT_KEYDOWN:
if (event.kbd.keycode >= '0' && event.kbd.keycode<='9'
&& (event.kbd.flags == OSystem::KBD_ALT ||
event.kbd.flags == OSystem::KBD_CTRL)) {
_saveLoadSlot = event.kbd.keycode - '0';
// don't overwrite autosave (slot 0)
if (_saveLoadSlot == 0)
_saveLoadSlot = 10;
2001-10-09 14:30:12 +00:00
sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot);
_saveLoadFlag = (event.kbd.flags == OSystem::KBD_ALT) ? 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')
g_debugger->attach(this, NULL);
else if (event.kbd.keycode == 's')
resourceStats();
else
_keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
} else if (event.kbd.flags & OSystem::KBD_ALT) {
// The result must be 273 for Alt-W
// because that's what MI2 looks for in
// its "instant win" cheat.
_keyPressed = event.kbd.keycode + 154;
} else if (event.kbd.ascii == 315 && _gameId == GID_CMI) {
// FIXME: support in-game menu screen. For now, this remaps F1 to F5 in COMI
_keyPressed = 319;
} else if (_gameId == GID_INDY4 && event.kbd.ascii >= '0' && event.kbd.ascii <= '9') {
// To support keyboard fighting in FOA, we need to remap the number keys.
// FOA apparently expects PC scancode values (see script 46 if you want
// to know where I got these numbers from).
static const int numpad[10] = {
'0',
335, 336, 337,
331, 332, 333,
327, 328, 329
};
_keyPressed = numpad[event.kbd.ascii - '0'];
} else if (event.kbd.ascii < 273 || event.kbd.ascii > 276) {
// don't let game have arrow keys as we currently steal them
// for keyboard cursor control
// this fixes bug with up arrow (273) corresponding to
// "instant win" cheat in MI2 mentioned above
_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;
break;
case OSystem::EVENT_LBUTTONDOWN:
_leftBtnPressed |= msClicked|msDown;
#if defined(_WIN32_WCE) || defined(__PALM_OS__)
_mouse.x = event.mouse.x;
_mouse.y = event.mouse.y;
#endif
break;
case OSystem::EVENT_RBUTTONDOWN:
_rightBtnPressed |= msClicked|msDown;
#if defined(_WIN32_WCE) || defined(__PALM_OS__)
_mouse.x = event.mouse.x;
_mouse.y = event.mouse.y;
#endif
break;
2001-10-09 14:30:12 +00:00
case OSystem::EVENT_LBUTTONUP:
_leftBtnPressed &= ~msDown;
break;
2001-10-09 14:30:12 +00:00
case OSystem::EVENT_RBUTTONUP:
_rightBtnPressed &= ~msDown;
break;
case OSystem::EVENT_QUIT:
if(_confirmExit)
confirmexitDialog();
else
_quit = true;
break;
default:
break;
}
}
}
2001-10-09 14:30:12 +00:00
void ScummEngine::clearClickedStatus() {
_keyPressed = 0;
_mouseButStat = 0;
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
}
2001-10-09 14:30:12 +00:00
void ScummEngine::processKbd() {
int saveloadkey;
_lastKeyHit = _keyPressed;
_keyPressed = 0;
if (_version <= 2 && 315 <= _lastKeyHit && _lastKeyHit < 315+12) {
// Convert F-Keys for V1/V2 games (they start at 1 instead of at 315)
_lastKeyHit -= 314;
}
//
// Clip the mouse coordinates, and compute _virtualMouse.x (and clip it, too)
//
if (_mouse.x < 0)
_mouse.x = 0;
if (_mouse.x > _screenWidth-1)
_mouse.x = _screenWidth-1;
if (_mouse.y < 0)
_mouse.y = 0;
if (_mouse.y > _screenHeight-1)
_mouse.y = _screenHeight-1;
2001-10-09 14:30:12 +00:00
_virtualMouse.x = _mouse.x + virtscr[0].xstart;
_virtualMouse.y = _mouse.y - virtscr[0].topline;
if (_features & GF_NEW_CAMERA)
_virtualMouse.y += _screenTop;
if (_virtualMouse.y < 0)
_virtualMouse.y = -1;
if (_virtualMouse.y >= virtscr[0].height)
_virtualMouse.y = -1;
2001-10-09 14:30:12 +00:00
//
// Determine the mouse button state.
//
_mouseButStat = 0;
// Interpret 'return' as left click and 'tab' as right click
if (_lastKeyHit && _cursor.state > 0) {
if (_lastKeyHit == 9) {
_mouseButStat = MBS_RIGHT_CLICK;
_lastKeyHit = 0;
} else if (_lastKeyHit == 13) {
_mouseButStat = MBS_LEFT_CLICK;
_lastKeyHit = 0;
}
}
if (_leftBtnPressed & msClicked && _rightBtnPressed & msClicked && _version > 3) {
// Pressing both mouse buttons is treated as if you pressed
// the cutscene exit key (i.e. ESC in most games). That mimicks
// the behaviour of the original engine where pressing both
// mouse buttons also skips the current cutscene.
_mouseButStat = 0;
_lastKeyHit = (uint)VAR(VAR_CUTSCENEEXIT_KEY);
} else if (_rightBtnPressed & msClicked && (_version < 4 && _gameId != GID_LOOM)) {
// Pressing right mouse button is treated as if you pressed
// the cutscene exit key (i.e. ESC in most games). That mimicks
// the behaviour of the original engine where pressing right
// mouse button also skips the current cutscene.
_mouseButStat = 0;
_lastKeyHit = (uint)VAR(VAR_CUTSCENEEXIT_KEY);
} else if (_leftBtnPressed & msClicked) {
_mouseButStat = MBS_LEFT_CLICK;
} else if (_rightBtnPressed & msClicked) {
_mouseButStat = MBS_RIGHT_CLICK;
}
if (_version == 8) {
VAR(VAR_MOUSE_BUTTONS) = 0;
VAR(VAR_MOUSE_HOLD) = 0;
VAR(VAR_RIGHTBTN_HOLD) = 0;
if (_leftBtnPressed & msClicked)
VAR(VAR_MOUSE_BUTTONS) += 1;
if (_rightBtnPressed & msClicked)
VAR(VAR_MOUSE_BUTTONS) += 2;
if (_leftBtnPressed & msDown)
VAR(VAR_MOUSE_HOLD) += 1;
if (_rightBtnPressed & msDown) {
VAR(VAR_RIGHTBTN_HOLD) = 1;
VAR(VAR_MOUSE_HOLD) += 2;
}
} else if (_version == 7) {
VAR(VAR_LEFTBTN_HOLD) = (_leftBtnPressed & msDown) != 0;
VAR(VAR_RIGHTBTN_HOLD) = (_rightBtnPressed & msDown) != 0;
}
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
if (!_lastKeyHit)
return;
// If a key script was specified (a V8 feature), and it's trigger
// key was pressed, run it.
if (_keyScriptNo && (_keyScriptKey == _lastKeyHit)) {
runScript(_keyScriptNo, 0, 0, 0);
return;
}
2001-10-26 17:34:50 +00:00
#ifdef _WIN32_WCE
if (_lastKeyHit == KEY_SET_OPTIONS) {
//_newgui->optionsDialog();
return;
2001-10-26 17:34:50 +00:00
}
if (_lastKeyHit == KEY_ALL_SKIP) {
// Skip cutscene
if (_insaneState) {
_videoFinished = true;
return;
}
else
if (vm.cutScenePtr[vm.cutSceneStackPointer])
_lastKeyHit = (uint16)VAR(VAR_CUTSCENEEXIT_KEY);
else
// Skip talk
if (_talkDelay > 0)
_lastKeyHit = (uint16)VAR(VAR_TALKSTOP_KEY);
else
// Escape
_lastKeyHit = 27;
}
#endif
2002-09-21 16:20:52 +00:00
if (VAR_RESTART_KEY != 0xFF && _lastKeyHit == VAR(VAR_RESTART_KEY)) {
warning("Restart not implemented");
//restart();
return;
}
if ((VAR_PAUSE_KEY != 0xFF && _lastKeyHit == VAR(VAR_PAUSE_KEY)) ||
(VAR_PAUSE_KEY == 0xFF && _lastKeyHit == ' ')) {
pauseGame();
2001-10-09 14:30:12 +00:00
return;
}
2001-10-09 14:30:12 +00:00
if (_version <= 2)
saveloadkey = 5; // F5
2003-09-27 20:31:19 +00:00
else if ((_version <= 3) || (_gameId == GID_CMI)) /* FIXME: Support ingame screen ? */
saveloadkey = 319; // F5
else
saveloadkey = VAR(VAR_SAVELOADDIALOG_KEY);
if (_lastKeyHit == VAR(VAR_CUTSCENEEXIT_KEY) ||
(VAR(VAR_CUTSCENEEXIT_KEY) == 4 && _lastKeyHit == 27)) {
// Skip cutscene (or active SMUSH video). For the V2 games, which
// normally use F4 for this, we add in a hack that makes escape work,
// too (just for convenience).
if (_insaneState) {
_videoFinished = true;
} else
abortCutscene();
2003-07-22 01:03:10 +00:00
if (_version <= 2) {
// Ensure that the input script also sees the key press.
// This is necessary so you can abort the airplane travel
// in Zak.
VAR(VAR_KEYPRESS) = VAR(VAR_CUTSCENEEXIT_KEY);
}
2003-07-20 01:09:42 +00:00
} else if (_lastKeyHit == saveloadkey) {
if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0);
2001-10-09 14:30:12 +00:00
saveloadDialog(); // Display NewGui
2001-10-09 14:30:12 +00:00
2003-07-20 01:09:42 +00:00
if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0);
return;
} else if (VAR_TALKSTOP_KEY != 0xFF && _lastKeyHit == VAR(VAR_TALKSTOP_KEY)) {
_talkDelay = 0;
if (_sound->_sfxMode & 2)
stopTalk();
return;
} else if (_lastKeyHit == '[') { // [ Music volume down
int vol = _sound->_sound_volume_music;
if (!(vol & 0xF) && vol)
vol -= 16;
vol = vol & 0xF0;
_sound->_sound_volume_music = vol;
if (_imuse)
_imuse->set_music_volume (vol);
} else if (_lastKeyHit == ']') { // ] Music volume up
int vol = _sound->_sound_volume_music;
vol = (vol + 16) & 0xFF0;
if (vol > 255) vol = 255;
_sound->_sound_volume_music = vol;
if (_imuse)
_imuse->set_music_volume (vol);
} else if (_lastKeyHit == '-') { // - text speed down
_defaultTalkDelay += 5;
if (_defaultTalkDelay > 90)
_defaultTalkDelay = 90;
2001-10-09 14:30:12 +00:00
VAR(VAR_CHARINC) = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '+') { // + text speed up
_defaultTalkDelay -= 5;
if (_defaultTalkDelay < 5)
_defaultTalkDelay = 5;
VAR(VAR_CHARINC) = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '~' || _lastKeyHit == '#') { // Debug console
g_debugger->attach(this, NULL);
} else if (_version <= 2) {
// Store the input type. So far we can't distinguish
// between 1, 3 and 5.
// 1) Verb 2) Scene 3) Inv. 4) Key
// 5) Sentence Bar
if (_lastKeyHit) { // Key Input
VAR(VAR_KEYPRESS) = _lastKeyHit;
}
}
2001-10-09 14:30:12 +00:00
_mouseButStat = _lastKeyHit;
}
2001-10-09 14:30:12 +00:00
#pragma mark -
#pragma mark --- SCUMM ---
#pragma mark -
/**
* Start a 'scene' by loading the specified room with the given main actor.
* The actor is placed next to the object indicated by objectNr.
*/
void ScummEngine::startScene(int room, Actor *a, int objectNr) {
int i, where;
CHECK_HEAP;
debug(1, "Loading room %d", room);
clearMsgQueue();
fadeOut(_switchRoomEffect2);
_newEffect = _switchRoomEffect;
ScriptSlot *ss = &vm.slot[_currentScript];
if (_currentScript != 0xFF) {
if (ss->where == WIO_ROOM || ss->where == WIO_FLOBJECT) {
if (ss->cutsceneOverride != 0)
error("Object %d stopped with active cutscene/override in exit", ss->number);
_currentScript = 0xFF;
} else if (ss->where == WIO_LOCAL) {
if (ss->cutsceneOverride != 0) {
if (_gameId == GID_ZAK256 && _roomResource == 15 && ss->number == 202) {
// HACK to make Zak256 work (see bug #770093)
warning("Script %d stopped with active cutscene/override in exit", ss->number);
} else if (_gameId == GID_INDY3 && (_features & GF_OLD_BUNDLE) && _roomResource == 3) {
// HACK to make Indy3 Demo work
warning("Script %d stopped with active cutscene/override in exit", ss->number);
} else {
error("Script %d stopped with active cutscene/override in exit", ss->number);
}
}
_currentScript = 0xFF;
}
}
if (!(_features & GF_SMALL_HEADER) && VAR_NEW_ROOM != 0xFF) // Disable for SH games. Overwrites
VAR(VAR_NEW_ROOM) = room; // gamevars, eg Zak cashcards
runExitScript();
killScriptsAndResources();
clearEnqueue();
stopCycle(0);
_sound->processSoundQues();
for (i = 1; i < _numActors; i++) {
_actors[i].hideActor();
}
if (_version < 7) {
for (i = 0; i < 256; i++) {
if (_features & GF_SMALL_HEADER)
_roomPalette[i] = i;
_shadowPalette[i] = i;
}
if (_features & GF_SMALL_HEADER)
setDirtyColors(0, 255);
}
clearDrawObjectQueue();
VAR(VAR_ROOM) = room;
_fullRedraw = true;
increaseResourceCounter();
_currentRoom = room;
VAR(VAR_ROOM) = room;
if (room >= 0x80 && _version < 7)
_roomResource = _resourceMapper[room & 0x7F];
else
_roomResource = room;
if (VAR_ROOM_RESOURCE != 0xFF)
VAR(VAR_ROOM_RESOURCE) = _roomResource;
if (room != 0)
ensureResourceLoaded(rtRoom, room);
clearRoomObjects();
if (_currentRoom == 0) {
_ENCD_offs = _EXCD_offs = 0;
_numObjectsInRoom = 0;
return;
}
initRoomSubBlocks();
if (_features & GF_OLD_BUNDLE)
loadRoomObjectsOldBundle();
else if (_features & GF_SMALL_HEADER)
loadRoomObjectsSmall();
else
loadRoomObjects();
2003-09-11 13:02:13 +00:00
if (VAR_V6_SCREEN_WIDTH != 0xFF && VAR_V6_SCREEN_HEIGHT != 0xFF) {
VAR(VAR_V6_SCREEN_WIDTH) = _roomWidth;
VAR(VAR_V6_SCREEN_HEIGHT) = _roomHeight;
}
VAR(VAR_CAMERA_MIN_X) = _screenWidth / 2;
VAR(VAR_CAMERA_MAX_X) = _roomWidth - (_screenWidth / 2);
if (_features & GF_NEW_CAMERA) {
VAR(VAR_CAMERA_MIN_Y) = _screenHeight / 2;
VAR(VAR_CAMERA_MAX_Y) = _roomHeight - (_screenHeight / 2);
setCameraAt(_screenWidth / 2, _screenHeight / 2);
} else {
camera._mode = CM_NORMAL;
if (_version > 2)
2003-09-11 13:02:13 +00:00
camera._cur.x = camera._dest.x = _screenWidth / 2;
camera._cur.y = camera._dest.y = _screenHeight / 2;
}
2003-09-11 13:02:13 +00:00
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 (_version <= 2)
runScript(5, 0, 0, 0);
2003-09-11 13:39:27 +00:00
else if (_version >= 4 && _version <= 6) {
2003-09-11 13:02:13 +00:00
// FIXME: The check above maybe should only trigger for V5&V6 games (i.e. not
// for V4). More investigation (ASM) needed. See also o5_loadRoomWithEgo().
if (a && !_egoPositioned) {
int x, y;
getObjectXYPos(objectNr, x, y);
a->putActor(x, y, _currentRoom);
a->moving = 0;
}
} else {
if (a) {
// FIXME: This hack mostly is there to fix the tomb/statue room
// in The Dig. What happens there is that when you enter, you are
// placed at object 399, coords (307,141), which is in box 25.
// But then the entry script locks that and other boxes. Hence
// after the entry script runs, you basically can only do one thing
// in that room, and that is to leave it - which means the game
// is unfinishable.
// By calling adjustActorPos, we can solve the problem in this case:
// there is a very close box (box 12) which contains point (307,144).
// If we call adjustActorPos, Commander Low is moved into that box,
// and we can go on. But aqudran looked this up in his IMB DB for
// The DIG; and nothing like this is done there. Also I am pretty
// sure this used to work in 0.3.1. So apparently something broke
// down here, and I have no clue what that might be :-/
a->adjustActorPos();
}
if (camera._follows) {
a = derefActor(camera._follows, "startScene: follows");
setCameraAt(a->_pos.x, a->_pos.y);
}
}
_doEffect = true;
CHECK_HEAP;
}
void ScummEngine::initRoomSubBlocks() {
int i;
const byte *ptr;
byte *roomptr, *searchptr, *roomResPtr;
const 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);
memset(_localScriptList, 0, sizeof(_localScriptList));
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
// Determine the room and room script base address
roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_version == 8)
roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
if (!roomptr || !roomResPtr)
error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
// Reset room color for V1 zak
if (_version == 1)
_roomPalette[0] = 0;
2003-04-13 01:51:24 +00:00
//
// Determine the room dimensions (width/height)
//
if (_features & GF_OLD_BUNDLE)
2003-05-28 20:01:47 +00:00
rmhd = (const RoomHeader *)(roomptr + 4);
else
2003-05-28 20:01:47 +00:00
rmhd = (const RoomHeader *)findResourceData(MKID('RMHD'), roomptr);
if (_version == 1) {
_roomWidth = roomptr[4] * 8;
_roomHeight = roomptr[5] * 8;
} else if (_version == 8) {
_roomWidth = READ_LE_UINT32(&(rmhd->v8.width));
_roomHeight = READ_LE_UINT32(&(rmhd->v8.height));
} else if (_version == 7) {
_roomWidth = READ_LE_UINT16(&(rmhd->v7.width));
_roomHeight = READ_LE_UINT16(&(rmhd->v7.height));
} else {
_roomWidth = READ_LE_UINT16(&(rmhd->old.width));
_roomHeight = READ_LE_UINT16(&(rmhd->old.height));
}
2001-10-09 14:30:12 +00:00
//
// Find the room image data
//
if (_version == 1) {
_IM00_offs = 0;
for (i = 0; i < 4; i++){
2003-06-06 00:16:34 +00:00
gdi._C64Colors[i] = roomptr[6 + i];
}
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 10), gdi._C64CharMap, 2048);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 12), gdi._C64PicMap, roomptr[4] * roomptr[5]);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 14), gdi._C64ColorMap, roomptr[4] * roomptr[5]);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 16), gdi._C64MaskMap, roomptr[4] * roomptr[5]);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 18) + 2, gdi._C64MaskChar, READ_LE_UINT16(roomptr + READ_LE_UINT16(roomptr + 18)));
gdi._C64ObjectMode = true;
} else if (_features & GF_OLD_BUNDLE) {
_IM00_offs = READ_LE_UINT16(roomptr + 0x0A);
2003-06-14 13:26:11 +00:00
if (_version == 2)
_roomStrips = gdi.generateStripTable(roomptr + _IM00_offs, _roomWidth, _roomHeight, _roomStrips);
} else if (_version == 8) {
_IM00_offs = getObjectImage(roomptr, 1) - roomptr;
} else if (_features & GF_SMALL_HEADER) {
_IM00_offs = findResourceData(MKID('IM00'), roomptr) - roomptr;
} else {
_IM00_offs = findResource(MKID('IM00'), findResource(MKID('RMIM'), roomptr)) - roomptr;
}
//
// Look for an exit script
//
int EXCD_len = -1;
if (_version <= 2) {
2003-04-21 11:19:56 +00:00
_EXCD_offs = READ_LE_UINT16(roomptr + 0x18);
EXCD_len = READ_LE_UINT16(roomptr + 0x1A) - _EXCD_offs + _resourceHeaderSize; // HACK
} else if (_features & GF_OLD_BUNDLE) {
_EXCD_offs = READ_LE_UINT16(roomptr + 0x19);
EXCD_len = READ_LE_UINT16(roomptr + 0x1B) - _EXCD_offs + _resourceHeaderSize; // HACK
} else {
ptr = findResourceData(MKID('EXCD'), roomResPtr);
if (ptr)
_EXCD_offs = ptr - roomResPtr;
2001-10-09 14:30:12 +00:00
}
if (_dumpScripts && _EXCD_offs)
dumpResource("exit-", _roomResource, roomResPtr + _EXCD_offs - _resourceHeaderSize, EXCD_len);
2001-10-09 14:30:12 +00:00
//
// Look for an entry script
//
int ENCD_len = -1;
if (_version <= 2) {
_ENCD_offs = READ_LE_UINT16(roomptr + 0x1A);
ENCD_len = READ_LE_UINT16(roomptr) - _ENCD_offs + _resourceHeaderSize; // HACK
} else if (_features & GF_OLD_BUNDLE) {
_ENCD_offs = READ_LE_UINT16(roomptr + 0x1B);
// FIXME - the following is a hack which assumes that immediately after
// the entry script the first local script follows.
int num_objects = *(roomResPtr + 20);
int num_sounds = *(roomResPtr + 23);
int num_scripts = *(roomResPtr + 24);
ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
ENCD_len = READ_LE_UINT16(ptr + 1) - _ENCD_offs + _resourceHeaderSize; // HACK
} else {
ptr = findResourceData(MKID('ENCD'), roomResPtr);
if (ptr)
_ENCD_offs = ptr - roomResPtr;
2001-10-09 14:30:12 +00:00
}
if (_dumpScripts && _ENCD_offs)
dumpResource("entry-", _roomResource, roomResPtr + _ENCD_offs - _resourceHeaderSize, ENCD_len);
2001-10-09 14:30:12 +00:00
//
// Load box data
//
if (_features & GF_SMALL_HEADER) {
if (_version <= 2)
2003-04-21 11:19:56 +00:00
ptr = roomptr + *(roomptr + 0x15);
2003-04-21 13:19:19 +00:00
else if (_features & GF_OLD_BUNDLE)
ptr = roomptr + READ_LE_UINT16(roomptr + 0x15);
else
ptr = findResourceData(MKID('BOXD'), roomptr);
if (ptr) {
byte numOfBoxes = *ptr;
int size;
if (_version <= 2)
size = numOfBoxes * SIZEOF_BOX_V2 + 1;
else if (_version == 3)
size = numOfBoxes * SIZEOF_BOX_V3 + 1;
else
size = numOfBoxes * SIZEOF_BOX + 1;
createResource(rtMatrix, 2, size);
memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
ptr += size;
if (_version <= 2) {
2003-05-19 00:17:34 +00:00
size = numOfBoxes * (numOfBoxes + 1);
} else if (_features & GF_OLD_BUNDLE)
// FIXME. This is an evil HACK!!!
2003-04-13 01:51:24 +00:00
size = (READ_LE_UINT16(roomptr + 0x0A) - READ_LE_UINT16(roomptr + 0x15)) - size;
else
size = getResourceDataSize(ptr - size - 6) - size;
if (size > 0) { // do this :)
2002-02-22 16:59:24 +00:00
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);
}
}
2001-10-09 14:30:12 +00:00
//
2003-04-28 12:36:21 +00:00
// Load scale data
//
if (_features & GF_OLD_BUNDLE)
ptr = 0;
else
ptr = findResourceData(MKID('SCAL'), roomptr);
2001-10-09 14:30:12 +00:00
if (ptr) {
int s1, s2, y1, y2;
if (_version == 8) {
for (i = 1; i < _maxScaleTable; i++, ptr += 16) {
s1 = READ_LE_UINT32(ptr);
y1 = READ_LE_UINT32(ptr + 4);
s2 = READ_LE_UINT32(ptr + 8);
y2 = READ_LE_UINT32(ptr + 12);
setScaleSlot(i, 0, y1, s1, 0, y2, s2);
}
} else {
for (i = 1; i < _maxScaleTable; i++, ptr += 8) {
s1 = READ_LE_UINT16(ptr);
y1 = READ_LE_UINT16(ptr + 2);
s2 = READ_LE_UINT16(ptr + 4);
y2 = READ_LE_UINT16(ptr + 6);
if (s1 || y1 || s2 || y2) {
setScaleSlot(i, 0, y1, s1, 0, y2, s2);
}
2001-10-09 14:30:12 +00:00
}
}
}
//
// Setup local scripts
//
// Determine the room script base address
roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_version == 8)
roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
searchptr = roomResPtr;
if (_features & GF_OLD_BUNDLE) {
int num_objects = *(roomResPtr + 20);
2003-04-21 11:19:56 +00:00
int num_sounds;
int num_scripts;
if (_version <= 2) {
2003-04-21 11:19:56 +00:00
num_sounds = *(roomResPtr + 22);
num_scripts = *(roomResPtr + 23);
ptr = roomptr + 28 + num_objects * 4;
while (num_sounds--)
loadResource(rtSound, *ptr++);
while (num_scripts--)
loadResource(rtScript, *ptr++);
} else if (_version == 3) {
2003-04-21 11:19:56 +00:00
num_sounds = *(roomResPtr + 23);
num_scripts = *(roomResPtr + 24);
ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
while (*ptr) {
int id = *ptr;
_localScriptList[id - _numGlobalScripts] = READ_LE_UINT16(ptr + 1);
ptr += 3;
2003-06-09 01:06:29 +00:00
if (_dumpScripts) {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
// HACK: to determine the sizes of the local scripts, we assume that
// a) their order in the data file is the same as in the index
// b) the last script at the same time is the last item in the room "header"
int len = - (int)_localScriptList[id - _numGlobalScripts] + _resourceHeaderSize;
if (*ptr)
len += READ_LE_UINT16(ptr + 1);
else
len += READ_LE_UINT16(roomResPtr);
dumpResource(buf, id, roomResPtr + _localScriptList[id - _numGlobalScripts] - _resourceHeaderSize, len);
}
}
}
} else if (_features & GF_SMALL_HEADER) {
ResourceIterator localScriptIterator(searchptr, true);
while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
id = ptr[0];
if (_dumpScripts) {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
dumpResource(buf, id, ptr - _resourceHeaderSize);
}
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomptr;
}
} else {
ResourceIterator localScriptIterator(searchptr, false);
while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
if (_version == 8) {
id = READ_LE_UINT32(ptr);
checkRange(NUM_LOCALSCRIPT + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
_localScriptList[id - _numGlobalScripts] = ptr + 4 - roomResPtr;
} else if (_version == 7) {
id = READ_LE_UINT16(ptr);
checkRange(NUM_LOCALSCRIPT + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
_localScriptList[id - _numGlobalScripts] = ptr + 2 - roomResPtr;
} else {
id = ptr[0];
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomResPtr;
}
if (_dumpScripts) {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
dumpResource(buf, id, ptr - _resourceHeaderSize);
}
}
}
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO ? do 16 bit games use a palette?!?
else if (_features & GF_SMALL_HEADER)
ptr = findResourceSmall(MKID('CLUT'), roomptr);
else
ptr = findResourceData(MKID('CLUT'), roomptr);
2001-10-16 10:01:48 +00:00
if (ptr) {
_CLUT_offs = ptr - roomptr;
setPaletteFromRes();
}
2001-10-09 14:30:12 +00:00
if (_version >= 6) {
ptr = findResource(MKID('PALS'), roomptr);
if (ptr) {
_PALS_offs = ptr - roomptr;
setPalette(0);
}
2001-10-16 10:01:48 +00:00
}
// Color cycling
if (_version >= 4) {
if (_features & GF_SMALL_HEADER)
ptr = findResourceSmall (MKID('CYCL'), roomptr);
else
ptr = findResourceData(MKID('CYCL'), roomptr);
if (ptr) {
initCycl(ptr);
}
}
2001-10-09 14:30:12 +00:00
// Transparent color
if (_features & GF_OLD_BUNDLE)
gdi._transparentColor = 255; // TODO - FIXME
else {
ptr = findResourceData(MKID('TRNS'), roomptr);
if (ptr)
gdi._transparentColor = ptr[0];
else if (_version == 8)
gdi._transparentColor = 5; // FIXME
else
gdi._transparentColor = 255;
}
2001-10-09 14:30:12 +00:00
initBGBuffers(_roomHeight);
2001-10-09 14:30:12 +00:00
}
void ScummEngine::pauseGame() {
pauseDialog();
2001-10-09 14:30:12 +00:00
}
void ScummEngine::shutDown() {
_quit = true;
2003-01-13 01:29:45 +00:00
}
void ScummEngine::restart() {
// TODO: Check this function - we should probably be reinitting a lot more stuff, and I suspect
// this leaks memory like a sieve
int i;
// Reset some stuff
_currentRoom = 0;
_currentScript = 0xFF;
killAllScriptsExceptCurrent();
setShake(0);
_sound->stopAllSounds();
2003-09-11 10:32:15 +00:00
// Clear the script variables
for (i = 0; i < 255; i++)
_scummVars[i] = 0;
// Empty inventory
for (i=0;i<_numGlobalObjects;i++)
clearOwnerOf(i);
// Reinit things
allocateArrays(); // Reallocate arrays
readIndexFile(); // Reread index (reset objectstate etc)
createResource(rtTemp, 6, 500); // Create temp buffer
initScummVars(); // Reinit scumm variables
_sound->setupSound(); // Reinit sound engine
// Re-run bootscript
runScript(1, 0, 0, &_bootParam);
}
void ScummEngine::startManiac() {
warning("stub startManiac()");
}
#pragma mark -
#pragma mark --- GUI ---
#pragma mark -
int ScummEngine::runDialog(Dialog &dialog) {
// Pause sound put
bool old_soundsPaused = _sound->_soundsPaused;
_sound->pauseSounds(true);
// Pause playing smush movie
bool oldSmushPlay = _smushPlay;
_smushPlay = false;
// Open & run the dialog
2003-09-11 10:32:15 +00:00
int result = dialog.runModal();
// Restore old cursor
updateCursor();
// Resume playing smush movie, if any
_smushPlay = oldSmushPlay;
// Resume sound output
_sound->pauseSounds(old_soundsPaused);
// Return the result
return result;
}
void ScummEngine::pauseDialog() {
if (!_pauseDialog)
_pauseDialog = new PauseDialog(_newgui, this);
2003-09-11 10:32:15 +00:00
runDialog(*_pauseDialog);
}
2003-06-08 12:11:14 +00:00
void ScummEngine::saveloadDialog() {
if (!_saveLoadDialog)
_saveLoadDialog = new SaveLoadDialog(_newgui, this);
2003-09-11 10:32:15 +00:00
runDialog(*_saveLoadDialog);
}
void ScummEngine::optionsDialog() {
if (!_optionsDialog)
_optionsDialog = new OptionsDialog(_newgui, this);
2003-09-11 10:32:15 +00:00
runDialog(*_optionsDialog);
}
void ScummEngine::confirmexitDialog() {
if (!_confirmExitDialog)
_confirmExitDialog = new ConfirmExitDialog(_newgui, this);
2003-09-11 10:32:15 +00:00
if (runDialog(*_confirmExitDialog)) {
_quit = true;
}
}
char ScummEngine::displayError(bool showCancel, const char *message, ...) {
2003-04-30 13:23:31 +00:00
#ifdef __PALM_OS__
2003-09-11 10:32:15 +00:00
char buf[256]; // 1024 is too big overflow the stack
2003-04-30 13:23:31 +00:00
#else
2003-09-11 10:32:15 +00:00
char buf[1024];
2003-04-30 13:23:31 +00:00
#endif
va_list va;
2003-03-06 17:58:13 +00:00
va_start(va, message);
vsprintf(buf, message, va);
va_end(va);
2003-09-11 10:32:15 +00:00
MessageDialog dialog(_newgui, buf, 0, true, showCancel);
return runDialog(dialog);
}
2002-12-29 19:54:11 +00:00
#pragma mark -
#pragma mark --- Miscellaneous ---
#pragma mark -
byte *ScummEngine::get2byteCharPtr(int idx) {
/*
switch(language)
case korean:
return ( (idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1;
case japanese:
...
case taiwan:
...
*/
idx = ( (idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1; // only for korean
return _2byteFontPtr + 2 * _2byteHeight * idx;
}
const char *ScummEngine::getGameDataPath() const {
#ifdef MACOSX
if (_version == 8 && !memcmp(_gameDataPath, "/Volumes/MONKEY3_", 17)) {
// Special case for COMI on Mac OS X. The mount points on OS X depend
// on the volume name. Hence if playing from CD, we'd get a problem.
// So if loading of a resource file fails, we fall back to the (fixed)
// CD mount points (/Volumes/MONKEY3_1 and /Volumes/MONKEY3_2).
//
// The check for whether we play from CD or not is very hackish, though.
static char buf[256];
struct stat st;
int disk = (_scummVars && _scummVars[VAR_CURRENTDISK] == 2) ? 2 : 1;
sprintf(buf, "/Volumes/MONKEY3_%d", disk);
if (!stat(buf, &st)) {
return buf;
}
// Apparently that disk is not inserted. However since many data files
// (fonts, comi.la0) are on both disks, we also try the other CD.
disk = (disk == 1) ? 2 : 1;
sprintf(buf, "/Volumes/MONKEY3_%d", disk);
return buf;
}
#endif
return _gameDataPath;
}
void ScummEngine::errorString(const char *buf1, char *buf2) {
if (_currentScript != 0xFF) {
ScriptSlot *ss = &vm.slot[_currentScript];
sprintf(buf2, "(%d:%d:0x%X): %s", _roomResource,
ss->number, _scriptPointer - _scriptOrgPointer, buf1);
} else {
strcpy(buf2, buf1);
}
#ifdef _WIN32_WCE
if (isSmartphone())
return;
#endif
// Unless an error -originated- within the debugger, spawn the debugger. Otherwise
// exit out normally.
if (!_debugger) {
printf("%s", buf2); // (Print it again in-case debugger segfaults)
g_debugger->attach(this, buf2);
g_debugger->on_frame();
}
}
#pragma mark -
#pragma mark --- Utilities ---
#pragma mark -
void checkRange(int max, int min, int no, const char *str) {
if (no < min || no > max) {
#ifdef __PALM_OS__
char buf[256]; // 1024 is too big overflow the stack
#else
char buf[1024];
#endif
sprintf(buf, str, no);
error("Value %d is out of bounds (%d,%d) (%s)", no, min, max, buf);
2003-05-08 21:02:46 +00:00
}
}
/**
* 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) {
assert(0 <= dir && dir <= 3);
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) {
if (dirType) {
const int16 directions[] = { 22, 72, 107, 157, 202, 252, 287, 337 };
for (int i = 0; i < 7; i++)
if (dir >= directions[i] && dir <= directions[i+1])
return i+1;
} else {
const int16 directions[] = { 71, 109, 251, 289 };
for (int i = 0; i < 3; i++)
if (dir >= directions[i] && dir <= directions[i+1])
return i+1;
}
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;
}
} // End of namespace Scumm
using namespace Scumm;
const TargetSettings *Engine_SCUMM_targetList() {
return scumm_settings;
}
Engine *Engine_SCUMM_create(GameDetector *detector, OSystem *syst) {
Engine *engine;
if (detector->_amiga)
detector->_game.features |= GF_AMIGA;
switch (detector->_platform) {
case 1:
if (!(detector->_game.features & GF_AMIGA))
detector->_game.features |= GF_AMIGA;
break;
case 2:
detector->_game.features |= GF_ATARI_ST;
break;
case 3:
detector->_game.features |= GF_MACINTOSH;
break;
}
switch (detector->_game.version) {
case 1:
case 2:
engine = new ScummEngine_v2(detector, syst);
break;
case 3:
engine = new ScummEngine_v3(detector, syst);
break;
case 4:
engine = new ScummEngine_v4(detector, syst);
break;
case 5:
engine = new ScummEngine_v5(detector, syst);
break;
case 6:
engine = new ScummEngine_v6(detector, syst);
break;
case 7:
engine = new ScummEngine_v7(detector, syst);
break;
case 8:
engine = new ScummEngine_v8(detector, syst);
break;
default:
error("Engine_SCUMM_create(): Unknown version of game engine");
}
return engine;
}
REGISTER_PLUGIN("Scumm Engine", Engine_SCUMM_targetList, Engine_SCUMM_create);