Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
9.4 kB · 296 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297#include "cellref.hpp"
#include <algorithm>#include <limits>
#include <components/debug/debuglog.hpp>
#include "esmreader.hpp"#include "esmwriter.hpp"
namespace{ constexpr int ZeroLock = std::numeric_limits<int>::max();}
namespace ESM{ namespace { template <bool load> void loadIdImpl(ESMReader& esm, bool wideRefNum, CellRef& cellRef) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved // references system. Its only purpose is a performance optimization for "immovable" things. We don't need // this, and it's problematic anyway, because any item can theoretically be moved by a script. if (esm.isNextSub("NAM0")) esm.skipHSub();
if constexpr (load) { cellRef.blank(); cellRef.mRefNum = esm.getFormId(wideRefNum); cellRef.mRefID = esm.getHNORefId("NAME");
if (cellRef.mRefID.empty()) Log(Debug::Warning) << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x" << std::hex << esm.getFileOffset(); } else { esm.getFormId(wideRefNum); esm.skipHNORefId("NAME"); } }
template <bool load> void loadDataImpl(ESMReader& esm, bool& isDeleted, CellRef& cellRef) { const auto getRefIdOrSkip = [&](ESM::RefId& refId) { if constexpr (load) refId = esm.getRefId(); else esm.skipHRefId(); };
const auto getHStringOrSkip = [&](std::string& value) { if constexpr (load) value = esm.getHString(); else esm.skipHString(); };
const auto getHTOrSkip = [&](auto& value) { if constexpr (load) esm.getHT(value); else esm.skipHT<std::decay_t<decltype(value)>>(); };
if constexpr (load) isDeleted = false;
bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().toInt()) { case fourCC("UNAM"): getHTOrSkip(cellRef.mReferenceBlocked); break; case fourCC("XSCL"): getHTOrSkip(cellRef.mScale); if constexpr (load) cellRef.mScale = std::clamp(cellRef.mScale, 0.5f, 2.0f); break; case fourCC("ANAM"): getRefIdOrSkip(cellRef.mOwner); break; case fourCC("BNAM"): getHStringOrSkip(cellRef.mGlobalVariable); break; case fourCC("XSOL"): getRefIdOrSkip(cellRef.mSoul); break; case fourCC("CNAM"): getRefIdOrSkip(cellRef.mFaction); break; case fourCC("INDX"): getHTOrSkip(cellRef.mFactionRank); break; case fourCC("XCHG"): getHTOrSkip(cellRef.mEnchantmentCharge); break; case fourCC("INTV"): getHTOrSkip(cellRef.mChargeInt); break; case fourCC("NAM9"): getHTOrSkip(cellRef.mCount); break; case fourCC("DODT"): if constexpr (load) { esm.getSubComposite(cellRef.mDoorDest); cellRef.mTeleport = true; } else esm.skipHSub(); break; case fourCC("DNAM"): getHStringOrSkip(cellRef.mDestCell); break; case fourCC("FLTV"): getHTOrSkip(cellRef.mLockLevel); break; case fourCC("KNAM"): getRefIdOrSkip(cellRef.mKey); break; case fourCC("TNAM"): getRefIdOrSkip(cellRef.mTrap); break; case fourCC("DATA"): if constexpr (load) esm.getSubComposite(cellRef.mPos); else esm.skipHSub(); break; case fourCC("NAM0"): { esm.skipHSub(); break; } case SREC_DELE: esm.skipHSub(); if constexpr (load) isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } }
if constexpr (load) { if (esm.getFormatVersion() == DefaultFormatVersion) // loading a content file cellRef.mIsLocked = !cellRef.mKey.empty() || cellRef.mLockLevel > 0; else cellRef.mIsLocked = cellRef.mLockLevel > 0; if (cellRef.mLockLevel == ZeroLock) cellRef.mLockLevel = 0; } } }
void CellRef::load(ESMReader& esm, bool& isDeleted, bool wideRefNum) { loadId(esm, wideRefNum); loadData(esm, isDeleted); }
void CellRef::loadId(ESMReader& esm, bool wideRefNum) { loadIdImpl<true>(esm, wideRefNum, *this); }
void CellRef::loadData(ESMReader& esm, bool& isDeleted) { loadDataImpl<true>(esm, isDeleted, *this); }
void CellRef::save(ESMWriter& esm, bool wideRefNum, bool inInventory, bool isDeleted) const { esm.writeFormId(mRefNum, wideRefNum);
esm.writeHNCRefId("NAME", mRefID);
if (isDeleted) { esm.writeHNString("DELE", "", 3); return; }
if (mScale != 1.0) { esm.writeHNT("XSCL", std::clamp(mScale, 0.5f, 2.0f)); }
if (!inInventory) esm.writeHNOCRefId("ANAM", mOwner);
esm.writeHNOCString("BNAM", mGlobalVariable); esm.writeHNOCRefId("XSOL", mSoul);
if (!inInventory) { esm.writeHNOCRefId("CNAM", mFaction); if (mFactionRank != -2) { esm.writeHNT("INDX", mFactionRank); } }
if (mEnchantmentCharge != -1) esm.writeHNT("XCHG", mEnchantmentCharge);
if (mChargeInt != -1) esm.writeHNT("INTV", mChargeInt);
if (mCount != 1) esm.writeHNT("NAM9", mCount);
if (!inInventory && mTeleport) { esm.writeNamedComposite("DODT", mDoorDest); esm.writeHNOCString("DNAM", mDestCell); }
if (!inInventory) { if (mIsLocked) { int lockLevel = mLockLevel; if (lockLevel == 0) lockLevel = ZeroLock; esm.writeHNT("FLTV", lockLevel); esm.writeHNOCRefId("KNAM", mKey); } esm.writeHNOCRefId("TNAM", mTrap); }
if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked);
if (!inInventory) esm.writeNamedComposite("DATA", mPos); }
void CellRef::blank() { mRefNum = RefNum{}; mRefID = ESM::RefId(); mScale = 1; mOwner = ESM::RefId(); mGlobalVariable.clear(); mSoul = ESM::RefId(); mFaction = ESM::RefId(); mFactionRank = -2; mChargeInt = -1; mChargeIntRemainder = 0.0f; mEnchantmentCharge = -1; mCount = 1; mDestCell.clear(); mLockLevel = 0; mIsLocked = false; mKey = ESM::RefId(); mTrap = ESM::RefId(); mReferenceBlocked = -1; mTeleport = false;
for (int i = 0; i < 3; ++i) { mDoorDest.pos[i] = 0; mDoorDest.rot[i] = 0; mPos.pos[i] = 0; mPos.rot[i] = 0; } }
void skipLoadCellRef(ESMReader& esm, bool wideRefNum) { CellRef cellRef; loadIdImpl<false>(esm, wideRefNum, cellRef); bool isDeleted; loadDataImpl<false>(esm, isDeleted, cellRef); }
CellRef makeBlankCellRef() { CellRef result; result.blank(); return result; }}