#ifndef CSM_WOLRD_REFIDDATA_H #define CSM_WOLRD_REFIDDATA_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "record.hpp" #include "universalid.hpp" namespace ESM { class ESMReader; } namespace CSMWorld { struct RefIdDataContainerBase { virtual ~RefIdDataContainerBase() = default; virtual int getSize() const = 0; virtual const RecordBase& getRecord(int index) const = 0; virtual RecordBase& getRecord(int index) = 0; virtual unsigned int getRecordFlags(int index) const = 0; virtual void appendRecord(const ESM::RefId& id, bool base) = 0; virtual void insertRecord(std::unique_ptr record) = 0; virtual int load(ESM::ESMReader& reader, bool base) = 0; ///< \return index of a loaded record or -1 if no record was loaded virtual void erase(int index, int count) = 0; virtual ESM::RefId getId(int index) const = 0; virtual void save(int index, ESM::ESMWriter& writer) const = 0; }; template struct RefIdDataContainer : public RefIdDataContainerBase { std::vector>> mContainer; int getSize() const override; const RecordBase& getRecord(int index) const override; RecordBase& getRecord(int index) override; unsigned int getRecordFlags(int index) const override; void appendRecord(const ESM::RefId& id, bool base) override; void insertRecord(std::unique_ptr record) override; int load(ESM::ESMReader& reader, bool base) override; ///< \return index of a loaded record or -1 if no record was loaded void erase(int index, int count) override; ESM::RefId getId(int index) const override; void save(int index, ESM::ESMWriter& writer) const override; }; template void RefIdDataContainer::insertRecord(std::unique_ptr record) { assert(record != nullptr); // convert base pointer to record type pointer std::unique_ptr> typedRecord(&dynamic_cast&>(*record)); record.release(); mContainer.push_back(std::move(typedRecord)); } template int RefIdDataContainer::getSize() const { return static_cast(mContainer.size()); } template const RecordBase& RefIdDataContainer::getRecord(int index) const { return *mContainer.at(index); } template RecordBase& RefIdDataContainer::getRecord(int index) { return *mContainer.at(index); } template unsigned int RefIdDataContainer::getRecordFlags(int index) const { return mContainer.at(index)->get().mRecordFlags; } template void RefIdDataContainer::appendRecord(const ESM::RefId& id, bool base) { auto record = std::make_unique>(); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record->mBase.mId = id; record->mModified.mId = id; (base ? record->mBase : record->mModified).blank(); mContainer.push_back(std::move(record)); } template int RefIdDataContainer::load(ESM::ESMReader& reader, bool base) { RecordT record; bool isDeleted = false; record.load(reader, isDeleted); int index = 0; int numRecords = static_cast(mContainer.size()); for (; index < numRecords; ++index) { if ((mContainer[index]->get().mId == record.mId)) { break; } } if (isDeleted) { if (index == numRecords) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user return -1; } // Flag the record as Deleted even for a base content file. // RefIdData is responsible for its erasure. mContainer[index]->mState = RecordBase::State_Deleted; } else { if (index == numRecords) { appendRecord(record.mId, base); if (base) { mContainer.back()->mBase = record; } else { mContainer.back()->mModified = record; } } else if (!base) { mContainer[index]->setModified(record); } else { // Overwrite mContainer[index]->setModified(record); mContainer[index]->merge(); } } return index; } template void RefIdDataContainer::erase(int index, int count) { if (index < 0 || index + count > getSize()) throw std::runtime_error("invalid RefIdDataContainer index"); mContainer.erase(mContainer.begin() + index, mContainer.begin() + index + count); } template ESM::RefId RefIdDataContainer::getId(int index) const { return mContainer.at(index)->get().mId; } template void RefIdDataContainer::save(int index, ESM::ESMWriter& writer) const { const Record& record = *mContainer.at(index); if (record.isModified() || record.mState == RecordBase::State_Deleted) { RecordT esmRecord = record.get(); writer.startRecord(esmRecord.sRecordId, esmRecord.mRecordFlags); esmRecord.save(writer, record.mState == RecordBase::State_Deleted); writer.endRecord(esmRecord.sRecordId); } } class RefIdData { public: typedef std::pair LocalIndex; private: RefIdDataContainer mActivators; RefIdDataContainer mPotions; RefIdDataContainer mApparati; RefIdDataContainer mArmors; RefIdDataContainer mBooks; RefIdDataContainer mClothing; RefIdDataContainer mContainers; RefIdDataContainer mCreatures; RefIdDataContainer mDoors; RefIdDataContainer mIngredients; RefIdDataContainer mCreatureLevelledLists; RefIdDataContainer mItemLevelledLists; RefIdDataContainer mLights; RefIdDataContainer mLockpicks; RefIdDataContainer mMiscellaneous; RefIdDataContainer mNpcs; RefIdDataContainer mProbes; RefIdDataContainer mRepairs; RefIdDataContainer mStatics; RefIdDataContainer mWeapons; std::map mIndex; std::map mRecordContainers; void erase(const LocalIndex& index, int count); ///< Must not spill over into another type. ESM::RefId getRecordId(const LocalIndex& index) const; public: RefIdData(); LocalIndex globalToLocalIndex(int index) const; int localToGlobalIndex(const LocalIndex& index) const; LocalIndex searchId(const ESM::RefId& id) const; void erase(int index, int count); void insertRecord(std::unique_ptr record, CSMWorld::UniversalId::Type type, const ESM::RefId& id); const RecordBase& getRecord(const LocalIndex& index) const; RecordBase& getRecord(const LocalIndex& index); unsigned int getRecordFlags(const ESM::RefId& id) const; void appendRecord(UniversalId::Type type, const ESM::RefId& id, bool base); int getAppendIndex(UniversalId::Type type) const; void load(ESM::ESMReader& reader, bool base, UniversalId::Type type); int getSize() const; std::vector getIds(bool listDeleted = true) const; ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list void save(int index, ESM::ESMWriter& writer) const; // RECORD CONTAINERS ACCESS METHODS const RefIdDataContainer& getBooks() const; const RefIdDataContainer& getActivators() const; const RefIdDataContainer& getPotions() const; const RefIdDataContainer& getApparati() const; const RefIdDataContainer& getArmors() const; const RefIdDataContainer& getClothing() const; const RefIdDataContainer& getContainers() const; const RefIdDataContainer& getCreatures() const; const RefIdDataContainer& getDoors() const; const RefIdDataContainer& getIngredients() const; const RefIdDataContainer& getCreatureLevelledLists() const; const RefIdDataContainer& getItemLevelledList() const; const RefIdDataContainer& getLights() const; const RefIdDataContainer& getLocpicks() const; const RefIdDataContainer& getMiscellaneous() const; const RefIdDataContainer& getNPCs() const; const RefIdDataContainer& getWeapons() const; const RefIdDataContainer& getProbes() const; const RefIdDataContainer& getRepairs() const; const RefIdDataContainer& getStatics() const; void copyTo(int index, RefIdData& target) const; }; } #endif