]> git.localhorst.tv Git - l2e.git/blobdiff - src/loader/Loader.cpp
relocate scripts
[l2e.git] / src / loader / Loader.cpp
index a1183f790f2b0b98af6bc90dd0c9c092e7413380..3c7f2bd5b3005611e6c41d93f0fc5f7b2895b96b 100644 (file)
@@ -1,32 +1,29 @@
-/*
- * Loader.cpp
- *
- *  Created on: Sep 13, 2012
- *      Author: holy
- */
-
 #include "Loader.h"
 
+#include "../common/Script.h"
+
 #include <climits>
 #include <cstring>
 #include <fstream>
+#include <ostream>
 #include <stdexcept>
+#include <utility>
+#include <SDL_image.h>
 
+using std::endl;
+using std::make_pair;
 using std::map;
+using std::ostream;
 using std::string;
 using std::vector;
 
 namespace loader {
 
 Loader::~Loader() {
-       for(map<string, LoadedObjectFile>::const_iterator i(objectFiles.begin()), end(objectFiles.end()); i != end; ++i) {
-               if (i->second.surfaceCount) {
-                       for (int j(0); j < i->second.surfaceCount; ++j) {
-                               SDL_FreeSurface(i->second.surfaces[j]);
-                       }
-                       delete[] i->second.surfaces;
-               }
-               delete[] i->second.allocPtr;
+       for(map<string, char *>::const_iterator
+                       i(objectFiles.begin()), end(objectFiles.end());
+                       i != end; ++i) {
+               delete[] i->second;
        }
 }
 
@@ -37,126 +34,289 @@ void Loader::Load(const std::string &filePath) {
        int fileLength(file.tellg());
        int length(fileLength + 15);
 
-       LoadedObjectFile &object(objectFiles[filePath]);
-       object.allocPtr = new char[length];
+       char *block = new char[length];
+       ObjectFileHeader *header =
+                       reinterpret_cast<ObjectFileHeader *>(block);
 
-       if (reinterpret_cast<unsigned long>(object.allocPtr) % 16) {
-               object.fileHeader = reinterpret_cast<ObjectFileHeader *>(object.allocPtr + (16 - (reinterpret_cast<unsigned long>(object.allocPtr) % 16)));
-       } else {
-               object.fileHeader = reinterpret_cast<ObjectFileHeader *>(object.allocPtr);
+       unsigned long padding =
+                       reinterpret_cast<unsigned long>(block) % 16;
+       if (padding) {
+               header = reinterpret_cast<ObjectFileHeader *>(
+                               block + (16 - padding));
        }
 
        file.seekg(0, std::ios::beg);
-       file.read(reinterpret_cast<char *>(object.fileHeader), fileLength);
-
-       if (object.fileHeader->versionId != FORMAT_ID) {
-               throw std::runtime_error("mismatched version id of file " + filePath);
-       }
+       file.read(reinterpret_cast<char *>(header), fileLength);
 
-       LoadHeader(object);
-       LoadExports(object);
-       LoadImages(object);
-       LinkObjects(object);
-       LinkExternals(object);
-}
+       try {
+               header->IntegrityCheck(fileLength);
 
-void Loader::LoadHeader(LoadedObjectFile &object) {
-       ObjectFileHeader *header(object.fileHeader);
-       char *charHeader(reinterpret_cast<char *>(header));
+               LoadExports(header->ident,
+                               header->ExportsBegin(),
+                               header->ExportsEnd());
+               LoadExternals(header->ident,
+                               header->ExternalsBegin(),
+                               header->ExternalsEnd());
+               LoadObjects(header->ident,
+                               header->ObjectsBegin(),
+                               header->ObjectsEnd());
+               LoadArrays(header->ident,
+                               header->ArraysBegin(),
+                               header->ArraysEnd());
+               LoadImages(header->ident,
+                               header->ImagesBegin(),
+                               header->ImagesEnd());
+               LoadScripts(header->ident,
+                               header->ScriptsBegin(),
+                               header->ScriptsEnd());
 
-       object.typeOffsetsBegin = reinterpret_cast<TypeOffset *>(charHeader + sizeof(ObjectFileHeader));
-       for (object.typeOffsetsEnd = object.typeOffsetsBegin; object.typeOffsetsEnd->begin != 0 && object.typeOffsetsEnd->end != 0; ++object.typeOffsetsEnd);
+               InitObjects(
+                               header->ObjectsBegin(),
+                               header->ObjectsEnd());
+               InitArrays(
+                               header->ArraysBegin(),
+                               header->ArraysEnd());
+       } catch (...) {
+               delete[] block;
+               throw;
+       }
+       objectFiles.insert(make_pair(filePath, block));
 
-       object.exportsBegin = reinterpret_cast<Export *>(charHeader + header->exportsBegin);
-       object.exportsEnd = reinterpret_cast<Export *>(charHeader + header->exportsEnd);
+       if (objectFiles.size() > 1) {
+               for (std::vector<MissingExternal>::iterator
+                               i(unlinked.begin()); i != unlinked.end();) {
+                       std::map<string, LoadedExport>::const_iterator
+                                       found(exports.find(i->identifier));
+                       if (found != exports.end()) {
+                               LinkExternal(*i, found->second);
+                               i = unlinked.erase(i);
+                       } else {
+                               ++i;
+                       }
+               }
+       }
+}
 
-       object.externalsBegin = reinterpret_cast<External *>(charHeader + header->externalsBegin);
-       object.externalsEnd = reinterpret_cast<External *>(charHeader + header->externalsEnd);
+void Loader::LinkExternal(
+               const MissingExternal &ext,
+               const LoadedExport &exp) {
+       if (ext.typeId != exp.typeId) {
+               throw std::runtime_error("casting not implemented in loader");
+       }
+       const TypeDescription &td = TypeDescription::Get(ext.typeId);
+       if (ext.inlined) {
+               std::memcpy(ext.dest, exp.location, td.Size());
+       } else {
+               std::memcpy(ext.dest, &exp.location, sizeof(void *));
+       }
+}
 
-       object.exportStringsBegin = charHeader + header->exportStringsBegin;
-       object.exportStringsEnd = charHeader + header->exportStringsEnd;
+void Loader::LoadExports(char *src, Export *begin, Export *end) {
+       for (Export *i = begin; i < end; ++i) {
+               string identifier(src + i->nameOffset);
+               LoadedExport &exp(exports[identifier]);
+               exp.typeId = i->typeId;
+               exp.location = src + i->dataOffset;
+               exports.insert(make_pair(identifier, exp));
+       }
+}
 
-       object.externalStringsBegin = charHeader + header->externalStringsBegin;
-       object.externalStringsEnd = charHeader + header->externalStringsEnd;
+void Loader::LoadExternals(char *src, External *begin, External *end) {
+       for (External *i = begin; i < end; ++i) {
+               string identifier(src + i->nameOffset);
+               char *dest = src + i->referenceOffset;
 
-       object.imagesBegin = charHeader + header->imagesBegin;
-       object.imagesEnd = charHeader + header->imagesEnd;
+               map<string, LoadedExport>::const_iterator
+                               exp(exports.find(identifier));
+               if (exp == exports.end()) {
+                       MissingExternal m;
+                       m.identifier = identifier;
+                       m.dest = dest;
+                       m.typeId = i->typeId;
+                       m.inlined = i->inlined;
+                       unlinked.push_back(m);
+               } else {
+                       const TypeDescription &td(TypeDescription::Get(exp->second.typeId));
+                       if (i->inlined) {
+                               std::memcpy(dest, exp->second.location, td.Size());
+                       } else {
+                               std::memcpy(dest, &exp->second.location, sizeof(char *));
+                       }
+               }
+       }
+}
 
-       object.objectsBegin = charHeader + header->objectsBegin;
-       object.objectsEnd = charHeader + header->objectsEnd;
+void Loader::LoadImages(char *src, Image *begin, Image *end) {
+       for (Image *i = begin; i != end; ++i) {
+               const string path(src + i->pathOffset);
+               SDL_Surface **dest = reinterpret_cast<SDL_Surface **>(src + i->destOffset);
+               std::map<string, SDL_Surface *>::const_iterator
+                               found(images.find(path));
+               if (found != images.end()) {
+                       *dest = found->second;
+               } else {
+                       SDL_Surface *loaded = IMG_Load(path.c_str());
+                       images.insert(make_pair(path, loaded));
+                       *dest = loaded;
+               }
+       }
 }
 
-void Loader::LoadExports(LoadedObjectFile &object) {
-       for (Export *i(object.exportsBegin); i != object.exportsEnd; ++i) {
-               string identifier(object.At(i->nameOffset));
-               LoadedExport &exp(exports[identifier]);
-               exp.typeId = i->typeId;
-               exp.location = object.At(i->dataOffset);
+void Loader::LoadObjects(char *src, Object *begin, Object *end) {
+       for (Object *i = begin; i < end; i = i->Next()) {
+               const TypeDescription &td =
+                               TypeDescription::Get(i->typeId);
+               LoadObject(src, i->RawObject(), td);
        }
 }
 
-void Loader::LinkExternals(LoadedObjectFile &object) {
-       for (External *i(object.externalsBegin); i != object.externalsEnd; ++i) {
-               string identifier(object.At(i->nameOffset));
-               map<string, LoadedExport>::const_iterator exp(exports.find(identifier));
-               if (exp == exports.end()) {
-                       throw std::runtime_error("undefined reference to " + identifier);
+void Loader::LoadObject(char *src, char *object, const TypeDescription &td) {
+       for (TypeDescription::FieldIterator
+                       i(td.FieldsBegin()), end(td.FieldsEnd());
+                       i != end; ++i) {
+               const FieldDescription &field = i->second;
+               if (field.IsReferenced() || field.IsAggregate()) {
+                       char **dest(reinterpret_cast<char **>(object + field.Offset()));
+                       if (*dest) {
+                               *dest = src + *reinterpret_cast<unsigned int *>(dest);
+                       }
+               } else {
+                       const TypeDescription &nestedType
+                                       = TypeDescription::Get(field.TypeId());
+                       LoadObject(src, object + field.Offset(), nestedType);
                }
-               const TypeDescription &td(TypeDescription::Get(exp->second.typeId));
-               char *dest(object.At(i->referenceOffset));
-               if (i->inlined) {
-                       std::memcpy(dest, exp->second.location, td.Size());
+       }
+}
+
+void Loader::LoadArrays(char *src, Array *begin, Array *end) {
+       for (Array *i = begin; i < end; i = i->Next()) {
+               if (i->ref) {
+                       for (char *j = i->Data(), *end = i->Data() + i->size;
+                                       j < end; j += sizeof(void *)) {
+                               unsigned int offset = *reinterpret_cast<unsigned int *>(j);
+                               if (offset) {
+                                       *reinterpret_cast<char **>(j) = src + offset;
+                               }
+                       }
                } else {
-                       std::memcpy(dest, &exp->second.location, sizeof(char *));
+                       const TypeDescription &td = TypeDescription::Get(i->typeId);
+                       for (char *j = i->Data(), *end = i->Data() + i->size;
+                                       j < end; j += td.Size()) {
+                               LoadObject(src, j, td);
+                       }
                }
        }
 }
 
-void Loader::LoadImages(LoadedObjectFile &object) {
-       for (char *i(object.imagesBegin); i != object.imagesEnd;) {
-               ImageProperties *props(reinterpret_cast<ImageProperties *>(i));
-               i += sizeof(ImageProperties) + props->width * props->height * (props->depth / CHAR_BIT + (props->depth % CHAR_BIT ? 1 : 0));
-               ++object.surfaceCount;
-       }
-       if (object.surfaceCount == 0) return;
-
-       object.surfaces = new SDL_Surface *[object.surfaceCount];
-       int index(0);
-       for (char *i(object.imagesBegin); i != object.imagesEnd;) {
-               ImageProperties *props(reinterpret_cast<ImageProperties *>(i));
-               i += sizeof(ImageProperties);
-               SDL_Surface *image(SDL_CreateRGBSurfaceFrom(
-                               i,
-                               props->width,
-                               props->height,
-                               props->depth,
-                               props->pitch,
-                               props->rmask,
-                               props->gmask,
-                               props->bmask,
-                               props->amask));
-               i += props->width * props->height * (props->depth / CHAR_BIT + (props->depth % CHAR_BIT ? 1 : 0));
-               object.surfaces[index] = image;
-               ++index;
+void Loader::LoadScripts(char *src, Script *begin, Script *end) {
+       for (Script *s = begin; s < end; s = s->Next()) {
+               for (char *i = s->Text(), *end = s->Text() + s->size; i < end;) {
+                       common::Script::Code *code =
+                                       reinterpret_cast<common::Script::Code *>(i);
+                       if (code->type == common::Script::TYPE_ADDRESS && code->numParams > 0) {
+                               if (code->reg1 == 7) {
+                                       char *addr = i + sizeof(common::Script::Code);
+                                       unsigned int offset = *reinterpret_cast<unsigned int *>(addr);
+                                       if (offset) {
+                                               *reinterpret_cast<const char **>(addr) = src + offset;
+                                       }
+                               }
+       
+                               if (code->numParams > 1 && code->reg2 == 7) {
+                                       char *addr = i + sizeof(common::Script::Code);
+                                       if (code->reg1 == 7) {
+                                               addr += sizeof(void *);
+                                       }
+                                       unsigned int offset = *reinterpret_cast<unsigned int *>(addr);
+                                       if (offset) {
+                                               *reinterpret_cast<const char **>(addr) = src + offset;
+                                       }
+                               }
+                       }
+                       i += code->Size();
+               }
+       }
+}
+
+
+void Loader::InitObjects(Object *begin, Object *end) {
+       for (Object *i = begin; i < end; i = i->Next()) {
+               const TypeDescription &td =
+                               TypeDescription::Get(i->typeId);
+               InitObject(i->RawObject(), td);
+       }
+}
+
+void Loader::InitObject(char *object, const TypeDescription &td) {
+       td.Init(object);
+       td.Load(object);
+       for (TypeDescription::FieldIterator
+                       i(td.FieldsBegin()), end(td.FieldsEnd());
+                       i != end; ++i) {
+               const FieldDescription &field = i->second;
+               if (field.IsReferenced() || field.IsAggregate()) {
+                       continue;
+               }
+               const TypeDescription &nestedType
+                               = TypeDescription::Get(field.TypeId());
+               InitObject(object + field.Offset(), nestedType);
        }
 }
 
-void Loader::LinkObjects(LoadedObjectFile &object) {
-       for (TypeOffset *type(object.typeOffsetsBegin); type != object.typeOffsetsEnd; ++type) {
-               const TypeDescription &td(TypeDescription::Get(type->typeId));
-               for (int offset(type->begin); offset != type->end; offset += td.Size()) {
-                       LinkObject(object, td, object.At(offset));
+void Loader::InitArrays(Array *begin, Array *end) {
+       for (Array *i = begin; i < end; i = i->Next()) {
+               if (i->ref) {
+                       continue;
+               }
+               const TypeDescription &td = TypeDescription::Get(i->typeId);
+               for (char *j = i->Data(), *end = i->Data() + i->size;
+                               j < end; j += td.Size()) {
+                       InitObject(j, td);
                }
        }
 }
 
-void Loader::LinkObject(LoadedObjectFile &file, const TypeDescription &td, char *object) {
-       for (TypeDescription::FieldIterator i(td.FieldsBegin()), end(td.FieldsEnd()); i != end; ++i) {
-               const FieldDescription &field(i->second);
-               if (!field.IsReferenced()) continue;
-               char **dest(reinterpret_cast<char **>(object + field.Offset()));
-               *dest = file.At(*reinterpret_cast<int *>(*dest));
+
+ostream &Loader::Dump(ostream &out) const {
+       out << "loaded objects" << endl;
+       out << "==============" << endl;
+
+       out << objectFiles.size() << " object files" << endl;
+       for (std::map<string, char *>::const_iterator
+                       i(objectFiles.begin()), end(objectFiles.end());
+                       i != end; ++i) {
+               out << " - " << i->first << endl;
        }
+
+       out << exports.size() << " exports" << endl;
+       for (std::map<string, LoadedExport>::const_iterator
+                       i(exports.begin()), end(exports.end());
+                       i != end; ++i) {
+               const TypeDescription &td = TypeDescription::Get(i->second.typeId);
+               out << " - " << td.TypeName() << ' ' << i->first << endl;
+       }
+
+       out << images.size() << " images" << endl;
+       for (std::map<string, SDL_Surface *>::const_iterator
+                       i(images.begin()), end(images.end());
+                       i != end; ++i) {
+               out << " - " << i->first << endl;
+       }
+
+       out << unlinked.size() << " missing objects" << endl;
+       for (std::vector<MissingExternal>::const_iterator
+                       i(unlinked.begin()), end(unlinked.end());
+                       i != end; ++i) {
+               const TypeDescription &td = TypeDescription::Get(
+                               i->typeId);
+               out << " - " << td.TypeName() << ' ';
+               if (!i->inlined) {
+                       out << '*';
+               }
+               out << i->identifier << endl;
+       }
+
+       return out;
 }
 
 }