From e9f1aac7bde34c61cc4199ea0a41048af2c68c76 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 6 May 2013 14:11:55 +0200 Subject: [PATCH] added data structures for referenceable records --- apps/opencs/CMakeLists.txt | 3 +- apps/opencs/model/world/refidadapter.cpp | 6 + apps/opencs/model/world/refidadapter.hpp | 36 ++++ apps/opencs/model/world/refidcollection.cpp | 202 ++++++++++++++++++++ apps/opencs/model/world/refidcollection.hpp | 95 +++++++++ apps/opencs/model/world/refiddata.cpp | 198 +++++++++++++++++++ apps/opencs/model/world/refiddata.hpp | 188 ++++++++++++++++++ 7 files changed, 727 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/model/world/refidadapter.cpp create mode 100644 apps/opencs/model/world/refidadapter.hpp create mode 100644 apps/opencs/model/world/refidcollection.cpp create mode 100644 apps/opencs/model/world/refidcollection.hpp create mode 100644 apps/opencs/model/world/refiddata.cpp create mode 100644 apps/opencs/model/world/refiddata.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index efc3551fd3..102b348906 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -22,7 +22,8 @@ opencs_units (model/world opencs_units_noqt (model/world - universalid data record idcollection commands columnbase scriptcontext cell + universalid data record idcollection commands columnbase scriptcontext cell refidcollection + refidadapter refiddata ) opencs_hdrs_noqt (model/world diff --git a/apps/opencs/model/world/refidadapter.cpp b/apps/opencs/model/world/refidadapter.cpp new file mode 100644 index 0000000000..94ae38c3c2 --- /dev/null +++ b/apps/opencs/model/world/refidadapter.cpp @@ -0,0 +1,6 @@ + +#include "refidadapter.hpp" + +CSMWorld::RefIdAdapter::RefIdAdapter() {} + +CSMWorld::RefIdAdapter::~RefIdAdapter() {} \ No newline at end of file diff --git a/apps/opencs/model/world/refidadapter.hpp b/apps/opencs/model/world/refidadapter.hpp new file mode 100644 index 0000000000..fa56e28368 --- /dev/null +++ b/apps/opencs/model/world/refidadapter.hpp @@ -0,0 +1,36 @@ +#ifndef CSM_WOLRD_REDIDADAPTER_H +#define CSM_WOLRD_REFIDADAPTER_H + +#include + +class QVariant; + +namespace CSMWorld +{ + class RefIdColumn; + class RefIdData; + class RecordBase; + + class RefIdAdapter + { + // not implemented + RefIdAdapter (const RefIdAdapter&); + RefIdAdapter& operator= (const RefIdAdapter&); + + public: + + RefIdAdapter(); + + virtual ~RefIdAdapter(); + + virtual QVariant getData (const RefIdColumn *column, const RefIdData& data) const = 0; + + virtual void setData (const RefIdColumn *column, RefIdData& data, const QVariant& value) + const = 0; + ///< If the data type does not match an exception is thrown. + + virtual std::string getId (const RecordBase& record) const = 0; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp new file mode 100644 index 0000000000..751d4e759c --- /dev/null +++ b/apps/opencs/model/world/refidcollection.cpp @@ -0,0 +1,202 @@ + +#include "refidcollection.hpp" + +#include + +#include "refidadapter.hpp" + +CSMWorld::RefIdColumn::RefIdColumn (const std::string& title, Display displayType, int flag, + bool editable, bool userEditable) +: ColumnBase (title, displayType, flag), mEditable (editable), mUserEditable (userEditable) +{} + +bool CSMWorld::RefIdColumn::isEditable() const +{ + return mEditable; +} + +bool CSMWorld::RefIdColumn::isUserEditable() const +{ + return mUserEditable; +} + + +const CSMWorld::RefIdAdapter& CSMWorld::RefIdCollection::findAdaptor (UniversalId::Type type) const +{ + std::map::const_iterator iter = mAdapters.find (type); + + if (iter==mAdapters.end()) + throw std::logic_error ("unsupported type in RefIdCollection"); + + return *iter->second; +} + +CSMWorld::RefIdCollection::RefIdCollection() +{ + mColumns.push_back (RefIdColumn ("ID", ColumnBase::Display_String, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + mColumns.push_back (RefIdColumn ("*", ColumnBase::Display_Integer, + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + mColumns.push_back (RefIdColumn ("Name", ColumnBase::Display_String)); +} + +CSMWorld::RefIdCollection::~RefIdCollection() +{ + for (std::map::iterator iter (mAdapters.begin()); + iter!=mAdapters.end(); ++iter) + delete iter->second; +} + +int CSMWorld::RefIdCollection::getSize() const +{ + return mData.getSize(); +} + +std::string CSMWorld::RefIdCollection::getId (int index) const +{ + return getData (index, 0).toString().toUtf8().constData(); +} + +int CSMWorld::RefIdCollection::getIndex (const std::string& id) const +{ + int index = searchId (id); + + if (index==-1) + throw std::runtime_error ("invalid ID: " + id); + + return index; +} + +int CSMWorld::RefIdCollection::getColumns() const +{ + return mColumns.size(); +} + +const CSMWorld::ColumnBase& CSMWorld::RefIdCollection::getColumn (int column) const +{ + return mColumns.at (column); +} + +QVariant CSMWorld::RefIdCollection::getData (int index, int column) const +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + + return adaptor.getData (&mColumns.at (column), mData); +} + +void CSMWorld::RefIdCollection::setData (int index, int column, const QVariant& data) +{ + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + const RefIdAdapter& adaptor = findAdaptor (localIndex.second); + + adaptor.setData (&mColumns.at (column), mData, data); +} + +void CSMWorld::RefIdCollection::removeRows (int index, int count) +{ + mData.erase (index, count); +} + +void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) +{ + mData.appendRecord (type, id); +} + +int CSMWorld::RefIdCollection::searchId (const std::string& id) const +{ + return mData.localToGlobalIndex (mData.searchId (id)); +} + +void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) +{ + mData.getRecord (mData.globalToLocalIndex (index)).assign (record); +} + +void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, + UniversalId::Type type) +{ + std::string id = findAdaptor (type).getId (record); + + int index = mData.getAppendIndex (type); + + mData.appendRecord (type, id); + + mData.getRecord (mData.globalToLocalIndex (index)).assign (record); +} + +const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const +{ + return mData.getRecord (mData.searchId (id)); +} + +const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) const +{ + return mData.getRecord (mData.globalToLocalIndex (index)); +} + +void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type) +{ + std::string id = reader.getHNOString ("NAME"); + + int index = searchId (id); + + if (reader.isNextSub ("DELE")) + { + reader.skipRecord(); + + if (index==-1) + { + // deleting a record that does not exist + + // ignore it for now + + /// \todo report the problem to the user + } + else if (base) + { + mData.erase (index, 1); + } + else + { + mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted; + } + } + else + { + if (index==-1) + { + // new record + int index = mData.getAppendIndex (type); + mData.appendRecord (type, id); + + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + mData.load (localIndex, reader, base); + + mData.getRecord (localIndex).mState = + base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + } + else + { + // old record + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + + if (!base) + if (mData.getRecord (localIndex).mState==RecordBase::State_Erased) + throw std::logic_error ("attempt to access a deleted record"); + + mData.load (localIndex, reader, base); + + if (!base) + mData.getRecord (localIndex).mState = RecordBase::State_Modified; + } + } +} + +int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const +{ + return mData.getAppendIndex (type); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp new file mode 100644 index 0000000000..4f8096d25b --- /dev/null +++ b/apps/opencs/model/world/refidcollection.hpp @@ -0,0 +1,95 @@ +#ifndef CSM_WOLRD_REFIDCOLLECTION_H +#define CSM_WOLRD_REFIDCOLLECTION_H + +#include +#include + +#include "columnbase.hpp" +#include "idcollection.hpp" +#include "refiddata.hpp" + +namespace CSMWorld +{ + class RefIdAdapter; + + class RefIdColumn : public ColumnBase + { + bool mEditable; + bool mUserEditable; + + public: + + RefIdColumn (const std::string& title, Display displayType, + int flag = Flag_Table | Flag_Dialogue, bool editable = true, + bool userEditable = true); + + virtual bool isEditable() const; + + virtual bool isUserEditable() const; + }; + + class RefIdCollection : public IdCollectionBase + { + private: + + RefIdData mData; + std::vector mColumns; + std::map mAdapters; + + private: + + const RefIdAdapter& findAdaptor (UniversalId::Type) const; + ///< Throws an exception if no adaptor for \a Type can be found. + + public: + + RefIdCollection(); + + virtual ~RefIdCollection(); + + virtual int getSize() const; + + virtual std::string getId (int index) const; + + virtual int getIndex (const std::string& id) const; + + virtual int getColumns() const; + + virtual const ColumnBase& getColumn (int column) const; + + virtual QVariant getData (int index, int column) const; + + virtual void setData (int index, int column, const QVariant& data); + + virtual void removeRows (int index, int count); + + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual int searchId (const std::string& id) const; + ////< Search record with \a id. + /// \return index of record (if found) or -1 (not found) + + virtual void replace (int index, const RecordBase& record); + ///< If the record type does not match, an exception is thrown. + /// + /// \attention \a record must not change the ID. + + virtual void appendRecord (const RecordBase& record, UniversalId::Type type); + ///< If the record type does not match, an exception is thrown. + /// + ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual const RecordBase& getRecord (const std::string& id) const; + + virtual const RecordBase& getRecord (int index) const; + + virtual void load (ESM::ESMReader& reader, bool base, UniversalId::Type type); + + virtual int getAppendIndex (UniversalId::Type type) const; + ///< \param type Will be ignored, unless the collection supports multiple record types + + }; +} + +#endif diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp new file mode 100644 index 0000000000..9f6ed9be3c --- /dev/null +++ b/apps/opencs/model/world/refiddata.cpp @@ -0,0 +1,198 @@ + +#include "refiddata.hpp" + +#include + +#include + +CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {} + +CSMWorld::RefIdData::RefIdData() +{ + mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList, + &mCreatureLevelledLists)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics)); + mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons)); +} + +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const +{ + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + { + if (indexsecond->getSize()) + return LocalIndex (index, iter->first); + + index -= iter->second->getSize(); + } + + throw std::runtime_error ("RefIdData index out of range"); +} + +int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index) + const +{ + std::map::const_iterator end = + mRecordContainers.find (index.second); + + if (end==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + int globalIndex = index.first; + + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=end; ++iter) + globalIndex += iter->second->getSize(); + + return globalIndex; +} + +CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId ( + const std::string& id) const +{ + std::string id2 = Misc::StringUtils::lowerCase (id); + + std::map >::const_iterator iter = mIndex.find (id2); + + if (iter!=mIndex.end()) + return std::make_pair (-1, CSMWorld::UniversalId::Type_None); + + return iter->second; +} + +void CSMWorld::RefIdData::erase (int index, int count) +{ + LocalIndex localIndex = globalToLocalIndex (index); + + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); + + while (count>0 && iter!=mRecordContainers.end()) + { + int size = iter->second->getSize(); + + if (localIndex.first+count>size) + { + erase (localIndex, size-localIndex.first); + count -= size-localIndex.first; + + ++iter; + + if (iter==mRecordContainers.end()) + throw std::runtime_error ("invalid count value for erase operation"); + + localIndex.first = 0; + localIndex.second = iter->first; + } + else + { + erase (localIndex, count); + count = 0; + } + } +} + +const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const +{ + std::map::const_iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return iter->second->getRecord (index.first); +} + +CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + return iter->second->getRecord (index.first); +} + +void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id) +{ + std::map::iterator iter = + mRecordContainers.find (type); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->appendRecord (id); + + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), + LocalIndex (iter->second->getSize()-1, type))); +} + +int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const +{ + int index = 0; + + for (std::map::const_iterator iter ( + mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter) + { + index += iter->second->getSize(); + + if (type==iter->first) + break; + } + + return index; +} + +void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->load (index.first, reader, base); +} + +void CSMWorld::RefIdData::erase (const LocalIndex& index, int count) +{ + std::map::iterator iter = + mRecordContainers.find (index.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + for (int i=index.first; i::iterator result = + mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i))); + + if (result!=mIndex.end()) + mIndex.erase (result); + } + + iter->second->erase (index.first, count); +} + +int CSMWorld::RefIdData::getSize() const +{ + return mIndex.size(); +} diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp new file mode 100644 index 0000000000..475566fb54 --- /dev/null +++ b/apps/opencs/model/world/refiddata.hpp @@ -0,0 +1,188 @@ +#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 "record.hpp" +#include "universalid.hpp" + +namespace ESM +{ + class ESMReader; +} + +namespace CSMWorld +{ + struct RefIdDataContainerBase + { + virtual ~RefIdDataContainerBase(); + + virtual int getSize() const = 0; + + virtual const RecordBase& getRecord (int index) const = 0; + + virtual RecordBase& getRecord (int index)= 0; + + virtual void appendRecord (const std::string& id) = 0; + + virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; + + virtual void erase (int index, int count) = 0; + + virtual std::string getId (int index) const = 0; + }; + + template + struct RefIdDataContainer : public RefIdDataContainerBase + { + std::vector > mContainer; + + virtual int getSize() const; + + virtual const RecordBase& getRecord (int index) const; + + virtual RecordBase& getRecord (int index); + + virtual void appendRecord (const std::string& id); + + virtual void load (int index, ESM::ESMReader& reader, bool base); + + virtual void erase (int index, int count); + + virtual std::string getId (int index) const; + }; + + 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 + void RefIdDataContainer::appendRecord (const std::string& id) + { + Record record; + record.mModified.mId = id; + record.mModified.blank(); + record.mState = RecordBase::State_ModifiedOnly; + + mContainer.push_back (record); + } + + template + void RefIdDataContainer::load (int index, ESM::ESMReader& reader, bool base) + { + (base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader); + } + + 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 + std::string RefIdDataContainer::getId (int index) const + { + return mContainer.at (index).get().mId; + } + + 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. + + public: + + RefIdData(); + + LocalIndex globalToLocalIndex (int index) const; + + int localToGlobalIndex (const LocalIndex& index) const; + + LocalIndex searchId (const std::string& id) const; + + void erase (int index, int count); + + const RecordBase& getRecord (const LocalIndex& index) const; + + RecordBase& getRecord (const LocalIndex& index); + + void appendRecord (UniversalId::Type type, const std::string& id); + + int getAppendIndex (UniversalId::Type type) const; + + void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); + + int getSize() const; + }; +} + +#endif