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/debug.h"
24 #include "common/stream.h"
25 
26 #include "petka/base.h"
27 #include "petka/big_dialogue.h"
28 #include "petka/interfaces/main.h"
29 #include "petka/interfaces/dialog_interface.h"
30 #include "petka/objects/heroes.h"
31 #include "petka/q_system.h"
32 #include "petka/petka.h"
33 
34 namespace Petka {
35 
36 const uint16 kFallback = 0xFFFE;
37 
BigDialogue(PetkaEngine & vm)38 BigDialogue::BigDialogue(PetkaEngine &vm)
39 	: _vm(vm) {
40 	_currOp = nullptr;
41 	_startOpIndex = 0;
42 
43 	Common::ScopedPtr<Common::SeekableReadStream> file(vm.openFile("dialogue.fix", true));
44 	if (!file)
45 		return;
46 
47 	_objDialogs.resize(file->readUint32LE());
48 	for (uint i = 0; i < _objDialogs.size(); ++i) {
49 		_objDialogs[i].objId = file->readUint32LE();
50 		_objDialogs[i].handlers.resize(file->readUint32LE());
51 		file->skip(4); // pointer
52 	}
53 	for (uint i = 0; i < _objDialogs.size(); ++i) {
54 		for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
55 			_objDialogs[i].handlers[j].opcode = file->readUint32LE();
56 			_objDialogs[i].handlers[j].dialogs.resize(file->readUint32LE());
57 			_objDialogs[i].handlers[j].startDialogIndex = file->readUint32LE();
58 			file->skip(4); // pointer
59 		}
60 		for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
61 			for (uint z = 0; z < _objDialogs[i].handlers[j].dialogs.size(); ++z) {
62 				_objDialogs[i].handlers[j].dialogs[z].startOpIndex = file->readUint32LE();
63 				file->skip(4 + 4); // opsCount + pointer
64 			}
65 		}
66 	}
67 
68 	load(file.get());
69 }
70 
loadSpeechesInfo()71 void BigDialogue::loadSpeechesInfo() {
72 	if (!_speeches.empty())
73 		return;
74 
75 	Common::ScopedPtr<Common::SeekableReadStream> file(_vm.openFile("dialogue.lod", true));
76 	if (!file)
77 		return;
78 
79 	_speeches.resize(file->readUint32LE());
80 	for (uint i = 0; i < _speeches.size(); ++i) {
81 		_speeches[i].speakerId = file->readUint32LE();
82 		file->read(_speeches[i].soundName, sizeof(_speeches[i].soundName));
83 		file->skip(4);
84 	}
85 
86 	char *str = new char[file->size() - file->pos()];
87 	char *curr = str;
88 	file->read(str, file->size() - file->pos());
89 	for (uint i = 0; i < _speeches.size(); ++i) {
90 		_speeches[i].text = Common::convertToU32String(curr, Common::kWindows1251);
91 		curr += strlen(curr) + 1;
92 	}
93 	delete[] str;
94 }
95 
getSpeechInfo(int * talkerId,const char ** soundName,int choice)96 const Common::U32String *BigDialogue::getSpeechInfo(int *talkerId, const char **soundName, int choice) {
97 	if (!_currOp)
98 		return nullptr;
99 	switch (_currOp->type) {
100 	case kOperationMenu: {
101 		Operation *menuOp = _currOp;
102 		uint bit = 1;
103 		if (_currOp->menu.bits <= choice || choice < 0) {
104 			break;
105 		}
106 		while (true) {
107 			_currOp += 1;
108 			if (choice == 0 && (bit & menuOp->menu.bitField))
109 				break;
110 			if (_currOp->type == kOperationBreak) {
111 				if (bit & menuOp->menu.bitField)
112 					choice--;
113 				bit *= 2;
114 			}
115 		}
116 		if (_currOp->type != kOperationPlay)
117 			next();
118 		if (_currOp->type != kOperationPlay) {
119 			_currOp = menuOp;
120 			break;
121 		}
122 
123 		uint index = _currOp->play.messageIndex;
124 		_currOp = menuOp;
125 		if (soundName)
126 			*soundName = _speeches[index].soundName;
127 		*talkerId = _speeches[index].speakerId;
128 		return &_speeches[index].text;
129 	}
130 	case kOperationCircle:
131 		circleMoveTo(_currOp->circle.curr);
132 		assert(_currOp->type == kOperationPlay);
133 		// fall through
134 	case kOperationPlay:
135 		if (soundName)
136 			*soundName = _speeches[_currOp->play.messageIndex].soundName;
137 		*talkerId = _speeches[_currOp->play.messageIndex].speakerId;
138 		return &_speeches[_currOp->play.messageIndex].text;
139 	default:
140 		break;
141 	}
142 	return nullptr;
143 }
144 
findHandler(uint objId,uint opcode,bool * fallback) const145 const DialogHandler *BigDialogue::findHandler(uint objId, uint opcode, bool *fallback) const {
146 	if (opcode == kEnd || opcode == kHalf) {
147 		return nullptr;
148 	}
149 	if (fallback) {
150 		*fallback = false;
151 	}
152 	for (uint i = 0; i < _objDialogs.size(); ++i) {
153 		if (_objDialogs[i].objId != objId) {
154 			continue;
155 		}
156 
157 		for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
158 			if (_objDialogs[i].handlers[j].opcode == opcode) {
159 				return &_objDialogs[i].handlers[j];
160 			}
161 		}
162 
163 		if ((uint16)opcode != kObjectUse) {
164 			continue;
165 		}
166 
167 		for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
168 			if (_objDialogs[i].handlers[j].opcode == kFallback) {
169 				if (fallback)
170 					*fallback = true;
171 				return &_objDialogs[i].handlers[j];
172 			}
173 		}
174 	}
175 	for (uint i = 0; i < _objDialogs.size(); ++i) {
176 		if (_objDialogs[i].objId != kFallback)
177 			continue;
178 		for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
179 			if (_objDialogs[i].handlers[j].opcode == opcode) {
180 				if (fallback)
181 					*fallback = true;
182 				return &_objDialogs[i].handlers[j];
183 			}
184 		}
185 	}
186 	return nullptr;
187 }
188 
setHandler(uint objId,uint opcode)189 void BigDialogue::setHandler(uint objId, uint opcode) {
190 	loadSpeechesInfo();
191 	const DialogHandler *h = findHandler(objId, opcode, nullptr);
192 	if (h) {
193 		_startOpIndex = h->dialogs[h->startDialogIndex].startOpIndex;
194 		_currOp = &_ops[_startOpIndex];
195 	}
196 }
197 
opcode()198 uint BigDialogue::opcode() {
199 	while (_currOp) {
200 		switch (_currOp->type) {
201 		case kOperationMenu:
202 			if (choicesCount() > 1)
203 				return kOpcodeMenu;
204 			next(0);
205 			break;
206 		case kOperationReturn:
207 			return kOpcodeEnd;
208 		case kOperationPlay:
209 		case kOperationCircle:
210 			return kOpcodePlay;
211 		case kOperationUserMessage:
212 			return kOpcodeUserMessage;
213 		default:
214 			next();
215 			break;
216 		}
217 	}
218 	return kOpcodeEnd;
219 }
220 
load(Common::ReadStream * s)221 void BigDialogue::load(Common::ReadStream *s) {
222 	uint32 opsCount = s->readUint32LE();
223 	_ops.resize(opsCount);
224 	for (uint i = 0; i < opsCount; ++i) {
225 		uint op = s->readUint32LE();
226 		_ops[i].type = (byte)(op >> 24);
227 		switch (_ops[i].type) {
228 		case kOperationBreak:
229 			break;
230 		case kOperationMenu:
231 			_ops[i].menu.bits = (byte)op;
232 			_ops[i].menu.bitField = (uint16)(op >> 8);
233 			break;
234 		case kOperationGoTo:
235 			_ops[i].goTo.opIndex = (uint16)(op);
236 			break;
237 		case kOperationDisableMenuItem:
238 			_ops[i].disableMenuItem.opIndex = (uint16)op;
239 			_ops[i].disableMenuItem.bit = (byte)(op >> 16);
240 			break;
241 		case kOperationEnableMenuItem:
242 			_ops[i].enableMenuItem.opIndex = (uint16)op;
243 			_ops[i].enableMenuItem.bit = (byte)(op >> 16);
244 			break;
245 		case kOperationReturn:
246 			break;
247 		case kOperationPlay:
248 			_ops[i].play.messageIndex = (uint16)op;
249 			break;
250 		case kOperationCircle:
251 			_ops[i].circle.count = (uint16)op;
252 			_ops[i].circle.curr = (byte)(op >> 16);
253 			break;
254 		case kOperationUserMessage:
255 			_ops[i].userMsg.arg = (uint16)op;
256 			break;
257 		default:
258 			break;
259 		}
260 	}
261 }
262 
save(Common::WriteStream * s)263 void BigDialogue::save(Common::WriteStream *s) {
264 	s->writeUint32LE(_ops.size());
265 	for (uint i = 0; i < _ops.size(); ++i) {
266 		switch (_ops[i].type) {
267 		case kOperationBreak:
268 			s->writeUint32LE(MKTAG(kOperationBreak, 0, 0, 0));
269 			break;
270 		case kOperationMenu:
271 			s->writeByte(_ops[i].menu.bits);
272 			s->writeUint16LE(_ops[i].menu.bitField);
273 			s->writeByte(kOperationMenu);
274 			break;
275 		case kOperationGoTo:
276 			s->writeUint16LE(_ops[i].goTo.opIndex);
277 			s->writeUint16LE(MKTAG16(kOperationGoTo, 0));
278 			break;
279 		case kOperationDisableMenuItem:
280 			s->writeUint16LE(_ops[i].disableMenuItem.opIndex);
281 			s->writeUint16LE(MKTAG16(kOperationDisableMenuItem, _ops[i].disableMenuItem.bit));
282 			break;
283 		case kOperationEnableMenuItem:
284 			s->writeUint16LE(_ops[i].enableMenuItem.opIndex);
285 			s->writeUint16LE(MKTAG16(kOperationEnableMenuItem, _ops[i].enableMenuItem.bit));
286 			break;
287 		case kOperationReturn:
288 			s->writeUint32LE(MKTAG(kOperationReturn, 0, 0, 0));
289 			break;
290 		case kOperationPlay:
291 			s->writeUint16LE(_ops[i].play.messageIndex);
292 			s->writeUint16LE(MKTAG16(kOperationPlay, 0));
293 			break;
294 		case kOperationCircle:
295 			s->writeUint16LE(_ops[i].circle.count);
296 			s->writeUint16LE(MKTAG16(kOperationCircle, _ops[i].circle.curr));
297 			break;
298 		case kOperationUserMessage:
299 			s->writeUint16LE(_ops[i].userMsg.arg);
300 			s->writeUint16LE(MKTAG16(kOperationUserMessage, 0));
301 			break;
302 		default:
303 			break;
304 		}
305 	}
306 }
307 
next(int choice)308 void BigDialogue::next(int choice) {
309 	bool processed = true;
310 
311 	if (!_currOp)
312 		return;
313 	if (choice != -1 && _currOp->type != kOperationMenu) {
314 		choice = -1;
315 	}
316 
317 	while (true) {
318 		switch (_currOp->type) {
319 		case kOperationBreak:
320 			while (_currOp->type != kOperationMenu && _currOp->type != kOperationCircle) {
321 				_currOp--;
322 			}
323 			next(choice);
324 			return;
325 		case kOperationMenu: {
326 			if (!processed)
327 				return;
328 			if (choice == -1)
329 				choice = 0;
330 			if (_currOp->menu.bits <= choice)
331 				choice = _currOp->menu.bits - 1;
332 			uint16 bitField = _currOp->menu.bitField;
333 			uint bits = _currOp->menu.bits;
334 			uint bit = 1;
335 			uint i = 0;
336 			while (bits) {
337 				if (_currOp->type == kOperationBreak) {
338 					bits--;
339 					if (!(bit & bitField) && i <= (uint)choice)
340 						choice++;
341 					bit *= 2;
342 					i++;
343 				}
344 				_currOp += 1;
345 			}
346 			_currOp += choice;
347 			processed = false;
348 			break;
349 		}
350 		case kOperationGoTo: {
351 			_currOp = &_ops[_currOp->goTo.opIndex];
352 			processed = false;
353 			break;
354 		}
355 		case kOperationDisableMenuItem:
356 			_ops[_currOp->disableMenuItem.opIndex].menu.bitField &= ~(1 << _currOp->disableMenuItem.bit); // disable menu item
357 			checkMenu(_startOpIndex);
358 			_currOp += 1;
359 			processed = false;
360 			break;
361 		case kOperationEnableMenuItem:
362 			_ops[_currOp->enableMenuItem.opIndex].menu.bitField |= (1 << _currOp->enableMenuItem.bit);
363 			_currOp += 1;
364 			processed = false;
365 			break;
366 		case kOperationReturn:
367 			return;
368 		case kOperationPlay:
369 			if (!processed)
370 				return;
371 			_currOp += 1;
372 			processed = false;
373 			break;
374 		case kOperationCircle:
375 			if (!processed)
376 				return;
377 			_currOp->circle.curr = (byte)((_currOp->circle.curr + 1) % _currOp->circle.count);
378 			circleMoveTo(_currOp->circle.count);
379 			processed = false;
380 			break;
381 		case kOperationUserMessage:
382 			if (processed)
383 				_currOp += 1;
384 			else {
385 				_vm.getQSystem()->_mainInterface->_dialog.startUserMsg(_currOp->userMsg.arg);
386 			}
387 			return;
388 		default:
389 			_currOp += 1;
390 			processed = false;
391 			break;
392 		}
393 	}
394 
395 }
396 
choicesCount()397 uint BigDialogue::choicesCount() {
398 	if (!_currOp || _currOp->type != kOperationMenu)
399 		return 0;
400 	uint count = 0;
401 	uint bit = 1;
402 	for (uint i = 0; i < _currOp->menu.bits; ++i) {
403 		if (_currOp->menu.bitField & bit) {
404 			count++;
405 		}
406 		bit <<= 1;
407 	}
408 	return count;
409 }
410 
findOperation(uint index,uint opType,uint * resIndex)411 bool BigDialogue::findOperation(uint index, uint opType, uint *resIndex) {
412 	while (_ops[index].type != opType) {
413 		switch(_ops[index].type) {
414 		case kOperationGoTo:
415 			if (index >= _ops[index].goTo.opIndex)
416 				return false;
417 			index = _ops[index].goTo.opIndex;
418 			break;
419 		case kOperationReturn:
420 			return false;
421 		default:
422 			index++;
423 			break;
424 		}
425 	}
426 	*resIndex = index;
427 	return true;
428 }
429 
checkMenu(uint menuIndex)430 bool BigDialogue::checkMenu(uint menuIndex) {
431 	if (_ops[menuIndex].type != kOperationMenu && !findOperation(menuIndex, kOperationMenu, &menuIndex)) {
432 		return true;
433 	}
434 
435 	uint count = 0;
436 	uint bit = 1;
437 	uint opIndex = menuIndex + 1;
438 	for (uint i = 0; i < _ops[menuIndex].menu.bits; ++i) {
439 		if (_ops[menuIndex].menu.bitField & bit) {
440 			count++;
441 		}
442 		findOperation(opIndex, kOperationBreak, &opIndex);
443 		opIndex++;
444 		bit <<= 1;
445 	}
446 
447 	if (!count)
448 		return false;
449 
450 	bit = 1;
451 	for (uint i = 0; i < _ops[menuIndex].menu.bits; ++i) {
452 		uint subMenuIndex;
453 		if ((_ops[menuIndex].menu.bitField & bit) && findOperation(_ops[opIndex + i].goTo.opIndex, kOperationMenu, &subMenuIndex) && !checkMenu(subMenuIndex)) {
454 			_ops[menuIndex].menu.bitField &= ~bit;
455 			count--;
456 			if (count < 1)
457 				return false;
458 		}
459 		bit <<= 1;
460 	}
461 	return true;
462 }
463 
getMenuChoices(Common::Array<Common::U32String> & choices)464 void BigDialogue::getMenuChoices(Common::Array<Common::U32String> &choices) {
465 	uint count = choicesCount();
466 	for (uint i = 0; i < count; ++i) {
467 		int id;
468 		choices.push_back(*getSpeechInfo(&id, nullptr, i));
469 	}
470 }
471 
circleMoveTo(byte index)472 void BigDialogue::circleMoveTo(byte index) {
473 	_currOp += 1;
474 	for (uint i = 0; i < index; ++i) {
475 		while (_currOp->type != kOperationBreak)
476 			_currOp += 1;
477 		_currOp += 1;
478 	}
479 }
480 
481 } // End of namespace Petka
482