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