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