diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a3614af96..2e53fe802 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -816,6 +816,9 @@ namespace MWClass void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::CreatureState& state2 = dynamic_cast (state); ensureCustomData(ptr); @@ -844,7 +847,6 @@ namespace MWClass customData.mContainerStore->readState (state2.mInventory); customData.mCreatureStats.readState (state2.mCreatureStats); - } void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -852,6 +854,12 @@ namespace MWClass { ESM::CreatureState& state2 = dynamic_cast (state); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + ensureCustomData (ptr); CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ac4182734..8f62cc1e8 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1275,6 +1275,9 @@ namespace MWClass void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::NpcState& state2 = dynamic_cast (state); ensureCustomData(ptr); @@ -1302,6 +1305,12 @@ namespace MWClass { ESM::NpcState& state2 = dynamic_cast (state); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + ensureCustomData (ptr); NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 4cf99fdb7..605c8f9d2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -85,7 +85,9 @@ namespace RecordType state; iter->save (state); + // recordId currently unused writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); + state.save (writer); } } @@ -93,12 +95,13 @@ namespace template void readReferenceCollection (ESM::ESMReader& reader, - MWWorld::CellRefList& collection, const std::map& contentFileMap) + MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); RecordType state; - state.load (reader); + state.mRef = cref; + state.load(reader); // If the reference came from a content file, make sure this content file is loaded if (state.mRef.mRefNum.hasContentFile()) @@ -640,109 +643,121 @@ namespace MWWorld while (reader.isNextSub ("OBJE")) { - unsigned int id = 0; - reader.getHT (id); + unsigned int unused; + reader.getHT (unused); + + // load the RefID first so we know what type of object it is + ESM::CellRef cref; + cref.loadId(reader, true); + + int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); + if (type == 0) + { + std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; + reader.skipHSubUntil("OBJE"); + continue; + } - switch (id) + switch (type) { case ESM::REC_ACTI: - readReferenceCollection (reader, mActivators, contentFileMap); + readReferenceCollection (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: - readReferenceCollection (reader, mPotions, contentFileMap); + readReferenceCollection (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: - readReferenceCollection (reader, mAppas, contentFileMap); + readReferenceCollection (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: - readReferenceCollection (reader, mArmors, contentFileMap); + readReferenceCollection (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: - readReferenceCollection (reader, mBooks, contentFileMap); + readReferenceCollection (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: - readReferenceCollection (reader, mClothes, contentFileMap); + readReferenceCollection (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, contentFileMap); + readReferenceCollection (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, contentFileMap); + readReferenceCollection (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, contentFileMap); + readReferenceCollection (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: - readReferenceCollection (reader, mIngreds, contentFileMap); + readReferenceCollection (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, contentFileMap); + readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: - readReferenceCollection (reader, mItemLists, contentFileMap); + readReferenceCollection (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: - readReferenceCollection (reader, mLockpicks, contentFileMap); + readReferenceCollection (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: - readReferenceCollection (reader, mMiscItems, contentFileMap); + readReferenceCollection (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, contentFileMap); + readReferenceCollection (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: - readReferenceCollection (reader, mProbes, contentFileMap); + readReferenceCollection (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: - readReferenceCollection (reader, mRepairs, contentFileMap); + readReferenceCollection (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: - readReferenceCollection (reader, mStatics, contentFileMap); + readReferenceCollection (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: - readReferenceCollection (reader, mWeapons, contentFileMap); + readReferenceCollection (reader, mWeapons, cref, contentFileMap); break; default: diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 0c2dfd5db..cd9fe8570 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -5,6 +5,12 @@ #include "esmwriter.hpp" void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +{ + loadId(esm, wideRefNum); + loadData(esm); +} + +void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) { // 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. @@ -19,8 +25,6 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHNT (mRefNum.mIndex, "FRMR"); mRefID = esm.getHNString ("NAME"); - - loadData(esm); } void ESM::CellRef::loadData(ESMReader &esm) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index b7b00a654..7aa0e4d44 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,8 +89,11 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; + /// Calls loadId and loadData void load (ESMReader& esm, bool wideRefNum = false); + void loadId (ESMReader& esm, bool wideRefNum = false); + /// Implicitly called by load void loadData (ESMReader& esm); diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index c2838f78d..c15becd98 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -5,18 +5,24 @@ void ESM::CreatureState::load (ESMReader &esm) { ObjectState::load (esm); - mInventory.load (esm); + if (mHasCustomState) + { + mInventory.load (esm); - mCreatureStats.load (esm); + mCreatureStats.load (esm); + } } void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); - mInventory.save (esm); + if (mHasCustomState) + { + mInventory.save (esm); - mCreatureStats.save (esm); + mCreatureStats.save (esm); + } } void ESM::CreatureState::blank() diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 7cf0de1a9..de110dd18 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -210,6 +210,17 @@ void ESMReader::skipHSubSize(int size) fail("skipHSubSize() mismatch"); } +void ESMReader::skipHSubUntil(const char *name) +{ + while (hasMoreSubs() && !isNextSub(name)) + { + mCtx.subCached = false; + skipHSub(); + } + if (hasMoreSubs()) + mCtx.subCached = true; +} + void ESMReader::getSubHeader() { if (mCtx.leftRec < 4) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 642ec4168..bb615af11 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -195,6 +195,9 @@ public: // Skip sub record and check its size void skipHSubSize(int size); + // Skip all subrecords until the given subrecord or no more subrecords remaining + void skipHSubUntil(const char* name); + /* Sub-record header. This updates leftRec beyond the current sub-record as well. leftSub contains size of current sub-record. */ diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index fdaba3322..b946f9501 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -11,6 +11,7 @@ namespace slot = -1; esm.getHNOT (slot, "SLOT"); + state.mRef.loadId(esm, true); state.load (esm); } diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index 6134193c2..724d67326 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -5,22 +5,28 @@ void ESM::NpcState::load (ESMReader &esm) { ObjectState::load (esm); - mInventory.load (esm); + if (mHasCustomState) + { + mInventory.load (esm); - mNpcStats.load (esm); + mNpcStats.load (esm); - mCreatureStats.load (esm); + mCreatureStats.load (esm); + } } void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); - mInventory.save (esm); + if (mHasCustomState) + { + mInventory.save (esm); - mNpcStats.save (esm); + mNpcStats.save (esm); - mCreatureStats.save (esm); + mCreatureStats.save (esm); + } } void ESM::NpcState::blank() @@ -28,4 +34,5 @@ void ESM::NpcState::blank() ObjectState::blank(); mNpcStats.blank(); mCreatureStats.blank(); + mHasCustomState = true; } diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 98cf3eba6..66fd49663 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -6,7 +6,7 @@ void ESM::ObjectState::load (ESMReader &esm) { - mRef.load (esm, true); + mRef.loadData(esm); mHasLocals = 0; esm.getHNOT (mHasLocals, "HLOC"); @@ -27,6 +27,10 @@ void ESM::ObjectState::load (ESMReader &esm) // used for lights only mTime = 0; esm.getHNOT (mTime, "LTIM"); + + // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files + mHasCustomState = true; + esm.getHNOT (mHasCustomState, "HCUS"); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const @@ -53,6 +57,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mTime) esm.writeHNT ("LTIM", mTime); + + if (!mHasCustomState) + esm.writeHNT ("HCUS", false); } void ESM::ObjectState::blank() @@ -68,6 +75,7 @@ void ESM::ObjectState::blank() mLocalRotation[i] = 0; } mTime = 0; + mHasCustomState = true; } ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 6b536bd01..b03689653 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -28,7 +28,15 @@ namespace ESM float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either + // Is there any class-specific state following the ObjectState + bool mHasCustomState; + + ObjectState() : mHasCustomState(true) + {} + + /// @note Does not load the CellRef ID, it should already be loaded before calling this method virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; /// Initialize to default state diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 52b44c945..70c4b79e2 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -6,6 +6,7 @@ void ESM::Player::load (ESMReader &esm) { + mObject.mRef.loadId(esm, true); mObject.load (esm); mCellId.load (esm);