mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 09:49:11 +00:00
dd3683f7e7
Added some HE GIDs svn-id: r13126
2985 lines
86 KiB
C++
2985 lines
86 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001 Ludvig Strigeus
|
|
* Copyright (C) 2001-2004 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 "backends/fs/fs.h"
|
|
|
|
#include "base/gameDetector.h"
|
|
#include "base/plugins.h"
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/md5.h"
|
|
|
|
#include "gui/message.h"
|
|
#include "gui/newgui.h"
|
|
|
|
#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/dimuse.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"
|
|
#include "scumm/player_v3a.h"
|
|
#include "scumm/resource.h"
|
|
#include "scumm/scumm.h"
|
|
#include "scumm/scumm-md5.h"
|
|
#include "scumm/sound.h"
|
|
#include "scumm/verbs.h"
|
|
|
|
#include "scumm/insane/insane.h"
|
|
|
|
#include "sound/mididrv.h"
|
|
#include "sound/mixer.h"
|
|
|
|
#ifdef MACOSX
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32_WCE
|
|
extern bool isSmartphone(void);
|
|
#endif
|
|
|
|
namespace Scumm {
|
|
|
|
enum MouseButtonStatus {
|
|
msDown = 1,
|
|
msClicked = 2
|
|
};
|
|
|
|
// Use g_scumm from error() ONLY
|
|
ScummEngine *g_scumm = 0;
|
|
|
|
struct ScummGameSettings {
|
|
const char *name;
|
|
const char *description;
|
|
byte id, version;
|
|
int midi; // MidiDriverType values
|
|
uint32 features;
|
|
const char *baseFilename;
|
|
|
|
GameSettings toGameSettings() const {
|
|
GameSettings dummy = { name, description, features };
|
|
return dummy;
|
|
}
|
|
};
|
|
|
|
|
|
static const ScummGameSettings 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, 0},
|
|
//{"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},
|
|
{"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, 0},
|
|
|
|
/* 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, 0},
|
|
{"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, 0},
|
|
{"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, 0},
|
|
{"zak256", "Zak McKracken 256 (deprecated)", GID_ZAK256, 3, MDT_TOWNS,
|
|
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, 0},
|
|
{"zakTowns", "Zak McKracken and the Alien Mindbenders (FM Towns)", GID_ZAK256, 3, MDT_TOWNS,
|
|
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, 0},
|
|
{"loom", "Loom", GID_LOOM, 3, MDT_PCSPK | MDT_ADLIB | MDT_NATIVE,
|
|
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, 0},
|
|
{"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_TOWNS,
|
|
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, 0},
|
|
|
|
/* 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, 0},
|
|
{"pass", "Passport to Adventure", GID_PASS, 4, MDT_PCSPK | MDT_ADLIB,
|
|
GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, 0},
|
|
|
|
/* 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, 0},
|
|
{"loomcd", "Loom (256 color CD version)", GID_LOOM256, 4, MDT_NONE,
|
|
GF_SMALL_HEADER | GF_USE_KEY | GF_AUDIOTRACKS, 0},
|
|
{"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},
|
|
|
|
{"atlantis", "Indiana Jones and the Fate of Atlantis", GID_INDY4, 5, MDT_ADLIB | MDT_NATIVE,
|
|
GF_USE_KEY, 0},
|
|
{"playfate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, MDT_ADLIB | MDT_NATIVE,
|
|
GF_USE_KEY, 0},
|
|
{"fate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, MDT_ADLIB | MDT_NATIVE,
|
|
GF_USE_KEY, 0},
|
|
{"indy4", "Indiana Jones and the Fate of Atlantis (FM Towns)", GID_INDY4, 5, 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},
|
|
|
|
/* 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_PUTTMOON, 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_PUTTMOON, 6, MDT_ADLIB | MDT_NATIVE,
|
|
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"funpack", "Putt-Putt's Fun Pack", GID_PUTTMOON, 6, MDT_ADLIB | MDT_NATIVE,
|
|
GF_NEW_OPCODES | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"fbpack", "Fatty Bear's Fun Pack", GID_FBPACK, 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_FBEAR, 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_FBEAR, 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 | GF_DEMO, 0},
|
|
{"ftpcdemo", "Full Throttle (PC Demo)", GID_FT, 7, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEMO, "ft"},
|
|
|
|
|
|
{"dig", "The Dig", GID_DIG, 7, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, 0},
|
|
{"digdemo", "The Dig (Demo)", GID_DIG, 7, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEMO, "dig"},
|
|
|
|
/* 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},
|
|
{"comidemo", "The Curse of Monkey Island (Demo)", GID_CMI, 8, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEFAULT_TO_1X_SCALER | GF_DEMO, "comi"},
|
|
|
|
/* Note that both full versions of Humongous games and demos were often released for
|
|
* several interpreter versions... */
|
|
{"zoodemo", "Putt-Putt Saves the Zoo (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES | GF_DEFAULT_TO_1X_SCALER, 0},
|
|
{"freddi", "Freddi Fish 1: The Case of the Missing Kelp Seeds", GID_FREDDI, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES | GF_DEFAULT_TO_1X_SCALER, 0},
|
|
{"freddemo", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Demo)", GID_FREDDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES | GF_DEFAULT_TO_1X_SCALER, 0},
|
|
{"pjs-demo", "Pajama Sam 1: No Need to Hide When It's Dark Outside (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES | GF_DEFAULT_TO_1X_SCALER, 0},
|
|
{"ff2-demo", "Freddi Fish 2: The Case of the Haunted Schoolhouse (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES | GF_DEFAULT_TO_1X_SCALER, 0},
|
|
|
|
#ifdef HEGAMES
|
|
{"puttwin", "Putt-Putt Joins The Parade (Windows)", GID_PUTTPUTT, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, "puttputt"},
|
|
|
|
// Humongous Entertainment Scumm Version 7
|
|
{"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},
|
|
{"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},
|
|
{"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},
|
|
|
|
// Humongous Entertainment Scumm Version 8.0 ? Scummsrc.80
|
|
{"pajama", "Pajama Sam 1: No Need to Hide When It's Dark Outside", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"ffhsdemo", "Freddi Fish 2: The Case of the Haunted Schoolhouse (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"freddi2", "Freddi Fish 2: The Case of the Haunted Schoolhouse", 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: The Case of the 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 Aren't so Frightening (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"pajama2", "Pajama Sam 2: Thunder and Lightning Aren't so Frightening", 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.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},
|
|
{"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},
|
|
{"freddi4", "Freddi Fish 4: The Case of the 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: The Case of the 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},
|
|
|
|
// Humongous Entertainment Scumm Version ? engine moved to c++
|
|
{"pj3-demo", "Pajama Sam 3: You Are What You Eat From Your Head to Your Feet (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"pajama3", "Pajama Sam 3: You Are What You Eat From Your Head to Your Feet", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"ff5demo", "Freddi Fish 5: The Case of the Creature of Coral Cave (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"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},
|
|
{"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},
|
|
{"sf2-demo", "Spyfox 2: Some Assembly Required (Demo)", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"spyfox2", "Spyfox 2: Some Assembly Required", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
{"spyozon", "Spyfox 3: Operation Ozone", GID_PJSDEMO, 6, MDT_NONE,
|
|
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
|
|
#endif
|
|
{NULL, NULL, 0, 0, MDT_NONE, 0, 0}
|
|
};
|
|
|
|
static int compareMD5Table(const void *a, const void *b) {
|
|
const char *key = (const char *)a;
|
|
const MD5Table *elem = (const MD5Table *)b;
|
|
return strcmp(key, elem->md5);
|
|
}
|
|
|
|
ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs)
|
|
: Engine(syst),
|
|
_gameId(gs.id),
|
|
_version(gs.version),
|
|
_features(gs.features),
|
|
gdi(this), _pauseDialog(0), _optionsDialog(0), _mainMenuDialog(0),
|
|
_targetName(detector->_targetName) {
|
|
|
|
// Init all vars - maybe now we can get rid of our custom new/delete operators?
|
|
_imuse = NULL;
|
|
_imuseDigital = NULL;
|
|
_musicEngine = NULL;
|
|
_verbs = NULL;
|
|
_objs = NULL;
|
|
_debugger = NULL;
|
|
_debugFlags = 0;
|
|
_sound = NULL;
|
|
memset(&res, 0, sizeof(res));
|
|
memset(&vm, 0, sizeof(vm));
|
|
_smushFrameRate = 0;
|
|
_videoFinished = false;
|
|
_smushPaused = false;
|
|
_quit = false;
|
|
_pauseDialog = NULL;
|
|
_optionsDialog = NULL;
|
|
_mainMenuDialog = NULL;
|
|
_fastMode = 0;
|
|
_actors = NULL;
|
|
_inventory = NULL;
|
|
_newNames = NULL;
|
|
_scummVars = NULL;
|
|
_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;
|
|
_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;
|
|
_objectOwnerTable = NULL;
|
|
_objectRoomTable = NULL;
|
|
_objectStateTable = NULL;
|
|
_numObjectsInRoom = 0;
|
|
_userPut = 0;
|
|
_userState = 0;
|
|
_resourceHeaderSize = 0;
|
|
_saveLoadFlag = 0;
|
|
_saveLoadSlot = 0;
|
|
_lastSaveTime = 0;
|
|
_saveTemporaryState = false;
|
|
memset(_saveLoadName, 0, sizeof(_saveLoadName));
|
|
_maxHeapThreshold = 0;
|
|
_minHeapThreshold = 0;
|
|
memset(_localScriptList, 0, sizeof(_localScriptList));
|
|
_scriptPointer = NULL;
|
|
_scriptOrgPointer = NULL;
|
|
_opcode = 0;
|
|
vm.numNestedScripts = 0;
|
|
_currentScript = 0;
|
|
_curExecScript = 0;
|
|
_lastCodePtr = NULL;
|
|
_resultVarNumber = 0;
|
|
_scummStackPos = 0;
|
|
memset(_vmStack, 0, sizeof(_vmStack));
|
|
_keyScriptKey = 0;
|
|
_keyScriptNo = 0;
|
|
_fileOffset = 0;
|
|
_dynamicRoomOffsets = false;
|
|
memset(_resourceMapper, 0, sizeof(_resourceMapper));
|
|
_allocatedSize = 0;
|
|
_expire_counter = 0;
|
|
_lastLoadedRoom = 0;
|
|
_roomResource = 0;
|
|
OF_OWNER_ROOM = 0;
|
|
_verbMouseOver = 0;
|
|
_inventoryOffset = 0;
|
|
_classData = NULL;
|
|
_actorToPrintStrFor = 0;
|
|
_sentenceNum = 0;
|
|
memset(_sentence, 0, sizeof(_sentence));
|
|
memset(_string, 0, sizeof(_string));
|
|
_screenB = 0;
|
|
_screenH = 0;
|
|
_roomHeight = 0;
|
|
_roomWidth = 0;
|
|
_screenHeight = 0;
|
|
_screenWidth = 0;
|
|
memset(virtscr, 0, sizeof(virtscr));
|
|
memset(&camera, 0, sizeof(CameraData));
|
|
memset(_colorCycle, 0, sizeof(_colorCycle));
|
|
_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;
|
|
memset(&_cursor, 0, sizeof(_cursor));
|
|
memset(_grabbedCursor, 0, sizeof(_grabbedCursor));
|
|
_currentCursor = 0;
|
|
_newEffect = 0;
|
|
_switchRoomEffect2 = 0;
|
|
_switchRoomEffect = 0;
|
|
_doEffect = false;
|
|
memset(&_flashlight, 0, sizeof(_flashlight));
|
|
_roomStrips = 0;
|
|
_bompActorPalettePtr = NULL;
|
|
_shakeEnabled= false;
|
|
_shakeFrame = 0;
|
|
_screenStartStrip = 0;
|
|
_screenEndStrip = 0;
|
|
_screenLeft = 0;
|
|
_screenTop = 0;
|
|
_blastObjectQueuePos = 0;
|
|
memset(_blastObjectQueue, 0, sizeof(_blastObjectQueue));
|
|
_blastTextQueuePos = 0;
|
|
memset(_blastTextQueue, 0, sizeof(_blastTextQueue));
|
|
_drawObjectQueNr = 0;
|
|
memset(_drawObjectQue, 0, sizeof(_drawObjectQue));
|
|
_palManipStart = 0;
|
|
_palManipEnd = 0;
|
|
_palManipCounter = 0;
|
|
_palManipPalette = NULL;
|
|
_palManipIntermediatePal = NULL;
|
|
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
|
|
_shadowPalette = NULL;
|
|
_shadowPaletteSize = 0;
|
|
memset(_currentPalette, 0, sizeof(_currentPalette));
|
|
memset(_proc_special_palette, 0, sizeof(_proc_special_palette));
|
|
_palDirtyMin = 0;
|
|
_palDirtyMax = 0;
|
|
_haveMsg = 0;
|
|
_useTalkAnims = false;
|
|
_defaultTalkDelay = 0;
|
|
_midiDriver = MD_NULL;
|
|
tempMusic = 0;
|
|
_saveSound = 0;
|
|
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
|
|
memset(_scaleSlots, 0, sizeof(_scaleSlots));
|
|
_charset = NULL;
|
|
_charsetColor = 0;
|
|
memset(_charsetColorMap, 0, sizeof(_charsetColorMap));
|
|
memset(_charsetData, 0, sizeof(_charsetData));
|
|
_charsetBufPos = 0;
|
|
memset(_charsetBuffer, 0, sizeof(_charsetBuffer));
|
|
_copyProtection = false;
|
|
_demoMode = false;
|
|
_confirmExit = false;
|
|
_msgPtrToAdd = NULL;
|
|
_messagePtr = NULL;
|
|
_talkDelay = 0;
|
|
_keepText = false;
|
|
_existLanguageFile = false;
|
|
_languageBuffer = NULL;
|
|
_languageIndex = NULL;
|
|
memset(_transText, 0, sizeof(_transText));
|
|
_costumeRenderer = NULL;
|
|
_2byteFontPtr = 0;
|
|
_V1_talkingActor = 0;
|
|
|
|
//
|
|
// Init all VARS to 0xFF
|
|
//
|
|
VAR_LANGUAGE = 0xFF;
|
|
VAR_KEYPRESS = 0xFF;
|
|
VAR_SYNC = 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;
|
|
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_MAINMENU_KEY = 0xFF;
|
|
VAR_FIXEDDISK = 0xFF;
|
|
VAR_CURSORSTATE = 0xFF;
|
|
VAR_USERPUT = 0xFF;
|
|
VAR_SOUNDRESULT = 0xFF;
|
|
VAR_TALKSTOP_KEY = 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_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;
|
|
|
|
VAR_BLAST_ABOVE_TEXT = 0xFF;
|
|
VAR_VOICE_MODE = 0xFF;
|
|
|
|
// Use g_scumm from error() ONLY
|
|
g_scumm = this;
|
|
|
|
// Read settings from the detector & config manager
|
|
_debugMode = ConfMan.hasKey("debuglevel");
|
|
_dumpScripts = detector->_dumpScripts;
|
|
_bootParam = ConfMan.getInt("boot_param");
|
|
|
|
// Allow the user to override the game name with a custom string.
|
|
// This allows some game versions to work which use filenames
|
|
// differing from the regular version(s) of that game.
|
|
_gameName = ConfMan.hasKey("basename") ? ConfMan.get("basename") : gs.baseFilename ? gs.baseFilename : gs.name;
|
|
|
|
_midiDriver = GameDetector::detectMusicDriver(gs.midi);
|
|
|
|
_copyProtection = ConfMan.getBool("copy_protection");
|
|
_demoMode = ConfMan.getBool("demo_mode");
|
|
if (ConfMan.hasKey("nosubtitles")) {
|
|
warning("Configuration key 'nosubtitles' is deprecated. Use 'subtitles' instead");
|
|
if (!ConfMan.hasKey("subtitles"))
|
|
ConfMan.set("subtitles", !ConfMan.getBool("nosubtitles"));
|
|
}
|
|
_confirmExit = ConfMan.getBool("confirm_exit");
|
|
_native_mt32 = ConfMan.getBool("native_mt32");
|
|
// TODO: We shouldn't rely on the global Language values matching those COMI etc. expect.
|
|
// Rather we should explicitly translate them.
|
|
_language = Common::parseLanguage(ConfMan.get("language"));
|
|
memset(&res, 0, sizeof(res));
|
|
_hexdumpScripts = false;
|
|
_showStack = false;
|
|
|
|
if (_features & GF_FMTOWNS) { // FMTowns V3 games use 320x240
|
|
_screenWidth = 320;
|
|
_screenHeight = 240;
|
|
} else if ((_gameId == GID_CMI) || (_features & GF_AFTER_HEV7)) {
|
|
_screenWidth = 640;
|
|
_screenHeight = 480;
|
|
} else if (_features & GF_NES) {
|
|
_screenWidth = 256;
|
|
_screenHeight = 240;
|
|
} else {
|
|
_screenWidth = 320;
|
|
_screenHeight = 200;
|
|
}
|
|
|
|
// Initialize backend
|
|
syst->initSize(_screenWidth, _screenHeight);
|
|
int cd_num = ConfMan.getInt("cdrom");
|
|
if (cd_num >= 0 && (_features & GF_AUDIOTRACKS))
|
|
syst->openCD(cd_num);
|
|
|
|
// Setup GDI object
|
|
gdi._numStrips = _screenWidth / 8;
|
|
|
|
_sound = new Sound(this);
|
|
|
|
#ifndef __GP32__ //ph0x FIXME, "quick dirty hack"
|
|
/* Bind the mixer to the system => mixer will be invoked
|
|
* automatically when samples need to be generated */
|
|
if (!_mixer->isReady()) {
|
|
warning("Sound mixer initialization failed");
|
|
if (_midiDriver == MD_ADLIB ||
|
|
_midiDriver == MD_PCSPK ||
|
|
_midiDriver == MD_PCJR) {
|
|
_midiDriver = MD_NULL;
|
|
warning("MIDI driver depends on sound mixer, switching to null MIDI driver");
|
|
}
|
|
}
|
|
_mixer->setVolume(ConfMan.getInt("sfx_volume") * ConfMan.getInt("master_volume") / 255);
|
|
_mixer->setMusicVolume(ConfMan.getInt("music_volume"));
|
|
|
|
// Init iMuse
|
|
if (_features & GF_DIGI_IMUSE) {
|
|
_musicEngine = _imuseDigital = new IMuseDigital(this);
|
|
} else if ((_features & GF_AMIGA) && (_version == 2)) {
|
|
_musicEngine = new Player_V2A(this);
|
|
} 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, _midiDriver != MD_PCSPK);
|
|
} else if (_version > 2) {
|
|
MidiDriver *driver = GameDetector::createMidi(_midiDriver);
|
|
if (driver && _native_mt32)
|
|
driver->property (MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
|
_musicEngine = _imuse = IMuse::create(syst, _mixer, driver);
|
|
if (_imuse) {
|
|
if (ConfMan.hasKey("tempo"))
|
|
_imuse->property(IMuse::PROP_TEMPO_BASE, ConfMan.getInt("tempo"));
|
|
_imuse->property(IMuse::PROP_OLD_ADLIB_INSTRUMENTS, (_features & GF_SMALL_HEADER) ? 1 : 0);
|
|
_imuse->property(IMuse::PROP_MULTI_MIDI, ConfMan.getBool("multi_midi") &&
|
|
_midiDriver != MD_NULL && (gs.midi & MDT_ADLIB));
|
|
_imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32);
|
|
if (_features & GF_HUMONGOUS || gs.midi == MDT_TOWNS) {
|
|
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
|
|
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
|
|
}
|
|
if (gs.midi == MDT_TOWNS)
|
|
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
|
|
_imuse->set_music_volume(ConfMan.getInt("music_volume"));
|
|
}
|
|
}
|
|
#endif // ph0x-hack
|
|
|
|
// Load game from specified slot, if any
|
|
if (ConfMan.hasKey("save_slot")) {
|
|
requestLoad(ConfMan.getInt("save_slot"));
|
|
}
|
|
loadLanguageBundle();
|
|
|
|
// Load CJK font
|
|
_CJKMode = false;
|
|
if ((_gameId == GID_DIG || _gameId == GID_CMI) && (_language == Common::KO_KOR || _language == Common::JA_JPN || _language == Common::ZH_TWN)) {
|
|
File fp;
|
|
const char *fontFile = NULL;
|
|
switch(_language) {
|
|
case Common::KO_KOR:
|
|
fontFile = "korean.fnt";
|
|
break;
|
|
case Common::JA_JPN:
|
|
fontFile = (_gameId == GID_DIG) ? "kanji16.fnt" : "japanese.fnt";
|
|
break;
|
|
case Common::ZH_TWN:
|
|
if (_gameId == GID_CMI) {
|
|
fontFile = "chinese.fnt";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (fontFile && fp.open(fontFile, getGameDataPath())) {
|
|
debug(2, "Loading CJK Font");
|
|
_CJKMode = true;
|
|
fp.seek(2, SEEK_CUR);
|
|
_2byteWidth = fp.readByte();
|
|
_2byteHeight = fp.readByte();
|
|
|
|
int numChar = 0;
|
|
switch(_language) {
|
|
case Common::KO_KOR:
|
|
numChar = 2350;
|
|
break;
|
|
case Common::JA_JPN:
|
|
numChar = (_gameId == GID_DIG) ? 1024 : 2048; //FIXME
|
|
break;
|
|
case Common::ZH_TWN:
|
|
numChar = 1; //FIXME
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
_2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar];
|
|
fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar);
|
|
fp.close();
|
|
}
|
|
} else if (_language == Common::JA_JPN && _version == 5) { //FM Towns Kanji
|
|
File fp;
|
|
int numChar = 256 * 32;
|
|
_2byteWidth = 16;
|
|
_2byteHeight = 16;
|
|
//use FM Towns font rom, since game files don't have kanji font resources
|
|
if (fp.open("fmt_fnt.rom", getGameDataPath()) || fp.open("fmt_fnt.rom", "./")) {
|
|
_CJKMode = true;
|
|
debug(2, "Loading FM Towns Kanji rom");
|
|
_2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar];
|
|
fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar);
|
|
fp.close();
|
|
}
|
|
}
|
|
|
|
// Create the charset renderer
|
|
if (_version <= 2)
|
|
_charset = new CharsetRendererV2(this, _language);
|
|
else if (_version == 3)
|
|
_charset = new CharsetRendererV3(this);
|
|
else if (_version == 8)
|
|
_charset = new CharsetRendererNut(this);
|
|
else
|
|
_charset = new CharsetRendererClassic(this);
|
|
|
|
// Create the costume renderer
|
|
if (_features & GF_NEW_COSTUMES)
|
|
_costumeRenderer = new AkosRenderer(this);
|
|
else
|
|
_costumeRenderer = new CostumeRenderer(this);
|
|
|
|
// Create FT INSANE object
|
|
if (_gameId == GID_FT)
|
|
_insane = new Insane((ScummEngine_v6 *)this);
|
|
else
|
|
_insane = 0;
|
|
}
|
|
|
|
ScummEngine::~ScummEngine() {
|
|
if (_musicEngine) {
|
|
_musicEngine->terminate();
|
|
delete _musicEngine;
|
|
}
|
|
|
|
_mixer->stopAll();
|
|
|
|
delete [] _actors;
|
|
|
|
delete _2byteFontPtr;
|
|
delete _charset;
|
|
delete _pauseDialog;
|
|
delete _optionsDialog;
|
|
delete _mainMenuDialog;
|
|
|
|
delete _sound;
|
|
free(_languageBuffer);
|
|
free(_audioNames);
|
|
|
|
delete _costumeRenderer;
|
|
|
|
free(_shadowPalette);
|
|
|
|
freeResources();
|
|
|
|
free(_objectStateTable);
|
|
free(_objectRoomTable);
|
|
free(_objectOwnerTable);
|
|
free(_inventory);
|
|
free(_verbs);
|
|
free(_objs);
|
|
free(_scummVars);
|
|
free(_bitVars);
|
|
free(_newNames);
|
|
free(_classData);
|
|
|
|
free(_roomStrips);
|
|
free(_languageIndex);
|
|
|
|
delete _debugger;
|
|
}
|
|
|
|
void ScummEngine::go() {
|
|
launch();
|
|
mainRun();
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark --- Initialization ---
|
|
#pragma mark -
|
|
|
|
void ScummEngine::launch() {
|
|
|
|
#ifdef __PALM_OS__
|
|
if (_features & GF_NEW_COSTUMES)
|
|
_maxHeapThreshold = gVars->memory[kMemScummNewCostGames];
|
|
else
|
|
_maxHeapThreshold = gVars->memory[kMemScummOldCostGames];
|
|
#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);
|
|
|
|
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;
|
|
|
|
if (!((_features & GF_MACINTOSH) && (_version == 3))) {
|
|
// This is NOT for the Mac version of Indy3/Loom
|
|
VAR(VAR_DEBUGMODE) = _debugMode;
|
|
}
|
|
}
|
|
|
|
if (_gameId == GID_MONKEY || _gameId == GID_MONKEY_SEGA)
|
|
_scummVars[74] = 1225;
|
|
|
|
if (_imuse) {
|
|
_imuse->setBase(res.address[rtSound]);
|
|
|
|
_imuse->setMasterVolume(ConfMan.getInt("master_volume"));
|
|
_imuse->set_music_volume(ConfMan.getInt("music_volume"));
|
|
}
|
|
_sound->setupSound();
|
|
|
|
// Create debugger
|
|
if (!_debugger)
|
|
_debugger = new ScummDebugger(this);
|
|
|
|
|
|
// If requested, load a save game instead of running the boot script
|
|
if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveTemporaryState)) {
|
|
int args[16];
|
|
memset(args, 0, sizeof(args));
|
|
args[0] = _bootParam;
|
|
|
|
_saveLoadFlag = 0;
|
|
if (_gameId == GID_MANIAC && _version == 1 && _demoMode)
|
|
runScript(9, 0, 0, args);
|
|
else
|
|
runScript(1, 0, 0, args);
|
|
} else {
|
|
_saveLoadFlag = 0;
|
|
}
|
|
}
|
|
|
|
void ScummEngine::scummInit() {
|
|
int i;
|
|
|
|
tempMusic = 0;
|
|
debug(9, "scummInit");
|
|
|
|
if ((_gameId == GID_MANIAC) && (_version == 1)) {
|
|
initScreens(16, 152);
|
|
} else if (_version >= 7) {
|
|
initScreens(0, _screenHeight);
|
|
} else {
|
|
initScreens(16, 144);
|
|
}
|
|
|
|
for (i = 0; i < 256; i++)
|
|
_roomPalette[i] = i;
|
|
if (_version == 1) {
|
|
// Use 17 color table for v1 games to allow
|
|
// correct color for inventory and sentence
|
|
// line
|
|
// Original games used some kind of dynamic
|
|
// color table remapping between rooms
|
|
if (_gameId == GID_MANIAC)
|
|
setupV1ManiacPalette();
|
|
else
|
|
setupV1ZakPalette();
|
|
} else if (_features & GF_16COLOR) {
|
|
for (i = 0; i < 16; 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 > 3 && _version < 8)
|
|
loadCharset(1);
|
|
|
|
if (_features & GF_OLD_BUNDLE)
|
|
loadCharset(0); // FIXME - HACK ?
|
|
|
|
setShake(0);
|
|
setupCursor();
|
|
|
|
// 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);
|
|
|
|
// this is from IDB
|
|
if (_version == 1)
|
|
_actors[i].setActorCostume(i);
|
|
}
|
|
|
|
vm.numNestedScripts = 0;
|
|
vm.cutSceneStackPointer = 0;
|
|
|
|
memset(vm.cutScenePtr, 0, sizeof(vm.cutScenePtr));
|
|
memset(vm.cutSceneData, 0, sizeof(vm.cutSceneData));
|
|
|
|
for (i = 0; i < _numVerbs; i++) {
|
|
_verbs[i].verbid = 0;
|
|
_verbs[i].curRect.right = _screenWidth - 1;
|
|
_verbs[i].oldRect.left = -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_NEW_CAMERA)) {
|
|
camera._leftTrigger = 10;
|
|
camera._rightTrigger = 30;
|
|
camera._mode = 0;
|
|
}
|
|
camera._follows = 0;
|
|
|
|
virtscr[0].xstart = 0;
|
|
|
|
if (VAR_CURRENT_LIGHTS != 0xFF) {
|
|
// Setup light
|
|
_flashlight.xStrips = 7;
|
|
_flashlight.yStrips = 7;
|
|
_flashlight.buffer = NULL;
|
|
}
|
|
|
|
_mouse.x = 104;
|
|
_mouse.y = 56;
|
|
|
|
_ENCD_offs = 0;
|
|
_EXCD_offs = 0;
|
|
|
|
_currentScript = 0xFF;
|
|
_sentenceNum = 0;
|
|
|
|
_currentRoom = 0;
|
|
_numObjectsInRoom = 0;
|
|
_actorToPrintStrFor = 0;
|
|
|
|
_charsetBufPos = 0;
|
|
_haveMsg = 0;
|
|
|
|
_varwatch = -1;
|
|
_screenStartStrip = 0;
|
|
|
|
_defaultTalkDelay = 3;
|
|
_talkDelay = 0;
|
|
_keepText = false;
|
|
|
|
_currentCursor = 0;
|
|
_cursor.state = 0;
|
|
_userPut = 0;
|
|
|
|
_newEffect = 129;
|
|
_fullRedraw = true;
|
|
|
|
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;
|
|
}
|
|
|
|
// all keys are released
|
|
for (i = 0; i < 512; i++)
|
|
_keyDownMap[i] = false;
|
|
|
|
initScummVars();
|
|
|
|
_lastSaveTime = _system->get_msecs();
|
|
}
|
|
|
|
|
|
void ScummEngine::initScummVars() {
|
|
|
|
// 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;
|
|
return;
|
|
}
|
|
|
|
if (_version < 6)
|
|
VAR(VAR_V5_TALK_STRING_Y) = -0x50;
|
|
|
|
if (_version == 8) { // Fixme: How do we deal with non-cd installs?
|
|
VAR(VAR_CURRENTDISK) = 1;
|
|
VAR(VAR_LANGUAGE) = _language;
|
|
} else if (_version >= 7) {
|
|
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_PC)) {
|
|
if (_gameId == GID_LOOM) {
|
|
char buf[50];
|
|
uint i = 82;
|
|
File f;
|
|
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");
|
|
}
|
|
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
|
|
VAR(VAR_SOUNDPARAM2) = 1; // soundblaster for sfx
|
|
} 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;
|
|
|
|
// Sets fade delay
|
|
// byte VAR_FADE_DELAY = (_version == 7) ? 117 : 59;
|
|
// VAR(VAR_FADE_DELAY) = 3;
|
|
}
|
|
|
|
if ((_features & GF_MACINTOSH) && (_version == 3)) {
|
|
// This is the for the Mac version of Indy3/Loom
|
|
VAR(39) = 320;
|
|
}
|
|
|
|
if (VAR_CURRENT_LIGHTS != 0xFF) {
|
|
// Setup light
|
|
VAR(VAR_CURRENT_LIGHTS) = LIGHTMODE_actor_base | LIGHTMODE_actor_color | LIGHTMODE_screen;
|
|
}
|
|
|
|
if (_gameId == GID_MONKEY || _gameId == GID_MONKEY_SEGA)
|
|
_scummVars[74] = 1225;
|
|
|
|
if (_version == 7)
|
|
VAR(VAR_VOICE_MODE) = ConfMan.getBool("subtitles");
|
|
|
|
VAR(VAR_CHARINC) = 4;
|
|
talkingActor(0);
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark --- Main loop ---
|
|
#pragma mark -
|
|
|
|
void ScummEngine::mainRun() {
|
|
int delta = 0;
|
|
int diff = _system->get_msecs();
|
|
|
|
while (!_quit) {
|
|
|
|
updatePalette();
|
|
_system->updateScreen();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
int ScummEngine::scummLoop(int delta) {
|
|
if (_debugger->isAttached())
|
|
_debugger->onFrame();
|
|
|
|
// 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;
|
|
|
|
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(false);
|
|
|
|
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;
|
|
if ((_features & GF_MACINTOSH) && (_version == 3)) {
|
|
// This is for the Mac version of Indy3/Loom
|
|
VAR(VAR_DEBUGMODE) = _debugMode;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Trigger autosave all 5 minutes.
|
|
if (!_saveLoadFlag && _system->get_msecs() > _lastSaveTime + 5 * 60 * 1000) {
|
|
_saveLoadSlot = 0;
|
|
sprintf(_saveLoadName, "Autosave %d", _saveLoadSlot);
|
|
_saveLoadFlag = 1;
|
|
_saveTemporaryState = false;
|
|
}
|
|
|
|
if (VAR_GAME_LOADED != 0xFF)
|
|
VAR(VAR_GAME_LOADED) = 0;
|
|
if (_saveLoadFlag) {
|
|
load_game:
|
|
bool success;
|
|
const char *errMsg = 0;
|
|
char filename[256];
|
|
|
|
if (_saveLoadFlag == 1) {
|
|
success = saveState(_saveLoadSlot, _saveTemporaryState);
|
|
if (!success)
|
|
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 && _saveTemporaryState && !(_features & GF_SMALL_HEADER) && _version < 8)
|
|
VAR(VAR_GAME_LOADED) = 201;
|
|
} else {
|
|
success = loadState(_saveLoadSlot, _saveTemporaryState);
|
|
if (!success)
|
|
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).
|
|
if (success && _saveTemporaryState && !(_features & GF_SMALL_HEADER))
|
|
VAR(VAR_GAME_LOADED) = 203;
|
|
}
|
|
|
|
makeSavegameName(filename, _saveLoadSlot, _saveTemporaryState);
|
|
if (!success) {
|
|
displayError(0, errMsg, filename);
|
|
} else if (_saveLoadFlag == 1 && _saveLoadSlot != 0 && !_saveTemporaryState) {
|
|
// Display "Save successful" message, except for auto saves
|
|
#ifdef __PALM_OS__
|
|
char buf[256]; // 1024 is too big overflow the stack
|
|
#else
|
|
char buf[1024];
|
|
#endif
|
|
sprintf(buf, "Successfully saved game state in file:\n\n%s", filename);
|
|
|
|
GUI::TimedMessageDialog dialog(buf, 1500);
|
|
runDialog(dialog);
|
|
}
|
|
if (success && _saveLoadFlag != 1)
|
|
clearClickedStatus();
|
|
|
|
_saveLoadFlag = 0;
|
|
_lastSaveTime = _system->get_msecs();
|
|
}
|
|
|
|
if (_completeScreenRedraw) {
|
|
_completeScreenRedraw = false;
|
|
_charset->clearCharsetMask();
|
|
_charset->_hasMask = false;
|
|
|
|
// HACK as in game save stuff isn't supported currently
|
|
if (_gameId == GID_LOOM || _gameId == GID_LOOM256) {
|
|
int args[16];
|
|
uint value;
|
|
memset(args, 0, sizeof(args));
|
|
args[0] = 2;
|
|
|
|
if (_features & GF_MACINTOSH)
|
|
value = 105;
|
|
else
|
|
value = (_gameId == GID_LOOM256) ? 150 : 100;
|
|
byte restoreScript = (_features & GF_FMTOWNS) ? 17 : 18;
|
|
// 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 < _numVerbs; i++)
|
|
drawVerb(i, 0);
|
|
} else {
|
|
redrawVerbs();
|
|
}
|
|
|
|
verbMouseOver(0);
|
|
|
|
if (_version <= 2) {
|
|
redrawV2Inventory();
|
|
checkV2MouseOver(_mouse);
|
|
}
|
|
|
|
_verbRedraw = false;
|
|
_fullRedraw = true;
|
|
}
|
|
|
|
runAllScripts();
|
|
checkExecVerbs();
|
|
checkAndRunSentenceScript();
|
|
|
|
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
|
|
// 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 {
|
|
walkActors();
|
|
moveCamera();
|
|
fixObjectFlags();
|
|
if (_version > 3)
|
|
CHARSET_1();
|
|
|
|
if (camera._cur.x != camera._last.x || _BgNeedsRedraw || _fullRedraw
|
|
|| ((_features & GF_NEW_CAMERA) && camera._cur.y != camera._last.y)) {
|
|
redrawBGAreas();
|
|
}
|
|
|
|
processDrawQue();
|
|
|
|
if (_verbRedraw) {
|
|
redrawVerbs();
|
|
}
|
|
|
|
setActorRedrawFlags();
|
|
resetActorBgs();
|
|
|
|
if (VAR_CURRENT_LIGHTS != 0xFF &&
|
|
!(VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_screen) &&
|
|
VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_flashlight) {
|
|
drawFlashlight();
|
|
setActorRedrawFlags();
|
|
}
|
|
|
|
processActors();
|
|
_fullRedraw = false;
|
|
cyclePalette();
|
|
palManipulate();
|
|
|
|
if (_doEffect) {
|
|
_doEffect = false;
|
|
fadeIn(_newEffect);
|
|
clearClickedStatus();
|
|
}
|
|
|
|
|
|
if (!_verbRedraw && _cursor.state > 0) {
|
|
verbMouseOver(checkMouseOver(_mouse.x, _mouse.y));
|
|
}
|
|
_verbRedraw = false;
|
|
|
|
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 (_version >= 7 && VAR(VAR_BLAST_ABOVE_TEXT) == 1) {
|
|
drawBlastTexts();
|
|
drawBlastObjects();
|
|
} else {
|
|
drawBlastObjects();
|
|
drawBlastTexts();
|
|
}
|
|
|
|
if (_version == 8)
|
|
processUpperActors();
|
|
drawDirtyScreenParts();
|
|
removeBlastTexts();
|
|
removeBlastObjects();
|
|
|
|
if (_version <= 5)
|
|
playActorSounds();
|
|
}
|
|
|
|
_sound->processSoundQues();
|
|
camera._last = camera._cur;
|
|
|
|
if (!(++_expire_counter)) {
|
|
increaseResourceCounter();
|
|
}
|
|
|
|
animateCursor();
|
|
|
|
/* show or hide mouse */
|
|
_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;
|
|
|
|
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_ALT ||
|
|
event.kbd.flags == OSystem::KBD_CTRL)) {
|
|
_saveLoadSlot = event.kbd.keycode - '0';
|
|
|
|
// don't overwrite autosave (slot 0)
|
|
if (_saveLoadSlot == 0)
|
|
_saveLoadSlot = 10;
|
|
|
|
sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot);
|
|
_saveLoadFlag = (event.kbd.flags == OSystem::KBD_ALT) ? 1 : 2;
|
|
_saveTemporaryState = 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')
|
|
_debugger->attach();
|
|
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 && !(_features & GF_DEMO))) {
|
|
// 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 || _gameId == GID_FT) {
|
|
// 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
|
|
//
|
|
// This is not applicable to Full Throttle as it processes keyboard
|
|
// cursor control by itself. Also it fixes derby scene
|
|
_keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
|
|
}
|
|
|
|
if (_keyPressed >= 512)
|
|
warning("_keyPressed > 512 (%d)", _keyPressed);
|
|
else
|
|
_keyDownMap[_keyPressed] = true;
|
|
break;
|
|
|
|
case OSystem::EVENT_KEYUP:
|
|
// FIXME: for some reason OSystem::KBD_ALT is set sometimes
|
|
// possible to a bug in sdl-common.cpp
|
|
if (event.kbd.ascii >= 512)
|
|
warning("keyPressed > 512 (%d)", event.kbd.ascii);
|
|
else
|
|
_keyDownMap[event.kbd.ascii] = false;
|
|
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;
|
|
|
|
case OSystem::EVENT_LBUTTONUP:
|
|
_leftBtnPressed &= ~msDown;
|
|
break;
|
|
|
|
case OSystem::EVENT_RBUTTONUP:
|
|
_rightBtnPressed &= ~msDown;
|
|
break;
|
|
|
|
case OSystem::EVENT_QUIT:
|
|
if (_confirmExit)
|
|
confirmexitDialog();
|
|
else
|
|
_quit = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScummEngine::clearClickedStatus() {
|
|
_keyPressed = 0;
|
|
_mouseButStat = 0;
|
|
_leftBtnPressed &= ~msClicked;
|
|
_rightBtnPressed &= ~msClicked;
|
|
}
|
|
|
|
void ScummEngine::processKbd(bool smushMode) {
|
|
int saveloadkey;
|
|
|
|
_lastKeyHit = _keyPressed;
|
|
_keyPressed = 0;
|
|
if (((_version <= 2) || (_features & GF_FMTOWNS)) && 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;
|
|
|
|
_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;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
#ifdef _WIN32_WCE
|
|
if (_lastKeyHit == KEY_SET_OPTIONS) {
|
|
//_newgui->optionsDialog();
|
|
return;
|
|
}
|
|
|
|
if (_lastKeyHit == KEY_ALL_SKIP) {
|
|
// Skip cutscene
|
|
if (smushMode) {
|
|
// Eek this is literally shouting for trouble...
|
|
// Probably should set _lastKey to VAR_CUTSCENEEXIT_KEY instead!
|
|
_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
|
|
|
|
if (VAR_RESTART_KEY != 0xFF && _lastKeyHit == VAR(VAR_RESTART_KEY) ||
|
|
(((_version <= 2) || (_features & GF_FMTOWNS)) && _lastKeyHit == 8)) {
|
|
confirmrestartDialog();
|
|
return;
|
|
}
|
|
|
|
if ((VAR_PAUSE_KEY != 0xFF && _lastKeyHit == VAR(VAR_PAUSE_KEY)) ||
|
|
(VAR_PAUSE_KEY == 0xFF && _lastKeyHit == ' ')) {
|
|
pauseGame();
|
|
return;
|
|
}
|
|
|
|
if ((_version <= 2) || (_features & GF_FMTOWNS))
|
|
saveloadkey = 5; // F5
|
|
else if ((_version <= 3) || (_gameId == GID_SAMNMAX) || (_gameId == GID_CMI))
|
|
saveloadkey = 319; // F5
|
|
else
|
|
saveloadkey = VAR(VAR_MAINMENU_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 (smushMode) {
|
|
if (_gameId == GID_FT)
|
|
_insane->escapeKeyHandler();
|
|
else
|
|
_videoFinished = true;
|
|
}
|
|
if (!smushMode || _videoFinished)
|
|
abortCutscene();
|
|
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);
|
|
}
|
|
} else if (_lastKeyHit == saveloadkey) {
|
|
if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
|
|
runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0);
|
|
|
|
mainMenuDialog(); // Display NewGui
|
|
|
|
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)) {
|
|
// Some text never times out, and should never be skipped. The
|
|
// Full Throttle conversation menus is the main - perhaps the
|
|
// only - example of this.
|
|
|
|
if (_talkDelay != -1) {
|
|
_talkDelay = 0;
|
|
if (_sound->_sfxMode & 2)
|
|
stopTalk();
|
|
}
|
|
return;
|
|
} else if (_lastKeyHit == '[') { // [ Music volume down
|
|
int vol = ConfMan.getInt("music_volume");
|
|
if (!(vol & 0xF) && vol)
|
|
vol -= 16;
|
|
vol = vol & 0xF0;
|
|
ConfMan.set("music_volume", vol);
|
|
if (_imuse)
|
|
_imuse->set_music_volume (vol);
|
|
} else if (_lastKeyHit == ']') { // ] Music volume up
|
|
int vol = ConfMan.getInt("music_volume");
|
|
vol = (vol + 16) & 0xFF0;
|
|
if (vol > 255) vol = 255;
|
|
ConfMan.set("music_volume", vol);
|
|
if (_imuse)
|
|
_imuse->set_music_volume (vol);
|
|
} else if (_lastKeyHit == '-') { // - text speed down
|
|
if (_defaultTalkDelay < 9)
|
|
_defaultTalkDelay++;
|
|
if (VAR_CHARINC != 0xFF)
|
|
VAR(VAR_CHARINC) = _defaultTalkDelay;
|
|
} else if (_lastKeyHit == '+') { // + text speed up
|
|
if (_defaultTalkDelay > 0)
|
|
_defaultTalkDelay--;
|
|
if (VAR_CHARINC != 0xFF)
|
|
VAR(VAR_CHARINC) = _defaultTalkDelay;
|
|
} else if (_lastKeyHit == '~' || _lastKeyHit == '#') { // Debug console
|
|
_debugger->attach();
|
|
} 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;
|
|
}
|
|
}
|
|
|
|
_mouseButStat = _lastKeyHit;
|
|
}
|
|
|
|
#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;
|
|
debugC(DEBUG_GENERAL, "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) {
|
|
// Earlier games only checked global scripts at this point
|
|
if (_version >= 5)
|
|
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++) {
|
|
_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();
|
|
|
|
|
|
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 = kNormalCameraMode;
|
|
if (_version > 2)
|
|
camera._cur.x = camera._dest.x = _screenWidth / 2;
|
|
camera._cur.y = camera._dest.y = _screenHeight / 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 (_version <= 2)
|
|
runScript(5, 0, 0, 0);
|
|
else if (_version >= 5 && _version <= 6) {
|
|
if (a && !_egoPositioned) {
|
|
int x, y;
|
|
getObjectXYPos(objectNr, x, y);
|
|
a->putActor(x, y, _currentRoom);
|
|
a->moving = 0;
|
|
}
|
|
} else if (_version >= 7) {
|
|
if ((_gameId == GID_DIG) && 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 < res.num[rtScaleTable]; 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;
|
|
|
|
//
|
|
// Determine the room dimensions (width/height)
|
|
//
|
|
if (_features & GF_OLD_BUNDLE)
|
|
rmhd = (const RoomHeader *)(roomptr + 4);
|
|
else
|
|
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));
|
|
}
|
|
|
|
//
|
|
// Find the room image data
|
|
//
|
|
if (_version == 1) {
|
|
_IM00_offs = 0;
|
|
for (i = 0; i < 4; i++){
|
|
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);
|
|
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) {
|
|
_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;
|
|
}
|
|
if (_dumpScripts && _EXCD_offs)
|
|
dumpResource("exit-", _roomResource, roomResPtr + _EXCD_offs - _resourceHeaderSize, EXCD_len);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
if (_dumpScripts && _ENCD_offs)
|
|
dumpResource("entry-", _roomResource, roomResPtr + _ENCD_offs - _resourceHeaderSize, ENCD_len);
|
|
|
|
//
|
|
// Load box data
|
|
//
|
|
if (_features & GF_SMALL_HEADER) {
|
|
if (_version <= 2)
|
|
ptr = roomptr + *(roomptr + 0x15);
|
|
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) {
|
|
size = numOfBoxes * (numOfBoxes + 1);
|
|
} else if (_features & GF_OLD_BUNDLE)
|
|
// FIXME. This is an evil HACK!!!
|
|
size = (READ_LE_UINT16(roomptr + 0x0A) - READ_LE_UINT16(roomptr + 0x15)) - size;
|
|
else
|
|
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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load scale data
|
|
//
|
|
if (_features & GF_OLD_BUNDLE)
|
|
ptr = 0;
|
|
else
|
|
ptr = findResourceData(MKID('SCAL'), roomptr);
|
|
if (ptr) {
|
|
int s1, s2, y1, y2;
|
|
if (_version == 8) {
|
|
for (i = 1; i < res.num[rtScaleTable]; 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 < res.num[rtScaleTable]; 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
int num_sounds;
|
|
int num_scripts;
|
|
|
|
if (_version <= 2) {
|
|
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) {
|
|
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;
|
|
|
|
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);
|
|
|
|
if (ptr) {
|
|
_CLUT_offs = ptr - roomptr;
|
|
setPaletteFromRes();
|
|
}
|
|
|
|
if (_version >= 6) {
|
|
ptr = findResource(MKID('PALS'), roomptr);
|
|
if (ptr) {
|
|
_PALS_offs = ptr - roomptr;
|
|
setPalette(0);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
initBGBuffers(_roomHeight);
|
|
}
|
|
|
|
void ScummEngine::pauseGame() {
|
|
pauseDialog();
|
|
}
|
|
|
|
void ScummEngine::shutDown() {
|
|
_quit = true;
|
|
}
|
|
|
|
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();
|
|
|
|
// 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)
|
|
scummInit(); // Reinit scumm variables
|
|
if (_imuse) {
|
|
_imuse->setBase(res.address[rtSound]);
|
|
}
|
|
_sound->setupSound(); // Reinit sound engine
|
|
|
|
// Re-run bootscript
|
|
int args[16];
|
|
memset(args, 0, sizeof(args));
|
|
args[0] = _bootParam;
|
|
if (_gameId == GID_MANIAC && _version == 1 && _demoMode)
|
|
runScript(9, 0, 0, args);
|
|
else
|
|
runScript(1, 0, 0, args);
|
|
}
|
|
|
|
void ScummEngine::startManiac() {
|
|
warning("stub startManiac()");
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark --- GUI ---
|
|
#pragma mark -
|
|
|
|
int ScummEngine::runDialog(Dialog &dialog) {
|
|
// Pause sound & video
|
|
bool old_soundsPaused = _sound->_soundsPaused;
|
|
_sound->pauseSounds(true);
|
|
bool oldSmushPaused = _smushPaused;
|
|
_smushPaused = true;
|
|
|
|
// Open & run the dialog
|
|
int result = dialog.runModal();
|
|
|
|
// Restore old cursor
|
|
updateCursor();
|
|
|
|
// Resume sound & video
|
|
_sound->pauseSounds(old_soundsPaused);
|
|
_smushPaused = oldSmushPaused;
|
|
|
|
// Return the result
|
|
return result;
|
|
}
|
|
|
|
void ScummEngine::pauseDialog() {
|
|
if (!_pauseDialog)
|
|
_pauseDialog = new PauseDialog(this);
|
|
runDialog(*_pauseDialog);
|
|
}
|
|
|
|
void ScummEngine::mainMenuDialog() {
|
|
if (!_mainMenuDialog)
|
|
_mainMenuDialog = new MainMenuDialog(this);
|
|
runDialog(*_mainMenuDialog);
|
|
}
|
|
|
|
void ScummEngine::optionsDialog() {
|
|
if (!_optionsDialog)
|
|
_optionsDialog = new ConfigDialog(this);
|
|
runDialog(*_optionsDialog);
|
|
}
|
|
|
|
void ScummEngine::confirmexitDialog() {
|
|
ConfirmDialog confirmExitDialog(this, "Do you really want to quit (y/n)?");
|
|
|
|
if (runDialog(confirmExitDialog)) {
|
|
_quit = true;
|
|
}
|
|
}
|
|
|
|
void ScummEngine::confirmrestartDialog() {
|
|
ConfirmDialog confirmRestartDialog(this, "Do you really want to restart (y/n)?");
|
|
|
|
if (runDialog(confirmRestartDialog)) {
|
|
restart();
|
|
}
|
|
}
|
|
|
|
char ScummEngine::displayError(const char *altButton, const char *message, ...) {
|
|
#ifdef __PALM_OS__
|
|
char buf[256]; // 1024 is too big overflow the stack
|
|
#else
|
|
char buf[1024];
|
|
#endif
|
|
va_list va;
|
|
|
|
va_start(va, message);
|
|
vsprintf(buf, message, va);
|
|
va_end(va);
|
|
|
|
GUI::MessageDialog dialog(buf, "OK", altButton);
|
|
return runDialog(dialog);
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark --- Miscellaneous ---
|
|
#pragma mark -
|
|
|
|
int SJIStoFMTChunk(int f, int s) //convert sjis code to fmt font offset
|
|
{
|
|
enum {
|
|
KANA = 0,
|
|
KANJI = 1,
|
|
EKANJI = 2
|
|
};
|
|
int base = s - (s % 32) - 1;
|
|
int c = 0, p = 0, chunk_f = 0, chunk = 0, cr, kanjiType = KANA;
|
|
|
|
if (f >= 0x81 && f <= 0x84) kanjiType = KANA;
|
|
if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI;
|
|
if (f >= 0xe0 && f <= 0xea) kanjiType = EKANJI;
|
|
|
|
if ((f > 0xe8 || (f == 0xe8 && base >= 0x9f)) || (f > 0x90 || (f == 0x90 && base >= 0x9f))) {
|
|
c = 48; //correction
|
|
p = -8; //correction
|
|
}
|
|
|
|
if (kanjiType == KANA) {//Kana
|
|
chunk_f = (f - 0x81) * 2;
|
|
} else if (kanjiType == KANJI) {//Standard Kanji
|
|
p += f - 0x88;
|
|
chunk_f = c + 2 * p;
|
|
} else if (kanjiType == EKANJI) {//Enhanced Kanji
|
|
p += f - 0xe0;
|
|
chunk_f = c + 2 * p;
|
|
}
|
|
|
|
if (base == 0x7f && s == 0x7f)
|
|
base -= 0x20; //correction
|
|
if ((base == 0x7f && s == 0x9e) || (base == 0x9f && s == 0xbe) || (base == 0xbf && s == 0xde))
|
|
base += 0x20; //correction
|
|
|
|
switch(base) {
|
|
case 0x3f:
|
|
cr = 0; //3f
|
|
if (kanjiType == KANA) chunk = 1;
|
|
else if (kanjiType == KANJI) chunk = 31;
|
|
else if (kanjiType == EKANJI) chunk = 111;
|
|
break;
|
|
case 0x5f:
|
|
cr = 0; //5f
|
|
if (kanjiType == KANA) chunk = 17;
|
|
else if (kanjiType == KANJI) chunk = 47;
|
|
else if (kanjiType == EKANJI) chunk = 127;
|
|
break;
|
|
case 0x7f:
|
|
cr = -1; //80
|
|
if (kanjiType == KANA) chunk = 9;
|
|
else if (kanjiType == KANJI) chunk = 63;
|
|
else if (kanjiType == EKANJI) chunk = 143;
|
|
break;
|
|
case 0x9f:
|
|
cr = 1; //9e
|
|
if (kanjiType == KANA) chunk = 2;
|
|
else if (kanjiType == KANJI) chunk = 32;
|
|
else if (kanjiType == EKANJI) chunk = 112;
|
|
break;
|
|
case 0xbf:
|
|
cr = 1; //be
|
|
if (kanjiType == KANA) chunk = 18;
|
|
else if (kanjiType == KANJI) chunk = 48;
|
|
else if (kanjiType == EKANJI) chunk = 128;
|
|
break;
|
|
case 0xdf:
|
|
cr = 1; //de
|
|
if (kanjiType == KANA) chunk = 10;
|
|
else if (kanjiType == KANJI) chunk = 64;
|
|
else if (kanjiType == EKANJI) chunk = 144;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return ((chunk_f + chunk) * 32 + (s - base)) + cr;
|
|
}
|
|
|
|
byte *ScummEngine::get2byteCharPtr(int idx) {
|
|
switch(_language) {
|
|
case Common::KO_KOR:
|
|
idx = ((idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1;
|
|
break;
|
|
case Common::JA_JPN:
|
|
idx = SJIStoFMTChunk((idx % 256), (idx / 256));
|
|
break;
|
|
case Common::ZH_TWN:
|
|
default:
|
|
idx = 0;
|
|
}
|
|
return _2byteFontPtr + ((_2byteWidth + 7) / 8) * _2byteHeight * idx;
|
|
}
|
|
|
|
|
|
const char *ScummEngine::getGameDataPath() const {
|
|
#ifdef MACOSX
|
|
if (_version == 8 && !memcmp(_gameDataPath.c_str(), "/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.c_str();
|
|
}
|
|
|
|
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 && !_debugger->isAttached()) {
|
|
printf("%s\n", buf2); // (Print it again in case debugger segfaults)
|
|
_debugger->attach(buf2);
|
|
_debugger->onFrame();
|
|
}
|
|
}
|
|
|
|
#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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
const char *tag2str(uint32 tag) {
|
|
static char str[5];
|
|
str[0] = (char)(tag >> 24);
|
|
str[1] = (char)(tag >> 16);
|
|
str[2] = (char)(tag >> 8);
|
|
str[3] = (char)tag;
|
|
str[4] = '\0';
|
|
return str;
|
|
}
|
|
|
|
} // End of namespace Scumm
|
|
|
|
using namespace Scumm;
|
|
|
|
GameList Engine_SCUMM_gameList() {
|
|
const ScummGameSettings *g = scumm_settings;
|
|
GameList games;
|
|
while (g->name) {
|
|
games.push_back(g->toGameSettings());
|
|
g++;
|
|
}
|
|
return games;
|
|
}
|
|
|
|
DetectedGameList Engine_SCUMM_detectGames(const FSList &fslist) {
|
|
DetectedGameList detectedGames;
|
|
const ScummGameSettings *g;
|
|
char detectName[128];
|
|
char detectName2[128];
|
|
|
|
typedef Common::Map<Common::String, bool> StringSet;
|
|
StringSet fileSet;
|
|
|
|
for (g = scumm_settings; g->name; ++g) {
|
|
// Determine the 'detectname' for this game, that is, the name of a
|
|
// file that *must* be presented if the directory contains the data
|
|
// for this game. For example, FOA requires atlantis.000
|
|
if (g->version <= 3) {
|
|
strcpy(detectName, "00.LFL");
|
|
detectName2[0] = '\0';
|
|
} else if (g->version == 4) {
|
|
strcpy(detectName, "000.LFL");
|
|
detectName2[0] = '\0';
|
|
} else {
|
|
const char *base = g->baseFilename ? g->baseFilename : g->name;
|
|
strcpy(detectName, base);
|
|
strcat(detectName, ".000");
|
|
strcpy(detectName2, base);
|
|
if (g->features & GF_HUMONGOUS) {
|
|
strcat(detectName2, ".he0");
|
|
} else if (g->version >= 7) {
|
|
strcat(detectName2, ".la0");
|
|
} else
|
|
strcat(detectName2, ".sm0");
|
|
}
|
|
|
|
// Iterate over all files in the given directory
|
|
for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
|
const char *name = file->displayName().c_str();
|
|
|
|
if ((0 == scumm_stricmp(detectName, name)) ||
|
|
(0 == scumm_stricmp(detectName2, name))) {
|
|
// Match found, add to list of candidates, then abort inner loop.
|
|
detectedGames.push_back(g->toGameSettings());
|
|
fileSet.addKey(file->path());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now, we check the MD5 sums of the 'candidate' files. If we have an exact match,
|
|
// only return that.
|
|
bool exactMatch = false;
|
|
for (StringSet::const_iterator iter = fileSet.begin(); iter != fileSet.end(); ++iter) {
|
|
uint8 md5sum[16];
|
|
const char *name = iter->_key.c_str();
|
|
if (md5_file(name, md5sum)) {
|
|
char md5str[32+1];
|
|
for (int j = 0; j < 16; j++) {
|
|
sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
|
|
}
|
|
|
|
const MD5Table *elem;
|
|
elem = (const MD5Table *)bsearch(md5str, md5table, ARRAYSIZE(md5table)-1, sizeof(MD5Table), compareMD5Table);
|
|
if (elem) {
|
|
if (!exactMatch)
|
|
detectedGames.clear(); // Clear all the non-exact candidates
|
|
// Find the GameSettings for that target
|
|
for (g = scumm_settings; g->name; ++g) {
|
|
if (0 == scumm_stricmp(g->name, elem->target))
|
|
break;
|
|
}
|
|
assert(g->name);
|
|
// Insert the 'enhanced' game data into the candidate list
|
|
detectedGames.push_back(DetectedGame(g->toGameSettings(), elem->language, elem->platform));
|
|
exactMatch = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return detectedGames;
|
|
}
|
|
|
|
Engine *Engine_SCUMM_create(GameDetector *detector, OSystem *syst) {
|
|
Engine *engine;
|
|
|
|
|
|
const ScummGameSettings *g = scumm_settings;
|
|
while (g->name) {
|
|
if (!scumm_stricmp(detector->_game.name, g->name))
|
|
break;
|
|
g++;
|
|
}
|
|
if (!g->name)
|
|
error("Invalid game '%s'\n", detector->_game.name);
|
|
|
|
ScummGameSettings game = *g;
|
|
|
|
if (ConfMan.hasKey("amiga")) {
|
|
warning("Configuration key 'amiga' is deprecated. Use 'platform=amiga' instead");
|
|
if (ConfMan.getBool("amiga"))
|
|
game.features |= GF_AMIGA;
|
|
}
|
|
|
|
switch (Common::parsePlatform(ConfMan.get("platform"))) {
|
|
case Common::kPlatformAmiga:
|
|
game.features |= GF_AMIGA;
|
|
break;
|
|
case Common::kPlatformAtariST:
|
|
game.features |= GF_ATARI_ST;
|
|
break;
|
|
case Common::kPlatformMacintosh:
|
|
game.features |= GF_MACINTOSH;
|
|
break;
|
|
case Common::kPlatformFMTowns:
|
|
if (game.version == 3) {
|
|
// The V5 FM-TOWNS games are mostly identical to the PC versions, it seems?
|
|
game.features |= GF_FMTOWNS;
|
|
game.midi = MDT_TOWNS;
|
|
}
|
|
break;
|
|
default:
|
|
if (!(game.features & GF_FMTOWNS))
|
|
game.features |= GF_PC;
|
|
break;
|
|
}
|
|
|
|
switch (game.version) {
|
|
case 1:
|
|
case 2:
|
|
engine = new ScummEngine_v2(detector, syst, game);
|
|
break;
|
|
case 3:
|
|
engine = new ScummEngine_v3(detector, syst, game);
|
|
break;
|
|
case 4:
|
|
engine = new ScummEngine_v4(detector, syst, game);
|
|
break;
|
|
case 5:
|
|
engine = new ScummEngine_v5(detector, syst, game);
|
|
break;
|
|
case 6:
|
|
if (game.features & GF_HUMONGOUS) {
|
|
// TODO: probably use another variable with version number
|
|
if (game.features & GF_AFTER_HEV7)
|
|
engine = new ScummEngine_v7he(detector, syst, game);
|
|
else
|
|
engine = new ScummEngine_v6he(detector, syst, game);
|
|
} else {
|
|
engine = new ScummEngine_v6(detector, syst, game);
|
|
}
|
|
break;
|
|
case 7:
|
|
engine = new ScummEngine_v7(detector, syst, game);
|
|
break;
|
|
case 8:
|
|
engine = new ScummEngine_v8(detector, syst, game);
|
|
break;
|
|
default:
|
|
error("Engine_SCUMM_create(): Unknown version of game engine");
|
|
}
|
|
|
|
return engine;
|
|
}
|
|
|
|
REGISTER_PLUGIN("Scumm Engine", Engine_SCUMM_gameList, Engine_SCUMM_create, Engine_SCUMM_detectGames)
|