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 SAGA_SAGA_H
24 #define SAGA_SAGA_H
25 
26 #include "engines/engine.h"
27 
28 #include "common/array.h"
29 #include "common/random.h"
30 #include "common/memstream.h"
31 #include "common/textconsole.h"
32 
33 #include "saga/gfx.h"
34 
35 struct ADGameFileDescription;
36 
37 /**
38  * This is the namespace of the SAGA engine.
39  *
40  * Status of this engine:
41  *
42  * This engine contains 2 main engine generations, SAGA and SAGA2
43  *
44  * SAGA status: complete
45  *
46  * SAGA2 status: in early stages of development, no recent activity. Contact sev
47  *  if you want to work on it, since we have some original source codes.
48  *
49  * Games using this engine:
50  *
51  * SAGA:
52  * - Inherit the Earth
53  * - I Have No Mouth And I Must Scream
54  *
55  * SAGA2:
56  * - Dinotopia
57  * - Faery Tale Adventure II: Halls of the Dead
58  *
59  */
60 namespace Saga {
61 
62 class SndRes;
63 class Sound;
64 class Music;
65 class Anim;
66 class Render;
67 class IsoMap;
68 class Gfx;
69 class Script;
70 class Actor;
71 class Font;
72 class Sprite;
73 class Scene;
74 class Interface;
75 class Console;
76 class Events;
77 class PalAnim;
78 class Puzzle;
79 class Resource;
80 
81 class ResourceContext;
82 
83 // #define SAGA_DEBUG 1		// define for test functions
84 #define SAGA_IMAGE_DATA_OFFSET 776
85 #define SAGA_IMAGE_HEADER_LEN  8
86 
87 // Note that IHNM has a smaller save title size than ITE
88 // We allocate the ITE save title size in savegames, to
89 // preserve savegame backwards compatibility. We only check
90 // for IHNM's save title during text input
91 #define SAVE_TITLE_SIZE 28
92 #define TITLESIZE 80
93 #define IHNM_SAVE_TITLE_SIZE 22
94 #define MAX_SAVES 96
95 #define MAX_FILE_NAME 256
96 
97 #define ID_NOTHING 0
98 #define ID_PROTAG 1
99 #define OBJECT_TYPE_SHIFT 13
100 #define OBJECT_TYPE_MASK ((1 << OBJECT_TYPE_SHIFT) - 1)
101 
102 #define IHNM_OBJ_PROFILE 0x4000
103 
104 #define memoryError(Place) error("%s Memory allocation error.", Place)
105 
106 enum ERRORCODE {
107 	FAILURE = -1,
108 	SUCCESS = 0
109 };
110 
111 enum GameIds {
112 	GID_ITE = 0,
113 	GID_IHNM = 1,
114 	GID_DINO = 2,
115 	GID_FTA2 = 3
116 };
117 
118 enum GameFileTypes {
119 	// Common
120 	GAME_RESOURCEFILE     = 1 << 0,    // Game resources
121 	GAME_SCRIPTFILE       = 1 << 1,    // Game scripts
122 	GAME_SOUNDFILE        = 1 << 2,    // SFX (also contains voices and MIDI music in SAGA 2 games)
123 	GAME_VOICEFILE        = 1 << 3,    // Voices (also contains SFX in the ITE floppy version)
124 	// ITE specific
125 	GAME_DIGITALMUSICFILE = 1 << 4,    // ITE digital music, added by Wyrmkeep
126 	GAME_MACBINARY        = 1 << 5,    // ITE Mac CD Guild
127 	GAME_DEMOFILE         = 1 << 6,    // Early ITE demo
128 	GAME_SWAPENDIAN       = 1 << 7,    // Used to identify the BE voice file in the ITE combined version
129 	// IHNM specific
130 	GAME_MUSICFILE_FM     = 1 << 8,    // IHNM
131 	GAME_MUSICFILE_GM     = 1 << 9,    // IHNM, ITE Mac CD Guild
132 	GAME_PATCHFILE        = 1 << 10,   // IHNM patch file (patch.re_/patch.res)
133 	// SAGA 2 (Dinotopia, FTA2)
134 	GAME_IMAGEFILE        = 1 << 11,   // Game images
135 	GAME_OBJRESOURCEFILE  = 1 << 12    // Game object data
136 };
137 
138 enum GameFeatures {
139 	GF_ITE_FLOPPY        = 1 << 0,
140 	GF_ITE_DOS_DEMO      = 1 << 1,
141 	GF_EXTRA_ITE_CREDITS = 1 << 2,
142 	GF_8BIT_UNSIGNED_PCM = 1 << 3,
143 	GF_IHNM_COLOR_FIX    = 1 << 4
144 };
145 
146 enum VerbTypeIds {
147 	kVerbITENone = 0,
148 	kVerbITEPickUp = 1,
149 	kVerbITELookAt = 2,
150 	kVerbITEWalkTo = 3,
151 	kVerbITETalkTo = 4,
152 	kVerbITEOpen = 5,
153 	kVerbITEClose = 6,
154 	kVerbITEGive = 7,
155 	kVerbITEUse = 8,
156 	kVerbITEOptions = 9,
157 	kVerbITEEnter = 10,
158 	kVerbITELeave = 11,
159 	kVerbITEBegin = 12,
160 	kVerbITEWalkOnly = 13,
161 	kVerbITELookOnly = 14,
162 
163 
164 	kVerbIHNMNone = 0,
165 	kVerbIHNMWalk = 1,
166 	kVerbIHNMLookAt = 2,
167 	kVerbIHNMTake = 3,
168 	kVerbIHNMUse = 4,
169 	kVerbIHNMTalkTo = 5,
170 	kVerbIHNMSwallow = 6,
171 	kVerbIHNMGive = 7,
172 	kVerbIHNMPush = 8,
173 	kVerbIHNMOptions = 9,
174 	kVerbIHNMEnter = 10,
175 	kVerbIHNMLeave = 11,
176 	kVerbIHNMBegin = 12,
177 	kVerbIHNMWalkOnly = 13,
178 	kVerbIHNMLookOnly = 14,
179 
180 	kVerbTypeIdsMax = kVerbITELookOnly + 1
181 };
182 
183 enum PanelButtonType {
184 	kPanelButtonVerb = 1 << 0,
185 	kPanelButtonArrow = 1 << 1,
186 	kPanelButtonConverseText = 1 << 2,
187 	kPanelButtonInventory = 1 << 3,
188 
189 	kPanelButtonOption = 1 << 4,
190 	kPanelButtonOptionSlider = 1 << 5,
191 	kPanelButtonOptionSaveFiles = 1 << 6,
192 	kPanelButtonOptionText = 1 << 7,
193 
194 	kPanelButtonQuit = 1 << 8,
195 	kPanelButtonQuitText = 1 << 9,
196 
197 	kPanelButtonLoad = 1 << 10,
198 	kPanelButtonLoadText = 1 << 11,
199 
200 	kPanelButtonSave = 1 << 12,
201 	kPanelButtonSaveText = 1 << 13,
202 	kPanelButtonSaveEdit = 1 << 14,
203 
204 	kPanelButtonProtectText = 1 << 15,
205 	kPanelButtonProtectEdit = 1 << 16,
206 
207 	kPanelAllButtons = 0xFFFFF
208 };
209 
210 enum TextStringIds {
211 	kTextPickUp,
212 	kTextLookAt,
213 	kTextWalkTo,
214 	kTextTalkTo,
215 	kTextOpen,
216 	kTextClose,
217 	kTextGive,
218 	kTextUse,
219 
220 	kTextOptions,
221 	kTextTest,
222 	kTextDemo,
223 	kTextHelp,
224 	kTextQuitGame,
225 	kTextFast,
226 	kTextSlow,
227 	kTextOn,
228 	kTextOff,
229 	kTextContinuePlaying,
230 	kTextLoad,
231 	kTextSave,
232 	kTextGameOptions,
233 	kTextReadingSpeed,
234 	kTextMusic,
235 	kTextSound,
236 	kTextCancel,
237 	kTextQuit,
238 	kTextOK,
239 	kTextMid,
240 	kTextClick,
241 	kText10Percent,
242 	kText20Percent,
243 	kText30Percent,
244 	kText40Percent,
245 	kText50Percent,
246 	kText60Percent,
247 	kText70Percent,
248 	kText80Percent,
249 	kText90Percent,
250 	kTextMax,
251 	kTextQuitTheGameQuestion,
252 	kTextLoadSuccessful,
253 	kTextEnterSaveGameName,
254 	kTextGiveTo,
255 	kTextUseWidth,
256 	kTextNewSave,
257 	kTextICantPickup,
258 	kTextNothingSpecial,
259 	kTextNoPlaceToOpen,
260 	kTextNoOpening,
261 	kTextDontKnow,
262 	kTextShowDialog,
263 	kTextEnterProtectAnswer,
264 	kTextVoices,
265 	kTextText,
266 	kTextAudio,
267 	kTextBoth,
268 	kTextLoadSavedGame
269 };
270 
271 struct GameResourceDescription {
272 	uint32 sceneLUTResourceId;
273 	uint32 moduleLUTResourceId;
274 	uint32 mainPanelResourceId;
275 	uint32 conversePanelResourceId;
276 	uint32 optionPanelResourceId;
277 	uint32 mainSpritesResourceId;
278 	uint32 mainPanelSpritesResourceId;
279 	uint32 mainStringsResourceId;
280 	// ITE specific resources
281 	uint32 actorsStringsResourceId;
282 	uint32 defaultPortraitsResourceId;
283 	// IHNM specific resources
284 	uint32 optionPanelSpritesResourceId;
285 	uint32 warningPanelResourceId;
286 	uint32 warningPanelSpritesResourceId;
287 	uint32 psychicProfileResourceId;
288 };
289 
290 struct GameFontDescription {
291 	uint32 fontResourceId;
292 };
293 
294 struct GameDisplayInfo;
295 
296 struct GamePatchDescription {
297 	const char *fileName;
298 	uint16 fileType;
299 	uint32 resourceId;
300 };
301 
302 struct SAGAGameDescription;
303 
304 enum GameObjectTypes {
305 	kGameObjectNone = 0,
306 	kGameObjectActor = 1,
307 	kGameObjectObject = 2,
308 	kGameObjectHitZone = 3,
309 	kGameObjectStepZone = 4
310 };
311 
312 enum ScriptTimings {
313 	kScriptTimeTicksPerSecond = (728L/10L),
314 	kRepeatSpeedTicks = (728L/10L)/3,
315 	kNormalFadeDuration = 320, // 64 steps, 5 msec each
316 	kQuickFadeDuration = 64,  // 64 steps, 1 msec each
317 	kPuzzleHintTime = 30000000L  // 30 secs. used in timer
318 };
319 
320 enum Directions {
321 	kDirUp = 0,
322 	kDirUpRight = 1,
323 	kDirRight = 2,
324 	kDirDownRight = 3,
325 	kDirDown = 4,
326 	kDirDownLeft = 5,
327 	kDirLeft = 6,
328 	kDirUpLeft = 7
329 };
330 
331 enum HitZoneFlags {
332 	kHitZoneEnabled = (1 << 0),   // Zone is enabled
333 	kHitZoneExit = (1 << 1),      // Causes char to exit
334 
335 	//	The following flag causes the zone to act differently.
336 	//	When the actor hits the zone, it will immediately begin walking
337 	//	in the specified direction, and the actual specified effect of
338 	//	the zone will be delayed until the actor leaves the zone.
339 	kHitZoneAutoWalk = (1 << 2),
340 
341 	//      When set on a hit zone, this causes the character not to walk
342 	//      to the object (but they will look at it).
343 	kHitZoneNoWalk = (1 << 2),
344 
345 	//	zone activates only when character stops walking
346 	kHitZoneTerminus = (1 << 3),
347 
348 	//      Hit zones only - when the zone is clicked on it projects the
349 	//      click point downwards from the middle of the zone until it
350 	//      reaches the lowest point in the zone.
351 	kHitZoneProject = (1 << 3)
352 };
353 
354 struct ImageHeader {
355 	int width;
356 	int height;
357 };
358 
359 struct StringsTable {
360 	Common::Array<char> buffer;
361 	Common::Array<char *> strings;
362 
getStringStringsTable363 	const char *getString(uint index) const {
364 		if (strings.size() <= index) {
365 			// This occurs at the end of Ted's chapter, right after the ending cutscene
366 			warning("StringsTable::getString wrong index 0x%X (%d)", index, strings.size());
367 			return "";
368 		}
369 		return strings[index];
370 	}
371 
clearStringsTable372 	void clear() {
373 		strings.clear();
374 		buffer.clear();
375 	}
376 };
377 
378 typedef Common::Array<Point> PointList;
379 
380 enum ColorId {
381 	kITEColorTransBlack = 0x00,
382 	kITEColorBrightWhite = 0x01,
383 	kITEColorWhite = 0x02,
384 	kITEColorLightGrey = 0x04,
385 	kITEColorGrey = 0x0a,
386 	kITEColorDarkGrey = 0x0b,
387 	kITEColorDarkGrey0C = 0x0C,
388 	kITEColorBlack = 0x0f,
389 	kITEColorRed = 0x65,
390 	kITEColorDarkBlue8a = 0x8a,
391 	kITEColorBlue89 = 0x89,
392 	kITEColorLightBlue92 = 0x92,
393 	kITEColorBlue = 0x93,
394 	kITEColorLightBlue94 = 0x94,
395 	kITEColorLightBlue96 = 0x96,
396 	kITEColorGreen = 0xba
397 };
398 
399 enum KnownColor {
400 	kKnownColorTransparent,
401 	kKnownColorBrightWhite,
402 	kKnownColorWhite,
403 	kKnownColorBlack,
404 
405 	kKnownColorSubtitleTextColor,
406 	kKnownColorVerbText,
407 	kKnownColorVerbTextShadow,
408 	kKnownColorVerbTextActive
409 };
410 
411 struct SaveFileData {
412 	char name[SAVE_TITLE_SIZE];
413 	uint slotNumber;
414 };
415 
416 struct SaveGameHeader {
417 	uint32 type;
418 	uint32 size;
419 	uint32 version;
420 	char name[SAVE_TITLE_SIZE];
421 };
422 
objectTypeId(uint16 objectId)423 inline int objectTypeId(uint16 objectId) {
424 	return objectId >> OBJECT_TYPE_SHIFT;
425 }
426 
objectIdToIndex(uint16 objectId)427 inline int objectIdToIndex(uint16 objectId) {
428 	return OBJECT_TYPE_MASK & objectId;
429 }
430 
objectIndexToId(int type,int index)431 inline uint16 objectIndexToId(int type, int index) {
432 	return (type << OBJECT_TYPE_SHIFT) | (OBJECT_TYPE_MASK & index);
433 }
434 
435 class ByteArray : public Common::Array<byte> {
436 public:
437 	/**
438 	 * Return a pointer to the start of the buffer underlying this byte array,
439 	 * or NULL if the buffer is empty.
440 	 */
getBuffer()441 	byte *getBuffer() {
442 		return empty() ? NULL : &front();
443 	}
444 
getBuffer()445 	const byte *getBuffer() const {
446 		return empty() ? NULL : &front();
447 	}
448 
assign(const ByteArray & src)449 	void assign(const ByteArray &src) {
450 		resize(src.size());
451 		if (!empty()) {
452 			memcpy(&front(), &src.front(), size());
453 		}
454 	}
455 };
456 
457 class ByteArrayReadStreamEndian : public Common::MemoryReadStreamEndian {
458 public:
459 	ByteArrayReadStreamEndian(const ByteArray & byteArray, bool bigEndian = false)
460 		: Common::MemoryReadStreamEndian(byteArray.getBuffer(), byteArray.size(), bigEndian) {
461 	}
462 };
463 
464 class SagaEngine : public Engine {
465 	friend class Scene;
466 
467 public:
468 	// Engine APIs
469 	virtual Common::Error run();
470 	bool hasFeature(EngineFeature f) const;
471 	void syncSoundSettings();
472 	void pauseEngineIntern(bool pause);
473 
474 	GUI::Debugger *getDebugger();
475 
476 	SagaEngine(OSystem *syst, const SAGAGameDescription *gameDesc);
477 	~SagaEngine();
478 
479 	void save(const char *fileName, const char *saveName);
480 	void load(const char *fileName);
getCurrentLoadVersion()481 	uint32 getCurrentLoadVersion() const {
482 		return _saveHeader.version;
483 	}
484 	void fillSaveList();
485 	char *calcSaveFileName(uint slotNumber);
486 
487 	SaveFileData *getSaveFile(uint idx);
488 	uint getNewSaveSlotNumber() const;
489 	bool locateSaveFile(char *saveName, uint &titleNumber);
isSaveListFull()490 	bool isSaveListFull() const {
491 		return _saveFilesCount == MAX_SAVES;
492 	}
getSaveFilesCount()493 	uint getSaveFilesCount() const {
494 		return isSaveListFull() ? _saveFilesCount : _saveFilesCount + 1;
495 	}
496 
isIHNMDemo()497 	bool isIHNMDemo() const { return _isIHNMDemo; }
498 
499 	int16 _framesEsc;
500 
501 	uint32 _globalFlags;
502 	int16 _ethicsPoints[8];
503 	int _spiritualBarometer;
504 
505 	int _soundVolume;
506 	int _musicVolume;
507 	int _speechVolume;
508 	bool _subtitlesEnabled;
509 	bool _voicesEnabled;
510 	bool _voiceFilesExist;
511 	int _readingSpeed;
512 
513 	bool _copyProtection;
514 	bool _musicWasPlaying;
515 	bool _isIHNMDemo;
516 	bool _hasITESceneSubstitutes;
517 
518 	SndRes *_sndRes;
519 	Sound *_sound;
520 	Music *_music;
521 	Anim *_anim;
522 	Render *_render;
523 	IsoMap *_isoMap;
524 	Gfx *_gfx;
525 	Script *_script;
526 	Actor *_actor;
527 	Font *_font;
528 	Sprite *_sprite;
529 	Scene *_scene;
530 	Interface *_interface;
531 	Console *_console;
532 	Events *_events;
533 	PalAnim *_palanim;
534 	Puzzle *_puzzle;
535 	Resource *_resource;
536 
537 
538 	// Random number generator
539 	Common::RandomSource _rnd;
540 
541 private:
542 	bool decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, ByteArray &outbuf);
543 	void flipImage(byte *imageBuffer, int columns, int scanlines);
544 	void unbankBGImage(byte *dest_buf, const byte *src_buf, int columns, int scanlines);
545 	uint32 _previousTicks;
546 
547 public:
548 	bool decodeBGImage(const ByteArray &imageData, ByteArray &outputBuffer, int *w, int *h, bool flip = false);
getImagePal(const ByteArray & imageData)549 	const byte *getImagePal(const ByteArray &imageData) {
550 		if (imageData.size() <= SAGA_IMAGE_HEADER_LEN) {
551 			return NULL;
552 		}
553 
554 		return &imageData.front() + SAGA_IMAGE_HEADER_LEN;
555 	}
556 	void loadStrings(StringsTable &stringsTable, const ByteArray &stringsData);
557 
558 	const char *getObjectName(uint16 objectId) const;
559 public:
560 	int processInput();
561 	Point mousePos() const;
562 
getMouseClickCount()563 	int getMouseClickCount() const {
564 		return _mouseClickCount;
565 	}
566 
incrementMouseClickCount()567 	void incrementMouseClickCount() {
568 		_mouseClickCount++;
569 	}
570 
resetMouseClickCount()571 	void resetMouseClickCount() {
572 		_mouseClickCount = 0;
573 	}
574 
leftMouseButtonPressed()575 	bool leftMouseButtonPressed() const {
576 		return _leftMouseButtonPressed;
577 	}
578 
rightMouseButtonPressed()579 	bool rightMouseButtonPressed() const {
580 		return _rightMouseButtonPressed;
581 	}
582 
mouseButtonPressed()583 	bool mouseButtonPressed() const {
584 		return _leftMouseButtonPressed || _rightMouseButtonPressed;
585 	}
586 
ticksToMSec(int tick)587 	inline int ticksToMSec(int tick) const {
588 		return tick * 1000 / kScriptTimeTicksPerSecond;
589 	}
590 
591  private:
592 	uint _saveFilesCount;
593 	SaveFileData _saveFiles[MAX_SAVES];
594 	SaveGameHeader _saveHeader;
595 
596 	bool _leftMouseButtonPressed;
597 	bool _rightMouseButtonPressed;
598 	int _mouseClickCount;
599 
600 //current game description
601 	int _gameNumber;
602 	const SAGAGameDescription *_gameDescription;
603 	Common::String _gameTitle;
604 	Common::Rect _displayClip;
605 
606 public:
607 	int32 _frameCount;
608 
609 public:
610 	bool initGame();
611 
612 	bool isBigEndian() const;
613 	bool isMacResources() const;
isSaga2()614 	bool isSaga2() const { return getGameId() == GID_DINO || getGameId() == GID_FTA2; }
615 	const GameResourceDescription *getResourceDescription() const;
616 
617 	const GameFontDescription *getFontDescription(int index) const;
618 	int getFontsCount() const;
619 
620 	int getGameId() const;
621 	uint32 getFeatures() const;
622 	Common::Language getLanguage() const;
623 	Common::Platform getPlatform() const;
624 	int getGameNumber() const;
625 	int getStartSceneNumber() const;
626 
627 	const GamePatchDescription *getPatchDescriptions() const;
628 
629 	const ADGameFileDescription *getFilesDescriptions() const;
630 
getDisplayClip()631 	const Common::Rect &getDisplayClip() const { return _displayClip;}
632 	Common::Error loadGameState(int slot);
633 	Common::Error saveGameState(int slot, const Common::String &desc);
634 	bool canLoadGameStateCurrently();
635 	bool canSaveGameStateCurrently();
636 	const GameDisplayInfo &getDisplayInfo();
637 
638 	const char *getTextString(int textStringId);
639 	void getExcuseInfo(int verb, const char *&textString, int &soundResourceId);
640 
641 private:
642 
643 public:
644 	ColorId KnownColor2ColorId(KnownColor knownColor);
645 	void setTalkspeed(int talkspeed);
646 	int getTalkspeed() const;
647 };
648 
649 } // End of namespace Saga
650 
651 #endif
652