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