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