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