#include "binds.h" #include "scriptarray.h" #include "main/references.h" #include "main/logging.h" #include "design/design.h" #include "network/message.h" #include "util/elevation_map.h" #include "util/save_file.h" #include "threads.h" #include "color.h" #include "empire.h" namespace scripts { static inline asITypeInfo* getSavable(asIScriptEngine* engine) { return (asITypeInfo*)engine->GetUserData(EDID_SavableType); } static inline asIScriptFunction* getSavableWrite(asIScriptEngine* engine) { return (asIScriptFunction*)engine->GetUserData(EDID_SavableWrite); } static inline asIScriptFunction* getSavableRead(asIScriptEngine* engine) { return (asIScriptFunction*)engine->GetUserData(EDID_SavableRead); } static void createSaveMessage(void* mem) { scripts::throwException("Don't instantiate savefiles manually."); } static void destroyMessage(SaveMessage& msg) { msg.~SaveMessage(); } static net::msize_t reserveInt(SaveMessage& msg) { try { return msg.reserve(); } catch(...) { scripts::throwException("Message needs to be aligned before reserving space."); return 0; } } static void fillInt(SaveMessage& msg, net::msize_t pos, int value) { msg.fill(pos, value); } static void msgDumpRead(SaveMessage& msg) { SaveMessage::Position pos = msg.getReadPosition(); print("Read position: %d bytes, %d bits", pos.bytes, pos.bits); } static void msgDumpWrite(SaveMessage& msg) { SaveMessage::Position pos = msg.getWritePosition(); print("Write position: %d bytes, %d bits", pos.bytes, pos.bits); } static void writeSerial(SaveMessage& msg, asIScriptObject* serial) { if(serial == 0) { scripts::throwException("Null Savable@ when writing"); return; } scripts::Manager& manager = scripts::Manager::fromEngine(serial->GetEngine()); Call cl = manager.call(getSavableWrite(manager.engine)); cl.setObject(serial); cl.push(&msg); cl.call(); serial->Release(); } static void readSerial(SaveMessage& msg, asIScriptObject* serial) { if(serial == 0) { scripts::throwException("Null Savable@ when reading"); return; } scripts::Manager& manager = scripts::Manager::fromEngine(serial->GetEngine()); Call cl = manager.call(getSavableRead(manager.engine)); cl.setObject(serial); cl.push(&msg); cl.call(); serial->Release(); } static SaveMessage& readElevation(SaveMessage& msg, ElevationMap& emap) { msg >> emap.generated; msg >> emap.gridStart; msg >> emap.gridSize; msg >> emap.gridInterval; msg >> emap.gridResolution; unsigned size = emap.gridResolution.x * emap.gridResolution.y; emap.grid = (float*)calloc(size, sizeof(float)); try { msg.readBits((uint8_t*)emap.grid, size * sizeof(float) * 8); } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); } return msg; } static SaveMessage& writeElevation(SaveMessage& msg, ElevationMap& emap) { msg << emap.generated; msg << emap.gridStart; msg << emap.gridSize; msg << emap.gridInterval; msg << emap.gridResolution; unsigned size = emap.gridResolution.x * emap.gridResolution.y; msg.writeBits((uint8_t*)emap.grid, size * sizeof(float) * 8); return msg; } SaveMessage& loadObject(SaveMessage& msg, Object** obj) { int id; try { msg >> id; } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); return msg; } if(Object* prev = *obj) prev->drop(); *obj = getObjectByID(id, true); return msg; } Object* readObject_e(SaveMessage& msg) { Object* obj = nullptr; loadObject(msg, &obj); return obj; } SaveMessage& saveObject(SaveMessage& msg, Object* obj) { if(obj) { msg << obj->id; } else { int id = 0; msg << id; } return msg; } SaveMessage& readEmpire(SaveMessage& msg, Empire** emp) { unsigned char empID; try { msg >> empID; } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); return msg; } *emp = Empire::getEmpireByID(empID); return msg; } SaveMessage& writeEmpire(SaveMessage& msg, Empire* emp) { if(emp) msg << emp->id; else msg << INVALID_EMPIRE; return msg; } static SaveMessage& readDesign(SaveMessage& msg, const Design** dsg) { unsigned char empID; if(auto* prev = *dsg) { prev->drop(); prev = 0; } try { msg >> empID; if(empID == 0) { *dsg = 0; return msg; } Empire* emp = Empire::getEmpireByID(empID); if(!emp) { *dsg = 0; return msg; } int dsgID; msg >> dsgID; *dsg = emp->getDesign(dsgID, true); } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); return msg; } return msg; } static SaveMessage& writeDesign(SaveMessage& msg, const Design** dsg) { if(*dsg) { if(!(*dsg)->owner) { unsigned char empID = 0; msg << empID; scripts::throwException("Cannot transmit design without owner."); return msg; } msg << (*dsg)->owner->id; msg << (*dsg)->id; } else { unsigned char empID = 0; msg << empID; } return msg; } static bool wrapReadBit(SaveMessage& msg) { try { return msg.readBit(); } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); } return false; } static void wrapReadAlign(SaveMessage& msg) { try { msg.readAlign(); } catch(net::MessageReadError) { scripts::throwException("Error aligning message: end of message."); } } template static SaveMessage& wrapRead(SaveMessage& msg, T& value) { try { msg >> value; } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); } return msg; } static unsigned getScriptVersion(SaveMessage& msg) { return msg.file.scriptVersion; } static void setScriptVersion(SaveMessage& msg, unsigned version) { msg.file.scriptVersion = version; } static unsigned getStartVersion(SaveMessage& msg) { return msg.file.startVersion; } static void setStartVersion(SaveMessage& msg, unsigned version) { msg.file.startVersion = version; } static void addIdentifier(SaveMessage& msg, unsigned type, int id, const std::string& ident) { if(!msg.file.addIdentifier(type, id, ident)) scripts::throwException(format("Duplicate identifier: '$1'.", ident.c_str()).c_str()); } static int readIdentifier(SaveMessage& msg, unsigned type) { int id; msg >> id; return msg.file.getIdentifier(type, id); } static int getIdentifier(SaveMessage& msg, unsigned type, unsigned id) { return msg.file.getIdentifier(type, id); } static void writeIdentifier(SaveMessage& msg, unsigned type, int id) { msg << id; } static unsigned getIdentifierCount(SaveMessage& msg, unsigned type) { return msg.file.getIdentifierCount(type); } static unsigned getPrevIdentifierCount(SaveMessage& msg, unsigned type) { return msg.file.getPrevIdentifierCount(type); } static int saveCmp(SaveMessage& msg, unsigned version) { if(version > msg.file.scriptVersion) return -1; else if(version < msg.file.scriptVersion) return 1; else return 0; } void RegisterSaveFileBinds(bool server, bool decl) { if(decl) { ClassBind msg("SaveFile", asOBJ_VALUE | asOBJ_APP_CLASS_CDA, sizeof(SaveMessage)); return; } InterfaceBind inf("Savable"); //Message manipulation ClassBind msg("SaveFile"); msg.addConstructor("void f()", asFUNCTION(createSaveMessage)); msg.addDestructor("void f()", asFUNCTION(destroyMessage)); msg.addMethod("void dump() const", asMETHOD(SaveMessage, dump)); msg.addMethod("uint get_size() const", asMETHOD(SaveMessage, size)); msg.addExternMethod("void set_scriptVersion(uint version)", asFUNCTION(setScriptVersion)); msg.addExternMethod("uint get_scriptVersion()", asFUNCTION(getScriptVersion)); msg.addExternMethod("void set_startVersion(uint version)", asFUNCTION(setStartVersion)); msg.addExternMethod("uint get_startVersion()", asFUNCTION(getStartVersion)); msg.addExternMethod("void addIdentifier(uint type, int id, const string&in ident)", asFUNCTION(addIdentifier)); msg.addExternMethod("uint readIdentifier(uint type)", asFUNCTION(readIdentifier)); msg.addExternMethod("int getIdentifier(uint ident, int id)", asFUNCTION(getIdentifier)); msg.addExternMethod("void writeIdentifier(uint type, int id)", asFUNCTION(writeIdentifier)); msg.addExternMethod("uint getIdentifierCount(uint type)", asFUNCTION(getIdentifierCount)); msg.addExternMethod("uint getPrevIdentifierCount(uint type)", asFUNCTION(getPrevIdentifierCount)); msg.addExternMethod("int opCmp(uint version)", asFUNCTION(saveCmp)); msg.addExternMethod("uint reserve()", asFUNCTION(reserveInt)); msg.addExternMethod("void fill(uint, int)", asFUNCTION(fillInt)); msg.addMethod("void writeBit(bool)", asMETHOD(SaveMessage, writeBit)); msg.addMethod("void write1()", asMETHOD(SaveMessage, write1)); msg.addMethod("void write0()", asMETHOD(SaveMessage, write0)); msg.addExternMethod("bool readBit()", asFUNCTION(wrapReadBit)); msg.addExternMethod("void readAlign()", asFUNCTION(wrapReadAlign)); msg.addMethod("void writeAlign()", asMETHOD(SaveMessage, writeAlign)); msg.addExternMethod("void dumpReadPosition()", asFUNCTION(msgDumpRead)); msg.addExternMethod("void dumpWritePosition()", asFUNCTION(msgDumpWrite)); msg.addExternMethod("Message& opShl(Savable@)", asFUNCTION(writeSerial)); msg.addExternMethod("Message& opShr(Savable@)", asFUNCTION(readSerial)); msg.addMethod("SaveFile& opShl(bool)", asMETHODPR(net::Message, operator<<, (bool), net::Message&)); msg.addExternMethod("SaveFile& opShr(bool&)", asFUNCTION(wrapRead)); #define BIND_TYPE(stype, rtype)\ msg.addMethod("SaveFile& opShl(" #stype "&)",\ asMETHODPR(net::Message, operator<<, (const rtype&), net::Message&));\ \ msg.addExternMethod("SaveFile& opShr(" #stype "&)",\ asFUNCTION(wrapRead)); BIND_TYPE(uint8, unsigned char); BIND_TYPE(uint16, unsigned short); BIND_TYPE(int8, char); BIND_TYPE(int16, short); BIND_TYPE(int64, int64_t); BIND_TYPE(int, int); BIND_TYPE(uint, unsigned); BIND_TYPE(float, float); BIND_TYPE(double, double); BIND_TYPE(string, std::string); BIND_TYPE(Color, Color); BIND_TYPE(vec3d, vec3d); BIND_TYPE(vec3f, vec3f); BIND_TYPE(vec3i, vec3i); BIND_TYPE(vec2d, vec2d); BIND_TYPE(vec2u, vec2u); BIND_TYPE(vec2i, vec2i); BIND_TYPE(vec2f, vec2f); BIND_TYPE(quaterniond, quaterniond); BIND_TYPE(locked_int, int); BIND_TYPE(locked_double, double); msg.addExternMethod("SaveFile& opShl(ElevationMap&)", asFUNCTION(writeElevation)); msg.addExternMethod("SaveFile& opShr(ElevationMap&)", asFUNCTION(readElevation)); msg.addExternMethod("SaveFile& opShl(Object@+)", asFUNCTION(saveObject)); msg.addExternMethod("SaveFile& opShr(Object@&)", asFUNCTION(loadObject)); msg.addExternMethod("Object@ readObject()", asFUNCTION(readObject_e)); msg.addExternMethod("SaveFile& opShl(Empire@)", asFUNCTION(writeEmpire)); msg.addExternMethod("SaveFile& opShr(Empire@&)", asFUNCTION(readEmpire)); msg.addExternMethod("SaveFile& opShl(const Design@&)", asFUNCTION(writeDesign)); msg.addExternMethod("SaveFile& opShr(const Design@&)", asFUNCTION(readDesign)); //Savable interface inf.addMethod("void load(SaveFile& file)"); inf.addMethod("void save(SaveFile& file)"); EnumBind glob("SaveGlobals"); glob["SAVE_IDENTIFIER_START"] = SI_SCRIPT_START; //Keep function pointers asITypeInfo* type = getEngine()->GetTypeInfoByName("Savable"); if(type) { asIScriptEngine* engine = getEngine(); engine->SetUserData(type, EDID_SavableType); engine->SetUserData(type->GetMethodByDecl("void save(SaveFile& file)"), EDID_SavableWrite); engine->SetUserData(type->GetMethodByDecl("void load(SaveFile& file)"), EDID_SavableRead); } else { error("ERROR: Problem registering Savable type interface."); } } };