1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef ADL_ADL_H
24 #define ADL_ADL_H
25 
26 #include "common/debug-channels.h"
27 #include "common/array.h"
28 #include "common/rect.h"
29 #include "common/str.h"
30 #include "common/hashmap.h"
31 #include "common/hash-str.h"
32 #include "common/func.h"
33 #include "common/ptr.h"
34 #include "common/scummsys.h"
35 
36 #include "engines/engine.h"
37 
38 #include "audio/mixer.h"
39 #include "audio/softsynth/pcspk.h"
40 
41 #include "adl/console.h"
42 #include "adl/disk.h"
43 #include "adl/sound.h"
44 #include "adl/detection.h"
45 
46 namespace Common {
47 class ReadStream;
48 class WriteStream;
49 class SeekableReadStream;
50 class File;
51 struct Event;
52 class RandomSource;
53 }
54 
55 namespace Adl {
56 
57 Common::String getDiskImageName(const AdlGameDescription &adlDesc, byte volume);
58 GameType getGameType(const AdlGameDescription &desc);
59 GameVersion getGameVersion(const AdlGameDescription &desc);
60 Common::Language getLanguage(const AdlGameDescription &desc);
61 Common::Platform getPlatform(const AdlGameDescription &desc);
62 
63 class Console;
64 class Display;
65 class GraphicsMan;
66 class ScriptEnv;
67 
68 enum kDebugChannels {
69 	kDebugChannelScript = 1 << 0
70 };
71 
72 enum ADLAction {
73 	kADLActionNone,
74 	kADLActionQuit,
75 
76 	kADLActionCount
77 };
78 
79 // Save and restore opcodes
80 #define IDO_ACT_SAVE           0x0f
81 #define IDO_ACT_LOAD           0x10
82 
83 #define IDI_CUR_ROOM 0xfc
84 #define IDI_VOID_ROOM 0xfd
85 #define IDI_ANY 0xfe
86 
87 #define IDI_WORD_SIZE 8
88 
89 enum Direction {
90 	IDI_DIR_NORTH,
91 	IDI_DIR_SOUTH,
92 	IDI_DIR_EAST,
93 	IDI_DIR_WEST,
94 	IDI_DIR_UP,
95 	IDI_DIR_DOWN,
96 	IDI_DIR_TOTAL
97 };
98 
99 struct Room {
RoomRoom100 	Room() :
101 			description(0),
102 			picture(0),
103 			curPicture(0),
104 			isFirstTime(true) {
105 		memset(connections, 0, sizeof(connections));
106 	}
107 
108 	byte description;
109 	byte connections[IDI_DIR_TOTAL];
110 	DataBlockPtr data;
111 	byte picture;
112 	byte curPicture;
113 	bool isFirstTime;
114 };
115 
116 typedef Common::HashMap<byte, DataBlockPtr> PictureMap;
117 
118 typedef Common::Array<byte> Script;
119 
120 struct Command {
121 	byte room;
122 	byte verb, noun;
123 	byte numCond, numAct;
124 	Script script;
125 };
126 
127 class ScriptEnv {
128 public:
ScriptEnv(const Command & cmd,byte room,byte verb,byte noun)129 	ScriptEnv(const Command &cmd, byte room, byte verb, byte noun) :
130 			_cmd(cmd), _room(room), _verb(verb), _noun(noun), _ip(0) { }
131 
~ScriptEnv()132 	virtual ~ScriptEnv() { }
133 
134 	enum kOpType {
135 		kOpTypeDone,
136 		kOpTypeCond,
137 		kOpTypeAct
138 	};
139 
op()140 	byte op() const { return _cmd.script[_ip]; }
141 	virtual kOpType getOpType() const = 0;
142 	// We keep this 1-based for easier comparison with the original engine
arg(uint i)143 	byte arg(uint i) const { return _cmd.script[_ip + i]; }
144 	virtual void next(uint numArgs) = 0;
145 
isMatch()146 	bool isMatch() const {
147 		return (_cmd.room == IDI_ANY || _cmd.room == _room) &&
148 		       (_cmd.verb == IDI_ANY || _cmd.verb == _verb) &&
149 		       (_cmd.noun == IDI_ANY || _cmd.noun == _noun);
150 	}
151 
getNoun()152 	byte getNoun() const { return _noun; }
getCommand()153 	const Command &getCommand() const { return _cmd; }
154 
155 protected:
156 	byte _ip;
157 
158 private:
159 	const Command &_cmd;
160 	const byte _room, _verb, _noun;
161 };
162 
163 enum {
164 	IDI_ITEM_NOT_MOVED,
165 	IDI_ITEM_DROPPED,
166 	IDI_ITEM_DOESNT_MOVE
167 };
168 
169 struct Item {
170 	byte id;
171 	byte noun;
172 	byte region;
173 	byte room;
174 	byte picture;
175 	bool isShape;
176 	Common::Point position;
177 	int state;
178 	byte description;
179 	Common::Array<byte> roomPictures;
180 	bool isOnScreen;
181 
ItemItem182 	Item() : id(0), noun(0), region(0), room(0), picture(0), isShape(false), state(0), description(0), isOnScreen(false) { }
183 };
184 
185 struct Time {
186 	byte hours, minutes;
187 
TimeTime188 	Time() : hours(12), minutes(0) { }
189 };
190 
191 struct RoomState {
192 	byte picture;
193 	byte isFirstTime;
194 };
195 
196 struct Region {
197 	Common::Array<byte> vars;
198 	Common::Array<RoomState> rooms;
199 };
200 
201 struct State {
202 	Common::Array<Region> regions;
203 	Common::Array<Room> rooms;
204 	Common::List<Item> items;
205 	Common::Array<byte> vars;
206 
207 	byte region, prevRegion;
208 	byte room;
209 	byte curPicture;
210 	uint16 moves;
211 	bool isDark;
212 	Time time;
213 
StateState214 	State() : region(0), prevRegion(0), room(1), curPicture(0), moves(1), isDark(false) { }
215 };
216 
217 typedef Common::List<Command> Commands;
218 typedef Common::HashMap<Common::String, uint> WordMap;
219 
220 struct RoomData {
221 	Common::String description;
222 	PictureMap pictures;
223 	Commands commands;
224 };
225 
226 // Opcode debugging macros
227 #define OP_DEBUG_0(F) do { \
228 	if (DebugMan.isDebugChannelEnabled(kDebugChannelScript) && op_debug(F)) \
229 		return 0; \
230 } while (0)
231 
232 #define OP_DEBUG_1(F, P1) do { \
233 	if (DebugMan.isDebugChannelEnabled(kDebugChannelScript) && op_debug(F, P1)) \
234 		return 1; \
235 } while (0)
236 
237 #define OP_DEBUG_2(F, P1, P2) do { \
238 	if (DebugMan.isDebugChannelEnabled(kDebugChannelScript) && op_debug(F, P1, P2)) \
239 		return 2; \
240 } while (0)
241 
242 #define OP_DEBUG_3(F, P1, P2, P3) do { \
243 	if (DebugMan.isDebugChannelEnabled(kDebugChannelScript) && op_debug(F, P1, P2, P3)) \
244 		return 3; \
245 } while (0)
246 
247 #define OP_DEBUG_4(F, P1, P2, P3, P4) do { \
248 	if (DebugMan.isDebugChannelEnabled(kDebugChannelScript) && op_debug(F, P1, P2, P3, P4)) \
249 		return 4; \
250 } while (0)
251 
252 class AdlEngine : public Engine {
253 friend class Console;
254 public:
255 	~AdlEngine() override;
256 
257 	bool pollEvent(Common::Event &event) const;
258 	void bell(uint count = 1) const;
259 
260 protected:
261 	AdlEngine(OSystem *syst, const AdlGameDescription *gd);
262 
263 	// Engine
264 	Common::Error loadGameState(int slot) override;
265 	Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
266 	bool canSaveGameStateCurrently() override;
267 	virtual Common::String getSaveStateName(int slot) const override;
getAutosaveSlot()268 	int getAutosaveSlot() const override { return 15; }
269 
getDiskImageName(byte volume)270 	Common::String getDiskImageName(byte volume) const { return Adl::getDiskImageName(*_gameDescription, volume); }
getGameType()271 	GameType getGameType() const { return Adl::getGameType(*_gameDescription); }
getGameVersion()272 	GameVersion getGameVersion() const { return Adl::getGameVersion(*_gameDescription); }
getLanguage()273 	Common::Language getLanguage() const { return Adl::getLanguage(*_gameDescription); }
274 	virtual void gameLoop();
275 	virtual void loadState(Common::ReadStream &stream);
276 	virtual void saveState(Common::WriteStream &stream);
277 	Common::String readString(Common::ReadStream &stream, byte until = 0) const;
278 	Common::String readStringAt(Common::SeekableReadStream &stream, uint offset, byte until = 0) const;
279 	void extractExeStrings(Common::ReadStream &stream, uint16 printAddr, Common::StringArray &strings) const;
280 
281 	virtual void printString(const Common::String &str) = 0;
282 	virtual Common::String loadMessage(uint idx) const = 0;
283 	virtual void printMessage(uint idx);
284 	virtual Common::String getItemDescription(const Item &item) const;
285 	void delay(uint32 ms) const;
286 
287 	virtual Common::String getLine();
288 	Common::String inputString(byte prompt = 0) const;
289 	byte inputKey(bool showCursor = true) const;
290 	virtual void getInput(uint &verb, uint &noun);
291 	Common::String getWord(const Common::String &line, uint &index) const;
292 
293 	virtual Common::String formatVerbError(const Common::String &verb) const;
294 	virtual Common::String formatNounError(const Common::String &verb, const Common::String &noun) const;
295 	void loadWords(Common::ReadStream &stream, WordMap &map, Common::StringArray &pri) const;
296 	void readCommands(Common::ReadStream &stream, Commands &commands);
297 	void removeCommand(Commands &commands, uint idx);
298 	Command &getCommand(Commands &commands, uint idx);
299 	void checkInput(byte verb, byte noun);
300 	virtual bool isInputValid(byte verb, byte noun, bool &is_any);
301 	virtual bool isInputValid(const Commands &commands, byte verb, byte noun, bool &is_any);
applyRoomWorkarounds(byte roomNr)302 	virtual void applyRoomWorkarounds(byte roomNr) { }
applyRegionWorkarounds()303 	virtual void applyRegionWorkarounds() { }
304 
305 	virtual void setupOpcodeTables();
306 	virtual void initState();
307 	virtual void switchRoom(byte roomNr);
308 	virtual byte roomArg(byte room) const;
advanceClock()309 	virtual void advanceClock() { }
310 	void loadDroppedItemOffsets(Common::ReadStream &stream, byte count);
311 
312 	// Opcodes
313 	typedef Common::SharedPtr<Common::Functor1<ScriptEnv &, int> > Opcode;
314 
315 	template <class T>
opcode(int (T::* f)(ScriptEnv &))316 	Opcode opcode(int (T::*f)(ScriptEnv &)) {
317 		return Opcode(new Common::Functor1Mem<ScriptEnv &, int, T>(static_cast<T *>(this), f));
318 	}
319 
320 	virtual int o_isItemInRoom(ScriptEnv &e);
321 	virtual int o_isMovesGT(ScriptEnv &e);
322 	virtual int o_isVarEQ(ScriptEnv &e);
323 	virtual int o_isCurPicEQ(ScriptEnv &e);
324 	virtual int o_isItemPicEQ(ScriptEnv &e);
325 
326 	virtual int o_varAdd(ScriptEnv &e);
327 	virtual int o_varSub(ScriptEnv &e);
328 	virtual int o_varSet(ScriptEnv &e);
329 	virtual int o_listInv(ScriptEnv &e);
330 	virtual int o_moveItem(ScriptEnv &e);
331 	virtual int o_setRoom(ScriptEnv &e);
332 	virtual int o_setCurPic(ScriptEnv &e);
333 	virtual int o_setPic(ScriptEnv &e);
334 	virtual int o_printMsg(ScriptEnv &e);
335 	virtual int o_setLight(ScriptEnv &e);
336 	virtual int o_setDark(ScriptEnv &e);
337 	virtual int o_save(ScriptEnv &e);
338 	virtual int o_restore(ScriptEnv &e);
339 	virtual int o_restart(ScriptEnv &e);
340 	virtual int o_quit(ScriptEnv &e);
341 	virtual int o_placeItem(ScriptEnv &e);
342 	virtual int o_setItemPic(ScriptEnv &e);
343 	virtual int o_resetPic(ScriptEnv &e);
344 	virtual int o_takeItem(ScriptEnv &e);
345 	virtual int o_dropItem(ScriptEnv &e);
346 	virtual int o_setRoomPic(ScriptEnv &e);
347 
348 	virtual int goDirection(ScriptEnv &e, Direction D);
o_goNorth(ScriptEnv & e)349 	int o_goNorth(ScriptEnv &e) { return goDirection(e, IDI_DIR_NORTH); }
o_goSouth(ScriptEnv & e)350 	int o_goSouth(ScriptEnv &e) { return goDirection(e, IDI_DIR_SOUTH); }
o_goEast(ScriptEnv & e)351 	int o_goEast(ScriptEnv &e) { return goDirection(e, IDI_DIR_EAST); }
o_goWest(ScriptEnv & e)352 	int o_goWest(ScriptEnv &e) { return goDirection(e, IDI_DIR_WEST); }
o_goUp(ScriptEnv & e)353 	int o_goUp(ScriptEnv &e) { return goDirection(e, IDI_DIR_UP); }
o_goDown(ScriptEnv & e)354 	int o_goDown(ScriptEnv &e) { return goDirection(e, IDI_DIR_DOWN); }
355 
356 	// Graphics
357 	void drawPic(byte pic, Common::Point pos = Common::Point()) const;
358 
359 	// Sound
360 	bool playTones(const Tones &tones, bool isMusic, bool allowSkip = false) const;
361 
362 	// Game state functions
363 	const Region &getRegion(uint i) const;
364 	Region &getRegion(uint i);
365 	const Room &getRoom(uint i) const;
366 	Room &getRoom(uint i);
367 	const Region &getCurRegion() const;
368 	Region &getCurRegion();
369 	const Room &getCurRoom() const;
370 	Room &getCurRoom();
371 	const Item &getItem(uint i) const;
372 	Item &getItem(uint i);
373 	byte getVar(uint i) const;
374 	void setVar(uint i, byte value);
375 	virtual void takeItem(byte noun);
376 	virtual void dropItem(byte noun);
377 	bool matchCommand(ScriptEnv &env) const;
378 	void doActions(ScriptEnv &env);
379 	bool doOneCommand(const Commands &commands, byte verb, byte noun);
380 	void doAllCommands(const Commands &commands, byte verb, byte noun);
381 	virtual ScriptEnv *createScriptEnv(const Command &cmd, byte room, byte verb, byte noun);
382 
383 	// Debug functions
384 	static Common::String toAscii(const Common::String &str);
385 	Common::String itemStr(uint i) const;
386 	Common::String roomStr(uint i) const;
387 	Common::String itemRoomStr(uint i) const;
388 	Common::String verbStr(uint i) const;
389 	Common::String nounStr(uint i) const;
390 	Common::String msgStr(uint i) const;
391 	Common::String dirStr(Direction dir) const;
392 	bool op_debug(const char *fmt, ...) const;
393 	Common::DumpFile *_dumpFile;
394 
395 	Display *_display;
396 	GraphicsMan *_graphics;
397 	bool _textMode;
398 
399 	// Opcodes
400 	Common::Array<Opcode> _condOpcodes, _actOpcodes;
401 	// Message strings in data file
402 	Common::Array<DataBlockPtr> _messages;
403 	// Picture data
404 	PictureMap _pictures;
405 	// Dropped item screen offsets
406 	Common::Array<Common::Point> _itemOffsets;
407 	// <room, verb, noun, script> lists
408 	Commands _roomCommands;
409 	Commands _globalCommands;
410 	// Data related to the current room
411 	RoomData _roomData;
412 
413 	WordMap _verbs;
414 	WordMap _nouns;
415 	Common::StringArray _priVerbs;
416 	Common::StringArray _priNouns;
417 
418 	struct {
419 		Common::String enterCommand;
420 		Common::String verbError;
421 		Common::String nounError;
422 		Common::String playAgain;
423 		Common::String pressReturn;
424 		Common::String lineFeeds;
425 	} _strings;
426 
427 	uint32 _verbErrorPos, _nounErrorPos;
428 
429 	struct {
430 		uint cantGoThere;
431 		uint dontUnderstand;
432 		uint itemDoesntMove;
433 		uint itemNotHere;
434 		uint thanksForPlaying;
435 	} _messageIds;
436 
437 	// Game state
438 	State _state;
439 
440 	uint _linesPrinted;
441 	bool _isRestarting, _isRestoring, _isQuitting;
442 	bool _canSaveNow, _canRestoreNow;
443 	bool _abortScript;
444 	Common::RandomSource *_random;
445 
446 	const AdlGameDescription *_gameDescription;
447 
448 	mutable Common::File *_inputScript;
449 	mutable uint _scriptDelay;
450 	mutable bool _scriptPaused;
451 
452 private:
runIntro()453 	virtual void runIntro() { }
454 	virtual void init() = 0;
455 	virtual void initGameState() = 0;
456 	virtual void drawItems() = 0;
457 	virtual void drawItem(Item &item, const Common::Point &pos) = 0;
458 	virtual void loadRoom(byte roomNr) = 0;
459 	virtual void showRoom() = 0;
switchRegion(byte region)460 	virtual void switchRegion(byte region) { }
461 	void runScript(const char *filename) const;
462 	void stopScript() const;
setScriptDelay(uint scriptDelay)463 	void setScriptDelay(uint scriptDelay) const { _scriptDelay = scriptDelay; }
464 	Common::String getScriptLine() const;
465 	// Engine
466 	Common::Error run() override;
467 	bool hasFeature(EngineFeature f) const override;
468 	bool canLoadGameStateCurrently() override;
469 
470 	// Text input
471 	byte convertKey(uint16 ascii) const;
472 
473 	byte _saveVerb, _saveNoun, _restoreVerb, _restoreNoun;
474 };
475 
476 } // End of namespace Adl
477 
478 #endif
479