#include "infocollection.hpp" #include #include #include #include #include #include "components/debug/debuglog.hpp" #include "components/esm3/infoorder.hpp" #include "components/esm3/loaddial.hpp" #include "components/esm3/loadinfo.hpp" #include "collection.hpp" #include "info.hpp" namespace CSMWorld { namespace { std::string_view getInfoTopicId(const ESM::RefId& infoId) { return parseInfoRefId(infoId).first; } } ESM::RefId makeCompositeInfoRefId(const ESM::RefId& topicId, const ESM::RefId& infoId) { return ESM::RefId::stringRefId(topicId.getRefIdString() + '#' + infoId.getRefIdString()); } } void CSMWorld::InfoCollection::load(const Info& value, bool base) { const int index = searchId(value.mId); if (index == -1) { // new record auto record = std::make_unique>(); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record->mBase : record->mModified) = value; insertRecord(std::move(record), getSize()); } else { // old record auto record = std::make_unique>(getRecord(index)); if (base) record->mBase = value; else record->setModified(value); setRecord(index, std::move(record)); } } void CSMWorld::InfoCollection::load( ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue, InfoOrderByTopic& infoOrders) { Info info; bool isDeleted = false; info.load(reader, isDeleted); const ESM::RefId id = makeCompositeInfoRefId(dialogue.mId, info.mId); if (isDeleted) { const int index = searchId(id); if (index == -1) { Log(Debug::Warning) << "Trying to delete absent info \"" << info.mId << "\" from topic \"" << dialogue.mId << "\""; return; } if (base) { infoOrders.at(dialogue.mId).removeInfo(info.mId); removeRows(index, 1); return; } auto record = std::make_unique>(getRecord(index)); record->mState = RecordBase::State_Deleted; setRecord(index, std::move(record)); return; } info.mTopicId = dialogue.mId; info.mOriginalId = info.mId; info.mId = id; load(info, base); infoOrders[dialogue.mId].insertInfo(OrderedInfo(info), isDeleted); } void CSMWorld::InfoCollection::sort(const InfoOrderByTopic& infoOrders) { std::vector order; order.reserve(getSize()); for (const auto& [topicId, infoOrder] : infoOrders) for (const OrderedInfo& info : infoOrder.getOrderedInfo()) order.push_back(getIndex(makeCompositeInfoRefId(topicId, info.mId))); reorderRowsImp(order); } CSMWorld::InfosRecordPtrByTopic CSMWorld::InfoCollection::getInfosByTopic() const { InfosRecordPtrByTopic result; for (const std::unique_ptr>& record : getRecords()) result[record->get().mTopicId].push_back(record.get()); return result; } int CSMWorld::InfoCollection::getAppendIndex(const ESM::RefId& id, UniversalId::Type /*type*/) const { const auto lessByTopicId = [](std::string_view lhs, const std::unique_ptr>& rhs) { return lhs < rhs->get().mTopicId; }; const auto it = std::upper_bound(getRecords().begin(), getRecords().end(), getInfoTopicId(id), lessByTopicId); return static_cast(it - getRecords().begin()); } bool CSMWorld::InfoCollection::reorderRows(int baseIndex, const std::vector& newOrder) { const int lastIndex = baseIndex + static_cast(newOrder.size()) - 1; if (lastIndex >= getSize()) return false; if (getRecord(baseIndex).get().mTopicId != getRecord(lastIndex).get().mTopicId) return false; return reorderRowsImp(baseIndex, newOrder); }