]> git.localhorst.tv Git - l2e.git/blob - src/loader/Interpreter.cpp
4b0f5e0ae1228e34680a72991a3d24efd858b874
[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 += sizeof(Script::Code);
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                         if (reg.size() != 2) {
367                                 throw Error("invalid register name " + reg);
368                         }
369                         ++i;
370                         if (i == end) {
371                                 throw Error("unexpected script end after move");
372                         }
373                         if ((*i)->GetType() != ScriptToken::REGISTER) {
374                                 switch (reg[0]) {
375                                         case 'a':
376                                                 size += sizeof(void *);
377                                                 break;
378                                         case 'i':
379                                                 size += sizeof(int);
380                                                 break;
381                                         case 'v':
382                                                 size += sizeof(Vector<int>);
383                                                 break;
384                                         default:
385                                                 throw Error("unknown register " + reg);
386                                 }
387                         }
388                 } else if (cmd == "add") {
389                         ++i;
390                         if (i == end) {
391                                 throw Error("unexpected script end after add");
392                         }
393                         const string &reg((*i)->RegisterName());
394                         if (reg.size() != 2) {
395                                 throw Error("invalid register name " + reg);
396                         }
397                         ++i;
398                         if (i == end) {
399                                 throw Error("unexpected script end after add");
400                         }
401                         if ((*i)->GetType() != ScriptToken::REGISTER) {
402                                 switch (reg[0]) {
403                                         case 'i':
404                                                 size += sizeof(int);
405                                                 break;
406                                         case 'v':
407                                                 size += sizeof(Vector<int>);
408                                                 break;
409                                         default:
410                                                 throw Error("expected register after add " + reg);
411                                 }
412                         }
413                 } else if (cmd == "mod") {
414                         ++i;
415                         if (i == end) {
416                                 throw Error("unexpected script end after mod");
417                         }
418                         ++i;
419                         if (i == end) {
420                                 throw Error("unexpected script end after mod");
421                         }
422                         if ((*i)->GetType() != ScriptToken::REGISTER) {
423                                 size += sizeof(int);
424                         }
425                 } else if (cmd == "rand") {
426                         ++i;
427                         if (i == end) {
428                                 throw Error("unexpected script end after rand");
429                         }
430                 } else if (cmd == "cmp") {
431                         ++i;
432                         if (i == end) {
433                                 throw Error("unexpected script end after cmp");
434                         }
435                         if ((*i)->GetType() != ScriptToken::REGISTER) {
436                                 size += sizeof(int);
437                         }
438                         ++i;
439                         if (i == end) {
440                                 throw Error("unexpected script end after cmp");
441                         }
442                         if ((*i)->GetType() != ScriptToken::REGISTER) {
443                                 size += sizeof(int);
444                         }
445                 } else if (cmd == "jmp") {
446                         size += sizeof(int);
447                         ++i;
448                         if (i == end) {
449                                 throw Error("unexpected script end after cmp");
450                         }
451                 } else if (cmd == "jeq") {
452                         size += sizeof(int);
453                         ++i;
454                         if (i == end) {
455                                 throw Error("unexpected script end after cmp");
456                         }
457                 } else if (cmd == "jne") {
458                         size += sizeof(int);
459                         ++i;
460                         if (i == end) {
461                                 throw Error("unexpected script end after cmp");
462                         }
463                 } else if (cmd == "jl") {
464                         size += sizeof(int);
465                         ++i;
466                         if (i == end) {
467                                 throw Error("unexpected script end after cmp");
468                         }
469                 } else if (cmd == "jle") {
470                         size += sizeof(int);
471                         ++i;
472                         if (i == end) {
473                                 throw Error("unexpected script end after cmp");
474                         }
475                 } else if (cmd == "jg") {
476                         size += sizeof(int);
477                         ++i;
478                         if (i == end) {
479                                 throw Error("unexpected script end after cmp");
480                         }
481                 } else if (cmd == "jge") {
482                         size += sizeof(int);
483                         ++i;
484                         if (i == end) {
485                                 throw Error("unexpected script end after cmp");
486                         }
487                 } else if (cmd == "sysc") {
488
489                 } else {
490                         throw Error("unknown command " + cmd);
491                 }
492         }
493
494         char *text(alloc.Alloc(size));
495         int cursor(0);
496         for (vector<ScriptToken *>::const_iterator i(s.begin()), end(s.end()); i != end; ++i) {
497                 if ((*i)->GetType() == ScriptToken::LABEL) {
498                         continue;
499                 }
500                 if ((*i)->GetType() != ScriptToken::COMMAND) {
501                         throw Error("unexpected script token");
502                 }
503                 const string &cmd((*i)->CommandName());
504                 if (cmd == "move") {
505                         Script::Code &code(CreateScriptCode(Script::COMMAND_MOVE, text + cursor));
506                         cursor += sizeof(Script::Code);
507                         code.numParams = 2;
508                         ++i;
509                         const string &reg((*i)->RegisterName());
510                         switch (reg[0]) {
511                                 case 'a':
512                                         code.type = Script::TYPE_ADDRESS;
513                                         break;
514                                 case 'i':
515                                         code.type = Script::TYPE_INTEGER;
516                                         break;
517                                 case 'v':
518                                         code.type = Script::TYPE_VECTOR;
519                                         break;
520                                 default:
521                                         throw Error("invalid register " + reg);
522                         }
523                         int regnum(reg[1] - '0');
524                         if (regnum < 0 || regnum > 6) {
525                                 throw Error("invalid register " + reg);
526                         }
527                         code.reg1 = regnum;
528                         ++i;
529
530                         if ((*i)->GetType() == ScriptToken::REGISTER) {
531                                 string reg2((*i)->RegisterName());
532                                 if (reg[0] != reg2[0]) {
533                                         throw Error("mixed-type commands not allowed");
534                                 }
535                                 int reg2num(reg[1] - '0');
536                                 if (reg2num < 0 || reg2num > 6) {
537                                         throw Error("invalid register " + reg2);
538                                 }
539                                 code.reg2 = reg2num;
540                         } else {
541                                 code.reg2 = 7;
542                                 switch (code.type) {
543                                         case Script::TYPE_ADDRESS:
544                                                 ReadScriptAddress(**i, text + cursor);
545                                                 cursor += sizeof(void *);
546                                                 break;
547                                         case Script::TYPE_INTEGER:
548                                                 ReadScriptInteger(**i, text + cursor);
549                                                 cursor += sizeof(int);
550                                                 break;
551                                         case Script::TYPE_VECTOR:
552                                                 ReadScriptVector(**i, text + cursor);
553                                                 cursor += sizeof(Vector<int>);
554                                                 break;
555                                 }
556                         }
557                 } else if (cmd == "add") {
558                         Script::Code &code(CreateScriptCode(Script::COMMAND_ADD, text + cursor));
559                         cursor += sizeof(Script::Code);
560                         code.numParams = 2;
561                         ++i;
562                         const string &reg((*i)->RegisterName());
563                         switch (reg[0]) {
564                                 case 'i':
565                                         code.type = Script::TYPE_INTEGER;
566                                         break;
567                                 case 'v':
568                                         code.type = Script::TYPE_VECTOR;
569                                         break;
570                                 default:
571                                         throw Error("invalid register " + reg);
572                         }
573                         int regnum(reg[1] - '0');
574                         if (regnum < 0 || regnum > 6) {
575                                 throw Error("invalid register " + reg);
576                         }
577                         code.reg1 = regnum;
578                         ++i;
579
580                         if ((*i)->GetType() == ScriptToken::REGISTER) {
581                                 string reg2((*i)->RegisterName());
582                                 if (reg[0] != reg2[0]) {
583                                         throw Error("mixed-type commands not allowed");
584                                 }
585                                 int reg2num(reg[1] - '0');
586                                 if (reg2num < 0 || reg2num > 6) {
587                                         throw Error("invalid register name " + reg2);
588                                 }
589                                 code.reg2 = reg2num;
590                         } else {
591                                 code.reg2 = 7;
592                                 switch (code.type) {
593                                         case Script::TYPE_INTEGER:
594                                                 ReadScriptInteger(**i, text + cursor);
595                                                 cursor += sizeof(int);
596                                                 break;
597                                         case Script::TYPE_VECTOR:
598                                                 ReadScriptVector(**i, text + cursor);
599                                                 cursor += sizeof(Vector<int>);
600                                                 break;
601                                 }
602                         }
603                 } else if (cmd == "mod") {
604                         Script::Code &code(CreateScriptCode(Script::COMMAND_MOD, text + cursor));
605                         cursor += sizeof(Script::Code);
606                         code.numParams = 2;
607                         ++i;
608                         const string &reg((*i)->RegisterName());
609                         if (reg[0] != 'i') {
610                                 throw Error("invalid register " + reg);
611                         }
612                         code.type = Script::TYPE_INTEGER;
613                         int regnum(reg[1] - '0');
614                         if (regnum < 0 || regnum > 6) {
615                                 throw Error("invalid register " + reg);
616                         }
617                         code.reg1 = regnum;
618                         ++i;
619
620                         if ((*i)->GetType() == ScriptToken::REGISTER) {
621                                 string reg2((*i)->RegisterName());
622                                 if (reg[0] != reg2[0]) {
623                                         throw Error("mixed-type commands not allowed");
624                                 }
625                                 int reg2num(reg[1] - '0');
626                                 if (reg2num < 0 || reg2num > 6) {
627                                         throw Error("invalid register name " + reg2);
628                                 }
629                                 code.reg2 = reg2num;
630                         } else {
631                                 code.reg2 = 7;
632                                 ReadScriptInteger(**i, text + cursor);
633                                 cursor += sizeof(int);
634                         }
635                 } else if (cmd == "rand") {
636                         Script::Code &code(CreateScriptCode(Script::COMMAND_RAND, text + cursor));
637                         cursor += sizeof(Script::Code);
638                         code.numParams = 1;
639                         ++i;
640                         const string &reg((*i)->RegisterName());
641                         if (reg[0] != 'i') {
642                                 throw Error("invalid register " + reg);
643                         }
644                         code.type = Script::TYPE_INTEGER;
645                         int regnum(reg[1] - '0');
646                         if (regnum < 0 || regnum > 6) {
647                                 throw Error("invalid register " + reg);
648                         }
649                         code.reg1 = regnum;
650                 } else if (cmd == "cmp") {
651                         Script::Code &code(CreateScriptCode(Script::COMMAND_CMP, text + cursor));
652                         cursor += sizeof(Script::Code);
653                         code.numParams = 2;
654                         ++i;
655                         const string &reg((*i)->RegisterName());
656                         if (reg[0] != 'i') {
657                                 throw Error("invalid register " + reg);
658                         }
659                         code.type = Script::TYPE_INTEGER;
660                         int regnum(reg[1] - '0');
661                         if (regnum < 0 || regnum > 6) {
662                                 throw Error("invalid register " + reg);
663                         }
664                         code.reg1 = regnum;
665                         ++i;
666
667                         if ((*i)->GetType() == ScriptToken::REGISTER) {
668                                 string reg2((*i)->RegisterName());
669                                 if (reg[0] != reg2[0]) {
670                                         throw Error("mixed-type commands not allowed");
671                                 }
672                                 int reg2num(reg[1] - '0');
673                                 if (reg2num < 0 || reg2num > 6) {
674                                         throw Error("invalid register name " + reg2);
675                                 }
676                                 code.reg2 = reg2num;
677                         } else {
678                                 code.reg2 = 7;
679                                 ReadScriptInteger(**i, text + cursor);
680                                 cursor += sizeof(int);
681                         }
682                 } else if (cmd == "jmp") {
683                         Script::Code &code(CreateScriptCode(Script::COMMAND_JMP, text + cursor));
684                         cursor += sizeof(Script::Code);
685                         code.numParams = 1;
686                         ++i;
687                         if (!labels.count((*i)->Identifier())) {
688                                 throw Error("use of undefined label " + (*i)->Identifier());
689                         }
690                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
691                         cursor += sizeof(int);
692                 } else if (cmd == "jeq") {
693                         Script::Code &code(CreateScriptCode(Script::COMMAND_JEQ, text + cursor));
694                         cursor += sizeof(Script::Code);
695                         code.numParams = 1;
696                         ++i;
697                         if (!labels.count((*i)->Identifier())) {
698                                 throw Error("use of undefined label " + (*i)->Identifier());
699                         }
700                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
701                         cursor += sizeof(int);
702                 } else if (cmd == "jne") {
703                         Script::Code &code(CreateScriptCode(Script::COMMAND_JNE, text + cursor));
704                         cursor += sizeof(Script::Code);
705                         code.numParams = 1;
706                         ++i;
707                         if (!labels.count((*i)->Identifier())) {
708                                 throw Error("use of undefined label " + (*i)->Identifier());
709                         }
710                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
711                         cursor += sizeof(int);
712                 } else if (cmd == "jl") {
713                         Script::Code &code(CreateScriptCode(Script::COMMAND_JL, text + cursor));
714                         cursor += sizeof(Script::Code);
715                         code.numParams = 1;
716                         ++i;
717                         if (!labels.count((*i)->Identifier())) {
718                                 throw Error("use of undefined label " + (*i)->Identifier());
719                         }
720                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
721                         cursor += sizeof(int);
722                 } else if (cmd == "jle") {
723                         Script::Code &code(CreateScriptCode(Script::COMMAND_JLE, text + cursor));
724                         cursor += sizeof(Script::Code);
725                         code.numParams = 1;
726                         ++i;
727                         if (!labels.count((*i)->Identifier())) {
728                                 throw Error("use of undefined label " + (*i)->Identifier());
729                         }
730                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
731                         cursor += sizeof(int);
732                 } else if (cmd == "jg") {
733                         Script::Code &code(CreateScriptCode(Script::COMMAND_JG, text + cursor));
734                         cursor += sizeof(Script::Code);
735                         code.numParams = 1;
736                         ++i;
737                         if (!labels.count((*i)->Identifier())) {
738                                 throw Error("use of undefined label " + (*i)->Identifier());
739                         }
740                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
741                         cursor += sizeof(int);
742                 } else if (cmd == "jge") {
743                         Script::Code &code(CreateScriptCode(Script::COMMAND_JGE, text + cursor));
744                         cursor += sizeof(Script::Code);
745                         code.numParams = 1;
746                         ++i;
747                         if (!labels.count((*i)->Identifier())) {
748                                 throw Error("use of undefined label " + (*i)->Identifier());
749                         }
750                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
751                         cursor += sizeof(int);
752                 } else if (cmd == "sysc") {
753                         Script::Code &code(CreateScriptCode(Script::COMMAND_SYSC, text + cursor));
754                         cursor += sizeof(Script::Code);
755                         code.numParams = 0;
756                 } else {
757                         throw Error("unknown command " + cmd);
758                 }
759         }
760
761         script->text = text;
762         script->textlen = size;
763 }
764
765 char *Interpreter::ReadScript(const vector<ScriptToken *> &s) {
766         char *mem(alloc.Alloc(sizeof(Script)));
767         new (mem) Script;
768         Script *script(reinterpret_cast<Script *>(mem));
769         ReadScript(s, script);
770         return mem;
771 }
772
773 Script::Code &Interpreter::CreateScriptCode(Script::Command c, char *dest) {
774         Script::Code &code(*reinterpret_cast<Script::Code *>(dest));
775         code.command = c;
776         code.numParams = 0;
777         code.type = Script::TYPE_NONE;
778         code.reg1 = 7;
779         code.reg2 = 7;
780         return code;
781 }
782
783 void Interpreter::ReadScriptAddress(const ScriptToken &t, char *dest) {
784         if (t.GetType() != ScriptToken::IDENTIFIER) {
785                 throw Error("expected identifier for address");
786         }
787         if (source.IsDefined(t.Identifier())) {
788                 const ParsedDefinition &def(GetDefinition(t.Identifier()));
789                 void *addr(GetObject(def.type, t.Identifier()));
790                 *reinterpret_cast<void **>(dest) = addr;
791         } else {
792                 throw Error("postponing values in scripts not implemented");
793         }
794 }
795
796 void Interpreter::ReadScriptInteger(const ScriptToken &t, char *dest) {
797         if (t.GetType() == ScriptToken::IDENTIFIER) {
798                 if (source.IsDefined(t.Identifier())) {
799                         void *num(GetObject(TypeDescription::GetTypeId("Number"), t.Identifier()));
800                         *reinterpret_cast<int *>(dest) = *reinterpret_cast<int *>(num);
801                 } else {
802                         throw Error("postponing values in scripts not implemented");
803                 }
804         } else if (t.GetType() == ScriptToken::LITERAL) {
805                 *reinterpret_cast<int *>(dest) = t.GetLiteral()->GetNumber();
806         } else {
807                 throw Error("expected identifier or integer literal");
808         }
809 }
810
811 void Interpreter::ReadScriptVector(const ScriptToken &t, char *dest) {
812         if (t.GetType() == ScriptToken::IDENTIFIER) {
813                 if (source.IsDefined(t.Identifier())) {
814                         void *vec(GetObject(TypeDescription::GetTypeId("Vector"), t.Identifier()));
815                         *reinterpret_cast<Vector<int> *>(dest) = *reinterpret_cast<Vector<int> *>(vec);
816                 } else {
817                         throw Error("postponing values in scripts not implemented");
818                 }
819         } else if (t.GetType() == ScriptToken::LITERAL) {
820                 *reinterpret_cast<Vector<int> *>(dest) = Vector<int>(t.GetLiteral()->GetX(), t.GetLiteral()->GetY());
821         } else {
822                 throw Error("expected identifier or vector literal");
823         }
824 }
825
826
827 SDL_Surface *Interpreter::GetImage(const string &path) {
828         std::map<string, SDL_Surface *>::const_iterator result(imageCache.find(path));
829         if (result != imageCache.end()) {
830                 return result->second;
831         } else {
832                 SDL_Surface *image(IMG_Load(path.c_str()));
833                 imageCache.insert(make_pair(path, image));
834                 return image;
835         }
836 }
837
838
839 bool Interpreter::CanLink(const Value &v) const {
840         return v.IsLiteral() || source.IsDefined(v.GetIdentifier());
841 }
842
843 void Interpreter::Postpone(int type, int id, std::ptrdiff_t offset, const std::string &identifier, int linkedType, bool inlined) {
844         char *str(alloc.Alloc(identifier.size() + 1));
845         std::memcpy(str, identifier.c_str(), identifier.size());
846         str[identifier.size()] = '\0';
847         postponedDefinitions.push_back(PostponedDefinition(type, id, offset, str, linkedType, inlined));
848 }
849
850
851 void Interpreter::CreateTypeDescriptions() {
852         {
853                 TypeDescription &td(TypeDescription::Create(BOOLEAN_ID, "Boolean"));
854                 td.SetDescription("Logical value which can be either true or false.");
855                 td.SetSize(sizeof(bool));
856         }
857         {
858                 TypeDescription &td(TypeDescription::Create(COLOR_ID, "Color"));
859                 td.SetDescription(
860                                 "A color in RGB format with an optional alpha channel.\n"
861                                 "Components range from 0 to 255.\n"
862                                 "Alpha defaults to 255 if omitted.");
863                 td.SetSize(sizeof(Color));
864         }
865         {
866                 TypeDescription &td(TypeDescription::Create(IMAGE_ID, "Image"));
867                 td.SetDescription("Path to a PNG file with image data.");
868                 td.SetSize(sizeof(SDL_Surface));
869         }
870         {
871                 TypeDescription &td(TypeDescription::Create(NUMBER_ID, "Number"));
872                 td.SetDescription("A signed integer.");
873                 td.SetSize(sizeof(int));
874         }
875         {;
876                 TypeDescription &td(TypeDescription::Create(PATH_ID, "Path"));
877                 td.SetDescription("A path in the filesystem which is interpreted relative to the source file's location.");
878                 td.SetSize(1);
879                 td.AddSupertype(STRING_ID, 0);
880         }
881         {
882                 TypeDescription &td(TypeDescription::Create(SCRIPT_ID, "Script"));
883                 td.SetDescription("Collection of commands that define a behaviour.");
884                 td.SetSize(sizeof(Script));
885         }
886         {
887                 TypeDescription &td(TypeDescription::Create(STRING_ID, "String"));
888                 td.SetDescription("Some characters.");
889                 td.SetSize(1);
890         }
891         {
892                 TypeDescription &td(TypeDescription::Create(VECTOR_ID, "Vector"));
893                 td.SetDescription("A pair of numbers usually describing a 2D translation or offset.");
894                 td.SetSize(sizeof(Vector<int>));
895         }
896 }
897
898 }