From 6e869c3123cd4879348b25d1167c62a6ea90bca4 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Tue, 12 Sep 2017 21:11:30 +0200 Subject: [PATCH 1/2] ESS-Importer: Convert ballistic projectiles (Feature #2320) --- apps/essimporter/CMakeLists.txt | 1 + apps/essimporter/converter.cpp | 84 ++++++++++++++++++++++++++-- apps/essimporter/converter.hpp | 12 ++++ apps/essimporter/importer.cpp | 14 +++++ apps/essimporter/importercontext.hpp | 10 ++++ apps/essimporter/importproj.cpp | 18 ++++++ apps/essimporter/importproj.h | 47 ++++++++++++++++ 7 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 apps/essimporter/importproj.cpp create mode 100644 apps/essimporter/importproj.h diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 82182b7fa..fc5fae72e 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -16,6 +16,7 @@ set(ESSIMPORTER_FILES importjour.cpp importscri.cpp importscpt.cpp + importproj.cpp importercontext.cpp converter.cpp convertacdt.cpp diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 68ee93c89..5c65332be 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "convertcrec.hpp" #include "convertcntc.hpp" @@ -53,6 +54,36 @@ namespace return true; return false; } + + void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId) + { + std::stringstream stream; + stream << std::hex << indexedRefId.substr(indexedRefId.size()-8,8); + stream >> refIndex; + + refId = indexedRefId.substr(0,indexedRefId.size()-8); + } + + int convertActorId(const std::string& indexedRefId, ESSImport::Context& context) + { + if (isIndexedRefId(indexedRefId)) + { + int refIndex; + std::string refId; + splitIndexedRefId(indexedRefId, refIndex, refId); + + auto it = context.mActorIdMap.find(std::make_pair(refIndex, refId)); + if (it == context.mActorIdMap.end()) + return -1; + return it->second; + } + else if (indexedRefId == "PlayerSaveGame") + { + return context.mPlayer.mObject.mCreatureStats.mActorId; + } + + return -1; + } } namespace ESSImport @@ -322,12 +353,9 @@ namespace ESSImport } else { - std::stringstream stream; - stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); int refIndex; - stream >> refIndex; + splitIndexedRefId(cellref.mIndexedRefId, refIndex, out.mRefID); - out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( @@ -347,6 +375,10 @@ namespace ESSImport convertNpcData(cellref, objstate.mNpcStats); convertNPCC(npccIt->second, objstate); convertCellRef(cellref, objstate); + + objstate.mCreatureStats.mActorId = mContext->generateActorId(); + mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId)); + esm.writeHNT ("OBJE", ESM::REC_NPC_); objstate.save(esm); continue; @@ -383,6 +415,10 @@ namespace ESSImport convertACSC(cellref.mACSC, objstate.mCreatureStats); convertCREC(crecIt->second, objstate); convertCellRef(cellref, objstate); + + objstate.mCreatureStats.mActorId = mContext->generateActorId(); + mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId)); + esm.writeHNT ("OBJE", ESM::REC_CREA); objstate.save(esm); continue; @@ -413,4 +449,44 @@ namespace ESSImport } } + void ConvertPROJ::read(ESM::ESMReader& esm) + { + mProj.load(esm); + } + + void ConvertPROJ::write(ESM::ESMWriter& esm) + { + for (const PROJ::PNAM& pnam : mProj.mProjectiles) + { + if (!pnam.isMagic()) + { + ESM::ProjectileState out; + out.mId = pnam.mArrowId.toString(); + out.mPosition = pnam.mPosition; + out.mOrientation.mValues[0] = out.mOrientation.mValues[1] = out.mOrientation.mValues[2] = 0.0f; + out.mOrientation.mValues[3] = 1.0f; + out.mActorId = convertActorId(pnam.mActorId.toString(), *mContext); + + out.mBowId = pnam.mBowId.toString(); + out.mVelocity = pnam.mVelocity; + out.mAttackStrength = pnam.mAttackStrength; + + esm.startRecord(ESM::REC_PROJ); + out.save(esm); + esm.endRecord(ESM::REC_PROJ); + } + else + { + // TODO: Implement magic projectile conversion. + + /*esm.startRecord(ESM::REC_MPRJ); + out.save(esm); + esm.endRecord(ESM::REC_MPRJ);*/ + + std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (not implemented)" << std::endl; + continue; + } + } + } + } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index e4985f993..a640d6756 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -35,6 +35,7 @@ #include "importques.hpp" #include "importjour.hpp" #include "importscpt.hpp" +#include "importproj.h" #include "convertacdt.hpp" #include "convertnpcc.hpp" @@ -593,6 +594,17 @@ private: std::vector mScripts; }; +/// Projectile converter +class ConvertPROJ : public Converter +{ +public: + virtual int getStage() override { return 2; } + virtual void read(ESM::ESMReader& esm) override; + virtual void write(ESM::ESMWriter& esm) override; +private: + PROJ mProj; +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index d38069d89..4c8222c56 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -303,6 +303,7 @@ namespace ESSImport converters[ESM::REC_QUES] = std::shared_ptr(new ConvertQUES()); converters[recJOUR ] = std::shared_ptr(new ConvertJOUR()); converters[ESM::REC_SCPT] = std::shared_ptr(new ConvertSCPT()); + converters[ESM::REC_PROJ] = std::shared_ptr(new ConvertPROJ()); // TODO: // - REGN (weather in certain regions?) @@ -420,6 +421,19 @@ namespace ESSImport context.mPlayer.save(writer); writer.endRecord(ESM::REC_PLAY); + writer.startRecord(ESM::REC_ACTC); + writer.writeHNT("COUN", context.mNextActorId); + writer.endRecord(ESM::REC_ACTC); + + // Stage 2 requires cell references to be written / actors IDs assigned + for (std::map >::const_iterator it = converters.begin(); + it != converters.end(); ++it) + { + if (it->second->getStage() != 2) + continue; + it->second->write(writer); + } + writer.startRecord (ESM::REC_DIAS); context.mDialogueState.save(writer); writer.endRecord(ESM::REC_DIAS); diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 0ad73c267..eb6ca9336 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -48,6 +48,9 @@ namespace ESSImport std::map, NPCC> mNpcChanges; std::map, CNTC> mContainerChanges; + std::map, int> mActorIdMap; + int mNextActorId; + std::map mCreatures; std::map mNpcs; @@ -56,6 +59,7 @@ namespace ESSImport , mMonth(0) , mYear(0) , mHour(0.f) + , mNextActorId(0) { mPlayer.mAutoMove = 0; ESM::CellId playerCellId; @@ -71,12 +75,18 @@ namespace ESSImport mPlayer.mObject.blank(); mPlayer.mObject.mEnabled = true; mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame + mPlayer.mObject.mCreatureStats.mActorId = generateActorId(); mGlobalMapState.mBounds.mMinX = 0; mGlobalMapState.mBounds.mMaxX = 0; mGlobalMapState.mBounds.mMinY = 0; mGlobalMapState.mBounds.mMaxY = 0; } + + int generateActorId() + { + return mNextActorId++; + } }; } diff --git a/apps/essimporter/importproj.cpp b/apps/essimporter/importproj.cpp new file mode 100644 index 000000000..b2dcf4e7d --- /dev/null +++ b/apps/essimporter/importproj.cpp @@ -0,0 +1,18 @@ +#include "importproj.h" + +#include + +namespace ESSImport +{ + +void ESSImport::PROJ::load(ESM::ESMReader& esm) +{ + while (esm.isNextSub("PNAM")) + { + PNAM pnam; + esm.getHT(pnam); + mProjectiles.push_back(pnam); + } +} + +} diff --git a/apps/essimporter/importproj.h b/apps/essimporter/importproj.h new file mode 100644 index 000000000..b8abab5fa --- /dev/null +++ b/apps/essimporter/importproj.h @@ -0,0 +1,47 @@ +#ifndef OPENMW_ESSIMPORT_IMPORTPROJ_H +#define OPENMW_ESSIMPORT_IMPORTPROJ_H + +#include +#include +#include + +namespace ESM +{ + class ESMReader; +} + +namespace ESSImport +{ + +struct PROJ +{ + +#pragma pack(push) +#pragma pack(1) + struct PNAM // 184 bytes + { + float mAttackStrength; + float mSpeed; + unsigned char mUnknown[4*2]; + float mFlightTime; + int mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles) + unsigned char mUnknown2[4]; + ESM::Vector3 mVelocity; + ESM::Vector3 mPosition; + unsigned char mUnknown3[4*9]; + ESM::NAME32 mActorId; // indexed refID (with the exception of "PlayerSaveGame") + ESM::NAME32 mArrowId; + ESM::NAME32 mBowId; + + bool isMagic() const { return mSplmIndex != 0; } + }; +#pragma pack(pop) + + std::vector mProjectiles; + + void load(ESM::ESMReader& esm); +}; + +} + +#endif From a66d310a1d1d266da479b3e4fe7a3d35b94ce325 Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Fri, 15 Sep 2017 00:21:02 +0200 Subject: [PATCH 2/2] ESS-Importer: Fix uninitialized paid crime ID --- apps/essimporter/importercontext.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index eb6ca9336..4896f5594 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -71,7 +71,8 @@ namespace ESSImport = mPlayer.mLastKnownExteriorPosition[2] = 0.0f; mPlayer.mHasMark = 0; - mPlayer.mCurrentCrimeId = 0; // TODO + mPlayer.mCurrentCrimeId = -1; // TODO + mPlayer.mPaidCrimeId = -1; mPlayer.mObject.blank(); mPlayer.mObject.mEnabled = true; mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame