diff --git a/src/ScriptResolving/AngelScript/AngelScripResolver.cpp b/src/ScriptResolving/AngelScript/AngelScripResolver.cpp index eaedf68..4da49e5 100644 --- a/src/ScriptResolving/AngelScript/AngelScripResolver.cpp +++ b/src/ScriptResolving/AngelScript/AngelScripResolver.cpp @@ -6,6 +6,8 @@ #include "../../../extern/angelscript_addons/scripthandle/scripthandle.h" #include "../../../extern/angelscript_addons/scripthelper/scripthelper.h" #include "../../../extern/angelscript_addons/scriptstdstring/scriptstdstring.h" +#include "ByteCodeHandling/FileByteCodeStream.hpp" +#include "ByteCodeHandling/MemoryByteCodeStream.hpp" #include "TypeRegistry/BasicScriptClass.hpp" #include "TypeRegistry/Battling/RegisterBattleClass.hpp" #include "TypeRegistry/Battling/RegisterBattleLibrary.hpp" @@ -25,26 +27,19 @@ CreatureLib::Battling::ScriptResolver* PkmnLib::Battling::BattleLibrary::CreateS return new AngelScripResolver(); } -static void TranslateException(asIScriptContext *ctx, void* /*userParam*/) -{ - try - { +static void TranslateException(asIScriptContext* ctx, void* /*userParam*/) { + try { // Retrow the original exception so we can catch it again throw; - } - catch( std::exception &e ) - { + } catch (std::exception& e) { // Tell the VM the type of exception that occurred ctx->SetException(e.what()); - } - catch(...) - { + } catch (...) { // The callback must not allow any exception to be thrown, but it is not necessary // to explicitly set an exception string if the default exception string is sufficient } } - void AngelScripResolver::Initialize(CreatureLib::Battling::BattleLibrary* arg) { for (auto scriptCategory : ScriptCategoryHelper::GetValues()) { _typeDatabase.Insert(scriptCategory, {}); @@ -125,7 +120,7 @@ void AngelScripResolver::MessageCallback(const asSMessageInfo* msg, void* param) } CreatureLib::Battling::Script* AngelScripResolver::LoadScript(ScriptCategory category, const ConstString& scriptName) { - Dictionary innerDb; + Dictionary innerDb; if (!_typeDatabase.TryGet(category, innerDb)) { _typeDatabase.Insert(category, innerDb); return nullptr; @@ -200,3 +195,58 @@ void AngelScripResolver::FinalizeModule() { void AngelScripResolver::CreateScript(const char* name, const char* script) { _builder.AddSectionFromMemory(name, script); } +void AngelScripResolver::WriteByteCodeToFile(const char* file, bool stripDebugInfo) { + FILE* wFile = fopen(file, "w"); + auto stream = new FileByteCodeStream(wFile); + _mainModule->SaveByteCode(stream, stripDebugInfo); + fclose(wFile); + delete stream; +} +void AngelScripResolver::LoadByteCodeFromFile( + const char* file, const Dictionary>& types) { + FILE* rFile = fopen(file, "r"); + auto stream = new FileByteCodeStream(rFile); + LoadByteCode(stream, types); + fclose(rFile); + delete stream; + //_typeDatabase = types; +} +uint8_t* AngelScripResolver::WriteByteCodeToMemory(size_t& size, bool stripDebugInfo) { + auto stream = new MemoryByteCodeStream(); + auto result = _mainModule->SaveByteCode(stream, stripDebugInfo); + Assert(result == asSUCCESS); + auto arr = stream->GetOut(); + size = stream->GetWrittenSize(); + arr = static_cast(realloc(arr, size)); + delete stream; + return arr; +} +void AngelScripResolver::LoadByteCodeFromMemory( + uint8_t* byte, size_t size, const Dictionary>& types) { + auto stream = new MemoryByteCodeStream(byte, size); + LoadByteCode(stream, types); + delete stream; +} +void AngelScripResolver::LoadByteCode(asIBinaryStream* stream, + const Dictionary>& types) { + int result = _mainModule->LoadByteCode(stream); + Assert(result == asSUCCESS); + + auto typeCount = _mainModule->GetObjectTypeCount(); + Dictionary objectTypes; + for (asUINT i = 0; i < typeCount; i++) { + auto t = _mainModule->GetObjectTypeByIndex(i); + objectTypes.Insert(ConstString::GetHash(t->GetName()), t); + } + Dictionary> typeDatabase; + for (auto& innerDb : types) { + Dictionary newInnerDb; + for (auto& val : innerDb.second) { + auto decl = val.second; + auto type = objectTypes[ConstString::GetHash(decl)]; + newInnerDb.Insert(val.first, new AngelScriptTypeInfo(val.first, type)); + } + typeDatabase.Insert(innerDb.first, newInnerDb); + } + _typeDatabase = typeDatabase; +} diff --git a/src/ScriptResolving/AngelScript/AngelScripResolver.hpp b/src/ScriptResolving/AngelScript/AngelScripResolver.hpp index 67a084f..d2495de 100644 --- a/src/ScriptResolving/AngelScript/AngelScripResolver.hpp +++ b/src/ScriptResolving/AngelScript/AngelScripResolver.hpp @@ -2,8 +2,8 @@ #define PKMNLIB_ANGELSCRIPRESOLVER_HPP #include -#include "../../Battling/Library/BattleLibrary.hpp" #include "../../../extern/angelscript_addons/scriptbuilder/scriptbuilder.h" +#include "../../Battling/Library/BattleLibrary.hpp" #define ANGELSCRIPT_DLL_LIBRARY_IMPORT #include @@ -21,15 +21,17 @@ private: static void MessageCallback(const asSMessageInfo* msg, void* param); static void Print(const std::string& str) { std::cout << str << std::endl; } - Dictionary> _typeDatabase; + Dictionary> _typeDatabase; void RegisterTypes(); + void LoadByteCode(asIBinaryStream* stream, + const Dictionary>& types); public: ~AngelScripResolver() override { delete _contextPool; for (const auto& category : _typeDatabase) { - for (const auto& type : category.second){ + for (const auto& type : category.second) { delete type.second; } } @@ -42,5 +44,16 @@ public: void FinalizeModule(); CreatureLib::Battling::Script* LoadScript(ScriptCategory category, const ConstString& scriptName) override; + + void WriteByteCodeToFile(const char* file, bool stripDebugInfo = false); + void LoadByteCodeFromFile(const char* file, + const Dictionary>& types); + uint8_t* WriteByteCodeToMemory(size_t& size, bool stripDebugInfo = false); + void LoadByteCodeFromMemory(uint8_t*, size_t size, + const Dictionary>& types); + + const Dictionary>& GetTypeDatabase() const noexcept { + return _typeDatabase; + } }; #endif // PKMNLIB_ANGELSCRIPRESOLVER_HPP diff --git a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp index 3508fc8..cadab39 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp +++ b/src/ScriptResolving/AngelScript/AngelScriptTypeInfo.hpp @@ -45,6 +45,10 @@ public: const ConstString& GetName() const noexcept { return _name; } + const char* GetDecl(){ + return _type->GetName(); + } + asIScriptFunction* GetFunction(const ConstString& functionName) { asIScriptFunction* func; if (_functions.TryGet(functionName, func)) { diff --git a/src/ScriptResolving/AngelScript/ByteCodeHandling/FileByteCodeStream.hpp b/src/ScriptResolving/AngelScript/ByteCodeHandling/FileByteCodeStream.hpp new file mode 100644 index 0000000..cd723d5 --- /dev/null +++ b/src/ScriptResolving/AngelScript/ByteCodeHandling/FileByteCodeStream.hpp @@ -0,0 +1,25 @@ +#ifndef PKMNLIB_FILEBYTECODESTREAM_HPP +#define PKMNLIB_FILEBYTECODESTREAM_HPP +#include +#include + +class FileByteCodeStream : public asIBinaryStream { +private: + FILE* _file; + +public: + explicit FileByteCodeStream(FILE* file) : _file(file) {} + + int Write(const void* ptr, asUINT size) { + if (size == 0) + return 0; + return fwrite(ptr, size, 1, _file); + } + int Read(void* ptr, asUINT size) { + if (size == 0) + return 0; + return fread(ptr, size, 1, _file); + } +}; + +#endif // PKMNLIB_FILEBYTECODESTREAM_HPP diff --git a/src/ScriptResolving/AngelScript/ByteCodeHandling/MemoryByteCodeStream.hpp b/src/ScriptResolving/AngelScript/ByteCodeHandling/MemoryByteCodeStream.hpp new file mode 100644 index 0000000..277c46e --- /dev/null +++ b/src/ScriptResolving/AngelScript/ByteCodeHandling/MemoryByteCodeStream.hpp @@ -0,0 +1,57 @@ +#ifndef PKMNLIB_MEMORYBYTECODESTREAM_HPP +#define PKMNLIB_MEMORYBYTECODESTREAM_HPP +#include +#include + +class MemoryByteCodeStream : public asIBinaryStream { +private: + uint8_t* _out; + size_t _index = 0; + size_t _size = 0; + size_t _capacity = 0; + +#define MEM_STEPS 256 + +public: + MemoryByteCodeStream() : _out((uint8_t*)malloc(MEM_STEPS * sizeof(uint8_t) )), _capacity(MEM_STEPS){}; + MemoryByteCodeStream(uint8_t* in, size_t size) : _out(in), _size(size) {} + + uint8_t* GetOut() const { return _out; } + size_t GetWrittenSize() const { return _size; } + + int Write(const void* ptr, asUINT size) final { + if (size == 0) + return 0; + auto initialSize = _size; + if (_size + size >= _capacity) { + _capacity += MEM_STEPS; + auto newLoc = realloc(_out, _capacity * sizeof(uint8_t)); + if (newLoc == nullptr) { + throw CreatureException("Out of memory."); + } + _out = (uint8_t*)newLoc; + } + auto start = reinterpret_cast(ptr); + for (auto index = 0; index < size; index++) { + _out[initialSize + index] = *(start + index); + } + _size += size; + return size; + } + int Read(void* ptr, asUINT size) final { + if (size == 0) + return 0; + auto start = reinterpret_cast(ptr); + auto toRead = size; + if (_index + toRead > _size) { + toRead = _size - _index; + } + for (auto index = 0; index < toRead; index++) { + *(start + index) = _out[_index + index]; + } + _index += toRead; + return toRead; + } +}; +#undef MEM_STEPS +#endif // PKMNLIB_MEMORYBYTECODESTREAM_HPP diff --git a/tests/ScriptTests/ScriptResolverTests.cpp b/tests/ScriptTests/ScriptResolverTests.cpp index 0ecdcb2..f53fd09 100644 --- a/tests/ScriptTests/ScriptResolverTests.cpp +++ b/tests/ScriptTests/ScriptResolverTests.cpp @@ -79,4 +79,68 @@ TEST_CASE("Build script resolver, create object, invoke addition method") { delete lib; } +TEST_CASE("Get a script resolver, save the byte code to memory, create new script resolver from it") { + auto originLib = dynamic_cast(PkmnLib::Battling::BattleLibrary::CreateScriptResolver()); + originLib->Initialize(TestLibrary::GetLibrary()); + originLib->CreateScript("testScript1" , _scripts["testScript1"]); + originLib->FinalizeModule(); + size_t size; + auto byteCode = originLib->WriteByteCodeToMemory(size); + auto typeDatabase = originLib->GetTypeDatabase(); + + Dictionary> types; + for (auto& innerDb: typeDatabase){ + Dictionary newInnerDb; + for (auto& kv : innerDb.second){ + INFO(kv.second->GetDecl()); + newInnerDb.Insert(kv.first, kv.second->GetDecl()); + } + types.Insert(innerDb.first, newInnerDb); + } + + auto newLib = dynamic_cast(PkmnLib::Battling::BattleLibrary::CreateScriptResolver()); + newLib->Initialize(TestLibrary::GetLibrary()); + newLib->LoadByteCodeFromMemory(byteCode, size, types); + auto obj = + dynamic_cast(newLib->LoadScript(ScriptCategory::Creature, "testScript1"_cnc)); + REQUIRE(obj != nullptr); + delete obj; + delete originLib; + delete newLib; + free(byteCode); +} + +TEST_CASE("Get a script resolver, save the byte code to file, create new script resolver from it") { + const char* TestFileName = "compiledAngelScriptTestFile.bin"; + auto originLib = dynamic_cast(PkmnLib::Battling::BattleLibrary::CreateScriptResolver()); + originLib->Initialize(TestLibrary::GetLibrary()); + originLib->CreateScript("testScript1" , _scripts["testScript1"]); + originLib->FinalizeModule(); + originLib->WriteByteCodeToFile(TestFileName); + auto typeDatabase = originLib->GetTypeDatabase(); + + Dictionary> types; + for (auto& innerDb: typeDatabase){ + Dictionary newInnerDb; + for (auto& kv : innerDb.second){ + INFO(kv.second->GetDecl()); + newInnerDb.Insert(kv.first, kv.second->GetDecl()); + } + types.Insert(innerDb.first, newInnerDb); + } + + auto newLib = dynamic_cast(PkmnLib::Battling::BattleLibrary::CreateScriptResolver()); + newLib->Initialize(TestLibrary::GetLibrary()); + newLib->LoadByteCodeFromFile(TestFileName, types); + auto obj = + dynamic_cast(newLib->LoadScript(ScriptCategory::Creature, "testScript1"_cnc)); + REQUIRE(obj != nullptr); + delete obj; + delete originLib; + delete newLib; + remove(TestFileName); +} + + + #endif \ No newline at end of file