mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
6.4 KiB
C++
199 lines
6.4 KiB
C++
#include "luascripts.hpp"
|
|
|
|
#include "components/esm3/esmreader.hpp"
|
|
#include "components/esm3/esmwriter.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 - Start of MWLua::WorldView data
|
|
// 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(), static_cast<int>(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 = 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");
|
|
};
|
|
|
|
lua_State* L = luaL_newstate();
|
|
LuaUtil::BasicSerializer serializer(adjustRefNumFn);
|
|
|
|
auto adjustLuaData = [&](std::string& data) {
|
|
if (data.empty())
|
|
return;
|
|
sol::object luaData = LuaUtil::deserialize(L, 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);
|
|
}
|
|
}
|
|
lua_close(L);
|
|
}
|
|
|
|
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"))
|
|
{
|
|
std::string 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);
|
|
}
|
|
}
|
|
}
|