]> git.localhorst.tv Git - l2e.git/blob - src/loader/Interpreter.cpp
added script labels in parser/interpreter
[l2e.git] / src / loader / Interpreter.cpp
1 /*
2  * Interpreter.cpp
3  *
4  *  Created on: Aug 26, 2012
5  *      Author: holy
6  */
7
8 #include "Interpreter.h"
9
10 #include "ParsedSource.h"
11 #include "../battle/Hero.h"
12 #include "../battle/Monster.h"
13 #include "../battle/PartyLayout.h"
14 #include "../battle/Resources.h"
15 #include "../common/Ikari.h"
16 #include "../common/Item.h"
17 #include "../common/Script.h"
18 #include "../common/Spell.h"
19 #include "../common/Stats.h"
20 #include "../common/TargetingMode.h"
21 #include "../graphics/ComplexAnimation.h"
22 #include "../graphics/Font.h"
23 #include "../graphics/Frame.h"
24 #include "../graphics/Gauge.h"
25 #include "../graphics/Menu.h"
26 #include "../graphics/SimpleAnimation.h"
27 #include "../graphics/Sprite.h"
28
29 #include <algorithm>
30 #include <cstring>
31 #include <SDL_image.h>
32
33 using battle::Hero;
34 using battle::Monster;
35 using battle::PartyLayout;
36 using common::Ikari;
37 using common::Item;
38 using common::Script;
39 using common::Spell;
40 using common::Stats;
41 using common::TargetingMode;
42 using graphics::Animation;
43 using graphics::Color;
44 using graphics::Font;
45 using graphics::Frame;
46 using graphics::Gauge;
47 using graphics::ComplexAnimation;
48 using graphics::SimpleAnimation;
49 using graphics::Sprite;
50 using geometry::Vector;
51 using std::make_pair;
52 using std::set;
53 using std::string;
54 using std::vector;
55
56 namespace loader {
57
58 Interpreter::~Interpreter() {
59         for (std::map<string, SDL_Surface *>::const_iterator i(imageCache.begin()), end(imageCache.end()); i != end; ++i) {
60                 SDL_FreeSurface(i->second);
61         }
62 }
63
64
65 const Interpreter::ParsedDefinition &Interpreter::GetDefinition(const string &identifier) {
66         std::map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(identifier));
67         if (i != parsedDefinitions.end()) {
68                 return i->second;
69         } else if (source.IsDefined(identifier)) {
70                 ReadDefinition(source.GetDefinition(identifier));
71                 return parsedDefinitions.at(identifier);
72         } else {
73                 throw Error("access to undefined object " + identifier);
74         }
75 }
76
77
78 void *Interpreter::GetObject(int typeId, const std::string &name) {
79         std::map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
80         if (i != parsedDefinitions.end()) {
81                 const TypeDescription &requested(TypeDescription::Get(typeId));
82                 const TypeDescription &actual(TypeDescription::Get(i->second.type));
83                 if (requested.TypeId() == actual.TypeId()) {
84                         return values[actual.TypeId()][i->second.id];
85                 } else if (actual.IsSubtypeOf(requested)) {
86                         char *sub(reinterpret_cast<char *>(values[actual.TypeId()][i->second.id]));
87                         std::ptrdiff_t offset(actual.SupertypeOffset(requested));
88                         return sub - offset;
89                 } else {
90                         throw Error("cannot cast " + actual.TypeName() + " to " + requested.TypeName());
91                 }
92         } else {
93                 throw Error("access to undefined object " + name);
94         }
95 }
96
97
98 void Interpreter::ReadSource() {
99         for (set<string>::const_iterator i(source.Exports().begin()), end(source.Exports().end()); i != end; ++i) {
100                 ReadDefinition(source.GetDefinition(*i));
101         }
102 }
103
104 void Interpreter::ReadDefinition(const Definition &dfn) {
105         if (parsedDefinitions.find(dfn.Identifier()) != parsedDefinitions.end()) {
106                 return;
107         }
108         if (dfn.HasLiteralValue()) {
109                 ReadLiteral(dfn);
110         } else {
111                 ReadObject(dfn);
112         }
113 }
114
115 void Interpreter::ReadLiteral(const Definition &dfn) {
116         const string &typeName(dfn.GetLiteral()->IsArray() ? "Array" : dfn.GetLiteral()->GetTypeName());
117         int typeId(TypeDescription::GetTypeId(typeName));
118         int id(values[typeId].size());
119         const TypeDescription &td(TypeDescription::Get(typeId));
120         int size(
121                         (dfn.GetLiteral()->GetType() == Literal::PATH
122                                         || dfn.GetLiteral()->GetType() == Literal::STRING)
123                         ? dfn.GetLiteral()->GetString().size() : td.Size());
124         char *object(alloc.Alloc(size));
125         values[typeId].push_back(object);
126         parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, typeId, id)));
127         if (dfn.GetLiteral()->GetType() == Literal::OBJECT) {
128                 ReadObject(typeId, id, object, *dfn.GetLiteral()->GetProperties());
129         } else {
130                 ReadLiteral(typeId, id, object, *dfn.GetLiteral());
131         }
132 }
133
134 void Interpreter::ReadLiteral(int typeId, int id, char *object, const Literal &literal) {
135         switch (literal.GetType()) {
136                 case Literal::ARRAY_VALUES:
137                         throw Error("named value arrays are not supported, sorry");
138                         break;
139                 case Literal::ARRAY_PROPS:
140                         throw Error("named property list arrays are not supported, sorry");
141                         break;
142                 case Literal::BOOLEAN:
143                         new (object) bool(literal.GetBoolean());
144                         break;
145                 case Literal::COLOR:
146                         new (object) Color(literal.GetRed(), literal.GetGreen(), literal.GetBlue(), literal.GetAlpha());
147                         break;
148                 case Literal::NUMBER:
149                         new (object) int(literal.GetNumber());
150                         break;
151                 case Literal::PATH:
152                         std::memcpy(object, literal.GetString().c_str(), literal.GetString().size());
153                         object[literal.GetString().size()] = '\0';
154                         break;
155                 case Literal::SCRIPT:
156                         new (object) Script;
157                         ReadScript(literal.GetScript(), reinterpret_cast<Script *>(object));
158                         break;
159                 case Literal::STRING:
160                         std::memcpy(object, literal.GetString().c_str(), literal.GetString().size());
161                         object[literal.GetString().size()] = '\0';
162                         break;
163                 case Literal::VECTOR:
164                         new (object) Vector<int>(literal.GetX(), literal.GetY());
165                         break;
166                 case Literal::OBJECT:
167                         throw Error("illogical branch: read literal object as non-object literal");
168         }
169 }
170
171
172 void *Interpreter::GetObject(int typeId, const Value &v) {
173         if (v.IsLiteral()) {
174                 if (v.GetLiteral().IsObject()) {
175                         int typeId(TypeDescription::GetTypeId(v.GetLiteral().GetTypeName()));
176                         const TypeDescription &td(TypeDescription::Get(typeId));
177                         char *object(alloc.Alloc(td.Size()));
178                         td.Construct(object);
179                         int id(values[typeId].size());
180                         values[typeId].push_back(object);
181                         ReadObject(typeId, id, object, *v.GetLiteral().GetProperties());
182                         return object;
183                 } else {
184                         int typeId(0), id(0);
185                         switch (v.GetLiteral().GetType()) {
186                                 case Literal::ARRAY_VALUES:
187                                         throw Error("cannot copy value arrays, sorry");
188                                         break;
189                                 case Literal::ARRAY_PROPS:
190                                         throw Error("cannot copy property list arrays, sorry");
191                                         break;
192                                 case Literal::BOOLEAN:
193                                         {
194                                                 typeId = TypeDescription::GetTypeId("Boolean");
195                                                 id = values[typeId].size();
196                                                 const TypeDescription &td(TypeDescription::Get(typeId));
197                                                 char *object(alloc.Alloc(td.Size()));
198                                                 values[typeId].push_back(new (object) bool(v.GetLiteral().GetBoolean()));
199                                         }
200                                         break;
201                                 case Literal::COLOR:
202                                         {
203                                                 typeId = TypeDescription::GetTypeId("Color");
204                                                 id = values[typeId].size();
205                                                 const TypeDescription &td(TypeDescription::Get(typeId));
206                                                 char *object(alloc.Alloc(td.Size()));
207                                                 values[typeId].push_back(new (object) Color(v.GetLiteral().GetRed(), v.GetLiteral().GetGreen(), v.GetLiteral().GetBlue(), v.GetLiteral().GetAlpha()));
208                                         }
209                                         break;
210                                 case Literal::NUMBER:
211                                         {
212                                                 typeId = TypeDescription::GetTypeId("Number");
213                                                 id = values[typeId].size();
214                                                 const TypeDescription &td(TypeDescription::Get(typeId));
215                                                 char *object(alloc.Alloc(td.Size()));
216                                                 values[typeId].push_back(new (object) int(v.GetLiteral().GetNumber()));
217                                         }
218                                         break;
219                                 case Literal::PATH:
220                                         {
221                                                 typeId = TypeDescription::GetTypeId("Path");
222                                                 id = values[typeId].size();
223                                                 char *str(alloc.Alloc(v.GetLiteral().GetString().size() + 1));
224                                                 std::memcpy(str, v.GetLiteral().GetString().c_str(), v.GetLiteral().GetString().size());
225                                                 str[v.GetLiteral().GetString().size()] = '\0';
226                                                 values[typeId].push_back(str);
227                                         }
228                                         break;
229                                 case Literal::SCRIPT:
230                                         {
231                                                 typeId = TypeDescription::GetTypeId("Script");
232                                                 char *script(ReadScript(v.GetLiteral().GetScript()));
233                                                 id = values[typeId].size();
234                                                 values[typeId].push_back(script);
235                                         }
236                                         break;
237                                 case Literal::STRING:
238                                         {
239                                                 typeId = TypeDescription::GetTypeId("String");
240                                                 id = values[typeId].size();
241                                                 char *str(alloc.Alloc(v.GetLiteral().GetString().size() + 1));
242                                                 std::memcpy(str, v.GetLiteral().GetString().c_str(), v.GetLiteral().GetString().size());
243                                                 str[v.GetLiteral().GetString().size()] = '\0';
244                                                 values[typeId].push_back(str);
245                                         }
246                                         break;
247                                 case Literal::VECTOR:
248                                         {
249                                                 typeId = TypeDescription::GetTypeId("Vector");
250                                                 id = values[typeId].size();
251                                                 const TypeDescription &td(TypeDescription::Get(typeId));
252                                                 char *object(alloc.Alloc(td.Size()));
253                                                 values[typeId].push_back(new (object) Vector<int>(v.GetLiteral().GetX(), v.GetLiteral().GetY()));
254                                         }
255                                         break;
256                                 case Literal::OBJECT:
257                                         {
258                                                 typeId = TypeDescription::GetTypeId(v.GetLiteral().GetTypeName());
259                                                 const TypeDescription &td(TypeDescription::Get(typeId));
260                                                 id = values[typeId].size();
261                                                 char *object(alloc.Alloc(td.Size()));
262                                                 td.Construct(object);
263                                                 ReadObject(typeId, id, object, *v.GetLiteral().GetProperties());
264                                         }
265                                         break;
266                         }
267                         return values[typeId][id];
268                 }
269         } else {
270                 ReadDefinition(source.GetDefinition(v.GetIdentifier()));
271                 return GetObject(typeId, v.GetIdentifier());
272         }
273 }
274
275
276 void Interpreter::ReadObject(const Definition &dfn) {
277         int typeId(TypeDescription::GetTypeId(dfn.TypeName()));
278         const TypeDescription &td(TypeDescription::Get(typeId));
279         int id(values[typeId].size());
280         char *object(alloc.Alloc(td.Size()));
281         td.Construct(object);
282         values[typeId].push_back(object);
283         parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, typeId, id)));
284         ReadObject(typeId, id, object, *dfn.GetProperties());
285 }
286
287
288 void Interpreter::ReadObject(int typeId, int id, char *object, const PropertyList &props) {
289         const TypeDescription &td(TypeDescription::Get(typeId));
290         for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
291                 const FieldDescription &fd(td.GetField(i->first));
292                 const TypeDescription &fieldType(TypeDescription::Get(fd.TypeId()));
293                 if (CanLink(*i->second)) {
294                         char *dest(object + fd.Offset());
295                         if (fd.IsAggregate()) {
296                                 int arraySize(i->second->GetLiteral().ArraySize());
297                                 char *aggregate(alloc.Alloc(fieldType.Size() * arraySize));
298                                 char *iter(aggregate);
299                                 if (i->second->GetLiteral().GetType() == Literal::ARRAY_PROPS) {
300                                         const vector<PropertyList *> &list(i->second->GetLiteral().GetPropertyLists());
301                                         for (vector<PropertyList *>::const_iterator j(list.begin()), end(list.end()); j != end; ++j, iter += fieldType.Size()) {
302                                                 fieldType.Construct(iter);
303                                                 ReadObject(fieldType.TypeId(), -1, iter, **j);
304                                         }
305                                 } else {
306                                         const vector<Value *> &list(i->second->GetLiteral().GetValues());
307                                         for (vector<Value *>::const_iterator j(list.begin()), end(list.end()); j != end; ++j, iter += fieldType.Size()) {
308                                                 fieldType.Construct(iter);
309                                                 ReadLiteral(fieldType.TypeId(), -1, iter, (*j)->GetLiteral());
310                                         }
311                                 }
312                                 if (fd.IsReferenced()) {
313                                         std::memcpy(dest, &aggregate, sizeof(char *));
314                                         dest += sizeof(char *);
315                                         std::memcpy(dest, &arraySize, sizeof(int));
316                                 } else {
317                                         throw Error("aggregate type fields must be referenced");
318                                 }
319                         } else if (i->second->IsLiteral() && !fd.IsReferenced()) {
320                                 // inline literals
321                                 if (i->second->GetLiteral().IsObject()) {
322                                         ReadObject(fd.TypeId(), -1, dest, *i->second->GetLiteral().GetProperties());
323                                 } else {
324                                         ReadLiteral(fd.TypeId(), -1, dest, i->second->GetLiteral());
325                                 }
326                         } else {
327                                 char *src(reinterpret_cast<char *>(GetObject(fd.TypeId(), *i->second)));
328                                 if (fd.TypeId() == TypeDescription::GetTypeId("Image")) {
329                                         src = reinterpret_cast<char *>(GetImage(src));
330                                 }
331                                 if (fd.IsReferenced()) {
332                                         std::memcpy(dest, &src, sizeof(char *));
333                                 } else {
334                                         std::memcpy(dest, src, fieldType.Size());
335                                 }
336                         }
337                 } else {
338                         Postpone(typeId, id, fd.Offset(), i->second->GetIdentifier(), fd.TypeId(), !fd.IsReferenced());
339                 }
340         }
341         td.Load(object);
342 }
343
344
345 void Interpreter::ReadScript(const std::vector<ScriptToken *> &s, Script *script) {
346         std::map<string, int> labels;
347         int size(0);
348         for (vector<ScriptToken *>::const_iterator i(s.begin()), end(s.end()); i != end; ++i) {
349                 if ((*i)->GetType() == ScriptToken::LABEL) {
350                         if (labels.count((*i)->Label())) {
351                                 throw Error("duplicate label " + (*i)->Label());
352                         } else {
353                                 labels[(*i)->Label()] = size;
354                         }
355                 } else if ((*i)->GetType() != ScriptToken::COMMAND) {
356                         throw Error("unexpected script token");
357                 }
358                 ++size;
359                 const string &cmd((*i)->CommandName());
360                 if (cmd == "move") {
361                         ++i;
362                         if (i == end) {
363                                 throw Error("unexpected script end after move");
364                         }
365                         const string &reg((*i)->RegisterName());
366                         switch (reg[0]) {
367                                 case 'a':
368                                         size += sizeof(void *);
369                                         break;
370                                 case 'i':
371                                         size += sizeof(int);
372                                         break;
373                                 case 'v':
374                                         size += sizeof(Vector<int>);
375                                         break;
376                                 default:
377                                         throw Error("unknown register " + reg);
378                         }
379                         ++i;
380                         if (i == end) {
381                                 throw Error("unexpected script end after move");
382                         }
383                 } else if (cmd == "add") {
384                         ++i;
385                         if (i == end) {
386                                 throw Error("unexpected script end after add");
387                         }
388                         const string &reg((*i)->RegisterName());
389                         switch (reg[0]) {
390                                 case 'i':
391                                         size += sizeof(int);
392                                         break;
393                                 case 'v':
394                                         size += sizeof(Vector<int>);
395                                         break;
396                                 default:
397                                         throw Error("expected register after add " + reg);
398                         }
399                         ++i;
400                         if (i == end) {
401                                 throw Error("unexpected script end after add");
402                         }
403                 } else if (cmd == "mod") {
404                         ++i;
405                         if (i == end) {
406                                 throw Error("unexpected script end after mod");
407                         }
408                         size += sizeof(int);
409                         ++i;
410                         if (i == end) {
411                                 throw Error("unexpected script end after mod");
412                         }
413                 } else if (cmd == "rand") {
414                         ++i;
415                         if (i == end) {
416                                 throw Error("unexpected script end after rand");
417                         }
418                         size += sizeof(int);
419                         ++i;
420                         if (i == end) {
421                                 throw Error("unexpected script end after rand");
422                         }
423                 } else if (cmd == "sysc") {
424
425                 } else {
426                         throw Error("unknown command " + cmd);
427                 }
428         }
429
430         unsigned char *text(reinterpret_cast<unsigned char *>(alloc.Alloc(size)));
431         int cursor(0);
432         for (vector<ScriptToken *>::const_iterator i(s.begin()), end(s.end()); i != end; ++i) {
433                 if ((*i)->GetType() == ScriptToken::LABEL) {
434                         continue;
435                 }
436                 if ((*i)->GetType() != ScriptToken::COMMAND) {
437                         throw Error("unexpected script token");
438                 }
439                 const string &cmd((*i)->CommandName());
440                 if (cmd == "move") {
441                         ++i;
442                         const string &reg((*i)->RegisterName());
443                         ++i;
444                         if (reg == "a0") {
445                                 text[cursor] = Script::CODE_MOVE_A0;
446                                 ++cursor;
447                                 ReadScriptAddress(**i, text + cursor);
448                                 cursor += sizeof(void *);
449                         } else if (reg == "a1") {
450                                 text[cursor] = Script::CODE_MOVE_A1;
451                                 ++cursor;
452                                 ReadScriptAddress(**i, text + cursor);
453                                 cursor += sizeof(void *);
454                         } else if (reg == "i0") {
455                                 text[cursor] = Script::CODE_MOVE_I0;
456                                 ++cursor;
457                                 ReadScriptInteger(**i, text + cursor);
458                                 cursor += sizeof(int);
459                         } else if (reg == "i1") {
460                                 text[cursor] = Script::CODE_MOVE_I1;
461                                 ++cursor;
462                                 ReadScriptInteger(**i, text + cursor);
463                                 cursor += sizeof(int);
464                         } else if (reg == "v0") {
465                                 text[cursor] = Script::CODE_MOVE_V0;
466                                 ++cursor;
467                                 ReadScriptVector(**i, text + cursor);
468                                 cursor += sizeof(Vector<int>);
469                         } else if (reg == "v1") {
470                                 text[cursor] = Script::CODE_MOVE_V1;
471                                 ++cursor;
472                                 ReadScriptVector(**i, text + cursor);
473                                 cursor += sizeof(Vector<int>);
474                         } else {
475                                 throw Error("unknown register " + reg);
476                         }
477                 } else if (cmd == "add") {
478                         ++i;
479                         const string &reg((*i)->RegisterName());
480                         ++i;
481                         if (reg == "i0") {
482                                 text[cursor] = Script::CODE_ADD_I0;
483                                 ++cursor;
484                                 ReadScriptInteger(**i, text + cursor);
485                                 cursor += sizeof(int);
486                         } else if (reg == "i1") {
487                                 text[cursor] = Script::CODE_ADD_I1;
488                                 ++cursor;
489                                 ReadScriptInteger(**i, text + cursor);
490                                 cursor += sizeof(int);
491                         } else if (reg == "v0") {
492                                 text[cursor] = Script::CODE_ADD_V0;
493                                 ++cursor;
494                                 ReadScriptVector(**i, text + cursor);
495                                 cursor += sizeof(Vector<int>);
496                         } else if (reg == "v1") {
497                                 text[cursor] = Script::CODE_ADD_V1;
498                                 ++cursor;
499                                 ReadScriptVector(**i, text + cursor);
500                                 cursor += sizeof(Vector<int>);
501                         } else {
502                                 throw Error("unexpected register " + reg);
503                         }
504                 } else if (cmd == "mod") {
505                         ++i;
506                         const string &reg((*i)->RegisterName());
507                         ++i;
508                         if (reg == "i0") {
509                                 text[cursor] = Script::CODE_MOD_I0;
510                                 ++cursor;
511                                 ReadScriptInteger(**i, text + cursor);
512                                 cursor += sizeof(int);
513                         } else if (reg == "i1") {
514                                 text[cursor] = Script::CODE_MOD_I1;
515                                 ++cursor;
516                                 ReadScriptInteger(**i, text + cursor);
517                                 cursor += sizeof(int);
518                         } else {
519                                 throw Error("unexpected register " + reg);
520                         }
521                 } else if (cmd == "rand") {
522                         ++i;
523                         const string &reg((*i)->RegisterName());
524                         if (reg == "i0") {
525                                 text[cursor] = Script::CODE_RAND_I0;
526                                 ++cursor;
527                         } else if (reg == "i1") {
528                                 text[cursor] = Script::CODE_RAND_I1;
529                                 ++cursor;
530                         } else {
531                                 throw Error("unexpected register " + reg);
532                         }
533                 } else if (cmd == "sysc") {
534                         text[cursor] = Script::CODE_SYSCALL;
535                         ++cursor;
536                 } else {
537                         throw Error("unknown command " + cmd);
538                 }
539         }
540
541         script->text = text;
542         script->textlen = size;
543 }
544
545 char *Interpreter::ReadScript(const vector<ScriptToken *> &s) {
546         char *mem(alloc.Alloc(sizeof(Script)));
547         new (mem) Script;
548         Script *script(reinterpret_cast<Script *>(mem));
549         ReadScript(s, script);
550         return mem;
551 }
552
553 void Interpreter::ReadScriptAddress(const ScriptToken &t, unsigned char *dest) {
554         if (t.GetType() != ScriptToken::IDENTIFIER) {
555                 throw Error("expected identifier for address");
556         }
557         if (source.IsDefined(t.Identifier())) {
558                 const ParsedDefinition &def(GetDefinition(t.Identifier()));
559                 void *addr(GetObject(def.type, t.Identifier()));
560                 *reinterpret_cast<void **>(dest) = addr;
561         } else {
562                 throw Error("postponing values in scripts not implemented");
563         }
564 }
565
566 void Interpreter::ReadScriptInteger(const ScriptToken &t, unsigned char *dest) {
567         if (t.GetType() == ScriptToken::IDENTIFIER) {
568                 if (source.IsDefined(t.Identifier())) {
569                         void *num(GetObject(TypeDescription::GetTypeId("Number"), t.Identifier()));
570                         *reinterpret_cast<int *>(dest) = *reinterpret_cast<int *>(num);
571                 } else {
572                         throw Error("postponing values in scripts not implemented");
573                 }
574         } else if (t.GetType() == ScriptToken::LITERAL) {
575                 *reinterpret_cast<int *>(dest) = t.GetLiteral()->GetNumber();
576         } else {
577                 throw Error("expected identifier or integer literal");
578         }
579 }
580
581 void Interpreter::ReadScriptVector(const ScriptToken &t, unsigned char *dest) {
582         if (t.GetType() == ScriptToken::IDENTIFIER) {
583                 if (source.IsDefined(t.Identifier())) {
584                         void *vec(GetObject(TypeDescription::GetTypeId("Vector"), t.Identifier()));
585                         *reinterpret_cast<Vector<int> *>(dest) = *reinterpret_cast<Vector<int> *>(vec);
586                 } else {
587                         throw Error("postponing values in scripts not implemented");
588                 }
589         } else if (t.GetType() == ScriptToken::LITERAL) {
590                 *reinterpret_cast<Vector<int> *>(dest) = Vector<int>(t.GetLiteral()->GetX(), t.GetLiteral()->GetY());
591         } else {
592                 throw Error("expected identifier or vector literal");
593         }
594 }
595
596
597 SDL_Surface *Interpreter::GetImage(const string &path) {
598         std::map<string, SDL_Surface *>::const_iterator result(imageCache.find(path));
599         if (result != imageCache.end()) {
600                 return result->second;
601         } else {
602                 SDL_Surface *image(IMG_Load(path.c_str()));
603                 imageCache.insert(make_pair(path, image));
604                 return image;
605         }
606 }
607
608
609 bool Interpreter::CanLink(const Value &v) const {
610         return v.IsLiteral() || source.IsDefined(v.GetIdentifier());
611 }
612
613 void Interpreter::Postpone(int type, int id, std::ptrdiff_t offset, const std::string &identifier, int linkedType, bool inlined) {
614         char *str(alloc.Alloc(identifier.size() + 1));
615         std::memcpy(str, identifier.c_str(), identifier.size());
616         str[identifier.size()] = '\0';
617         postponedDefinitions.push_back(PostponedDefinition(type, id, offset, str, linkedType, inlined));
618 }
619
620
621 void Interpreter::CreateTypeDescriptions() {
622         {
623                 TypeDescription &td(TypeDescription::CreateOrGet("Boolean"));
624                 td.SetDescription("Logical value which can be either true or false.");
625                 td.SetSize(sizeof(bool));
626         }
627         {
628                 TypeDescription &td(TypeDescription::CreateOrGet("Color"));
629                 td.SetDescription(
630                                 "A color in RGB format with an optional alpha channel.\n"
631                                 "Components range from 0 to 255.\n"
632                                 "Alpha defaults to 255 if omitted.");
633                 td.SetSize(sizeof(Color));
634         }
635         {
636                 TypeDescription &td(TypeDescription::CreateOrGet("Image"));
637                 td.SetDescription("Path to a PNG file with image data.");
638                 td.SetSize(sizeof(SDL_Surface));
639         }
640         {
641                 TypeDescription &td(TypeDescription::CreateOrGet("Number"));
642                 td.SetDescription("A signed integer.");
643                 td.SetSize(sizeof(int));
644         }
645         {
646                 int stringId(TypeDescription::GetTypeId("String"));
647                 TypeDescription &td(TypeDescription::CreateOrGet("Path"));
648                 td.SetDescription("A path in the filesystem which is interpreted relative to the source file's location.");
649                 td.SetSize(1);
650                 td.AddSupertype(stringId, 0);
651         }
652         {
653                 TypeDescription &td(TypeDescription::CreateOrGet("Script"));
654                 td.SetDescription("Collection of commands that define a behaviour.");
655                 td.SetSize(sizeof(Script));
656         }
657         {
658                 TypeDescription &td(TypeDescription::CreateOrGet("String"));
659                 td.SetDescription("Some characters.");
660                 td.SetSize(1);
661         }
662         {
663                 TypeDescription &td(TypeDescription::CreateOrGet("Vector"));
664                 td.SetDescription("A pair of numbers usually describing a 2D translation or offset.");
665                 td.SetSize(sizeof(Vector<int>));
666         }
667 }
668
669 }