1 #ifndef CSM_WOLRD_REFIDDATA_H
2 #define CSM_WOLRD_REFIDDATA_H
3 
4 #include <vector>
5 #include <map>
6 
7 #include <components/esm/loadacti.hpp>
8 #include <components/esm/loadalch.hpp>
9 #include <components/esm/loadappa.hpp>
10 #include <components/esm/loadarmo.hpp>
11 #include <components/esm/loadbook.hpp>
12 #include <components/esm/loadclot.hpp>
13 #include <components/esm/loadcont.hpp>
14 #include <components/esm/loadcrea.hpp>
15 #include <components/esm/loaddoor.hpp>
16 #include <components/esm/loadingr.hpp>
17 #include <components/esm/loadlevlist.hpp>
18 #include <components/esm/loadligh.hpp>
19 #include <components/esm/loadlock.hpp>
20 #include <components/esm/loadprob.hpp>
21 #include <components/esm/loadrepa.hpp>
22 #include <components/esm/loadstat.hpp>
23 #include <components/esm/loadweap.hpp>
24 #include <components/esm/loadnpc.hpp>
25 #include <components/esm/loadmisc.hpp>
26 #include <components/esm/esmwriter.hpp>
27 
28 #include <components/misc/stringops.hpp>
29 
30 #include "record.hpp"
31 #include "universalid.hpp"
32 
33 namespace ESM
34 {
35     class ESMReader;
36 }
37 
38 namespace CSMWorld
39 {
40     struct RefIdDataContainerBase
41     {
42         virtual ~RefIdDataContainerBase();
43 
44         virtual int getSize() const = 0;
45 
46         virtual const RecordBase& getRecord (int index) const = 0;
47 
48         virtual RecordBase& getRecord (int index)= 0;
49 
50         virtual void appendRecord (const std::string& id, bool base) = 0;
51 
52         virtual void insertRecord (RecordBase& record) = 0;
53 
54         virtual int load (ESM::ESMReader& reader, bool base) = 0;
55         ///< \return index of a loaded record or -1 if no record was loaded
56 
57         virtual void erase (int index, int count) = 0;
58 
59         virtual std::string getId (int index) const = 0;
60 
61         virtual void save (int index, ESM::ESMWriter& writer) const = 0;
62     };
63 
64     template<typename RecordT>
65     struct RefIdDataContainer : public RefIdDataContainerBase
66     {
67         std::vector<Record<RecordT> > mContainer;
68 
69         int getSize() const override;
70 
71         const RecordBase& getRecord (int index) const override;
72 
73         RecordBase& getRecord (int index) override;
74 
75         void appendRecord (const std::string& id, bool base) override;
76 
77         void insertRecord (RecordBase& record) override;
78 
79         int load (ESM::ESMReader& reader, bool base) override;
80         ///< \return index of a loaded record or -1 if no record was loaded
81 
82         void erase (int index, int count) override;
83 
84         std::string getId (int index) const override;
85 
86         void save (int index, ESM::ESMWriter& writer) const override;
87     };
88 
89     template<typename RecordT>
insertRecord(RecordBase & record)90     void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)
91     {
92         Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
93         mContainer.push_back(newRecord);
94     }
95 
96     template<typename RecordT>
getSize() const97     int RefIdDataContainer<RecordT>::getSize() const
98     {
99         return static_cast<int> (mContainer.size());
100     }
101 
102     template<typename RecordT>
getRecord(int index) const103     const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
104     {
105         return mContainer.at (index);
106     }
107 
108     template<typename RecordT>
getRecord(int index)109     RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
110     {
111         return mContainer.at (index);
112     }
113 
114     template<typename RecordT>
appendRecord(const std::string & id,bool base)115     void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
116     {
117         Record<RecordT> record;
118 
119         record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
120 
121         record.mBase.mId = id;
122         record.mModified.mId = id;
123         (base ? record.mBase : record.mModified).blank();
124 
125         mContainer.push_back (record);
126     }
127 
128     template<typename RecordT>
load(ESM::ESMReader & reader,bool base)129     int RefIdDataContainer<RecordT>::load (ESM::ESMReader& reader, bool base)
130     {
131         RecordT record;
132         bool isDeleted = false;
133 
134         record.load(reader, isDeleted);
135 
136         int index = 0;
137         int numRecords = static_cast<int>(mContainer.size());
138         for (; index < numRecords; ++index)
139         {
140             if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))
141             {
142                 break;
143             }
144         }
145 
146         if (isDeleted)
147         {
148             if (index == numRecords)
149             {
150                 // deleting a record that does not exist
151                 // ignore it for now
152                 /// \todo report the problem to the user
153                 return -1;
154             }
155 
156             // Flag the record as Deleted even for a base content file.
157             // RefIdData is responsible for its erasure.
158             mContainer[index].mState = RecordBase::State_Deleted;
159         }
160         else
161         {
162             if (index == numRecords)
163             {
164                 appendRecord(record.mId, base);
165                 if (base)
166                 {
167                     mContainer.back().mBase = record;
168                 }
169                 else
170                 {
171                     mContainer.back().mModified = record;
172                 }
173             }
174             else if (!base)
175             {
176                 mContainer[index].setModified(record);
177             }
178             else
179             {
180                 // Overwrite
181                 mContainer[index].setModified(record);
182                 mContainer[index].merge();
183             }
184         }
185 
186         return index;
187     }
188 
189     template<typename RecordT>
erase(int index,int count)190     void RefIdDataContainer<RecordT>::erase (int index, int count)
191     {
192         if (index<0 || index+count>getSize())
193             throw std::runtime_error ("invalid RefIdDataContainer index");
194 
195         mContainer.erase (mContainer.begin()+index, mContainer.begin()+index+count);
196     }
197 
198     template<typename RecordT>
getId(int index) const199     std::string RefIdDataContainer<RecordT>::getId (int index) const
200     {
201         return mContainer.at (index).get().mId;
202     }
203 
204     template<typename RecordT>
save(int index,ESM::ESMWriter & writer) const205     void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
206     {
207         Record<RecordT> record = mContainer.at(index);
208 
209         if (record.isModified() || record.mState == RecordBase::State_Deleted)
210         {
211             RecordT esmRecord = record.get();
212             writer.startRecord(esmRecord.sRecordId);
213             esmRecord.save(writer, record.mState == RecordBase::State_Deleted);
214             writer.endRecord(esmRecord.sRecordId);
215         }
216     }
217 
218 
219     class RefIdData
220     {
221         public:
222 
223             typedef std::pair<int, UniversalId::Type> LocalIndex;
224 
225         private:
226 
227             RefIdDataContainer<ESM::Activator> mActivators;
228             RefIdDataContainer<ESM::Potion> mPotions;
229             RefIdDataContainer<ESM::Apparatus> mApparati;
230             RefIdDataContainer<ESM::Armor> mArmors;
231             RefIdDataContainer<ESM::Book> mBooks;
232             RefIdDataContainer<ESM::Clothing> mClothing;
233             RefIdDataContainer<ESM::Container> mContainers;
234             RefIdDataContainer<ESM::Creature> mCreatures;
235             RefIdDataContainer<ESM::Door> mDoors;
236             RefIdDataContainer<ESM::Ingredient> mIngredients;
237             RefIdDataContainer<ESM::CreatureLevList> mCreatureLevelledLists;
238             RefIdDataContainer<ESM::ItemLevList> mItemLevelledLists;
239             RefIdDataContainer<ESM::Light> mLights;
240             RefIdDataContainer<ESM::Lockpick> mLockpicks;
241             RefIdDataContainer<ESM::Miscellaneous> mMiscellaneous;
242             RefIdDataContainer<ESM::NPC> mNpcs;
243             RefIdDataContainer<ESM::Probe> mProbes;
244             RefIdDataContainer<ESM::Repair> mRepairs;
245             RefIdDataContainer<ESM::Static> mStatics;
246             RefIdDataContainer<ESM::Weapon> mWeapons;
247 
248             std::map<std::string, LocalIndex> mIndex;
249 
250             std::map<UniversalId::Type, RefIdDataContainerBase *> mRecordContainers;
251 
252             void erase (const LocalIndex& index, int count);
253             ///< Must not spill over into another type.
254 
255             std::string getRecordId(const LocalIndex &index) const;
256 
257         public:
258 
259             RefIdData();
260 
261             LocalIndex globalToLocalIndex (int index) const;
262 
263             int localToGlobalIndex (const LocalIndex& index) const;
264 
265             LocalIndex searchId (const std::string& id) const;
266 
267             void erase (int index, int count);
268 
269             void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type,
270                 const std::string& id);
271 
272             const RecordBase& getRecord (const LocalIndex& index) const;
273 
274             RecordBase& getRecord (const LocalIndex& index);
275 
276             void appendRecord (UniversalId::Type type, const std::string& id, bool base);
277 
278             int getAppendIndex (UniversalId::Type type) const;
279 
280             void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);
281 
282             int getSize() const;
283 
284             std::vector<std::string> getIds (bool listDeleted = true) const;
285             ///< Return a sorted collection of all IDs
286             ///
287             /// \param listDeleted include deleted record in the list
288 
289             void save (int index, ESM::ESMWriter& writer) const;
290 
291             //RECORD CONTAINERS ACCESS METHODS
292             const RefIdDataContainer<ESM::Book>& getBooks() const;
293             const RefIdDataContainer<ESM::Activator>& getActivators() const;
294             const RefIdDataContainer<ESM::Potion>& getPotions() const;
295             const RefIdDataContainer<ESM::Apparatus>& getApparati() const;
296             const RefIdDataContainer<ESM::Armor>& getArmors() const;
297             const RefIdDataContainer<ESM::Clothing>& getClothing() const;
298             const RefIdDataContainer<ESM::Container>& getContainers() const;
299             const RefIdDataContainer<ESM::Creature>& getCreatures() const;
300             const RefIdDataContainer<ESM::Door>& getDoors() const;
301             const RefIdDataContainer<ESM::Ingredient>& getIngredients() const;
302             const RefIdDataContainer<ESM::CreatureLevList>& getCreatureLevelledLists() const;
303             const RefIdDataContainer<ESM::ItemLevList>& getItemLevelledList() const;
304             const RefIdDataContainer<ESM::Light>& getLights() const;
305             const RefIdDataContainer<ESM::Lockpick>& getLocpicks() const;
306             const RefIdDataContainer<ESM::Miscellaneous>& getMiscellaneous() const;
307             const RefIdDataContainer<ESM::NPC>& getNPCs() const;
308             const RefIdDataContainer<ESM::Weapon >& getWeapons() const;
309             const RefIdDataContainer<ESM::Probe >& getProbes() const;
310             const RefIdDataContainer<ESM::Repair>& getRepairs() const;
311             const RefIdDataContainer<ESM::Static>& getStatics() const;
312 
313             void copyTo (int index, RefIdData& target) const;
314     };
315 }
316 
317 #endif
318