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/savefile.h"
24 #include "common/system.h"
25 
26 #include "sludge/fileset.h"
27 #include "sludge/moreio.h"
28 #include "sludge/newfatal.h"
29 #include "sludge/objtypes.h"
30 #include "sludge/people.h"
31 #include "sludge/sludge.h"
32 #include "sludge/variable.h"
33 
34 namespace Sludge {
35 
36 const char *typeName[] = { "undefined", "number", "user function", "string",
37 		"built-in function", "file", "stack", "object type", "animation",
38 		"costume", "fast array" };
39 
unlinkVar()40 void Variable::unlinkVar() {
41 	switch (varType) {
42 	case SVT_STRING:
43 		delete []varData.theString;
44 		varData.theString = NULL;
45 		break;
46 
47 	case SVT_STACK:
48 		varData.theStack->timesUsed--;
49 		if (varData.theStack->timesUsed <= 0) {
50 			while (varData.theStack->first)
51 				trimStack(varData.theStack->first);
52 			delete varData.theStack;
53 			varData.theStack = NULL;
54 		}
55 		break;
56 
57 	case SVT_FASTARRAY:
58 		varData.fastArray->timesUsed--;
59 		if (varData.theStack->timesUsed <= 0) {
60 			delete varData.fastArray->fastVariables;
61 			delete[] varData.fastArray;
62 			varData.fastArray = NULL;
63 		}
64 		break;
65 
66 	case SVT_ANIM:
67 		if (varData.animHandler) {
68 			delete varData.animHandler;
69 			varData.animHandler = nullptr;
70 		}
71 		break;
72 
73 	default:
74 		break;
75 	}
76 }
77 
setVariable(VariableType vT,int value)78 void Variable::setVariable(VariableType vT, int value) {
79 	unlinkVar();
80 	varType = vT;
81 	varData.intValue = value;
82 }
83 
makeAnimationVariable(PersonaAnimation * i)84 void Variable::makeAnimationVariable(PersonaAnimation  *i) {
85 	unlinkVar();
86 	varType = SVT_ANIM;
87 	varData.animHandler = i;
88 }
89 
getAnimationFromVar()90 PersonaAnimation *Variable::getAnimationFromVar() {
91 	if (varType == SVT_ANIM)
92 		return new PersonaAnimation(varData.animHandler);
93 
94 	if (varType == SVT_INT && varData.intValue == 0)
95 		return new PersonaAnimation();
96 
97 	fatal("Expecting an animation variable; found Variable of type", typeName[varType]);
98 	return NULL;
99 }
100 
makeCostumeVariable(Persona * i)101 void Variable::makeCostumeVariable(Persona *i) {
102 	unlinkVar();
103 	varType = SVT_COSTUME;
104 	varData.costumeHandler = i;
105 }
106 
getCostumeFromVar()107 Persona *Variable::getCostumeFromVar() {
108 	Persona *p = NULL;
109 
110 	switch (varType) {
111 	case SVT_ANIM:
112 		p = new Persona;
113 		if (!checkNew(p))
114 			return NULL;
115 		p->numDirections = 1;
116 		p->animation = new PersonaAnimation  *[3];
117 		if (!checkNew(p->animation))
118 			return NULL;
119 
120 		for (int iii = 0; iii < 3; iii++)
121 			p->animation[iii] = new PersonaAnimation(varData.animHandler);
122 
123 		break;
124 
125 	case SVT_COSTUME:
126 		return varData.costumeHandler;
127 		break;
128 
129 	default:
130 		fatal("Expecting an animation variable; found Variable of type", typeName[varType]);
131 	}
132 
133 	return p;
134 }
135 
getStackSize() const136 int StackHandler::getStackSize() const {
137 	int r = 0;
138 	VariableStack *a = first;
139 	while (a) {
140 		r++;
141 		a = a->next;
142 	}
143 	return r;
144 }
145 
getSavedGamesStack(const Common::String & ext)146 bool StackHandler::getSavedGamesStack(const Common::String &ext) {
147 	// Make pattern
148 	uint len = ext.size();
149 	Common::String pattern = "*";
150 	pattern += ext;
151 
152 	// Get all saved files
153 	Common::StringArray sa = g_system->getSavefileManager()->listSavefiles(pattern);
154 
155 	// Save file names to stacks
156 	Variable newName;
157 	newName.varType = SVT_NULL;
158 	Common::StringArray::iterator it;
159 	for (it = sa.begin(); it != sa.end(); ++it) {
160 		(*it).erase((*it).size() - len, len);
161 		newName.makeTextVar((*it));
162 		if (!addVarToStack(newName, first))
163 			return false;
164 		if (last == NULL)
165 			last = first;
166 	}
167 
168 	return true;
169 }
170 
copyStack(const Variable & from)171 bool Variable::copyStack(const Variable &from) {
172 	varType = SVT_STACK;
173 	varData.theStack = new StackHandler;
174 	if (!checkNew(varData.theStack))
175 		return false;
176 	varData.theStack->first = NULL;
177 	varData.theStack->last = NULL;
178 	varData.theStack->timesUsed = 1;
179 	VariableStack *a = from.varData.theStack->first;
180 
181 	while (a) {
182 		addVarToStack(a->thisVar, varData.theStack->first);
183 		if (varData.theStack->last == NULL) {
184 			varData.theStack->last = varData.theStack->first;
185 		}
186 		a = a->next;
187 	}
188 
189 	return true;
190 }
191 
addVariablesInSecond(const Variable & other)192 void Variable::addVariablesInSecond(const Variable &other) {
193 	if (other.varType == SVT_INT && varType == SVT_INT) {
194 		varData.intValue += other.varData.intValue;
195 	} else {
196 		Common::String string1 = other.getTextFromAnyVar();
197 		Common::String string2 = getTextFromAnyVar();
198 
199 		unlinkVar();
200 		varData.theString = createCString(string1 + string2);
201 		varType = SVT_STRING;
202 	}
203 }
204 
compareVars(const Variable & other) const205 int Variable::compareVars(const Variable &other) const {
206 	int re = 0;
207 	if (other.varType == varType) {
208 		switch (other.varType) {
209 		case SVT_NULL:
210 			re = 1;
211 			break;
212 
213 		case SVT_COSTUME:
214 			re = (other.varData.costumeHandler == varData.costumeHandler);
215 			break;
216 
217 		case SVT_ANIM:
218 			re = (other.varData.animHandler == varData.animHandler);
219 			break;
220 
221 		case SVT_STRING:
222 			re = (strcmp(other.varData.theString, varData.theString) == 0);
223 			break;
224 
225 		case SVT_STACK:
226 			re = (other.varData.theStack == varData.theStack);
227 			break;
228 
229 		default:
230 			re = (other.varData.intValue == varData.intValue);
231 		}
232 	}
233 	return re;
234 }
235 
compareVariablesInSecond(const Variable & other)236 void Variable::compareVariablesInSecond(const Variable &other) {
237 	setVariable(SVT_INT, compareVars(other));
238 }
239 
makeTextVar(const Common::String & txt)240 void Variable::makeTextVar(const Common::String &txt) {
241 	unlinkVar();
242 	varType = SVT_STRING;
243 	varData.theString = createCString(txt);
244 }
245 
loadStringToVar(int value)246 bool Variable::loadStringToVar(int value) {
247 	makeTextVar(g_sludge->_resMan->getNumberedString(value));
248 	return (bool)(varData.theString != NULL);
249 }
250 
getTextFromAnyVar() const251 Common::String Variable::getTextFromAnyVar() const {
252 	switch (varType) {
253 	case SVT_STRING:
254 		return varData.theString;
255 
256 	case SVT_FASTARRAY: {
257 		Common::String builder = "FAST:";
258 		Common::String builder2 = "";
259 		Common::String grabText = "";
260 
261 		for (int i = 0; i < varData.fastArray->size; i++) {
262 			builder2 = builder + " ";
263 			grabText = varData.fastArray->fastVariables[i].getTextFromAnyVar();
264 			builder.clear();
265 			builder = builder2 + grabText;
266 		}
267 		return builder;
268 	}
269 
270 	case SVT_STACK: {
271 		Common::String builder = "ARRAY:";
272 		Common::String builder2 = "";
273 		Common::String grabText = "";
274 
275 		VariableStack *stacky = varData.theStack->first;
276 
277 		while (stacky) {
278 			builder2 = builder + " ";
279 			grabText = stacky->thisVar.getTextFromAnyVar();
280 			builder.clear();
281 			builder = builder2 + grabText;
282 			stacky = stacky->next;
283 		}
284 		return builder;
285 	}
286 
287 	case SVT_INT: {
288 		Common::String buff = Common::String::format("%i", varData.intValue);
289 		return buff;
290 	}
291 
292 	case SVT_FILE: {
293 		return g_sludge->_resMan->resourceNameFromNum(varData.intValue);
294 	}
295 
296 	case SVT_OBJTYPE: {
297 		ObjectType *thisType = g_sludge->_objMan->findObjectType(varData.intValue);
298 		if (thisType)
299 			return thisType->screenName;
300 		break;
301 	}
302 
303 	default:
304 		break;
305 	}
306 
307 	return typeName[varType];
308 }
309 
getBoolean() const310 bool Variable::getBoolean() const {
311 	switch (varType) {
312 	case SVT_NULL:
313 		return false;
314 
315 	case SVT_INT:
316 		return (bool)(varData.intValue != 0);
317 
318 	case SVT_STACK:
319 		return (bool)(varData.theStack->first != NULL);
320 
321 	case SVT_STRING:
322 		return (bool)(varData.theString[0] != 0);
323 
324 	case SVT_FASTARRAY:
325 		return (bool)(varData.fastArray->size != 0);
326 
327 	default:
328 		break;
329 	}
330 	return true;
331 }
332 
copyMain(const Variable & from)333 bool Variable::copyMain(const Variable &from) {
334 	varType = from.varType;
335 	switch (varType) {
336 	case SVT_INT:
337 	case SVT_FUNC:
338 	case SVT_BUILT:
339 	case SVT_FILE:
340 	case SVT_OBJTYPE:
341 		varData.intValue = from.varData.intValue;
342 		return true;
343 
344 	case SVT_FASTARRAY:
345 		varData.fastArray = from.varData.fastArray;
346 		varData.fastArray->timesUsed++;
347 		return true;
348 
349 	case SVT_STRING:
350 		varData.theString = createCString(from.varData.theString);
351 		return varData.theString ? true : false;
352 
353 	case SVT_STACK:
354 		varData.theStack = from.varData.theStack;
355 		varData.theStack->timesUsed++;
356 		return true;
357 
358 	case SVT_COSTUME:
359 		varData.costumeHandler = from.varData.costumeHandler;
360 		return true;
361 
362 	case SVT_ANIM:
363 		varData.animHandler = new PersonaAnimation(from.varData.animHandler);
364 		return true;
365 
366 	case SVT_NULL:
367 		return true;
368 
369 	default:
370 		break;
371 	}
372 	fatal("Unknown value type");
373 	return false;
374 }
375 
copyFrom(const Variable & from)376 bool Variable::copyFrom(const Variable &from) {
377 	unlinkVar();
378 	return copyMain(from);
379 }
380 
fastArrayGetByIndex(uint theIndex)381 Variable *FastArrayHandler::fastArrayGetByIndex(uint theIndex) {
382 	if ((int)theIndex >= size)
383 		return NULL;
384 	return &fastVariables[theIndex];
385 }
386 
makeFastArraySize(int size)387 bool Variable::makeFastArraySize(int size) {
388 	if (size < 0)
389 		return fatal("Can't create a fast array with a negative number of elements!");
390 	unlinkVar();
391 	varType = SVT_FASTARRAY;
392 	varData.fastArray = new FastArrayHandler;
393 	if (!checkNew(varData.fastArray))
394 		return false;
395 	varData.fastArray->fastVariables = new Variable[size];
396 	if (!checkNew(varData.fastArray->fastVariables))
397 		return false;
398 	varData.fastArray->size = size;
399 	varData.fastArray->timesUsed = 1;
400 	return true;
401 }
402 
makeFastArrayFromStack(const StackHandler * stacky)403 bool Variable::makeFastArrayFromStack(const StackHandler *stacky) {
404 	int size = stacky->getStackSize();
405 	if (!makeFastArraySize(size))
406 		return false;
407 
408 	// Now let's fill up the new array
409 
410 	VariableStack *allV = stacky->first;
411 	size = 0;
412 	while (allV) {
413 		varData.fastArray->fastVariables[size].copyMain(allV->thisVar);
414 		size++;
415 		allV = allV->next;
416 	}
417 	return true;
418 }
419 
addVarToStack(const Variable & va,VariableStack * & thisStack)420 bool addVarToStack(const Variable &va, VariableStack *&thisStack) {
421 	VariableStack *newStack = new VariableStack;
422 	if (!checkNew(newStack))
423 		return false;
424 
425 	if (!newStack->thisVar.copyMain(va))
426 		return false;
427 	newStack->next = thisStack;
428 	thisStack = newStack;
429 	//debugC(2, kSludgeDebugStackMachine, "Variable %s was added to stack", getTextFromAnyVar(va));
430 	return true;
431 }
432 
addVarToStackQuick(Variable & va,VariableStack * & thisStack)433 bool addVarToStackQuick(Variable &va, VariableStack *&thisStack) {
434 	VariableStack *newStack = new VariableStack;
435 	if (!checkNew(newStack))
436 		return false;
437 
438 //	if (! copyMain (va, newStack -> thisVar)) return false;
439 
440 	memcpy(&(newStack->thisVar), &va, sizeof(Variable));
441 	va.varType = SVT_NULL;
442 
443 	newStack->next = thisStack;
444 	thisStack = newStack;
445 	//debugC(2, kSludgeDebugStackMachine, "Variable %s was added to stack quick", getTextFromAnyVar(va));
446 	return true;
447 }
448 
stackSetByIndex(uint theIndex,const Variable & va)449 bool VariableStack::stackSetByIndex(uint theIndex, const Variable &va) {
450 	VariableStack *vS = this;
451 	while (theIndex--) {
452 		vS = vS->next;
453 		if (!vS)
454 			return fatal("Index past end of stack.");
455 	}
456 	return vS->thisVar.copyFrom(va);
457 }
458 
stackGetByIndex(uint theIndex)459 Variable *VariableStack::stackGetByIndex(uint theIndex) {
460 	VariableStack *vS = this;
461 	while (theIndex--) {
462 		vS = vS->next;
463 		if (!vS) {
464 			return NULL;
465 		}
466 	}
467 	return &(vS->thisVar);
468 }
469 
deleteVarFromStack(const Variable & va,VariableStack * & thisStack,bool allOfEm)470 int deleteVarFromStack(const Variable &va, VariableStack *&thisStack, bool allOfEm) {
471 	VariableStack **huntVar = &thisStack;
472 	VariableStack *killMe;
473 	int reply = 0;
474 
475 	while (*huntVar) {
476 		if (va.compareVars((*huntVar)->thisVar)) {
477 			killMe = *huntVar;
478 			*huntVar = killMe->next;
479 			killMe->thisVar.unlinkVar();
480 			delete killMe;
481 			if (!allOfEm)
482 				return 1;
483 			reply++;
484 		} else {
485 			huntVar = &((*huntVar)->next);
486 		}
487 	}
488 
489 	return reply;
490 }
491 
492 // Would be a LOT better just to keep this up to date in the above function... ah well
stackFindLast()493 VariableStack *VariableStack::stackFindLast() {
494 	VariableStack *hunt = this;
495 	while (hunt->next)
496 		hunt = hunt->next;
497 
498 	return hunt;
499 }
500 
getValueType(int & toHere,VariableType vT) const501 bool Variable::getValueType(int &toHere, VariableType vT) const {
502 	if (varType != vT) {
503 		Common::String e1 = "Can only perform specified operation on a value which is of type ";
504 		e1 += typeName[vT];
505 		Common::String e2 = "... value supplied was of type ";
506 		e2 += typeName[varType];
507 		fatal(e1, e2);
508 
509 		return false;
510 	}
511 	toHere = varData.intValue;
512 	return true;
513 }
514 
trimStack(VariableStack * & stack)515 void trimStack(VariableStack *&stack) {
516 	VariableStack *killMe = stack;
517 	stack = stack->next;
518 
519 	//debugC(2, kSludgeDebugStackMachine, "Variable %s was removed from stack", getTextFromAnyVar(killMe->thisVar));
520 
521 	// When calling this, we've ALWAYS checked that stack != NULL
522 	killMe->thisVar.unlinkVar();
523 	delete killMe;
524 }
525 
526 //----------------------------------------------------------------------
527 // Globals (so we know what's saved already and what's a reference
528 //----------------------------------------------------------------------
529 
530 struct stackLibrary {
531 	StackHandler *stack;
532 	stackLibrary *next;
533 };
534 
535 int stackLibTotal = 0;
536 stackLibrary *stackLib = NULL;
537 
538 //----------------------------------------------------------------------
539 // For saving and loading stacks...
540 //----------------------------------------------------------------------
saveStack(VariableStack * vs,Common::WriteStream * stream)541 void saveStack(VariableStack *vs, Common::WriteStream *stream) {
542 	int elements = 0;
543 	int a;
544 
545 	VariableStack *search = vs;
546 	while (search) {
547 		elements++;
548 		search = search->next;
549 	}
550 
551 	stream->writeUint16BE(elements);
552 	search = vs;
553 	for (a = 0; a < elements; a++) {
554 		search->thisVar.save(stream);
555 		search = search->next;
556 	}
557 }
558 
loadStack(Common::SeekableReadStream * stream,VariableStack ** last)559 VariableStack *loadStack(Common::SeekableReadStream *stream, VariableStack **last) {
560 	int elements = stream->readUint16BE();
561 	int a;
562 	VariableStack *first = NULL;
563 	VariableStack **changeMe = &first;
564 
565 	for (a = 0; a < elements; a++) {
566 		VariableStack *nS = new VariableStack;
567 		if (!checkNew(nS))
568 			return NULL;
569 		nS->thisVar.load(stream);
570 		if (last && a == elements - 1) {
571 			*last = nS;
572 		}
573 		nS->next = NULL;
574 		(*changeMe) = nS;
575 		changeMe = &(nS->next);
576 	}
577 
578 	return first;
579 }
580 
saveStackRef(StackHandler * vs,Common::WriteStream * stream)581 bool saveStackRef(StackHandler *vs, Common::WriteStream *stream) {
582 	stackLibrary *s = stackLib;
583 	int a = 0;
584 	while (s) {
585 		if (s->stack == vs) {
586 			stream->writeByte(1);
587 			stream->writeUint16BE(stackLibTotal - a);
588 			return true;
589 		}
590 		s = s->next;
591 		a++;
592 	}
593 	stream->writeByte(0);
594 	saveStack(vs->first, stream);
595 	s = new stackLibrary;
596 	stackLibTotal++;
597 	if (!checkNew(s))
598 		return false;
599 	s->next = stackLib;
600 	s->stack = vs;
601 	stackLib = s;
602 	return true;
603 }
604 
clearStackLib()605 void clearStackLib() {
606 	stackLibrary *k;
607 	while (stackLib) {
608 		k = stackLib;
609 		stackLib = stackLib->next;
610 		delete k;
611 	}
612 	stackLibTotal = 0;
613 }
614 
getStackFromLibrary(int n)615 StackHandler *getStackFromLibrary(int n) {
616 	n = stackLibTotal - n;
617 	while (n) {
618 		stackLib = stackLib->next;
619 		n--;
620 	}
621 	return stackLib->stack;
622 }
623 
loadStackRef(Common::SeekableReadStream * stream)624 StackHandler *loadStackRef(Common::SeekableReadStream *stream) {
625 	StackHandler *nsh;
626 
627 	if (stream->readByte()) {    // It's one we've loaded already...
628 		nsh = getStackFromLibrary(stream->readUint16BE());
629 		nsh->timesUsed++;
630 	} else {
631 		// Load the new stack
632 
633 		nsh = new StackHandler;
634 		if (!checkNew(nsh))
635 			return NULL;
636 		nsh->last = NULL;
637 		nsh->first = loadStack(stream, &nsh->last);
638 		nsh->timesUsed = 1;
639 
640 		// Add it to the library of loaded stacks
641 
642 		stackLibrary *s = new stackLibrary;
643 		if (!checkNew(s))
644 			return NULL;
645 		s->stack = nsh;
646 		s->next = stackLib;
647 		stackLib = s;
648 		stackLibTotal++;
649 	}
650 	return nsh;
651 }
652 
653 //----------------------------------------------------------------------
654 // For saving and loading variables...
655 //----------------------------------------------------------------------
save(Common::WriteStream * stream)656 bool Variable::save(Common::WriteStream *stream) {
657 	stream->writeByte(varType);
658 	switch (varType) {
659 	case SVT_INT:
660 	case SVT_FUNC:
661 	case SVT_BUILT:
662 	case SVT_FILE:
663 	case SVT_OBJTYPE:
664 		stream->writeUint32LE(varData.intValue);
665 		return true;
666 
667 	case SVT_STRING:
668 		writeString(varData.theString, stream);
669 		return true;
670 
671 	case SVT_STACK:
672 		return saveStackRef(varData.theStack, stream);
673 
674 	case SVT_COSTUME:
675 		varData.costumeHandler->save(stream);
676 		return false;
677 
678 	case SVT_ANIM:
679 		varData.animHandler->save(stream);
680 		return false;
681 
682 	case SVT_NULL:
683 		return false;
684 
685 	default:
686 		fatal("Can't save variables of this type:", (varType < SVT_NUM_TYPES - 1) ? typeName[varType] : "bad ID");
687 	}
688 	return true;
689 }
690 
load(Common::SeekableReadStream * stream)691 bool Variable::load(Common::SeekableReadStream *stream) {
692 	varType = (VariableType)stream->readByte();
693 	switch (varType) {
694 	case SVT_INT:
695 	case SVT_FUNC:
696 	case SVT_BUILT:
697 	case SVT_FILE:
698 	case SVT_OBJTYPE:
699 		varData.intValue = stream->readUint32LE();
700 		return true;
701 
702 	case SVT_STRING:
703 		varData.theString = createCString(readString(stream));
704 		return true;
705 
706 	case SVT_STACK:
707 		varData.theStack = loadStackRef(stream);
708 		return true;
709 
710 	case SVT_COSTUME:
711 		varData.costumeHandler = new Persona;
712 		if (!checkNew(varData.costumeHandler))
713 			return false;
714 		varData.costumeHandler->load(stream);
715 		return true;
716 
717 	case SVT_ANIM:
718 		varData.animHandler = new PersonaAnimation;
719 		if (!checkNew(varData.animHandler))
720 			return false;
721 		varData.animHandler->load(stream);
722 		return true;
723 
724 	default:
725 		break;
726 	}
727 	return true;
728 }
729 
730 } // End of namespace Sludge
731