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