1 #include "refiddata.hpp"
2 
3 #include <cassert>
4 #include <memory>
5 
~RefIdDataContainerBase()6 CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
7 
8 
getRecordId(const CSMWorld::RefIdData::LocalIndex & index) const9 std::string CSMWorld::RefIdData::getRecordId(const CSMWorld::RefIdData::LocalIndex &index) const
10 {
11     std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator found =
12         mRecordContainers.find (index.second);
13 
14     if (found == mRecordContainers.end())
15         throw std::logic_error ("invalid local index type");
16 
17     return found->second->getId(index.first);
18 }
19 
RefIdData()20 CSMWorld::RefIdData::RefIdData()
21 {
22     mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators));
23     mRecordContainers.insert (std::make_pair (UniversalId::Type_Potion, &mPotions));
24     mRecordContainers.insert (std::make_pair (UniversalId::Type_Apparatus, &mApparati));
25     mRecordContainers.insert (std::make_pair (UniversalId::Type_Armor, &mArmors));
26     mRecordContainers.insert (std::make_pair (UniversalId::Type_Book, &mBooks));
27     mRecordContainers.insert (std::make_pair (UniversalId::Type_Clothing, &mClothing));
28     mRecordContainers.insert (std::make_pair (UniversalId::Type_Container, &mContainers));
29     mRecordContainers.insert (std::make_pair (UniversalId::Type_Creature, &mCreatures));
30     mRecordContainers.insert (std::make_pair (UniversalId::Type_Door, &mDoors));
31     mRecordContainers.insert (std::make_pair (UniversalId::Type_Ingredient, &mIngredients));
32     mRecordContainers.insert (std::make_pair (UniversalId::Type_CreatureLevelledList,
33         &mCreatureLevelledLists));
34     mRecordContainers.insert (std::make_pair (UniversalId::Type_ItemLevelledList, &mItemLevelledLists));
35     mRecordContainers.insert (std::make_pair (UniversalId::Type_Light, &mLights));
36     mRecordContainers.insert (std::make_pair (UniversalId::Type_Lockpick, &mLockpicks));
37     mRecordContainers.insert (std::make_pair (UniversalId::Type_Miscellaneous, &mMiscellaneous));
38     mRecordContainers.insert (std::make_pair (UniversalId::Type_Npc, &mNpcs));
39     mRecordContainers.insert (std::make_pair (UniversalId::Type_Probe, &mProbes));
40     mRecordContainers.insert (std::make_pair (UniversalId::Type_Repair, &mRepairs));
41     mRecordContainers.insert (std::make_pair (UniversalId::Type_Static, &mStatics));
42     mRecordContainers.insert (std::make_pair (UniversalId::Type_Weapon, &mWeapons));
43 }
44 
globalToLocalIndex(int index) const45 CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::globalToLocalIndex (int index) const
46 {
47     for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (
48         mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter)
49     {
50         if (index<iter->second->getSize())
51             return LocalIndex (index, iter->first);
52 
53         index -= iter->second->getSize();
54     }
55 
56     throw std::runtime_error ("RefIdData index out of range");
57 }
58 
localToGlobalIndex(const LocalIndex & index) const59 int CSMWorld::RefIdData::localToGlobalIndex (const LocalIndex& index)
60     const
61 {
62     std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator end =
63         mRecordContainers.find (index.second);
64 
65     if (end==mRecordContainers.end())
66         throw std::logic_error ("invalid local index type");
67 
68     int globalIndex = index.first;
69 
70     for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (
71         mRecordContainers.begin()); iter!=end; ++iter)
72         globalIndex += iter->second->getSize();
73 
74     return globalIndex;
75 }
76 
searchId(const std::string & id) const77 CSMWorld::RefIdData::LocalIndex CSMWorld::RefIdData::searchId (
78     const std::string& id) const
79 {
80     std::string id2 = Misc::StringUtils::lowerCase (id);
81 
82     std::map<std::string, std::pair<int, UniversalId::Type> >::const_iterator iter = mIndex.find (id2);
83 
84     if (iter==mIndex.end())
85         return std::make_pair (-1, CSMWorld::UniversalId::Type_None);
86 
87     return iter->second;
88 }
89 
erase(int index,int count)90 void CSMWorld::RefIdData::erase (int index, int count)
91 {
92     LocalIndex localIndex = globalToLocalIndex (index);
93 
94     std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
95         mRecordContainers.find (localIndex.second);
96 
97     while (count>0 && iter!=mRecordContainers.end())
98     {
99         int size = iter->second->getSize();
100 
101         if (localIndex.first+count>size)
102         {
103             erase (localIndex, size-localIndex.first);
104             count -= size-localIndex.first;
105 
106             ++iter;
107 
108             if (iter==mRecordContainers.end())
109                 throw std::runtime_error ("invalid count value for erase operation");
110 
111             localIndex.first = 0;
112             localIndex.second = iter->first;
113         }
114         else
115         {
116             erase (localIndex, count);
117             count = 0;
118         }
119     }
120 }
121 
getRecord(const LocalIndex & index) const122 const CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) const
123 {
124     std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
125         mRecordContainers.find (index.second);
126 
127     if (iter==mRecordContainers.end())
128         throw std::logic_error ("invalid local index type");
129 
130     return iter->second->getRecord (index.first);
131 }
132 
getRecord(const LocalIndex & index)133 CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index)
134 {
135     std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
136         mRecordContainers.find (index.second);
137 
138     if (iter==mRecordContainers.end())
139         throw std::logic_error ("invalid local index type");
140 
141     return iter->second->getRecord (index.first);
142 }
143 
appendRecord(UniversalId::Type type,const std::string & id,bool base)144 void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id, bool base)
145 {
146     std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
147         mRecordContainers.find (type);
148 
149     if (iter==mRecordContainers.end())
150         throw std::logic_error ("invalid local index type");
151 
152     iter->second->appendRecord (id, base);
153 
154     mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
155         LocalIndex (iter->second->getSize()-1, type)));
156 }
157 
getAppendIndex(UniversalId::Type type) const158 int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const
159 {
160     int index = 0;
161 
162     for (std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter (
163         mRecordContainers.begin()); iter!=mRecordContainers.end(); ++iter)
164     {
165         index += iter->second->getSize();
166 
167         if (type==iter->first)
168             break;
169     }
170 
171     return index;
172 }
173 
load(ESM::ESMReader & reader,bool base,CSMWorld::UniversalId::Type type)174 void CSMWorld::RefIdData::load (ESM::ESMReader& reader, bool base, CSMWorld::UniversalId::Type type)
175 {
176     std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator found =
177         mRecordContainers.find (type);
178 
179     if (found == mRecordContainers.end())
180         throw std::logic_error ("Invalid Referenceable ID type");
181 
182     int index = found->second->load(reader, base);
183     if (index != -1)
184     {
185         LocalIndex localIndex = LocalIndex(index, type);
186         if (base && getRecord(localIndex).mState == RecordBase::State_Deleted)
187         {
188             erase(localIndex, 1);
189         }
190         else
191         {
192             mIndex[Misc::StringUtils::lowerCase(getRecordId(localIndex))] = localIndex;
193         }
194     }
195 }
196 
erase(const LocalIndex & index,int count)197 void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
198 {
199     std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
200         mRecordContainers.find (index.second);
201     if (iter==mRecordContainers.end())
202         throw std::logic_error ("invalid local index type");
203 
204     for (int i=index.first; i<index.first+count; ++i)
205     {
206         std::map<std::string, LocalIndex>::iterator result =
207             mIndex.find (Misc::StringUtils::lowerCase (iter->second->getId (i)));
208 
209         if (result!=mIndex.end())
210             mIndex.erase (result);
211     }
212 
213     // Adjust the local indexes to avoid gaps between them after removal of records
214     int recordIndex = index.first + count;
215     int recordCount = iter->second->getSize();
216     while (recordIndex < recordCount)
217     {
218         std::map<std::string, LocalIndex>::iterator recordIndexFound =
219             mIndex.find(Misc::StringUtils::lowerCase(iter->second->getId(recordIndex)));
220         if (recordIndexFound != mIndex.end())
221         {
222             recordIndexFound->second.first -= count;
223         }
224         ++recordIndex;
225     }
226 
227     iter->second->erase (index.first, count);
228 }
229 
getSize() const230 int CSMWorld::RefIdData::getSize() const
231 {
232     return mIndex.size();
233 }
234 
getIds(bool listDeleted) const235 std::vector<std::string> CSMWorld::RefIdData::getIds (bool listDeleted) const
236 {
237     std::vector<std::string> ids;
238 
239     for (std::map<std::string, LocalIndex>::const_iterator iter (mIndex.begin()); iter!=mIndex.end();
240          ++iter)
241     {
242         if (listDeleted || !getRecord (iter->second).isDeleted())
243         {
244             std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator container =
245                 mRecordContainers.find (iter->second.second);
246 
247             if (container==mRecordContainers.end())
248                 throw std::logic_error ("Invalid referenceable ID type");
249 
250             ids.push_back (container->second->getId (iter->second.first));
251         }
252     }
253 
254     return ids;
255 }
256 
save(int index,ESM::ESMWriter & writer) const257 void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const
258 {
259     LocalIndex localIndex = globalToLocalIndex (index);
260 
261     std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator iter =
262         mRecordContainers.find (localIndex.second);
263 
264     if (iter==mRecordContainers.end())
265         throw std::logic_error ("invalid local index type");
266 
267     iter->second->save (localIndex.first, writer);
268 }
269 
getBooks() const270 const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const
271 {
272     return mBooks;
273 }
274 
getActivators() const275 const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const
276 {
277     return mActivators;
278 }
279 
getPotions() const280 const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const
281 {
282     return mPotions;
283 }
284 
getApparati() const285 const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const
286 {
287     return mApparati;
288 }
289 
getArmors() const290 const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const
291 {
292     return mArmors;
293 }
294 
getClothing() const295 const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const
296 {
297     return mClothing;
298 }
299 
getContainers() const300 const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const
301 {
302     return mContainers;
303 }
304 
getCreatures() const305 const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const
306 {
307     return mCreatures;
308 }
309 
getDoors() const310 const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const
311 {
312     return mDoors;
313 }
314 
getIngredients() const315 const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const
316 {
317     return mIngredients;
318 }
319 
getCreatureLevelledLists() const320 const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const
321 {
322     return mCreatureLevelledLists;
323 }
324 
getItemLevelledList() const325 const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const
326 {
327     return mItemLevelledLists;
328 }
329 
getLights() const330 const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const
331 {
332     return mLights;
333 }
334 
getLocpicks() const335 const CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const
336 {
337     return mLockpicks;
338 }
339 
getMiscellaneous() const340 const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const
341 {
342     return mMiscellaneous;
343 }
344 
getNPCs() const345 const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const
346 {
347     return mNpcs;
348 }
349 
getWeapons() const350 const CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const
351 {
352     return mWeapons;
353 }
354 
getProbes() const355 const CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const
356 {
357     return mProbes;
358 }
359 
getRepairs() const360 const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const
361 {
362     return mRepairs;
363 }
364 
getStatics() const365 const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const
366 {
367     return mStatics;
368 }
369 
insertRecord(CSMWorld::RecordBase & record,CSMWorld::UniversalId::Type type,const std::string & id)370 void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
371 {
372   std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
373         mRecordContainers.find (type);
374 
375     if (iter==mRecordContainers.end())
376         throw std::logic_error ("invalid local index type");
377 
378     iter->second->insertRecord(record);
379 
380     mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
381         LocalIndex (iter->second->getSize()-1, type)));
382 }
383 
copyTo(int index,RefIdData & target) const384 void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const
385 {
386     LocalIndex localIndex = globalToLocalIndex (index);
387 
388     RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
389 
390     std::string id = source->getId (localIndex.first);
391 
392     std::unique_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy());
393 
394     target.insertRecord (*newRecord, localIndex.second, id);
395 }
396