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  * Copyright 2020 Google
22  *
23  */
24 
25 #ifndef HADESCH_VIDEOROOM_H
26 #define HADESCH_VIDEOROOM_H
27 
28 #include "common/scummsys.h"
29 #include "common/array.h"
30 #include "audio/audiostream.h"
31 #include "audio/mixer.h"
32 #include "common/rect.h"
33 #include "common/ptr.h"
34 #include "hadesch/pod_file.h"
35 #include "hadesch/pod_image.h"
36 #include "common/hashmap.h"
37 #include "common/str.h"
38 #include "hadesch/enums.h"
39 #include "hadesch/event.h"
40 #include "hadesch/hotzone.h"
41 #include "hadesch/table.h"
42 #include "common/queue.h"
43 
44 namespace Video {
45 class SmackerDecoder;
46 }
47 
48 namespace Hadesch {
49 
50 class PodImage;
51 class HadeschEngine;
52 class TagFile;
53 
54 static const int kDefaultSpeed = 100;
55 
56 class Renderable {
57 public:
58 		Renderable(Common::Array<PodImage> images);
59 	const PodImage &getFrame(int time);
60 	void startAnimation(int startms, int msperframe,
61 			    bool loop, int first, int last);
62 	bool isAnimationFinished(int time);
63 	void selectFrame(int frame);
64 	int getAnimationFrameNum(int time);
getNumFrames()65 	int getNumFrames() {
66 		return _images.size();
67 	}
68 
69 private:
70 	int getLen();
71 	Common::Array<PodImage> _images;
72 	int _msperframe;
73 	int _startms;
74 	int _first;
75 	int _last;
76 	bool _loop;
77 };
78 
79 class LayerId {
80 public:
LayerId(const Common::String & name)81 	LayerId(const Common::String &name) {
82 		_name = name;
83 		_idx = -1;
84 	}
85 
LayerId()86 	LayerId() {
87 		_idx = -1;
88 	}
89 
LayerId(const char * name)90 	LayerId(const char *name) {
91 		_name = name;
92 		_idx = -1;
93 	}
94 
LayerId(const Common::String & name,int idx,const Common::String & qualifier)95 	LayerId(const Common::String &name, int idx, const Common::String &qualifier) {
96 		_qualifier = qualifier;
97 		_name = name;
98 		_idx = idx;
99 	}
100 
getFilename()101 	Common::String getFilename() const {
102 		return _name;
103 	}
104 
105 	Common::String getDebug() const;
106 
107 	bool operator== (const LayerId &b) const;
108 
109 private:
110 	Common::String _name;
111 	int _idx;
112 	Common::String _qualifier;
113 };
114 
115 struct Animation {
116 	Audio::SoundHandle _soundHandle;
117 	LayerId _animName;
118 	EventHandlerWrapper _callbackEvent;
119 	bool _finished;
120 	bool _keepLastFrame;
121 	bool _skippable;
122 	int _subtitleID;
123 };
124 
125 class PlayAnimParams {
126 public:
127 	static PlayAnimParams loop();
128 	static PlayAnimParams keepLastFrame();
129 	static PlayAnimParams disappear();
130 	bool getKeepLastFrame();
131 	bool isLoop();
132 	int getSpeed();
133 	int getFirstFrame();
134 	int getLastFrame();
135 	PlayAnimParams partial(int first, int last) const;
136 	PlayAnimParams speed(int msperframe) const;
137 	PlayAnimParams backwards() const;
138 private:
139 	PlayAnimParams(bool loop, bool keepLastFrame);
140 	bool _loop;
141 	bool _keepLastFrame;
142 	int _firstFrame;
143 	int _lastFrame;
144 	int _msperframe;
145 };
146 
147 struct TranscribedSound {
148 	const char *soundName;
149 	const char *transcript;
150 
makeTranscribedSound151 	static TranscribedSound make(const char *s, const char *t) {
152 		TranscribedSound res;
153 		res.soundName = s;
154 		res.transcript = t;
155 		return res;
156 	}
157 };
158 
159 class VideoRoom {
160 public:
161 	VideoRoom(const Common::String &dir, const Common::String &pod,
162 		  const Common::String &assetMapFile);
163 	~VideoRoom();
164 
165 	void nextFrame(Common::SharedPtr<GfxContext> context, int time, bool stopVideo);
166 	uint getWidth();
167 	uint getHeight();
168 	int getCursor();
169 
170 	// Hotzones and mouse
171 	void setHotzoneEnabled(const Common::String &name, bool enabled);
172 	void enableHotzone(const Common::String &name);
173 	void disableHotzone(const Common::String &name);
174 	void pushHotZones(const Common::String &hotzoneFile, bool enable = true,
175 			  Common::Point offset = Common::Point(0, 0));
176 	void popHotZones();
177 	void loadHotZones(const Common::String &hotzoneFile, bool enable = true,
178 			  Common::Point offset = Common::Point(0, 0));
179 	void computeHotZone(int time, Common::Point mousePos);
180 	Common::String getHotZone();
181 	void setHotZoneOffset(const Common::String &name, Common::Point offset);
182 	Common::String mapClick(Common::Point mousePos);
enableMouse()183 	void enableMouse() {
184 		_mouseEnabled = true;
185 	}
disableMouse()186 	void disableMouse() {
187 		_mouseEnabled = false;
188 	}
isMouseEnabled()189 	bool isMouseEnabled() {
190 		return _mouseEnabled;
191 	}
192 	int getCursorAnimationFrame(int time);
193 
194 	// Animations and layers
195 	void setLayerEnabled(const LayerId &name, bool enabled);
196 	void setLayerParallax(const LayerId &name, int val);
197 	void setColorScale(const LayerId &name, int val);
198 	void setScale(const LayerId &name, int val);
199 	int getNumFrames(const LayerId &animName);
200 
201 	// Main animation API
202 	void playAnimWithSpeech(const LayerId &animName,
203 				const TranscribedSound &sound,
204 				int zValue,
205 				PlayAnimParams params,
206 				EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
207 				Common::Point offset = Common::Point(0, 0));
208 	void playAnimWithSFX(const LayerId &animName,
209 			     const Common::String &soundName,
210 			     int zValue,
211 			     PlayAnimParams params,
212 			     EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
213 			     Common::Point offset = Common::Point(0, 0));
214 		void playAnimWithMusic(const LayerId &animName,
215 			       const Common::String &soundName,
216 			       int zValue,
217 			       PlayAnimParams params,
218 			       EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
219 			       Common::Point offset = Common::Point(0, 0));
220 	void playAnim(const LayerId &animName, int zValue,
221 		      PlayAnimParams params,
222 		      EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
223 		      Common::Point offset = Common::Point(0, 0));
224 
225 	void stopAnim(const LayerId &animName);
226 	// Like stopAnim but also remove layer altogether
227 	void purgeAnim(const LayerId &animName);
228   	bool isAnimationFinished(const LayerId &name, int time);
229 	void addStaticLayer(const LayerId &name, int zValue, Common::Point offset = Common::Point(0, 0));
230 	void selectFrame(const LayerId &name, int zValue, int val, Common::Point offset = Common::Point(0, 0));
231 	bool doesLayerExist(const LayerId &name);
232 	PodImage getLayerFrame(const LayerId &name);
233   	int getAnimFrameNum(const LayerId &name);
234 	void dumpLayers();
235 
236 	// Convenience wrappers
237 	void playAnimLoop(const LayerId &animName, int zValue, Common::Point offset = Common::Point(0, 0));
238 	void playAnimKeepLastFrame(const LayerId &animName, int zValue, EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
239 				   Common::Point offset = Common::Point(0, 0));
240 
241 	// Videos
242 	void playVideo(const Common::String &name, int zValue,
243 		       EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
244 		       Common::Point offset = Common::Point(0, 0));
245 	void cancelVideo();
246 	bool isVideoPlaying();
247 
248 	// Panning
249 	void panLeftAnim(EventHandlerWrapper callbackEvent = EventHandlerWrapper());
250 	void panRightAnim(EventHandlerWrapper callbackEvent = EventHandlerWrapper());
251 	void panRightInstant();
252 	void setPannable(bool pannable);
253 	void setUserPanCallback(EventHandlerWrapper leftStart,
254 				EventHandlerWrapper leftEnd,
255 				EventHandlerWrapper rightStart,
256 				EventHandlerWrapper rightEnd);
isPanLeft()257 	bool isPanLeft() {
258 		return _pan == 0;
259 	}
260 
isPanRight()261 	bool isPanRight() {
262 		return _pan == 640;
263 	}
264 
265 	// Hero belt
enableHeroBelt()266 	void enableHeroBelt() {
267 		_heroBeltEnabled = true;
268 	}
disableHeroBelt()269 	void disableHeroBelt() {
270 		_heroBeltEnabled = false;
271 	}
isHeroBeltEnabled()272 	bool isHeroBeltEnabled() {
273 		return _heroBeltEnabled;
274 	}
275 
276 	// Font
277 	void renderString(const Common::String &font, const Common::U32String &str,
278 			  Common::Point startPos, int zVal, int fontDelta = 0, const Common::String &extraId = "letter");
279 	void renderStringCentered(const Common::String &font, const Common::U32String &str,
280 				  Common::Point centerPos, int zVal, int fontDelta = 0, const Common::String &extraId = "letter");
281 	void hideString(const Common::String &font, size_t maxLen, const Common::String &extraId = "letter");
282 	int computeStringWidth(const Common::String &font, const Common::U32String &str, int fontDelta = 0);
283 
284 	// Misc
285 	void playSFX(const Common::String &soundName,
286 		     EventHandlerWrapper callbackEvent = EventHandlerWrapper());
287 	void playMusic(const Common::String &soundName,
288 		       EventHandlerWrapper callbackEvent = EventHandlerWrapper());
289 	void playSFXLoop(const Common::String &soundName);
290 	void playMusicLoop(const Common::String &soundName);
291 	void playSpeech(const TranscribedSound &sound,
292 				 EventHandlerWrapper callbackEvent = EventHandlerWrapper());
293 	void playStatueSMK(StatueId id, const LayerId &animName, int zValue,
294 			   const Common::Array<Common::String> &smkNames,
295 			   int startOfLoop, int startOfEnd,
296 			   Common::Point offset = Common::Point(0, 0));
297 	Common::SeekableReadStream *openFile(const Common::String &name);
298 	void fadeOut(int ms, const EventHandlerWrapper &callback);
299 	void resetFade();
300 	void resetLayers();
301 	void drag(const Common::String &name, int frame, Common::Point hotspot);
302 	PodImage *getDragged();
303 	void clearDrag();
304 	void pause();
305 	void unpause();
306 	void finish();
307 	void cancelAllSubtitles();
setViewportOffset(Common::Point vp)308 	void setViewportOffset(Common::Point vp) {
309 		_viewportOffset = vp;
310 	}
311 
312 private:
313 	struct Layer {
314 		Common::SharedPtr<Renderable> renderable;
315 		LayerId name;
316 		Common::Point offset;
317 		bool isEnabled;
318 		int genCounter;
319 		int zValue;
320 		int parallax;
321 		int colorScale; // From 0 to 0x100
322 		int scale; // From 0 to 100
323 	};
324 
325 	struct SubtitleLine {
326 		Common::U32String line;
327 		int32 maxTime;
328 		int ID;
329 	};
330 
331 	void playAnimWithSoundInternal(const LayerId &animName,
332 				       const Common::String &soundName,
333 				       Audio::Mixer::SoundType soundType,
334 				       int zValue,
335 				       PlayAnimParams params,
336 				       EventHandlerWrapper callbackEvent,
337 				       Common::Point offset,
338 				       int subID = -1);
339 	void playSubtitles(const char *text, int subID);
340 	void addLayer(Renderable *renderable, const LayerId &name,
341 		      int zValue,
342 		      bool isEnabled = true, Common::Point offset = Common::Point(0, 0));
343 	void startAnimationInternal(const LayerId &name, int zValue, int msperframe, bool loop,
344 				    bool fixedFrame, int first, int last, Common::Point offset);
345 	Audio::RewindableAudioStream *getAudioStream(const Common::String &soundName);
346 	Common::String mapAsset(const Common::String &name);
347 	Common::String mapAsset(const LayerId &name);
348 	void addAnimLayerInternal(const LayerId &name, int zValue, Common::Point offset = Common::Point(0, 0));
349 	void playSoundInternal(const Common::String &soundName, EventHandlerWrapper callbackEvent, bool loop,
350 			       bool skippable, Audio::Mixer::SoundType soundType, int subtitleID = -1);
351 	static int layerComparator (const Layer &a, const Layer &b);
352 	void loadFontWidth(const Common::String &font);
353 
354 	uint _videoW, _videoH;
355 	Common::Point _videoOffset, _videoSurfOffset;
356 	Common::SharedPtr<byte> _videoPixels;
357 	byte _videoPalette[256 * 3];
358 
359 	HotZoneArray _hotZones;
360 	Common::Array<HotZoneArray> _hotZoneStack;
361 	Common::SortedArray<Layer, const Layer&> _layers;
362 	int _layerGenCounter;
363 
364 	int _startHotTime;
365 	int _hotZone;
366 	int _cursor;
367 	// We need to keep cursor pointer valid for at
368 	// least one more frame. Hence use circular buffer
369 	PodImage _draggedImage[5];
370 	int _draggingPtr;
371 	bool _isDragging;
372 	int _pan, _panSpeed;
373 	EventHandlerWrapper _panCallback;
374 	EventHandlerWrapper _userPanStartLeftCallback;
375 	EventHandlerWrapper _userPanStartRightCallback;
376 	EventHandlerWrapper _userPanEndLeftCallback;
377 	EventHandlerWrapper _userPanEndRightCallback;
378 
379 	bool _pannable;
380 	bool _leftEdge;
381 	bool _rightEdge;
382 	bool _heroBeltEnabled;
383 	int _edgeStartTime;
384 	Common::String _smkPath;
385 	Common::String _podPath;
386 
387 	Common::SharedPtr<Video::SmackerDecoder> _videoDecoder;
388 	Common::Point _viewportOffset;
389 	Common::Array<Animation> _anims;
390 	EventHandlerWrapper _videoDecoderEndEvent;
391 	int _videoZ;
392 	Common::SharedPtr<PodFile> _podFile;
393 	Common::HashMap<Common::String, Common::Array<int> > _fontWidths;
394 	Common::Queue<SubtitleLine> _subtitles;
395 	Common::HashMap<int, int> _countQueuedSubtitles;
396 	TextTable _assetMap;
397 	bool _mouseEnabled;
398 
399 	int _finalFade, _finalFadeSpeed;
400 	EventHandlerWrapper _finalFadeCallback;
401 };
402 
403 static const int kVideoWidth = 640;
404 static const int kVideoHeight = 480;
405 #define kOffsetRightRoom (Common::Point(kVideoWidth, 0))
406 #define kZeroPoint (Common::Point(10, 50))
407 
408 struct PrePoint {
409 	int x, y;
410 
getPrePoint411 	Common::Point get() const {
412 		return Common::Point(x, y);
413 	}
414 };
415 
416 }
417 #endif
418