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