]> git.localhorst.tv Git - l2e.git/blob - src/loader/Interpreter.cpp
simplified definition postponing
[l2e.git] / src / loader / Interpreter.cpp
1 #include "Interpreter.h"
2
3 #include "ParsedSource.h"
4 #include "TypeDescription.h"
5 #include "../battle/Hero.h"
6 #include "../battle/Monster.h"
7 #include "../battle/PartyLayout.h"
8 #include "../battle/Resources.h"
9 #include "../common/Ikari.h"
10 #include "../common/Item.h"
11 #include "../common/Script.h"
12 #include "../common/Spell.h"
13 #include "../common/Stats.h"
14 #include "../common/TargetingMode.h"
15 #include "../graphics/ComplexAnimation.h"
16 #include "../graphics/Font.h"
17 #include "../graphics/Frame.h"
18 #include "../graphics/Gauge.h"
19 #include "../graphics/Menu.h"
20 #include "../graphics/SimpleAnimation.h"
21 #include "../graphics/Sprite.h"
22
23 #include <algorithm>
24 #include <cstring>
25 #include <SDL_image.h>
26
27 using battle::Hero;
28 using battle::Monster;
29 using battle::PartyLayout;
30 using common::Ikari;
31 using common::Item;
32 using common::Script;
33 using common::Spell;
34 using common::Stats;
35 using common::TargetingMode;
36 using graphics::Animation;
37 using graphics::Color;
38 using graphics::Font;
39 using graphics::Frame;
40 using graphics::Gauge;
41 using graphics::ComplexAnimation;
42 using graphics::SimpleAnimation;
43 using graphics::Sprite;
44 using math::Vector;
45 using std::make_pair;
46 using std::set;
47 using std::string;
48 using std::vector;
49
50 namespace loader {
51
52 Interpreter::~Interpreter() {
53         for (std::map<string, SDL_Surface *>::const_iterator i(imageCache.begin()), end(imageCache.end()); i != end; ++i) {
54                 SDL_FreeSurface(i->second);
55         }
56 }
57
58
59 const Interpreter::ParsedDefinition &Interpreter::GetDefinition(const string &identifier) {
60         std::map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(identifier));
61         if (i != parsedDefinitions.end()) {
62                 return i->second;
63         } else if (source.IsDefined(identifier)) {
64                 ReadDefinition(source.GetDefinition(identifier));
65                 return parsedDefinitions.at(identifier);
66         } else {
67                 throw Error("access to undefined object " + identifier);
68         }
69 }
70
71
72 void *Interpreter::GetObject(int typeId, const std::string &name) {
73         std::map<string, ParsedDefinition>::const_iterator i(parsedDefinitions.find(name));
74         if (i != parsedDefinitions.end()) {
75                 const TypeDescription &requested(TypeDescription::Get(typeId));
76                 const TypeDescription &actual(TypeDescription::Get(i->second.type));
77                 if (requested.TypeId() == actual.TypeId()) {
78                         return values[actual.TypeId()][i->second.id];
79                 } else if (actual.IsSubtypeOf(requested)) {
80                         char *sub(reinterpret_cast<char *>(values[actual.TypeId()][i->second.id]));
81                         std::ptrdiff_t offset(actual.SupertypeOffset(requested));
82                         return sub - offset;
83                 } else {
84                         throw Error("cannot cast " + actual.TypeName() + " to " + requested.TypeName());
85                 }
86         } else {
87                 throw Error("access to undefined object " + name);
88         }
89 }
90
91
92 void Interpreter::ReadSource() {
93         for (set<string>::const_iterator i(source.Exports().begin()), end(source.Exports().end()); i != end; ++i) {
94                 ReadDefinition(source.GetDefinition(*i));
95         }
96 }
97
98 void Interpreter::ReadDefinition(const Definition &dfn) {
99         if (parsedDefinitions.find(dfn.Identifier()) != parsedDefinitions.end()) {
100                 return;
101         }
102         if (dfn.HasLiteralValue()) {
103                 ReadLiteral(dfn);
104         } else {
105                 ReadObject(dfn);
106         }
107 }
108
109 void Interpreter::ReadLiteral(const Definition &dfn) {
110         const string &typeName(dfn.GetLiteral()->IsArray() ? "Array" : dfn.GetLiteral()->GetTypeName());
111         int typeId(TypeDescription::GetTypeId(typeName));
112         int id(values[typeId].size());
113         const TypeDescription &td(TypeDescription::Get(typeId));
114         int size(
115                         (dfn.GetLiteral()->GetType() == Literal::PATH
116                                         || dfn.GetLiteral()->GetType() == Literal::STRING)
117                         ? dfn.GetLiteral()->GetString().size() : td.Size());
118         char *object(alloc.Alloc(size));
119         values[typeId].push_back(object);
120         parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, typeId, id)));
121         if (dfn.GetLiteral()->GetType() == Literal::OBJECT) {
122                 ReadObject(typeId, id, object, *dfn.GetLiteral()->GetProperties());
123         } else {
124                 ReadLiteral(typeId, id, object, *dfn.GetLiteral());
125         }
126 }
127
128 void Interpreter::ReadLiteral(int typeId, int id, char *object, const Literal &literal) {
129         switch (literal.GetType()) {
130                 case Literal::ARRAY_VALUES:
131                 case Literal::ARRAY_PROPS:
132                 case Literal::ARRAY_IDENTS:
133                         throw Error("named arrays are not supported, sorry");
134                         break;
135                 case Literal::BOOLEAN:
136                         new (object) bool(literal.GetBoolean());
137                         break;
138                 case Literal::COLOR:
139                         new (object) Color(literal.GetRed(), literal.GetGreen(), literal.GetBlue(), literal.GetAlpha());
140                         break;
141                 case Literal::NUMBER:
142                         new (object) int(literal.GetNumber());
143                         break;
144                 case Literal::PATH:
145                         std::memcpy(object, literal.GetString().c_str(), literal.GetString().size());
146                         object[literal.GetString().size()] = '\0';
147                         break;
148                 case Literal::SCRIPT:
149                         new (object) Script;
150                         ReadScript(literal.GetScript(), reinterpret_cast<Script *>(object));
151                         break;
152                 case Literal::STRING:
153                         std::memcpy(object, literal.GetString().c_str(), literal.GetString().size());
154                         object[literal.GetString().size()] = '\0';
155                         break;
156                 case Literal::VECTOR:
157                         new (object) Vector<int>(literal.GetX(), literal.GetY());
158                         break;
159                 case Literal::OBJECT:
160                         throw Error("illogical branch: read literal object as non-object literal");
161         }
162 }
163
164
165 void *Interpreter::GetObject(int typeId, const Value &v) {
166         if (v.IsLiteral()) {
167                 if (v.GetLiteral().IsObject()) {
168                         int typeId(TypeDescription::GetTypeId(v.GetLiteral().GetTypeName()));
169                         const TypeDescription &td(TypeDescription::Get(typeId));
170                         char *object(alloc.Alloc(td.Size()));
171                         td.Construct(object);
172                         int id(values[typeId].size());
173                         values[typeId].push_back(object);
174                         ReadObject(typeId, id, object, *v.GetLiteral().GetProperties());
175                         return object;
176                 } else {
177                         int typeId(0), id(0);
178                         switch (v.GetLiteral().GetType()) {
179                                 case Literal::ARRAY_VALUES:
180                                 case Literal::ARRAY_PROPS:
181                                 case Literal::ARRAY_IDENTS:
182                                         throw Error("cannot copy arrays, sorry");
183                                         break;
184                                 case Literal::BOOLEAN:
185                                         {
186                                                 typeId = TypeDescription::GetTypeId("Boolean");
187                                                 id = values[typeId].size();
188                                                 const TypeDescription &td(TypeDescription::Get(typeId));
189                                                 char *object(alloc.Alloc(td.Size()));
190                                                 values[typeId].push_back(new (object) bool(v.GetLiteral().GetBoolean()));
191                                         }
192                                         break;
193                                 case Literal::COLOR:
194                                         {
195                                                 typeId = TypeDescription::GetTypeId("Color");
196                                                 id = values[typeId].size();
197                                                 const TypeDescription &td(TypeDescription::Get(typeId));
198                                                 char *object(alloc.Alloc(td.Size()));
199                                                 values[typeId].push_back(new (object) Color(v.GetLiteral().GetRed(), v.GetLiteral().GetGreen(), v.GetLiteral().GetBlue(), v.GetLiteral().GetAlpha()));
200                                         }
201                                         break;
202                                 case Literal::NUMBER:
203                                         {
204                                                 typeId = TypeDescription::GetTypeId("Number");
205                                                 id = values[typeId].size();
206                                                 const TypeDescription &td(TypeDescription::Get(typeId));
207                                                 char *object(alloc.Alloc(td.Size()));
208                                                 values[typeId].push_back(new (object) int(v.GetLiteral().GetNumber()));
209                                         }
210                                         break;
211                                 case Literal::PATH:
212                                         {
213                                                 typeId = TypeDescription::GetTypeId("Path");
214                                                 id = values[typeId].size();
215                                                 char *str(alloc.Alloc(v.GetLiteral().GetString().size() + 1));
216                                                 std::memcpy(str, v.GetLiteral().GetString().c_str(), v.GetLiteral().GetString().size());
217                                                 str[v.GetLiteral().GetString().size()] = '\0';
218                                                 values[typeId].push_back(str);
219                                         }
220                                         break;
221                                 case Literal::SCRIPT:
222                                         {
223                                                 typeId = TypeDescription::GetTypeId("Script");
224                                                 char *script(ReadScript(v.GetLiteral().GetScript()));
225                                                 id = values[typeId].size();
226                                                 values[typeId].push_back(script);
227                                         }
228                                         break;
229                                 case Literal::STRING:
230                                         {
231                                                 typeId = TypeDescription::GetTypeId("String");
232                                                 id = values[typeId].size();
233                                                 char *str(alloc.Alloc(v.GetLiteral().GetString().size() + 1));
234                                                 std::memcpy(str, v.GetLiteral().GetString().c_str(), v.GetLiteral().GetString().size());
235                                                 str[v.GetLiteral().GetString().size()] = '\0';
236                                                 values[typeId].push_back(str);
237                                         }
238                                         break;
239                                 case Literal::VECTOR:
240                                         {
241                                                 typeId = TypeDescription::GetTypeId("Vector");
242                                                 id = values[typeId].size();
243                                                 const TypeDescription &td(TypeDescription::Get(typeId));
244                                                 char *object(alloc.Alloc(td.Size()));
245                                                 values[typeId].push_back(new (object) Vector<int>(v.GetLiteral().GetX(), v.GetLiteral().GetY()));
246                                         }
247                                         break;
248                                 case Literal::OBJECT:
249                                         {
250                                                 typeId = TypeDescription::GetTypeId(v.GetLiteral().GetTypeName());
251                                                 const TypeDescription &td(TypeDescription::Get(typeId));
252                                                 id = values[typeId].size();
253                                                 char *object(alloc.Alloc(td.Size()));
254                                                 td.Construct(object);
255                                                 ReadObject(typeId, id, object, *v.GetLiteral().GetProperties());
256                                         }
257                                         break;
258                         }
259                         return values[typeId][id];
260                 }
261         } else {
262                 ReadDefinition(source.GetDefinition(v.GetIdentifier()));
263                 return GetObject(typeId, v.GetIdentifier());
264         }
265 }
266
267
268 void Interpreter::ReadObject(const Definition &dfn) {
269         int typeId(TypeDescription::GetTypeId(dfn.TypeName()));
270         const TypeDescription &td(TypeDescription::Get(typeId));
271         int id(values[typeId].size());
272         char *object(alloc.Alloc(td.Size()));
273         td.Construct(object);
274         values[typeId].push_back(object);
275         parsedDefinitions.insert(make_pair(dfn.Identifier(), ParsedDefinition(&dfn, typeId, id)));
276         ReadObject(typeId, id, object, *dfn.GetProperties());
277 }
278
279
280 void Interpreter::ReadObject(int typeId, int id, char *object, const PropertyList &props) {
281         const TypeDescription &td(TypeDescription::Get(typeId));
282         for (PropertyList::ConstIterator i(props.Begin()), end(props.End()); i != end; ++i) {
283                 const FieldDescription &fd(td.GetField(i->first));
284                 const TypeDescription &fieldType(TypeDescription::Get(fd.TypeId()));
285                 if (CanLink(*i->second)) {
286                         char *dest(object + fd.Offset());
287                         if (fd.IsAggregate()) {
288                                 int arraySize(i->second->GetLiteral().ArraySize());
289                                 size_t memberSize = fd.IsReferenced() ? sizeof(char *) : fieldType.Size();
290                                 char *aggregate = alloc.Alloc(arraySize * memberSize);
291                                 char *iter = aggregate;
292                                 if (i->second->GetLiteral().GetType() == Literal::ARRAY_PROPS) {
293                                         const vector<PropertyList *> &list(i->second->GetLiteral().GetPropertyLists());
294                                         for (vector<PropertyList *>::const_iterator j(list.begin()), end(list.end());
295                                                         j != end; ++j, iter += memberSize) {
296                                                 char *member;
297                                                 if (fd.IsReferenced()) {
298                                                         member = alloc.Alloc(fieldType.Size());
299                                                         *reinterpret_cast<char **>(iter) = member;
300                                                 } else {
301                                                         member = iter;
302                                                 }
303                                                 fieldType.Construct(member);
304                                                 ReadObject(fieldType.TypeId(), -1, member, **j);
305                                         }
306                                 } else if (i->second->GetLiteral().GetType() == Literal::ARRAY_VALUES) {
307                                         const vector<Value *> &list(i->second->GetLiteral().GetValues());
308                                         for (vector<Value *>::const_iterator j(list.begin()), end(list.end());
309                                                         j != end; ++j, iter += memberSize) {
310                                                 char *member;
311                                                 if (fd.IsReferenced()) {
312                                                         member = alloc.Alloc(fieldType.Size());
313                                                         *reinterpret_cast<char **>(iter) = member;
314                                                 } else {
315                                                         member = iter;
316                                                 }
317                                                 fieldType.Construct(member);
318                                                 ReadLiteral(fieldType.TypeId(), -1, member, (*j)->GetLiteral());
319                                         }
320                                 } else {
321                                         if (!fd.IsReferenced()) {
322                                                 // TODO: implement inline identifier arrays
323                                                 throw std::runtime_error("inline identifier arrays not implemented (yet)");
324                                         }
325                                         const vector<string> &list(i->second->GetLiteral().GetIdentifiers());
326                                         for (vector<string>::const_iterator j(list.begin()), end(list.end());
327                                                         j != end; ++j, iter += memberSize) {
328                                                 if (source.IsDefined(*j)) {
329                                                         *reinterpret_cast<void **>(iter)
330                                                                         = GetObject(fd.TypeId(), *j);
331                                                 } else {
332                                                         Postpone(iter, *j, fd.TypeId(), false);
333                                                 }
334                                         }
335                                 }
336                                 std::memcpy(dest, &aggregate, sizeof(char *));
337                                 dest += sizeof(char *);
338                                 std::memcpy(dest, &arraySize, sizeof(int));
339                         } else if (i->second->IsLiteral() && !fd.IsReferenced()) {
340                                 // inline literals
341                                 if (i->second->GetLiteral().IsObject()) {
342                                         ReadObject(fd.TypeId(), -1, dest, *i->second->GetLiteral().GetProperties());
343                                 } else {
344                                         ReadLiteral(fd.TypeId(), -1, dest, i->second->GetLiteral());
345                                 }
346                         } else {
347                                 char *src(reinterpret_cast<char *>(GetObject(fd.TypeId(), *i->second)));
348                                 if (fd.TypeId() == TypeDescription::GetTypeId("Image")) {
349                                         src = reinterpret_cast<char *>(GetImage(src));
350                                 }
351                                 if (fd.IsReferenced()) {
352                                         std::memcpy(dest, &src, sizeof(char *));
353                                 } else {
354                                         std::memcpy(dest, src, fieldType.Size());
355                                 }
356                         }
357                 } else {
358                         Postpone(object, i->second->GetIdentifier(), fd.TypeId(), !fd.IsReferenced(), fd.IsAggregate());
359                 }
360         }
361         td.Load(object);
362 }
363
364
365 void Interpreter::ReadScript(const std::vector<ScriptToken *> &s, Script *script) {
366         std::map<string, int> labels;
367         int size(0);
368         for (vector<ScriptToken *>::const_iterator i(s.begin()), end(s.end()); i != end; ++i) {
369                 if ((*i)->GetType() == ScriptToken::LABEL) {
370                         if (labels.count((*i)->Label())) {
371                                 throw Error("duplicate label " + (*i)->Label());
372                         } else {
373                                 labels[(*i)->Label()] = size;
374                         }
375                 } else if ((*i)->GetType() != ScriptToken::COMMAND) {
376                         throw Error("unexpected script token");
377                 }
378                 size += sizeof(Script::Code);
379                 const string &cmd((*i)->CommandName());
380                 if (cmd == "move") {
381                         ++i;
382                         if (i == end) {
383                                 throw Error("unexpected script end after move");
384                         }
385                         const string &reg((*i)->RegisterName());
386                         if (reg.size() != 2) {
387                                 throw Error("invalid register name " + reg);
388                         }
389                         ++i;
390                         if (i == end) {
391                                 throw Error("unexpected script end after move");
392                         }
393                         if ((*i)->GetType() != ScriptToken::REGISTER) {
394                                 switch (reg[0]) {
395                                         case 'a':
396                                                 size += sizeof(void *);
397                                                 break;
398                                         case 'i':
399                                                 size += sizeof(int);
400                                                 break;
401                                         case 'v':
402                                                 size += sizeof(Vector<int>);
403                                                 break;
404                                         default:
405                                                 throw Error("unknown register " + reg);
406                                 }
407                         }
408                 } else if (cmd == "add") {
409                         ++i;
410                         if (i == end) {
411                                 throw Error("unexpected script end after add");
412                         }
413                         const string &reg((*i)->RegisterName());
414                         if (reg.size() != 2) {
415                                 throw Error("invalid register name " + reg);
416                         }
417                         ++i;
418                         if (i == end) {
419                                 throw Error("unexpected script end after add");
420                         }
421                         if ((*i)->GetType() != ScriptToken::REGISTER) {
422                                 switch (reg[0]) {
423                                         case 'i':
424                                                 size += sizeof(int);
425                                                 break;
426                                         case 'v':
427                                                 size += sizeof(Vector<int>);
428                                                 break;
429                                         default:
430                                                 throw Error("expected register after add " + reg);
431                                 }
432                         }
433                 } else if (cmd == "mod") {
434                         ++i;
435                         if (i == end) {
436                                 throw Error("unexpected script end after mod");
437                         }
438                         ++i;
439                         if (i == end) {
440                                 throw Error("unexpected script end after mod");
441                         }
442                         if ((*i)->GetType() != ScriptToken::REGISTER) {
443                                 size += sizeof(int);
444                         }
445                 } else if (cmd == "rand") {
446                         ++i;
447                         if (i == end) {
448                                 throw Error("unexpected script end after rand");
449                         }
450                 } else if (cmd == "cmp") {
451                         ++i;
452                         if (i == end) {
453                                 throw Error("unexpected script end after cmp");
454                         }
455                         if ((*i)->GetType() != ScriptToken::REGISTER) {
456                                 size += sizeof(int);
457                         }
458                         ++i;
459                         if (i == end) {
460                                 throw Error("unexpected script end after cmp");
461                         }
462                         if ((*i)->GetType() != ScriptToken::REGISTER) {
463                                 size += sizeof(int);
464                         }
465                 } else if (cmd == "jmp") {
466                         size += sizeof(int);
467                         ++i;
468                         if (i == end) {
469                                 throw Error("unexpected script end after cmp");
470                         }
471                 } else if (cmd == "jeq") {
472                         size += sizeof(int);
473                         ++i;
474                         if (i == end) {
475                                 throw Error("unexpected script end after cmp");
476                         }
477                 } else if (cmd == "jne") {
478                         size += sizeof(int);
479                         ++i;
480                         if (i == end) {
481                                 throw Error("unexpected script end after cmp");
482                         }
483                 } else if (cmd == "jl") {
484                         size += sizeof(int);
485                         ++i;
486                         if (i == end) {
487                                 throw Error("unexpected script end after cmp");
488                         }
489                 } else if (cmd == "jle") {
490                         size += sizeof(int);
491                         ++i;
492                         if (i == end) {
493                                 throw Error("unexpected script end after cmp");
494                         }
495                 } else if (cmd == "jg") {
496                         size += sizeof(int);
497                         ++i;
498                         if (i == end) {
499                                 throw Error("unexpected script end after cmp");
500                         }
501                 } else if (cmd == "jge") {
502                         size += sizeof(int);
503                         ++i;
504                         if (i == end) {
505                                 throw Error("unexpected script end after cmp");
506                         }
507                 } else if (cmd == "sysc") {
508
509                 } else {
510                         throw Error("unknown command " + cmd);
511                 }
512         }
513
514         char *text(alloc.Alloc(size));
515         int cursor(0);
516         for (vector<ScriptToken *>::const_iterator i(s.begin()), end(s.end()); i != end; ++i) {
517                 if ((*i)->GetType() == ScriptToken::LABEL) {
518                         continue;
519                 }
520                 if ((*i)->GetType() != ScriptToken::COMMAND) {
521                         throw Error("unexpected script token");
522                 }
523                 const string &cmd((*i)->CommandName());
524                 if (cmd == "move") {
525                         Script::Code &code(CreateScriptCode(Script::COMMAND_MOVE, text + cursor));
526                         cursor += sizeof(Script::Code);
527                         code.numParams = 2;
528                         ++i;
529                         const string &reg((*i)->RegisterName());
530                         switch (reg[0]) {
531                                 case 'a':
532                                         code.type = Script::TYPE_ADDRESS;
533                                         break;
534                                 case 'i':
535                                         code.type = Script::TYPE_INTEGER;
536                                         break;
537                                 case 'v':
538                                         code.type = Script::TYPE_VECTOR;
539                                         break;
540                                 default:
541                                         throw Error("invalid register " + reg);
542                         }
543                         int regnum(reg[1] - '0');
544                         if (regnum < 0 || regnum > 6) {
545                                 throw Error("invalid register " + reg);
546                         }
547                         code.reg1 = regnum;
548                         ++i;
549
550                         if ((*i)->GetType() == ScriptToken::REGISTER) {
551                                 string reg2((*i)->RegisterName());
552                                 if (reg[0] != reg2[0]) {
553                                         throw Error("mixed-type commands not allowed");
554                                 }
555                                 int reg2num(reg[1] - '0');
556                                 if (reg2num < 0 || reg2num > 6) {
557                                         throw Error("invalid register " + reg2);
558                                 }
559                                 code.reg2 = reg2num;
560                         } else {
561                                 code.reg2 = 7;
562                                 switch (code.type) {
563                                         case Script::TYPE_ADDRESS:
564                                                 ReadScriptAddress(**i, text + cursor);
565                                                 cursor += sizeof(void *);
566                                                 break;
567                                         case Script::TYPE_INTEGER:
568                                                 ReadScriptInteger(**i, text + cursor);
569                                                 cursor += sizeof(int);
570                                                 break;
571                                         case Script::TYPE_VECTOR:
572                                                 ReadScriptVector(**i, text + cursor);
573                                                 cursor += sizeof(Vector<int>);
574                                                 break;
575                                 }
576                         }
577                 } else if (cmd == "add") {
578                         Script::Code &code(CreateScriptCode(Script::COMMAND_ADD, text + cursor));
579                         cursor += sizeof(Script::Code);
580                         code.numParams = 2;
581                         ++i;
582                         const string &reg((*i)->RegisterName());
583                         switch (reg[0]) {
584                                 case 'i':
585                                         code.type = Script::TYPE_INTEGER;
586                                         break;
587                                 case 'v':
588                                         code.type = Script::TYPE_VECTOR;
589                                         break;
590                                 default:
591                                         throw Error("invalid register " + reg);
592                         }
593                         int regnum(reg[1] - '0');
594                         if (regnum < 0 || regnum > 6) {
595                                 throw Error("invalid register " + reg);
596                         }
597                         code.reg1 = regnum;
598                         ++i;
599
600                         if ((*i)->GetType() == ScriptToken::REGISTER) {
601                                 string reg2((*i)->RegisterName());
602                                 if (reg[0] != reg2[0]) {
603                                         throw Error("mixed-type commands not allowed");
604                                 }
605                                 int reg2num(reg[1] - '0');
606                                 if (reg2num < 0 || reg2num > 6) {
607                                         throw Error("invalid register name " + reg2);
608                                 }
609                                 code.reg2 = reg2num;
610                         } else {
611                                 code.reg2 = 7;
612                                 switch (code.type) {
613                                         case Script::TYPE_INTEGER:
614                                                 ReadScriptInteger(**i, text + cursor);
615                                                 cursor += sizeof(int);
616                                                 break;
617                                         case Script::TYPE_VECTOR:
618                                                 ReadScriptVector(**i, text + cursor);
619                                                 cursor += sizeof(Vector<int>);
620                                                 break;
621                                 }
622                         }
623                 } else if (cmd == "mod") {
624                         Script::Code &code(CreateScriptCode(Script::COMMAND_MOD, text + cursor));
625                         cursor += sizeof(Script::Code);
626                         code.numParams = 2;
627                         ++i;
628                         const string &reg((*i)->RegisterName());
629                         if (reg[0] != 'i') {
630                                 throw Error("invalid register " + reg);
631                         }
632                         code.type = Script::TYPE_INTEGER;
633                         int regnum(reg[1] - '0');
634                         if (regnum < 0 || regnum > 6) {
635                                 throw Error("invalid register " + reg);
636                         }
637                         code.reg1 = regnum;
638                         ++i;
639
640                         if ((*i)->GetType() == ScriptToken::REGISTER) {
641                                 string reg2((*i)->RegisterName());
642                                 if (reg[0] != reg2[0]) {
643                                         throw Error("mixed-type commands not allowed");
644                                 }
645                                 int reg2num(reg[1] - '0');
646                                 if (reg2num < 0 || reg2num > 6) {
647                                         throw Error("invalid register name " + reg2);
648                                 }
649                                 code.reg2 = reg2num;
650                         } else {
651                                 code.reg2 = 7;
652                                 ReadScriptInteger(**i, text + cursor);
653                                 cursor += sizeof(int);
654                         }
655                 } else if (cmd == "rand") {
656                         Script::Code &code(CreateScriptCode(Script::COMMAND_RAND, text + cursor));
657                         cursor += sizeof(Script::Code);
658                         code.numParams = 1;
659                         ++i;
660                         const string &reg((*i)->RegisterName());
661                         if (reg[0] != 'i') {
662                                 throw Error("invalid register " + reg);
663                         }
664                         code.type = Script::TYPE_INTEGER;
665                         int regnum(reg[1] - '0');
666                         if (regnum < 0 || regnum > 6) {
667                                 throw Error("invalid register " + reg);
668                         }
669                         code.reg1 = regnum;
670                 } else if (cmd == "cmp") {
671                         Script::Code &code(CreateScriptCode(Script::COMMAND_CMP, text + cursor));
672                         cursor += sizeof(Script::Code);
673                         code.numParams = 2;
674                         ++i;
675                         const string &reg((*i)->RegisterName());
676                         if (reg[0] != 'i') {
677                                 throw Error("invalid register " + reg);
678                         }
679                         code.type = Script::TYPE_INTEGER;
680                         int regnum(reg[1] - '0');
681                         if (regnum < 0 || regnum > 6) {
682                                 throw Error("invalid register " + reg);
683                         }
684                         code.reg1 = regnum;
685                         ++i;
686
687                         if ((*i)->GetType() == ScriptToken::REGISTER) {
688                                 string reg2((*i)->RegisterName());
689                                 if (reg[0] != reg2[0]) {
690                                         throw Error("mixed-type commands not allowed");
691                                 }
692                                 int reg2num(reg[1] - '0');
693                                 if (reg2num < 0 || reg2num > 6) {
694                                         throw Error("invalid register name " + reg2);
695                                 }
696                                 code.reg2 = reg2num;
697                         } else {
698                                 code.reg2 = 7;
699                                 ReadScriptInteger(**i, text + cursor);
700                                 cursor += sizeof(int);
701                         }
702                 } else if (cmd == "jmp") {
703                         Script::Code &code(CreateScriptCode(Script::COMMAND_JMP, 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 == "jeq") {
713                         Script::Code &code(CreateScriptCode(Script::COMMAND_JEQ, 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 == "jne") {
723                         Script::Code &code(CreateScriptCode(Script::COMMAND_JNE, 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 == "jl") {
733                         Script::Code &code(CreateScriptCode(Script::COMMAND_JL, 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 == "jle") {
743                         Script::Code &code(CreateScriptCode(Script::COMMAND_JLE, 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 == "jg") {
753                         Script::Code &code(CreateScriptCode(Script::COMMAND_JG, text + cursor));
754                         cursor += sizeof(Script::Code);
755                         code.numParams = 1;
756                         ++i;
757                         if (!labels.count((*i)->Identifier())) {
758                                 throw Error("use of undefined label " + (*i)->Identifier());
759                         }
760                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
761                         cursor += sizeof(int);
762                 } else if (cmd == "jge") {
763                         Script::Code &code(CreateScriptCode(Script::COMMAND_JGE, text + cursor));
764                         cursor += sizeof(Script::Code);
765                         code.numParams = 1;
766                         ++i;
767                         if (!labels.count((*i)->Identifier())) {
768                                 throw Error("use of undefined label " + (*i)->Identifier());
769                         }
770                         *reinterpret_cast<int *>(text + cursor) = labels[(*i)->Identifier()];
771                         cursor += sizeof(int);
772                 } else if (cmd == "sysc") {
773                         Script::Code &code(CreateScriptCode(Script::COMMAND_SYSC, text + cursor));
774                         cursor += sizeof(Script::Code);
775                         code.numParams = 0;
776                 } else {
777                         throw Error("unknown command " + cmd);
778                 }
779         }
780
781         script->text = text;
782         script->textlen = size;
783 }
784
785 char *Interpreter::ReadScript(const vector<ScriptToken *> &s) {
786         char *mem(alloc.Alloc(sizeof(Script)));
787         new (mem) Script;
788         Script *script(reinterpret_cast<Script *>(mem));
789         ReadScript(s, script);
790         return mem;
791 }
792
793 Script::Code &Interpreter::CreateScriptCode(Script::Command c, char *dest) {
794         Script::Code &code(*reinterpret_cast<Script::Code *>(dest));
795         code.command = c;
796         code.numParams = 0;
797         code.type = Script::TYPE_NONE;
798         code.reg1 = 7;
799         code.reg2 = 7;
800         return code;
801 }
802
803 void Interpreter::ReadScriptAddress(const ScriptToken &t, char *dest) {
804         if (t.GetType() != ScriptToken::IDENTIFIER) {
805                 throw Error("expected identifier for address");
806         }
807         if (source.IsDefined(t.Identifier())) {
808                 const ParsedDefinition &def(GetDefinition(t.Identifier()));
809                 void *addr(GetObject(def.type, t.Identifier()));
810                 *reinterpret_cast<void **>(dest) = addr;
811         } else {
812                 throw Error("postponing values in scripts not implemented");
813         }
814 }
815
816 void Interpreter::ReadScriptInteger(const ScriptToken &t, char *dest) {
817         if (t.GetType() == ScriptToken::IDENTIFIER) {
818                 if (source.IsDefined(t.Identifier())) {
819                         void *num(GetObject(TypeDescription::GetTypeId("Number"), t.Identifier()));
820                         *reinterpret_cast<int *>(dest) = *reinterpret_cast<int *>(num);
821                 } else {
822                         throw Error("postponing values in scripts not implemented");
823                 }
824         } else if (t.GetType() == ScriptToken::LITERAL) {
825                 *reinterpret_cast<int *>(dest) = t.GetLiteral()->GetNumber();
826         } else {
827                 throw Error("expected identifier or integer literal");
828         }
829 }
830
831 void Interpreter::ReadScriptVector(const ScriptToken &t, char *dest) {
832         if (t.GetType() == ScriptToken::IDENTIFIER) {
833                 if (source.IsDefined(t.Identifier())) {
834                         void *vec(GetObject(TypeDescription::GetTypeId("Vector"), t.Identifier()));
835                         *reinterpret_cast<Vector<int> *>(dest) = *reinterpret_cast<Vector<int> *>(vec);
836                 } else {
837                         throw Error("postponing values in scripts not implemented");
838                 }
839         } else if (t.GetType() == ScriptToken::LITERAL) {
840                 *reinterpret_cast<Vector<int> *>(dest) = Vector<int>(t.GetLiteral()->GetX(), t.GetLiteral()->GetY());
841         } else {
842                 throw Error("expected identifier or vector literal");
843         }
844 }
845
846
847 SDL_Surface *Interpreter::GetImage(const string &path) {
848         std::map<string, SDL_Surface *>::const_iterator result(imageCache.find(path));
849         if (result != imageCache.end()) {
850                 return result->second;
851         } else {
852                 SDL_Surface *image(IMG_Load(path.c_str()));
853                 imageCache.insert(make_pair(path, image));
854                 return image;
855         }
856 }
857
858
859 bool Interpreter::CanLink(const Value &v) const {
860         return v.IsLiteral() || source.IsDefined(v.GetIdentifier());
861 }
862
863 void Interpreter::Postpone(
864                 char *dest,
865                 const std::string &identifier,
866                 int type,
867                 bool inlined,
868                 bool aggregate) {
869         char *str(alloc.Alloc(identifier.size() + 1));
870         std::memcpy(str, identifier.c_str(), identifier.size());
871         str[identifier.size()] = '\0';
872         postponedDefinitions.push_back(
873                         PostponedDefinition(dest, str, type, inlined, aggregate));
874 }
875
876
877 void Interpreter::CreateTypeDescriptions() {
878         {
879                 TypeDescription &td(TypeDescription::Create(BOOLEAN_ID, "Boolean"));
880                 td.SetDescription("Logical value which can be either true or false.");
881                 td.SetSize(sizeof(bool));
882         }
883         {
884                 TypeDescription &td(TypeDescription::Create(COLOR_ID, "Color"));
885                 td.SetDescription(
886                                 "A color in RGB format with an optional alpha channel.\n"
887                                 "Components range from 0 to 255.\n"
888                                 "Alpha defaults to 255 if omitted.");
889                 td.SetSize(sizeof(Color));
890         }
891         {
892                 TypeDescription &td(TypeDescription::Create(IMAGE_ID, "Image"));
893                 td.SetDescription("Path to a PNG file with image data.");
894                 td.SetSize(sizeof(SDL_Surface));
895         }
896         {
897                 TypeDescription &td(TypeDescription::Create(NUMBER_ID, "Number"));
898                 td.SetDescription("A signed integer.");
899                 td.SetSize(sizeof(int));
900         }
901         {;
902                 TypeDescription &td(TypeDescription::Create(PATH_ID, "Path"));
903                 td.SetDescription("A path in the filesystem which is interpreted relative to the source file's location.");
904                 td.SetSize(1);
905                 td.AddSupertype(STRING_ID, 0);
906         }
907         {
908                 TypeDescription &td(TypeDescription::Create(SCRIPT_ID, "Script"));
909                 td.SetDescription("Collection of commands that define a behaviour.");
910                 td.SetSize(sizeof(Script));
911         }
912         {
913                 TypeDescription &td(TypeDescription::Create(STRING_ID, "String"));
914                 td.SetDescription("Some characters.");
915                 td.SetSize(1);
916         }
917         {
918                 TypeDescription &td(TypeDescription::Create(VECTOR_ID, "Vector"));
919                 td.SetDescription("A pair of numbers usually describing a 2D translation or offset.");
920                 td.SetSize(sizeof(Vector<int>));
921         }
922 }
923
924 }