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