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 #include "common/config-manager.h"
24 #include "common/debug-channels.h"
25 #include "common/error.h"
26 #include "common/events.h"
27 #include "common/ini-file.h"
28 #include "common/stream.h"
29 #include "common/system.h"
30 #include "common/file.h"
31 
32 #include "engines/advancedDetector.h"
33 #include "engines/util.h"
34 
35 #include "graphics/surface.h"
36 
37 #include "video/avi_decoder.h"
38 
39 #include "petka/file_mgr.h"
40 #include "petka/video.h"
41 #include "petka/sound.h"
42 #include "petka/petka.h"
43 #include "petka/q_manager.h"
44 #include "petka/interfaces/interface.h"
45 #include "petka/q_system.h"
46 #include "petka/big_dialogue.h"
47 
48 namespace Petka {
49 
50 PetkaEngine *g_vm;
51 
PetkaEngine(OSystem * system,const ADGameDescription * desc)52 PetkaEngine::PetkaEngine(OSystem *system, const ADGameDescription *desc)
53 	: Engine(system), _console(nullptr), _fileMgr(nullptr), _resMgr(nullptr),
54 	_qsystem(nullptr), _vsys(nullptr), _desc(desc), _videoDec(nullptr), _rnd("petka") {
55 
56 	_part = 0xFF;
57 	_chapter = 0;
58 	_shouldChangePart = false;
59 	_nextPart = 0;
60 	_saveSlot = -1;
61 	g_vm = this;
62 
63 	debug("PetkaEngine::ctor");
64 }
65 
~PetkaEngine()66 PetkaEngine::~PetkaEngine() {
67 	debug("PetkaEngine::dtor");
68 }
69 
run()70 Common::Error PetkaEngine::run() {
71 	debug("PetkaEngine::run");
72 	const Graphics::PixelFormat format(2, 5, 6, 5, 0, 11, 5, 0, 0);
73 	initGraphics(640, 480, &format);
74 
75 	const char *const videos[] = {"buka.avi", "skif.avi", "adv.avi"};
76 	for (uint i = 0; i < sizeof(videos) / sizeof(char *); ++i) {
77 		Common::ScopedPtr<Common::File> file(new Common::File);
78 		if (file->open(videos[i])) {
79 			playVideo(file.release());
80 		} else {
81 			debugC(kPetkaDebugResources, "Video file %s can't be opened", videos[i]);
82 		}
83 	}
84 
85 	_console.reset(new Console(this));
86 	_fileMgr.reset(new FileMgr());
87 	_soundMgr.reset(new SoundMgr(*this));
88 	_vsys.reset(new VideoSystem(*this));
89 	_resMgr.reset(new QManager(*this));
90 
91 	loadPart(isDemo() ? 1 : 0);
92 
93 	if (ConfMan.hasKey("save_slot")) {
94 		loadGameState(ConfMan.getInt("save_slot"));
95 	}
96 
97 	while (!shouldQuit()) {
98 		Common::Event event;
99 		while (_eventMan->pollEvent(event)) {
100 			switch (event.type) {
101 			case Common::EVENT_QUIT:
102 			case Common::EVENT_RETURN_TO_LAUNCHER:
103 				return Common::kNoError;
104 			default:
105 				_qsystem->onEvent(event);
106 				break;
107 			}
108 		}
109 		_qsystem->update();
110 
111 		if (_shouldChangePart) {
112 			loadPart(_nextPart);
113 			if (_saveSlot != -1)
114 				loadGameState(_saveSlot);
115 			_saveSlot = -1;
116 			_shouldChangePart = false;
117 			_vsys->makeAllDirty();
118 		}
119 
120 		_vsys->update();
121 		_system->delayMillis(20);
122 	}
123 	return Common::kNoError;
124 }
125 
openFile(const Common::String & name,bool addCurrentPath)126 Common::SeekableReadStream *PetkaEngine::openFile(const Common::String &name, bool addCurrentPath) {
127 	if (name.empty()) {
128 		return nullptr;
129 	}
130 	return _fileMgr->getFileStream(addCurrentPath ? _currentPath + name : name);
131 }
132 
loadStores()133 void PetkaEngine::loadStores() {
134 	debug("PetkaEngine::loadStores");
135 	_fileMgr->closeAll();
136 
137 	_fileMgr->openStore("patch.str");
138 	_fileMgr->openStore("main.str");
139 
140 	Common::INIFile parts;
141 	Common::ScopedPtr<Common::SeekableReadStream> stream(_fileMgr->getFileStream("PARTS.INI"));
142 
143 	if (!stream || !parts.loadFromStream(*stream)) {
144 		debugC(kPetkaDebugResources, "PARTS.INI opening failed");
145 		return;
146 	}
147 
148 	const char *const names[] = {"Background", "Flics", "Wav", "SFX", "Music", "Speech"};
149 	const Common::String section = Common::String::format("Part %d", _part);
150 
151 	parts.getKey("CurrentPath", section, _currentPath);
152 	parts.getKey("PathSpeech", section, _speechPath);
153 
154 	Common::String storeName;
155 	for (uint i = 0; i < sizeof(names) / sizeof(char *); ++i) {
156 		parts.getKey(names[i], section, storeName);
157 		_fileMgr->openStore(storeName);
158 	}
159 
160 	parts.getKey("Chapter", Common::String::format("Part %d Chapter %d", _part, _chapter), _chapterStoreName);
161 	_fileMgr->openStore(_chapterStoreName);
162 }
163 
getQSystem() const164 QSystem *PetkaEngine::getQSystem() const {
165 	return _qsystem.get();
166 }
167 
getRnd()168 Common::RandomSource &PetkaEngine::getRnd() {
169 	return _rnd;
170 }
171 
playVideo(Common::SeekableReadStream * stream)172 void PetkaEngine::playVideo(Common::SeekableReadStream *stream) {
173 	PauseToken token = pauseEngine();
174 	Graphics::PixelFormat fmt = _system->getScreenFormat();
175 
176 	_videoDec.reset(new Video::AVIDecoder);
177 	if (!_videoDec->loadStream(stream)) {
178 		_videoDec.reset();
179 		return;
180 	}
181 
182 	_videoDec->start();
183 
184 	while (!_videoDec->endOfVideo() && !shouldQuit()) {
185 		Common::Event event;
186 		while (_eventMan->pollEvent(event)) {
187 			switch (event.type) {
188 			case Common::EVENT_RETURN_TO_LAUNCHER:
189 			case Common::EVENT_QUIT:
190 			case Common::EVENT_LBUTTONDOWN:
191 			case Common::EVENT_RBUTTONDOWN:
192 			case Common::EVENT_KEYDOWN:
193 				_videoDec.reset();
194 				return;
195 			default:
196 				break;
197 			}
198 		}
199 
200 		if (_videoDec->needsUpdate()) {
201 			const Graphics::Surface *frame = _videoDec->decodeNextFrame();
202 			if (frame) {
203 				Common::ScopedPtr<Graphics::Surface, Graphics::SurfaceDeleter> f(frame->convertTo(fmt));
204 				_system->copyRectToScreen(f->getPixels(), f->pitch, 0, 0, f->w, f->h);
205 			}
206 		}
207 
208 		_system->updateScreen();
209 		_system->delayMillis(15);
210 	}
211 
212 	_videoDec.reset();
213 }
214 
isDemo() const215 bool PetkaEngine::isDemo() const {
216 	return _desc->flags & ADGF_DEMO;
217 }
218 
isPetka2() const219 bool PetkaEngine::isPetka2() const {
220 	return strcmp(_desc->gameId, "petka2") == 0;
221 }
222 
soundMgr() const223 SoundMgr *PetkaEngine::soundMgr() const {
224 	return _soundMgr.get();
225 }
226 
resMgr() const227 QManager *PetkaEngine::resMgr() const {
228 	return _resMgr.get();
229 }
230 
videoSystem() const231 VideoSystem *PetkaEngine::videoSystem() const {
232 	return _vsys.get();
233 }
234 
getPart()235 byte PetkaEngine::getPart() {
236 	return _part;
237 }
238 
loadPart(byte part)239 void PetkaEngine::loadPart(byte part) {
240 	debug("PetkaEngine::loadPart %d", part);
241 	_part = part;
242 
243 	_soundMgr->removeAll();
244 	loadStores();
245 
246 	_resMgr.reset(new QManager(*this));
247 	_resMgr->init();
248 	_dialogMan.reset(new BigDialogue(*this));
249 	_qsystem.reset(new QSystem(*this));
250 	_qsystem->init();
251 }
252 
loadPartAtNextFrame(byte part)253 void PetkaEngine::loadPartAtNextFrame(byte part) {
254 	_shouldChangePart = true;
255 	_nextPart = part;
256 	_chapter = 1;
257 	_saveSlot = -1;
258 }
259 
loadChapter(byte chapter)260 void PetkaEngine::loadChapter(byte chapter) {
261 	Common::INIFile parts;
262 	Common::ScopedPtr<Common::SeekableReadStream> stream(_fileMgr->getFileStream("PARTS.INI"));
263 
264 	if (!stream || !parts.loadFromStream(*stream)) {
265 		debugC(kPetkaDebugResources, "PARTS.INI opening failed");
266 		return;
267 	}
268 
269 	_fileMgr->closeStore(_chapterStoreName);
270 
271 	const Common::String section = Common::String::format("Part %d Chapter %d", _part, chapter);
272 	parts.getKey("Chapter", section, _chapterStoreName);
273 	if (_chapterStoreName.empty())
274 		return;
275 
276 	_fileMgr->openStore(_chapterStoreName);
277 
278 	Common::ScopedPtr<Common::SeekableReadStream> namesStream(openFile("Names.ini", true));
279 	Common::ScopedPtr<Common::SeekableReadStream> castStream(openFile("Cast.ini", true));
280 
281 	Common::INIFile namesIni;
282 	Common::INIFile castIni;
283 
284 	namesIni.allowNonEnglishCharacters();
285 	castIni.allowNonEnglishCharacters();
286 
287 	if (namesStream)
288 		namesIni.loadFromStream(*namesStream);
289 	if (castStream)
290 		castIni.loadFromStream(*castStream);
291 
292 	for (uint i = 0; i < _qsystem->_allObjects.size(); ++i) {
293 		QMessageObject *obj = _qsystem->_allObjects[i];
294 		obj->readInisData(namesIni, castIni, nullptr);
295 	}
296 	_chapter = chapter;
297 }
298 
getBigDialogue() const299 BigDialogue *PetkaEngine::getBigDialogue() const {
300 	return _dialogMan.get();
301 }
302 
getSpeechPath()303 const Common::String &PetkaEngine::getSpeechPath() {
304 	return _speechPath;
305 }
306 
hasFeature(EngineFeature f) const307 bool PetkaEngine::hasFeature(EngineFeature f) const {
308 	return
309 		f == kSupportsReturnToLauncher ||
310 		f == kSupportsLoadingDuringRuntime ||
311 		f == kSupportsSavingDuringRuntime ||
312 		f == kSupportsChangingOptionsDuringRuntime;
313 }
314 
pauseEngineIntern(bool pause)315 void PetkaEngine::pauseEngineIntern(bool pause) {
316 	if (!pause && _vsys)
317 		_vsys->updateTime();
318 
319 	if (_videoDec)
320 		_videoDec->pauseVideo(pause);
321 
322 	Engine::pauseEngineIntern(pause);
323 }
324 
325 } // End of namespace Petka
326