mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-26 08:56:37 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			201 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "luascripts.hpp"
 | |
| 
 | |
| #include <components/esm3/esmreader.hpp>
 | |
| #include <components/esm3/esmwriter.hpp>
 | |
| 
 | |
| #include <components/lua/luastateptr.hpp>
 | |
| #include <components/lua/serialization.hpp>
 | |
| 
 | |
| // List of all records, that are related to Lua.
 | |
| //
 | |
| // Records:
 | |
| // LUAL - LuaScriptsCfg - list of all scripts (in content files)
 | |
| // LUAM - MWLua::LuaManager (in saves)
 | |
| //
 | |
| // Subrecords:
 | |
| // LUAF - LuaScriptCfg::mFlags and ESM::RecNameInts list
 | |
| // LUAW - Simulation time and last generated RefNum
 | |
| // LUAE - Start of MWLua::LocalEvent or MWLua::GlobalEvent (eventName)
 | |
| // LUAS - VFS path to a Lua script
 | |
| // LUAD - Serialized Lua variable
 | |
| // LUAT - MWLua::ScriptsContainer::Timer
 | |
| // LUAC - Name of a timer callback (string)
 | |
| // LUAR - Attach script to a specific record (LuaScriptCfg::PerRecordCfg)
 | |
| // LUAI - Attach script to a specific instance (LuaScriptCfg::PerRefCfg)
 | |
| 
 | |
| void ESM::saveLuaBinaryData(ESMWriter& esm, const std::string& data)
 | |
| {
 | |
|     if (data.empty())
 | |
|         return;
 | |
|     esm.startSubRecord("LUAD");
 | |
|     esm.write(data.data(), data.size());
 | |
|     esm.endRecord("LUAD");
 | |
| }
 | |
| 
 | |
| std::string ESM::loadLuaBinaryData(ESMReader& esm)
 | |
| {
 | |
|     std::string data;
 | |
|     if (esm.isNextSub("LUAD"))
 | |
|     {
 | |
|         esm.getSubHeader();
 | |
|         data.resize(esm.getSubSize());
 | |
|         esm.getExact(data.data(), data.size());
 | |
|     }
 | |
|     return data;
 | |
| }
 | |
| 
 | |
| static bool readBool(ESM::ESMReader& esm)
 | |
| {
 | |
|     char c;
 | |
|     esm.getT<char>(c);
 | |
|     return c != 0;
 | |
| }
 | |
| 
 | |
| void ESM::LuaScriptsCfg::load(ESMReader& esm)
 | |
| {
 | |
|     while (esm.isNextSub("LUAS"))
 | |
|     {
 | |
|         mScripts.emplace_back();
 | |
|         ESM::LuaScriptCfg& script = mScripts.back();
 | |
|         script.mScriptPath = VFS::Path::Normalized(esm.getHString());
 | |
| 
 | |
|         esm.getSubNameIs("LUAF");
 | |
|         esm.getSubHeader();
 | |
|         if (esm.getSubSize() < 4 || (esm.getSubSize() % 4 != 0))
 | |
|             esm.fail("Incorrect LUAF size");
 | |
|         esm.getT(script.mFlags);
 | |
|         script.mTypes.resize((esm.getSubSize() - 4) / 4);
 | |
|         for (uint32_t& type : script.mTypes)
 | |
|             esm.getT(type);
 | |
| 
 | |
|         script.mInitializationData = loadLuaBinaryData(esm);
 | |
| 
 | |
|         while (esm.isNextSub("LUAR"))
 | |
|         {
 | |
|             esm.getSubHeader();
 | |
|             script.mRecords.emplace_back();
 | |
|             ESM::LuaScriptCfg::PerRecordCfg& recordCfg = script.mRecords.back();
 | |
|             recordCfg.mAttach = readBool(esm);
 | |
|             recordCfg.mRecordId = esm.getRefId(esm.getSubSize() - 1);
 | |
|             recordCfg.mInitializationData = loadLuaBinaryData(esm);
 | |
|         }
 | |
|         while (esm.isNextSub("LUAI"))
 | |
|         {
 | |
|             esm.getSubHeader();
 | |
|             script.mRefs.emplace_back();
 | |
|             ESM::LuaScriptCfg::PerRefCfg& refCfg = script.mRefs.back();
 | |
|             refCfg.mAttach = readBool(esm);
 | |
|             esm.getT<uint32_t>(refCfg.mRefnumIndex);
 | |
|             esm.getT<int32_t>(refCfg.mRefnumContentFile);
 | |
|             refCfg.mInitializationData = loadLuaBinaryData(esm);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void ESM::LuaScriptsCfg::adjustRefNums(const ESMReader& esm)
 | |
| {
 | |
|     auto adjustRefNumFn = [&esm](int contentFile) -> int {
 | |
|         if (contentFile == 0)
 | |
|             return esm.getIndex();
 | |
|         else if (contentFile > 0 && contentFile <= static_cast<int>(esm.getParentFileIndices().size()))
 | |
|             return esm.getParentFileIndices()[contentFile - 1];
 | |
|         else
 | |
|             throw std::runtime_error("Incorrect contentFile index");
 | |
|     };
 | |
| 
 | |
|     LuaUtil::LuaStatePtr state(luaL_newstate());
 | |
|     if (state == nullptr)
 | |
|         throw std::runtime_error("Failed to create Lua runtime");
 | |
| 
 | |
|     LuaUtil::BasicSerializer serializer(adjustRefNumFn);
 | |
| 
 | |
|     auto adjustLuaData = [&](std::string& data) {
 | |
|         if (data.empty())
 | |
|             return;
 | |
|         sol::object luaData = LuaUtil::deserialize(state.get(), data, &serializer);
 | |
|         data = LuaUtil::serialize(luaData, &serializer);
 | |
|     };
 | |
| 
 | |
|     for (LuaScriptCfg& script : mScripts)
 | |
|     {
 | |
|         adjustLuaData(script.mInitializationData);
 | |
|         for (LuaScriptCfg::PerRecordCfg& recordCfg : script.mRecords)
 | |
|             adjustLuaData(recordCfg.mInitializationData);
 | |
|         for (LuaScriptCfg::PerRefCfg& refCfg : script.mRefs)
 | |
|         {
 | |
|             adjustLuaData(refCfg.mInitializationData);
 | |
|             refCfg.mRefnumContentFile = adjustRefNumFn(refCfg.mRefnumContentFile);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void ESM::LuaScriptsCfg::save(ESMWriter& esm) const
 | |
| {
 | |
|     for (const LuaScriptCfg& script : mScripts)
 | |
|     {
 | |
|         esm.writeHNString("LUAS", script.mScriptPath);
 | |
|         esm.startSubRecord("LUAF");
 | |
|         esm.writeT<uint32_t>(script.mFlags);
 | |
|         for (uint32_t type : script.mTypes)
 | |
|             esm.writeT<uint32_t>(type);
 | |
|         esm.endRecord("LUAF");
 | |
|         saveLuaBinaryData(esm, script.mInitializationData);
 | |
|         for (const LuaScriptCfg::PerRecordCfg& recordCfg : script.mRecords)
 | |
|         {
 | |
|             esm.startSubRecord("LUAR");
 | |
|             esm.writeT<char>(recordCfg.mAttach ? 1 : 0);
 | |
|             esm.writeHRefId(recordCfg.mRecordId);
 | |
|             esm.endRecord("LUAR");
 | |
|             saveLuaBinaryData(esm, recordCfg.mInitializationData);
 | |
|         }
 | |
|         for (const LuaScriptCfg::PerRefCfg& refCfg : script.mRefs)
 | |
|         {
 | |
|             esm.startSubRecord("LUAI");
 | |
|             esm.writeT<char>(refCfg.mAttach ? 1 : 0);
 | |
|             esm.writeT<uint32_t>(refCfg.mRefnumIndex);
 | |
|             esm.writeT<int32_t>(refCfg.mRefnumContentFile);
 | |
|             esm.endRecord("LUAI");
 | |
|             saveLuaBinaryData(esm, refCfg.mInitializationData);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void ESM::LuaScripts::load(ESMReader& esm)
 | |
| {
 | |
|     while (esm.isNextSub("LUAS"))
 | |
|     {
 | |
|         VFS::Path::Normalized name(esm.getHString());
 | |
|         std::string data = loadLuaBinaryData(esm);
 | |
|         std::vector<LuaTimer> timers;
 | |
|         while (esm.isNextSub("LUAT"))
 | |
|         {
 | |
|             esm.getSubHeader();
 | |
|             LuaTimer timer;
 | |
|             esm.getT(timer.mType);
 | |
|             esm.getT(timer.mTime);
 | |
|             timer.mCallbackName = esm.getHNString("LUAC");
 | |
|             timer.mCallbackArgument = loadLuaBinaryData(esm);
 | |
|             timers.push_back(std::move(timer));
 | |
|         }
 | |
|         mScripts.push_back({ std::move(name), std::move(data), std::move(timers) });
 | |
|     }
 | |
| }
 | |
| 
 | |
| void ESM::LuaScripts::save(ESMWriter& esm) const
 | |
| {
 | |
|     for (const LuaScript& script : mScripts)
 | |
|     {
 | |
|         esm.writeHNString("LUAS", script.mScriptPath);
 | |
|         saveLuaBinaryData(esm, script.mData);
 | |
|         for (const LuaTimer& timer : script.mTimers)
 | |
|         {
 | |
|             esm.startSubRecord("LUAT");
 | |
|             esm.writeT(timer.mType);
 | |
|             esm.writeT(timer.mTime);
 | |
|             esm.endRecord("LUAT");
 | |
|             esm.writeHNString("LUAC", timer.mCallbackName);
 | |
|             if (!timer.mCallbackArgument.empty())
 | |
|                 saveLuaBinaryData(esm, timer.mCallbackArgument);
 | |
|         }
 | |
|     }
 | |
| }
 |