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