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