diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 5db3a86dc..409b8bcff 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -23,6 +23,7 @@ set(ESSIMPORTER_FILES convertinventory.cpp convertcrec.cpp convertcntc.cpp + convertscri.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 75c2911fd..3b82988b8 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -9,6 +9,7 @@ #include "convertcrec.hpp" #include "convertcntc.hpp" +#include "convertscri.hpp" namespace { @@ -29,6 +30,8 @@ namespace objstate.mRef.mRefNum = cellref.mRefNum; if (cellref.mDeleted) objstate.mCount = 0; + convertSCRI(cellref.mSCRI, objstate.mLocals); + objstate.mHasLocals = !objstate.mLocals.mVariables.empty(); } bool isIndexedRefId(const std::string& indexedRefId) diff --git a/apps/essimporter/convertscri.cpp b/apps/essimporter/convertscri.cpp new file mode 100644 index 000000000..dbe35a010 --- /dev/null +++ b/apps/essimporter/convertscri.cpp @@ -0,0 +1,32 @@ +#include "convertscri.hpp" + +#include + +namespace +{ + + template + void storeVariables(const std::vector& variables, ESM::Locals& locals, const std::string& scriptname) + { + for (typename std::vector::const_iterator it = variables.begin(); it != variables.end(); ++it) + { + ESM::Variant val(*it); + val.setType(VariantType); + locals.mVariables.push_back(std::make_pair(std::string(), val)); + } + } + +} + +namespace ESSImport +{ + + void convertSCRI(const SCRI &scri, ESM::Locals &locals) + { + // order *is* important, as we do not have variable names available in this format + storeVariables (scri.mShorts, locals, scri.mScript); + storeVariables (scri.mLongs, locals, scri.mScript); + storeVariables (scri.mFloats, locals, scri.mScript); + } + +} diff --git a/apps/essimporter/convertscri.hpp b/apps/essimporter/convertscri.hpp new file mode 100644 index 000000000..2d8945666 --- /dev/null +++ b/apps/essimporter/convertscri.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTSCRI_H +#define OPENMW_ESSIMPORT_CONVERTSCRI_H + +#include "importscri.hpp" + +#include + +namespace ESSImport +{ + + /// Convert script variable assignments + void convertSCRI (const SCRI& scri, ESM::Locals& locals); + +} + +#endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index ed149b023..73a12769a 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -237,6 +237,12 @@ namespace ESSImport converters[recJOUR ] = boost::shared_ptr(new ConvertJOUR()); converters[ESM::REC_SCPT] = boost::shared_ptr(new ConvertSCPT()); + // TODO: + // - REGN (weather in certain regions?) + // - VFXM + // - SPLM (active spell effects) + // - PROJ (magic projectiles in air) + std::set unknownRecords; for (std::map >::const_iterator it = converters.begin(); diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index a1ee48ae6..fe3e49827 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -10,6 +10,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/scriptmanager.hpp" +#include + namespace MWScript { void Locals::configure (const ESM::Script& script) @@ -124,27 +126,60 @@ namespace MWScript const Compiler::Locals& declarations = MWBase::Environment::get().getScriptManager()->getLocals(script); - for (std::vector >::const_iterator iter - = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter) + int index = 0, numshorts = 0, numlongs = 0; + for (unsigned int v=0; vfirst); - char index = declarations.getIndex (iter->first); + ESM::VarType type = locals.mVariables[v].second.getType(); + if (type == ESM::VT_Short) + ++numshorts; + else if (type == ESM::VT_Int) + ++numlongs; + } - try + for (std::vector >::const_iterator iter + = locals.mVariables.begin(); iter!=locals.mVariables.end(); ++iter,++index) + { + if (iter->first.empty()) { - switch (type) + // no variable names available (this will happen for legacy, i.e. ESS-imported savegames only) + try { - case 's': mShorts.at (index) = iter->second.getInteger(); break; - case 'l': mLongs.at (index) = iter->second.getInteger(); break; - case 'f': mFloats.at (index) = iter->second.getFloat(); break; - - // silently ignore locals that don't exist anymore + if (index >= numshorts+numlongs) + mFloats.at(index - (numshorts+numlongs)) = iter->second.getFloat(); + else if (index >= numshorts) + mLongs.at(index - numshorts) = iter->second.getInteger(); + else + mShorts.at(index) = iter->second.getInteger(); + } + catch (std::exception& e) + { + std::cerr << "Failed to read local variable state for script '" + << script << "' (legacy format): " << e.what() + << "\nNum shorts: " << numshorts << " / " << mShorts.size() + << " Num longs: " << numlongs << " / " << mLongs.size() << std::endl; } } - catch (...) + else { - // ignore type changes - /// \todo write to log + char type = declarations.getType (iter->first); + char index = declarations.getIndex (iter->first); + + try + { + switch (type) + { + case 's': mShorts.at (index) = iter->second.getInteger(); break; + case 'l': mLongs.at (index) = iter->second.getInteger(); break; + case 'f': mFloats.at (index) = iter->second.getFloat(); break; + + // silently ignore locals that don't exist anymore + } + } + catch (...) + { + // ignore type changes + /// \todo write to log + } } } } diff --git a/components/esm/locals.cpp b/components/esm/locals.cpp index 9c470a025..f0cfd49f0 100644 --- a/components/esm/locals.cpp +++ b/components/esm/locals.cpp @@ -11,7 +11,7 @@ void ESM::Locals::load (ESMReader &esm) std::string id = esm.getHString(); Variant value; - value.read (esm, Variant::Format_Info); + value.read (esm, Variant::Format_Local); mVariables.push_back (std::make_pair (id, value)); } @@ -23,6 +23,6 @@ void ESM::Locals::save (ESMWriter &esm) const iter!=mVariables.end(); ++iter) { esm.writeHNString ("LOCA", iter->first); - iter->second.write (esm, Variant::Format_Info); + iter->second.write (esm, Variant::Format_Local); } -} \ No newline at end of file +} diff --git a/components/esm/variant.cpp b/components/esm/variant.cpp index 4127a9d62..c65eed5e0 100644 --- a/components/esm/variant.cpp +++ b/components/esm/variant.cpp @@ -13,6 +13,7 @@ namespace const uint32_t STRV = ESM::FourCC<'S','T','R','V'>::value; const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value; const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value; + const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value; } ESM::Variant::Variant() : mType (VT_None), mData (0) {} @@ -141,7 +142,7 @@ void ESM::Variant::read (ESMReader& esm, Format format) esm.fail ("invalid subrecord: " + name.toString()); } } - else // info + else if (format == Format_Info) { esm.getSubName(); NAME name = esm.retSubName(); @@ -157,6 +158,26 @@ void ESM::Variant::read (ESMReader& esm, Format format) else esm.fail ("invalid subrecord: " + name.toString()); } + else if (format == Format_Local) + { + esm.getSubName(); + NAME name = esm.retSubName(); + + if (name==INTV) + { + type = VT_Int; + } + else if (name==FLTV) + { + type = VT_Float; + } + else if (name==STTV) + { + type = VT_Short; + } + else + esm.fail ("invalid subrecord: " + name.toString()); + } setType (type); @@ -179,6 +200,9 @@ void ESM::Variant::write (ESMWriter& esm, Format format) const if (format==Format_Info) throw std::runtime_error ("can not serialise variant of type none to info format"); + if (format==Format_Local) + throw std::runtime_error ("can not serialise variant of type none to local format"); + // nothing to do here for GMST format } else diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index d6c1a5489..5f179a7bd 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -33,7 +33,8 @@ namespace ESM { Format_Global, Format_Gmst, - Format_Info // also used for local variables in saved game files + Format_Info, + Format_Local // local script variables in save game files }; Variant(); diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 1bacdc077..fd068fc19 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -81,6 +81,9 @@ void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarTy if (format==Variant::Format_Info) esm.fail ("info variables of type string not supported"); + if (format==Variant::Format_Local) + esm.fail ("local variables of type string not supported"); + // GMST mValue = esm.getHString(); } @@ -173,6 +176,21 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT esm.getHT (mValue); } + else if (format==Variant::Format_Local) + { + if (type==VT_Short) + { + short value; + esm.getHT(value); + mValue = value; + } + else if (type==VT_Int) + { + esm.getHT(mValue); + } + else + esm.fail("unsupported local variable integer type"); + } } void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, VarType type) const @@ -204,6 +222,15 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var esm.writeHNT ("INTV", mValue); } + else if (format==Variant::Format_Local) + { + if (type==VT_Short) + esm.writeHNT ("STTV", (short)mValue); + else if (type == VT_Int) + esm.writeHNT ("INTV", mValue); + else + throw std::runtime_error("unsupported local variable integer type"); + } } bool ESM::VariantIntegerData::isEqual (const VariantDataBase& value) const @@ -252,7 +279,7 @@ void ESM::VariantFloatData::read (ESMReader& esm, Variant::Format format, VarTyp { esm.getHNT (mValue, "FLTV"); } - else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { esm.getHT (mValue); } @@ -268,7 +295,7 @@ void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarTy esm.writeHNString ("FNAM", "f"); esm.writeHNT ("FLTV", mValue); } - else if (format==Variant::Format_Gmst || format==Variant::Format_Info) + else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { esm.writeHNT ("FLTV", mValue); } @@ -277,4 +304,4 @@ void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarTy bool ESM::VariantFloatData::isEqual (const VariantDataBase& value) const { return dynamic_cast (value).mValue==mValue; -} \ No newline at end of file +}