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 /*
24  * This file is based on WME Lite.
25  * http://dead-code.org/redir.php?target=wmelite
26  * Copyright (c) 2011 Jan Nedoma
27  */
28 
29 #include "engines/wintermute/base/base_game.h"
30 #include "engines/wintermute/ad/ad_layer.h"
31 #include "engines/wintermute/ad/ad_scene_node.h"
32 #include "engines/wintermute/base/base_dynamic_buffer.h"
33 #include "engines/wintermute/base/base_file_manager.h"
34 #include "engines/wintermute/base/base_parser.h"
35 #include "engines/wintermute/base/scriptables/script_value.h"
36 #include "engines/wintermute/base/scriptables/script.h"
37 #include "engines/wintermute/base/scriptables/script_stack.h"
38 #include "engines/wintermute/platform_osystem.h"
39 #include "common/str.h"
40 
41 namespace Wintermute {
42 
IMPLEMENT_PERSISTENT(AdLayer,false)43 IMPLEMENT_PERSISTENT(AdLayer, false)
44 
45 //////////////////////////////////////////////////////////////////////////
46 AdLayer::AdLayer(BaseGame *inGame) : BaseObject(inGame) {
47 	_main = false;
48 	_width = _height = 0;
49 	_active = true;
50 	_closeUp = false;
51 }
52 
53 
54 //////////////////////////////////////////////////////////////////////////
~AdLayer()55 AdLayer::~AdLayer() {
56 	for (uint32 i = 0; i < _nodes.size(); i++) {
57 		delete _nodes[i];
58 	}
59 	_nodes.clear();
60 }
61 
62 
63 //////////////////////////////////////////////////////////////////////////
loadFile(const char * filename)64 bool AdLayer::loadFile(const char *filename) {
65 	char *buffer = (char *)BaseFileManager::getEngineInstance()->readWholeFile(filename);
66 	if (buffer == nullptr) {
67 		_gameRef->LOG(0, "AdLayer::LoadFile failed for file '%s'", filename);
68 		return STATUS_FAILED;
69 	}
70 
71 	bool ret;
72 
73 	setFilename(filename);
74 
75 	if (DID_FAIL(ret = loadBuffer(buffer, true))) {
76 		_gameRef->LOG(0, "Error parsing LAYER file '%s'", filename);
77 	}
78 
79 	delete[] buffer;
80 
81 	return ret;
82 }
83 
84 
85 TOKEN_DEF_START
TOKEN_DEF(LAYER)86 TOKEN_DEF(LAYER)
87 TOKEN_DEF(TEMPLATE)
88 TOKEN_DEF(NAME)
89 TOKEN_DEF(WIDTH)
90 TOKEN_DEF(HEIGHT)
91 TOKEN_DEF(MAIN)
92 TOKEN_DEF(ENTITY)
93 TOKEN_DEF(REGION)
94 TOKEN_DEF(ACTIVE)
95 TOKEN_DEF(EDITOR_SELECTED)
96 TOKEN_DEF(SCRIPT)
97 TOKEN_DEF(CAPTION)
98 TOKEN_DEF(PROPERTY)
99 TOKEN_DEF(CLOSE_UP)
100 TOKEN_DEF(EDITOR_PROPERTY)
101 TOKEN_DEF_END
102 //////////////////////////////////////////////////////////////////////////
103 bool AdLayer::loadBuffer(char *buffer, bool complete) {
104 	TOKEN_TABLE_START(commands)
105 	TOKEN_TABLE(LAYER)
106 	TOKEN_TABLE(TEMPLATE)
107 	TOKEN_TABLE(NAME)
108 	TOKEN_TABLE(WIDTH)
109 	TOKEN_TABLE(HEIGHT)
110 	TOKEN_TABLE(MAIN)
111 	TOKEN_TABLE(ENTITY)
112 	TOKEN_TABLE(REGION)
113 	TOKEN_TABLE(ACTIVE)
114 	TOKEN_TABLE(EDITOR_SELECTED)
115 	TOKEN_TABLE(SCRIPT)
116 	TOKEN_TABLE(CAPTION)
117 	TOKEN_TABLE(PROPERTY)
118 	TOKEN_TABLE(CLOSE_UP)
119 	TOKEN_TABLE(EDITOR_PROPERTY)
120 	TOKEN_TABLE_END
121 
122 	char *params;
123 	int cmd;
124 	BaseParser parser;
125 
126 	if (complete) {
127 		if (parser.getCommand(&buffer, commands, &params) != TOKEN_LAYER) {
128 			_gameRef->LOG(0, "'LAYER' keyword expected.");
129 			return STATUS_FAILED;
130 		}
131 		buffer = params;
132 	}
133 
134 	while ((cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
135 		switch (cmd) {
136 		case TOKEN_TEMPLATE:
137 			if (DID_FAIL(loadFile(params))) {
138 				cmd = PARSERR_GENERIC;
139 			}
140 			break;
141 
142 		case TOKEN_NAME:
143 			setName(params);
144 			break;
145 
146 		case TOKEN_CAPTION:
147 			setCaption(params);
148 			break;
149 
150 		case TOKEN_MAIN:
151 			parser.scanStr(params, "%b", &_main);
152 			break;
153 
154 		case TOKEN_CLOSE_UP:
155 			parser.scanStr(params, "%b", &_closeUp);
156 			break;
157 
158 		case TOKEN_WIDTH:
159 			parser.scanStr(params, "%d", &_width);
160 			break;
161 
162 		case TOKEN_HEIGHT:
163 			parser.scanStr(params, "%d", &_height);
164 			break;
165 
166 		case TOKEN_ACTIVE:
167 			parser.scanStr(params, "%b", &_active);
168 			break;
169 
170 		case TOKEN_REGION: {
171 			AdRegion *region = new AdRegion(_gameRef);
172 			AdSceneNode *node = new AdSceneNode(_gameRef);
173 			if (!region || !node || DID_FAIL(region->loadBuffer(params, false))) {
174 				cmd = PARSERR_GENERIC;
175 				delete region;
176 				delete node;
177 				region = nullptr;
178 				node = nullptr;
179 			} else {
180 				node->setRegion(region);
181 				_nodes.add(node);
182 			}
183 		}
184 		break;
185 
186 		case TOKEN_ENTITY: {
187 			AdEntity *entity = new AdEntity(_gameRef);
188 			AdSceneNode *node = new AdSceneNode(_gameRef);
189 			if (entity) {
190 				entity->_zoomable = false;    // scene entites default to NOT zoom
191 			}
192 			if (!entity || !node || DID_FAIL(entity->loadBuffer(params, false))) {
193 				cmd = PARSERR_GENERIC;
194 				delete entity;
195 				delete node;
196 				entity = nullptr;
197 				node = nullptr;
198 			} else {
199 				node->setEntity(entity);
200 				_nodes.add(node);
201 			}
202 		}
203 		break;
204 
205 		case TOKEN_EDITOR_SELECTED:
206 			parser.scanStr(params, "%b", &_editorSelected);
207 			break;
208 
209 		case TOKEN_SCRIPT:
210 			addScript(params);
211 			break;
212 
213 		case TOKEN_PROPERTY:
214 			parseProperty(params, false);
215 			break;
216 
217 		case TOKEN_EDITOR_PROPERTY:
218 			parseEditorProperty(params, false);
219 			break;
220 
221 		default:
222 			break;
223 		}
224 	}
225 	if (cmd == PARSERR_TOKENNOTFOUND) {
226 		_gameRef->LOG(0, "Syntax error in LAYER definition");
227 		return STATUS_FAILED;
228 	}
229 
230 	return STATUS_OK;
231 }
232 
233 
234 //////////////////////////////////////////////////////////////////////////
235 // high level scripting interface
236 //////////////////////////////////////////////////////////////////////////
scCallMethod(ScScript * script,ScStack * stack,ScStack * thisStack,const char * name)237 bool AdLayer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
238 	//////////////////////////////////////////////////////////////////////////
239 	// GetNode
240 	//////////////////////////////////////////////////////////////////////////
241 	if (strcmp(name, "GetNode") == 0) {
242 		stack->correctParams(1);
243 		ScValue *val = stack->pop();
244 		int node = -1;
245 
246 		if (val->_type == VAL_INT) {
247 			node = val->getInt();
248 		} else { // get by name
249 			for (uint32 i = 0; i < _nodes.size(); i++) {
250 				if ((_nodes[i]->_type == OBJECT_ENTITY && scumm_stricmp(_nodes[i]->_entity->getName(), val->getString()) == 0) ||
251 				        (_nodes[i]->_type == OBJECT_REGION && scumm_stricmp(_nodes[i]->_region->getName(), val->getString()) == 0)) {
252 					node = i;
253 					break;
254 				}
255 			}
256 		}
257 
258 		if (node < 0 || node >= (int32)_nodes.size()) {
259 			stack->pushNULL();
260 		} else {
261 			switch (_nodes[node]->_type) {
262 			case OBJECT_ENTITY:
263 				stack->pushNative(_nodes[node]->_entity, true);
264 				break;
265 			case OBJECT_REGION:
266 				stack->pushNative(_nodes[node]->_region, true);
267 				break;
268 			default:
269 				stack->pushNULL();
270 			}
271 		}
272 		return STATUS_OK;
273 	}
274 
275 	//////////////////////////////////////////////////////////////////////////
276 	// AddRegion / AddEntity
277 	//////////////////////////////////////////////////////////////////////////
278 	else if (strcmp(name, "AddRegion") == 0 || strcmp(name, "AddEntity") == 0) {
279 		stack->correctParams(1);
280 		ScValue *val = stack->pop();
281 
282 		AdSceneNode *node = new AdSceneNode(_gameRef);
283 		if (strcmp(name, "AddRegion") == 0) {
284 			AdRegion *region = new AdRegion(_gameRef);
285 			if (!val->isNULL()) {
286 				region->setName(val->getString());
287 			}
288 			node->setRegion(region);
289 			stack->pushNative(region, true);
290 		} else {
291 			AdEntity *entity = new AdEntity(_gameRef);
292 			if (!val->isNULL()) {
293 				entity->setName(val->getString());
294 			}
295 			node->setEntity(entity);
296 			stack->pushNative(entity, true);
297 		}
298 		_nodes.add(node);
299 		return STATUS_OK;
300 	}
301 
302 	//////////////////////////////////////////////////////////////////////////
303 	// InsertRegion / InsertEntity
304 	//////////////////////////////////////////////////////////////////////////
305 	else if (strcmp(name, "InsertRegion") == 0 || strcmp(name, "InsertEntity") == 0) {
306 		stack->correctParams(2);
307 		int index = stack->pop()->getInt();
308 		ScValue *val = stack->pop();
309 
310 		AdSceneNode *node = new AdSceneNode(_gameRef);
311 		if (strcmp(name, "InsertRegion") == 0) {
312 			AdRegion *region = new AdRegion(_gameRef);
313 			if (!val->isNULL()) {
314 				region->setName(val->getString());
315 			}
316 			node->setRegion(region);
317 			stack->pushNative(region, true);
318 		} else {
319 			AdEntity *entity = new AdEntity(_gameRef);
320 			if (!val->isNULL()) {
321 				entity->setName(val->getString());
322 			}
323 			node->setEntity(entity);
324 			stack->pushNative(entity, true);
325 		}
326 		if (index < 0) {
327 			index = 0;
328 		}
329 		if (index <= (int32)_nodes.size() - 1) {
330 			_nodes.insert_at(index, node);
331 		} else {
332 			_nodes.add(node);
333 		}
334 
335 		return STATUS_OK;
336 	}
337 
338 	//////////////////////////////////////////////////////////////////////////
339 	// DeleteNode
340 	//////////////////////////////////////////////////////////////////////////
341 	else if (strcmp(name, "DeleteNode") == 0) {
342 		stack->correctParams(1);
343 		ScValue *val = stack->pop();
344 
345 		AdSceneNode *toDelete = nullptr;
346 		if (val->isNative()) {
347 			BaseScriptable *temp = val->getNative();
348 			for (uint32 i = 0; i < _nodes.size(); i++) {
349 				if (_nodes[i]->_region == temp || _nodes[i]->_entity == temp) {
350 					toDelete = _nodes[i];
351 					break;
352 				}
353 			}
354 		} else {
355 			int index = val->getInt();
356 			if (index >= 0 && index < (int32)_nodes.size()) {
357 				toDelete = _nodes[index];
358 			}
359 		}
360 		if (toDelete == nullptr) {
361 			stack->pushBool(false);
362 			return STATUS_OK;
363 		}
364 
365 		for (uint32 i = 0; i < _nodes.size(); i++) {
366 			if (_nodes[i] == toDelete) {
367 				delete _nodes[i];
368 				_nodes[i] = nullptr;
369 				_nodes.remove_at(i);
370 				break;
371 			}
372 		}
373 		stack->pushBool(true);
374 		return STATUS_OK;
375 	} else {
376 		return BaseObject::scCallMethod(script, stack, thisStack, name);
377 	}
378 }
379 
380 
381 //////////////////////////////////////////////////////////////////////////
scGetProperty(const Common::String & name)382 ScValue *AdLayer::scGetProperty(const Common::String &name) {
383 	_scValue->setNULL();
384 
385 	//////////////////////////////////////////////////////////////////////////
386 	// Type
387 	//////////////////////////////////////////////////////////////////////////
388 	if (name == "Type") {
389 		_scValue->setString("layer");
390 		return _scValue;
391 	}
392 
393 	//////////////////////////////////////////////////////////////////////////
394 	// NumNodes (RO)
395 	//////////////////////////////////////////////////////////////////////////
396 	else if (name == "NumNodes") {
397 		_scValue->setInt(_nodes.size());
398 		return _scValue;
399 	}
400 
401 	//////////////////////////////////////////////////////////////////////////
402 	// Width
403 	//////////////////////////////////////////////////////////////////////////
404 	else if (name == "Width") {
405 		_scValue->setInt(_width);
406 		return _scValue;
407 	}
408 
409 	//////////////////////////////////////////////////////////////////////////
410 	// Height
411 	//////////////////////////////////////////////////////////////////////////
412 	else if (name == "Height") {
413 		_scValue->setInt(_height);
414 		return _scValue;
415 	}
416 
417 	//////////////////////////////////////////////////////////////////////////
418 	// Main (RO)
419 	//////////////////////////////////////////////////////////////////////////
420 	else if (name == "Main") {
421 		_scValue->setBool(_main);
422 		return _scValue;
423 	}
424 
425 	//////////////////////////////////////////////////////////////////////////
426 	// CloseUp
427 	//////////////////////////////////////////////////////////////////////////
428 	else if (name == "CloseUp") {
429 		_scValue->setBool(_closeUp);
430 		return _scValue;
431 	}
432 
433 	//////////////////////////////////////////////////////////////////////////
434 	// Active
435 	//////////////////////////////////////////////////////////////////////////
436 	else if (name == "Active") {
437 		_scValue->setBool(_active);
438 		return _scValue;
439 	} else {
440 		return BaseObject::scGetProperty(name);
441 	}
442 }
443 
444 
445 //////////////////////////////////////////////////////////////////////////
scSetProperty(const char * name,ScValue * value)446 bool AdLayer::scSetProperty(const char *name, ScValue *value) {
447 	//////////////////////////////////////////////////////////////////////////
448 	// Name
449 	//////////////////////////////////////////////////////////////////////////
450 	if (strcmp(name, "Name") == 0) {
451 		setName(value->getString());
452 		return STATUS_OK;
453 	}
454 
455 	//////////////////////////////////////////////////////////////////////////
456 	// CloseUp
457 	//////////////////////////////////////////////////////////////////////////
458 	else if (strcmp(name, "CloseUp") == 0) {
459 		_closeUp = value->getBool();
460 		return STATUS_OK;
461 	}
462 
463 	//////////////////////////////////////////////////////////////////////////
464 	// Width
465 	//////////////////////////////////////////////////////////////////////////
466 	else if (strcmp(name, "Width") == 0) {
467 		_width = value->getInt();
468 		if (_width < 0) {
469 			_width = 0;
470 		}
471 		return STATUS_OK;
472 	}
473 
474 	//////////////////////////////////////////////////////////////////////////
475 	// Height
476 	//////////////////////////////////////////////////////////////////////////
477 	else if (strcmp(name, "Height") == 0) {
478 		_height = value->getInt();
479 		if (_height < 0) {
480 			_height = 0;
481 		}
482 		return STATUS_OK;
483 	}
484 
485 	//////////////////////////////////////////////////////////////////////////
486 	// Active
487 	//////////////////////////////////////////////////////////////////////////
488 	else if (strcmp(name, "Active") == 0) {
489 		bool b = value->getBool();
490 		if (b == false && _main) {
491 			_gameRef->LOG(0, "Warning: cannot deactivate scene's main layer");
492 		} else {
493 			_active = b;
494 		}
495 		return STATUS_OK;
496 	} else {
497 		return BaseObject::scSetProperty(name, value);
498 	}
499 }
500 
501 
502 //////////////////////////////////////////////////////////////////////////
scToString()503 const char *AdLayer::scToString() {
504 	return "[layer]";
505 }
506 
507 
508 //////////////////////////////////////////////////////////////////////////
saveAsText(BaseDynamicBuffer * buffer,int indent)509 bool AdLayer::saveAsText(BaseDynamicBuffer *buffer, int indent) {
510 	buffer->putTextIndent(indent, "LAYER {\n");
511 	buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName());
512 	buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption());
513 	buffer->putTextIndent(indent + 2, "MAIN=%s\n", _main ? "TRUE" : "FALSE");
514 	buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width);
515 	buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height);
516 	buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE");
517 	buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE");
518 	if (_closeUp) {
519 		buffer->putTextIndent(indent + 2, "CLOSE_UP=%s\n", _closeUp ? "TRUE" : "FALSE");
520 	}
521 
522 	for (uint32 i = 0; i < _scripts.size(); i++) {
523 		buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename);
524 	}
525 
526 	if (_scProp) {
527 		_scProp->saveAsText(buffer, indent + 2);
528 	}
529 
530 	for (uint32 i = 0; i < _nodes.size(); i++) {
531 		switch (_nodes[i]->_type) {
532 		case OBJECT_ENTITY:
533 			_nodes[i]->_entity->saveAsText(buffer, indent + 2);
534 			break;
535 		case OBJECT_REGION:
536 			_nodes[i]->_region->saveAsText(buffer, indent + 2);
537 			break;
538 		default:
539 			error("AdLayer::SaveAsText - Unhandled enum");
540 			break;
541 		}
542 	}
543 
544 	BaseClass::saveAsText(buffer, indent + 2);
545 
546 	buffer->putTextIndent(indent, "}\n\n");
547 
548 	return STATUS_OK;
549 }
550 
551 
552 //////////////////////////////////////////////////////////////////////////
persist(BasePersistenceManager * persistMgr)553 bool AdLayer::persist(BasePersistenceManager *persistMgr) {
554 
555 	BaseObject::persist(persistMgr);
556 
557 	persistMgr->transferBool(TMEMBER(_active));
558 	persistMgr->transferBool(TMEMBER(_closeUp));
559 	persistMgr->transferSint32(TMEMBER(_height));
560 	persistMgr->transferBool(TMEMBER(_main));
561 	_nodes.persist(persistMgr);
562 	persistMgr->transferSint32(TMEMBER(_width));
563 
564 	return STATUS_OK;
565 }
566 
567 } // End of namespace Wintermute
568