mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-21 06:39:42 +00:00
Merge branch 'small_ref_id' into 'master'
Make ESM::RefId to be fixed size cheap to copy and support different implementation types See merge request OpenMW/openmw!2708
This commit is contained in:
commit
b55313c08e
63 changed files with 1210 additions and 392 deletions
|
@ -82,7 +82,7 @@ namespace EsmTool
|
|||
{
|
||||
}
|
||||
|
||||
std::string getId() const override { return mData.mId.getRefIdString(); }
|
||||
std::string getId() const override { return mData.mId.toDebugString(); }
|
||||
|
||||
T& get() { return mData; }
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ void CSMDoc::Document::addOptionalMagicEffects()
|
|||
{
|
||||
ESM::MagicEffect effect;
|
||||
effect.mIndex = i;
|
||||
effect.mId = ESM::RefId::stringRefId(ESM::MagicEffect::indexToId(i));
|
||||
effect.mId = ESM::MagicEffect::indexToRefId(i);
|
||||
effect.blank();
|
||||
|
||||
addOptionalMagicEffect(effect);
|
||||
|
@ -207,7 +207,7 @@ void CSMDoc::Document::createBase()
|
|||
{
|
||||
ESM::Skill record;
|
||||
record.mIndex = i;
|
||||
record.mId = ESM::RefId::stringRefId(ESM::Skill::indexToId(record.mIndex));
|
||||
record.mId = ESM::Skill::indexToRefId(record.mIndex);
|
||||
record.blank();
|
||||
|
||||
getData().getSkills().add(record);
|
||||
|
@ -288,7 +288,7 @@ void CSMDoc::Document::createBase()
|
|||
ESM::MagicEffect record;
|
||||
|
||||
record.mIndex = i;
|
||||
record.mId = ESM::RefId::stringRefId(ESM::MagicEffect::indexToId(i));
|
||||
record.mId = ESM::MagicEffect::indexToRefId(i);
|
||||
|
||||
record.blank();
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace CSMDoc
|
|||
template <class CollectionT>
|
||||
void WriteCollectionStage<CollectionT>::perform(int stage, Messages& messages)
|
||||
{
|
||||
if (CSMWorld::getScopeFromId(mCollection.getRecord(stage).get().mId.getRefIdString()) != mScope)
|
||||
if (CSMWorld::getScopeFromId(mCollection.getRecord(stage).get().mId) != mScope)
|
||||
return;
|
||||
|
||||
ESM::ESMWriter& writer = mState.getWriter();
|
||||
|
|
|
@ -71,7 +71,7 @@ void CSMTools::ClassCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
|||
for (auto& skill : skills)
|
||||
if (skill.second > 1)
|
||||
{
|
||||
messages.add(id, "Skill " + ESM::Skill::indexToId(skill.first) + " is listed more than once", "",
|
||||
CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Skill " + ESM::Skill::indexToRefId(skill.first).toString() + " is listed more than once",
|
||||
"", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,8 +61,8 @@ void CSMTools::FactionCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
|||
for (auto& skill : skills)
|
||||
if (skill.second > 1)
|
||||
{
|
||||
messages.add(id, "Skill " + ESM::Skill::indexToId(skill.first) + " is listed more than once", "",
|
||||
CSMDoc::Message::Severity_Error);
|
||||
messages.add(id, "Skill " + ESM::Skill::indexToRefId(skill.first).toString() + " is listed more than once",
|
||||
"", CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
|
||||
/// \todo check data members that can't be edited in the table view
|
||||
|
|
|
@ -25,5 +25,5 @@ int CSMTools::MandatoryIdStage::setup()
|
|||
void CSMTools::MandatoryIdStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
if (mIdCollection.searchId(mIds.at(stage)) == -1 || mIdCollection.getRecord(mIds.at(stage)).isDeleted())
|
||||
messages.add(mCollectionId, "Missing mandatory record: " + mIds.at(stage).getRefIdString());
|
||||
messages.add(mCollectionId, "Missing mandatory record: " + mIds.at(stage).toDebugString());
|
||||
}
|
||||
|
|
|
@ -425,11 +425,6 @@ namespace CSMWorld
|
|||
return index;
|
||||
}
|
||||
|
||||
bool ActorAdapter::is1stPersonPart(const std::string& name) const
|
||||
{
|
||||
return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos;
|
||||
}
|
||||
|
||||
ActorAdapter::RaceDataPtr ActorAdapter::getRaceData(const ESM::RefId& id)
|
||||
{
|
||||
// Return cached race data if it exists
|
||||
|
@ -519,8 +514,7 @@ namespace CSMWorld
|
|||
}
|
||||
|
||||
auto& part = partRecord.get();
|
||||
if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin
|
||||
&& !is1stPersonPart(part.mId.getRefIdString()))
|
||||
if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin && !ESM::isFirstPersonBodyPart(part))
|
||||
{
|
||||
auto type = (ESM::BodyPart::MeshPart)part.mData.mPart;
|
||||
bool female = part.mData.mFlags & ESM::BodyPart::BPF_Female;
|
||||
|
|
|
@ -149,7 +149,6 @@ namespace CSMWorld
|
|||
ActorAdapter& operator=(const ActorAdapter&) = delete;
|
||||
|
||||
QModelIndex getHighestIndex(QModelIndex) const;
|
||||
bool is1stPersonPart(const std::string& id) const;
|
||||
|
||||
RaceDataPtr getRaceData(const ESM::RefId& raceId);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace CSMWorld
|
|||
|
||||
QVariant LandTextureNicknameColumn::get(const Record<LandTexture>& record) const
|
||||
{
|
||||
return QString::fromUtf8(record.get().mId.getRefIdString().c_str());
|
||||
return QString::fromStdString(record.get().mId.toString());
|
||||
}
|
||||
|
||||
void LandTextureNicknameColumn::set(Record<LandTexture>& record, const QVariant& data)
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace CSMWorld
|
|||
|
||||
QVariant get(const Record<ESXRecordT>& record) const override
|
||||
{
|
||||
return QString::fromUtf8(record.get().mId.getRefIdString().c_str());
|
||||
return QString::fromStdString(record.get().mId.toString());
|
||||
}
|
||||
|
||||
bool isEditable() const override { return false; }
|
||||
|
@ -404,7 +404,7 @@ namespace CSMWorld
|
|||
{
|
||||
int skill = record.get().mData.getSkill(mIndex, mMajor);
|
||||
|
||||
return QString::fromUtf8(ESM::Skill::indexToId(skill).c_str());
|
||||
return QString::fromStdString(ESM::Skill::indexToRefId(skill).toString());
|
||||
}
|
||||
|
||||
void set(Record<ESXRecordT>& record, const QVariant& data) override
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace CSMWorld
|
|||
= static_cast<const Record<RecordT>&>(data.getRecord(RefIdData::LocalIndex(index, mType)));
|
||||
|
||||
if (column == mBase.mId)
|
||||
return QString::fromUtf8(record.get().mId.getRefIdString().c_str());
|
||||
return QString::fromStdString(record.get().mId.toString());
|
||||
|
||||
if (column == mBase.mModified)
|
||||
{
|
||||
|
|
|
@ -658,7 +658,7 @@ int CSMWorld::RefIdCollection::getIndex(const ESM::RefId& id) const
|
|||
int index = searchId(id);
|
||||
|
||||
if (index == -1)
|
||||
throw std::runtime_error("invalid ID: " + id.getRefIdString());
|
||||
throw std::runtime_error("invalid ID: " + id.toDebugString());
|
||||
|
||||
return index;
|
||||
}
|
||||
|
|
|
@ -2,23 +2,43 @@
|
|||
|
||||
#include <string_view>
|
||||
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/esm/refid.hpp>
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
|
||||
CSMWorld::Scope CSMWorld::getScopeFromId(const std::string& id)
|
||||
namespace CSMWorld
|
||||
{
|
||||
// get root namespace
|
||||
std::string namespace_;
|
||||
namespace
|
||||
{
|
||||
struct GetScope
|
||||
{
|
||||
Scope operator()(ESM::StringRefId v) const
|
||||
{
|
||||
std::string_view namespace_;
|
||||
|
||||
std::string::size_type i = id.find("::");
|
||||
const std::string::size_type i = v.getValue().find("::");
|
||||
|
||||
if (i != std::string::npos)
|
||||
namespace_ = Misc::StringUtils::lowerCase(std::string_view(id).substr(0, i));
|
||||
if (i != std::string::npos)
|
||||
namespace_ = std::string_view(v.getValue()).substr(0, i);
|
||||
|
||||
if (namespace_ == "project")
|
||||
return Scope_Project;
|
||||
if (Misc::StringUtils::ciEqual(namespace_, "project"))
|
||||
return Scope_Project;
|
||||
|
||||
if (namespace_ == "session")
|
||||
return Scope_Session;
|
||||
if (Misc::StringUtils::ciEqual(namespace_, "session"))
|
||||
return Scope_Session;
|
||||
|
||||
return Scope_Content;
|
||||
return Scope_Content;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Scope operator()(const T& /*v*/) const
|
||||
{
|
||||
return Scope_Content;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
CSMWorld::Scope CSMWorld::getScopeFromId(ESM::RefId id)
|
||||
{
|
||||
return visit(GetScope{}, id);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#ifndef CSM_WOLRD_SCOPE_H
|
||||
#define CSM_WOLRD_SCOPE_H
|
||||
|
||||
#include <string>
|
||||
namespace ESM
|
||||
{
|
||||
class RefId;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
@ -17,7 +20,7 @@ namespace CSMWorld
|
|||
Scope_Session = 4
|
||||
};
|
||||
|
||||
Scope getScopeFromId(const std::string& id);
|
||||
Scope getScopeFromId(ESM::RefId id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,8 +40,7 @@ namespace MWDialogue
|
|||
return;
|
||||
}
|
||||
|
||||
throw std::runtime_error(
|
||||
"unknown info ID " + mInfoId.getRefIdString() + " for topic " + topic.getRefIdString());
|
||||
throw std::runtime_error("unknown info ID " + mInfoId.toDebugString() + " for topic " + topic.toDebugString());
|
||||
}
|
||||
|
||||
Entry::Entry(const ESM::JournalEntry& record)
|
||||
|
@ -98,7 +97,7 @@ namespace MWDialogue
|
|||
return iter->mId;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown journal index for topic " + topic.getRefIdString());
|
||||
throw std::runtime_error("unknown journal index for topic " + topic.toDebugString());
|
||||
}
|
||||
|
||||
StampedJournalEntry::StampedJournalEntry()
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace MWDialogue
|
|||
[&](const auto& info) { return info.mId == entry.mInfoId; });
|
||||
|
||||
if (info == dialogue->mInfo.end() || info->mData.mJournalIndex == -1)
|
||||
throw std::runtime_error("unknown journal entry for topic " + mTopic.getRefIdString());
|
||||
throw std::runtime_error("unknown journal entry for topic " + mTopic.toDebugString());
|
||||
|
||||
if (info->mQuestStatus == ESM::DialInfo::QS_Finished || info->mQuestStatus == ESM::DialInfo::QS_Restart)
|
||||
mFinished = info->mQuestStatus == ESM::DialInfo::QS_Finished;
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWDialogue
|
|||
bool Topic::addEntry(const JournalEntry& entry)
|
||||
{
|
||||
if (entry.mTopic != mTopic)
|
||||
throw std::runtime_error("topic does not match: " + mTopic.getRefIdString());
|
||||
throw std::runtime_error("topic does not match: " + mTopic.toDebugString());
|
||||
|
||||
// bail out if we already have heard this
|
||||
for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it)
|
||||
|
|
|
@ -896,7 +896,7 @@ namespace MWGui
|
|||
= Misc::ResourceHelpers::correctTexturePath("textures\\levelup\\" + classId.getRefIdString() + ".dds", vfs);
|
||||
if (!vfs->exists(classImage))
|
||||
{
|
||||
Log(Debug::Warning) << "No class image for " << classId.getRefIdString() << ", falling back to default";
|
||||
Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";
|
||||
classImage = "textures\\levelup\\warrior.dds";
|
||||
}
|
||||
imageBox->setImageTexture(classImage);
|
||||
|
|
|
@ -683,7 +683,7 @@ namespace MWGui
|
|||
if (!mConsoleMode.empty())
|
||||
title = mConsoleMode + " " + title;
|
||||
if (!mPtr.isEmpty())
|
||||
title.append(" (" + mPtr.getCellRef().getRefId().getRefIdString() + ")");
|
||||
title.append(" (" + mPtr.getCellRef().getRefId().toDebugString() + ")");
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
|
|
|
@ -321,7 +321,6 @@ namespace MWGui
|
|||
|
||||
for (const ESM::BodyPart& bodypart : store)
|
||||
{
|
||||
const std::string& idString = bodypart.mId.getRefIdString();
|
||||
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
||||
continue;
|
||||
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
||||
|
@ -330,8 +329,7 @@ namespace MWGui
|
|||
continue;
|
||||
if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
|
||||
continue;
|
||||
bool firstPerson = Misc::StringUtils::ciEndsWith(idString, "1st");
|
||||
if (firstPerson)
|
||||
if (ESM::isFirstPersonBodyPart(bodypart))
|
||||
continue;
|
||||
if (bodypart.mRace == mCurrentRaceId)
|
||||
out.push_back(bodypart.mId);
|
||||
|
|
|
@ -160,12 +160,7 @@ namespace
|
|||
if (result != 0)
|
||||
return result > 0;
|
||||
|
||||
// compare items by Id
|
||||
leftName = left.mBase.getCellRef().getRefId().getRefIdString();
|
||||
rightName = right.mBase.getCellRef().getRefId().getRefIdString();
|
||||
|
||||
result = leftName.compare(rightName);
|
||||
return result < 0;
|
||||
return left.mBase.getCellRef().getRefId() < right.mBase.getCellRef().getRefId();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace MWGui
|
|||
ToolTipInfo info;
|
||||
info.caption = mFocusObject.getClass().getName(mFocusObject);
|
||||
if (info.caption.empty())
|
||||
info.caption = mFocusObject.getCellRef().getRefId().getRefIdString();
|
||||
info.caption = mFocusObject.getCellRef().getRefId().toDebugString();
|
||||
info.icon.clear();
|
||||
tooltipSize = createToolTip(info, checkOwned());
|
||||
}
|
||||
|
@ -686,7 +686,7 @@ namespace MWGui
|
|||
if (!creature)
|
||||
return {};
|
||||
if (creature->mName.empty())
|
||||
return " (" + creature->mId.getRefIdString() + ")";
|
||||
return " (" + creature->mId.toDebugString() + ")";
|
||||
return " (" + creature->mName + ")";
|
||||
}
|
||||
|
||||
|
@ -720,10 +720,10 @@ namespace MWGui
|
|||
for (std::pair<ESM::RefId, int>& owner : itemOwners)
|
||||
{
|
||||
if (owner.second == std::numeric_limits<int>::max())
|
||||
ret += std::string("\nStolen from ") + owner.first.getRefIdString(); // for legacy (ESS) savegames
|
||||
ret += std::string("\nStolen from ") + owner.first.toDebugString(); // for legacy (ESS) savegames
|
||||
else
|
||||
ret += std::string("\nStolen ") + MyGUI::utility::toString(owner.second) + " from "
|
||||
+ owner.first.getRefIdString();
|
||||
+ owner.first.toDebugString();
|
||||
}
|
||||
|
||||
ret += getMiscString(cellref.getGlobalVariable(), "Global");
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace MWLua
|
|||
{
|
||||
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
|
||||
objectT["recordId"] = sol::readonly_property(
|
||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); });
|
||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().toDebugString(); });
|
||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
|
||||
const MWWorld::Ptr& ptr = o.ptr();
|
||||
if (ptr.isInCell())
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace MWMechanics
|
|||
case ESM::REC_NPC_:
|
||||
return ::withBaseRecord<ESM::NPC>(mId, function);
|
||||
default:
|
||||
throw std::logic_error("failed to update base record for " + mId.getRefIdString());
|
||||
throw std::logic_error("failed to update base record for " + mId.toDebugString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace MWMechanics
|
|||
case ESM::REC_NPC_:
|
||||
return getSpellList<ESM::NPC>(mId);
|
||||
default:
|
||||
throw std::logic_error("failed to get spell list for " + mId.getRefIdString());
|
||||
throw std::logic_error("failed to get spell list for " + mId.toDebugString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,6 @@ namespace
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class HeadAnimationTime : public SceneUtil::ControllerSource
|
||||
{
|
||||
private:
|
||||
|
@ -761,12 +760,6 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
bool NpcAnimation::isFirstPersonPart(const ESM::BodyPart* bodypart)
|
||||
{
|
||||
std::string_view partName = bodypart->mId.getRefIdString();
|
||||
return partName.size() >= 3 && partName.substr(partName.size() - 3, 3) == "1st";
|
||||
}
|
||||
|
||||
bool NpcAnimation::isFemalePart(const ESM::BodyPart* bodypart)
|
||||
{
|
||||
return bodypart->mData.mFlags & ESM::BodyPart::BPF_Female;
|
||||
|
@ -1220,7 +1213,7 @@ namespace MWRender
|
|||
if (!(bodypart.mRace == race))
|
||||
continue;
|
||||
|
||||
bool partFirstPerson = isFirstPersonPart(&bodypart);
|
||||
const bool partFirstPerson = ESM::isFirstPersonBodyPart(bodypart);
|
||||
|
||||
bool isHand = bodypart.mData.mPart == ESM::BodyPart::MP_Hand
|
||||
|| bodypart.mData.mPart == ESM::BodyPart::MP_Wrist
|
||||
|
@ -1277,7 +1270,7 @@ namespace MWRender
|
|||
parts[bIt->second] = &bodypart;
|
||||
|
||||
// If we have 3d person fallback bodypart for hand and 1st person fallback found (2)
|
||||
else if (isHand && !isFirstPersonPart(parts[bIt->second]) && partFirstPerson)
|
||||
else if (isHand && !ESM::isFirstPersonBodyPart(*parts[bIt->second]) && partFirstPerson)
|
||||
parts[bIt->second] = &bodypart;
|
||||
|
||||
++bIt;
|
||||
|
|
|
@ -99,7 +99,6 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<RotateController> mFirstPersonNeckController;
|
||||
|
||||
static bool isFirstPersonPart(const ESM::BodyPart* bodypart);
|
||||
static bool isFemalePart(const ESM::BodyPart* bodypart);
|
||||
static NpcType getNpcType(const MWWorld::Ptr& ptr);
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ namespace MWScript
|
|||
return iter->second;
|
||||
}
|
||||
|
||||
throw std::logic_error("script " + name.getRefIdString() + " does not exist");
|
||||
throw std::logic_error("script " + name.toDebugString() + " does not exist");
|
||||
}
|
||||
|
||||
GlobalScripts& ScriptManager::getGlobalScripts()
|
||||
|
|
|
@ -65,8 +65,8 @@ namespace MWScript
|
|||
from = container;
|
||||
else
|
||||
{
|
||||
std::string error = "Failed to find the container of object '"
|
||||
+ from.getCellRef().getRefId().getRefIdString() + "'";
|
||||
const std::string error
|
||||
= "Failed to find the container of object " + from.getCellRef().getRefId().toDebugString();
|
||||
runtime.getContext().report(error);
|
||||
Log(Debug::Error) << error;
|
||||
runtime.push(0.f);
|
||||
|
@ -77,7 +77,7 @@ namespace MWScript
|
|||
const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->searchPtr(name, false);
|
||||
if (to.isEmpty())
|
||||
{
|
||||
std::string error = "Failed to find an instance of object '" + name.getRefIdString() + "'";
|
||||
const std::string error = "Failed to find an instance of object " + name.toDebugString();
|
||||
runtime.getContext().report(error);
|
||||
Log(Debug::Error) << error;
|
||||
runtime.push(0.f);
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
if (it == invStore.end())
|
||||
throw std::runtime_error("ActionEquip can't find item " + object.getCellRef().getRefId().getRefIdString());
|
||||
throw std::runtime_error("ActionEquip can't find item " + object.getCellRef().getRefId().toDebugString());
|
||||
|
||||
// equip the item in the first free slot
|
||||
std::vector<int>::const_iterator slot = slots_.first.begin();
|
||||
|
|
|
@ -950,13 +950,13 @@ namespace MWWorld
|
|||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Error) << "Cell reference '" + ref.mRefID.getRefIdString() + "' not found!";
|
||||
Log(Debug::Error) << "Cell reference " << ref.mRefID << " is not found!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handledType)
|
||||
{
|
||||
Log(Debug::Error) << "Error: Ignoring reference '" << ref.mRefID.getRefIdString() << "' of unhandled type";
|
||||
Log(Debug::Error) << "Error: Ignoring reference " << ref.mRefID << " of unhandled type";
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -783,7 +783,7 @@ int MWWorld::ContainerStore::getType(const ConstPtr& ptr)
|
|||
if (ptr.getType() == ESM::Weapon::sRecordId)
|
||||
return Type_Weapon;
|
||||
|
||||
throw std::runtime_error("Object '" + ptr.getCellRef().getRefId().getRefIdString() + "' of type "
|
||||
throw std::runtime_error("Object " + ptr.getCellRef().getRefId().toDebugString() + " of type "
|
||||
+ std::string(ptr.getTypeDescription()) + " can not be placed into a container");
|
||||
}
|
||||
|
||||
|
|
|
@ -729,12 +729,9 @@ namespace MWWorld
|
|||
{
|
||||
return npcs.insert(npc);
|
||||
}
|
||||
const ESM::RefId id = ESM::RefId::stringRefId("$dynamic" + std::to_string(mDynamicCount++));
|
||||
const ESM::RefId id = ESM::RefId::generated(mDynamicCount++);
|
||||
if (npcs.search(id) != nullptr)
|
||||
{
|
||||
const std::string msg = "Try to override existing record '" + id.getRefIdString() + "'";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
throw std::runtime_error("Try to override existing record: " + id.toDebugString());
|
||||
ESM::NPC record = npc;
|
||||
|
||||
record.mId = id;
|
||||
|
|
|
@ -185,14 +185,11 @@ namespace MWWorld
|
|||
template <class T>
|
||||
const T* insert(const T& x)
|
||||
{
|
||||
const ESM::RefId id = ESM::RefId::stringRefId("$dynamic" + std::to_string(mDynamicCount++));
|
||||
const ESM::RefId id = ESM::RefId::generated(mDynamicCount++);
|
||||
|
||||
Store<T>& store = getWritable<T>();
|
||||
if (store.search(id) != nullptr)
|
||||
{
|
||||
const std::string msg = "Try to override existing record '" + id.getRefIdString() + "'";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
throw std::runtime_error("Try to override existing record: " + id.toDebugString());
|
||||
T record = x;
|
||||
|
||||
record.mId = id;
|
||||
|
@ -224,10 +221,7 @@ namespace MWWorld
|
|||
{
|
||||
Store<T>& store = getWritable<T>();
|
||||
if (store.search(x.mId) != nullptr)
|
||||
{
|
||||
const std::string msg = "Try to override existing record '" + x.mId.getRefIdString() + "'";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
throw std::runtime_error("Try to override existing record " + x.mId.toDebugString());
|
||||
|
||||
T* ptr = store.insertStatic(x);
|
||||
if constexpr (std::is_convertible_v<Store<T>*, DynamicStore*>)
|
||||
|
|
|
@ -94,10 +94,10 @@ MWWorld::ManualRef::ManualRef(const MWWorld::ESMStore& store, const ESM::RefId&
|
|||
create(store.get<ESM4::Static>(), name, mRef, mPtr);
|
||||
break;
|
||||
case 0:
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.getRefIdString() + " (unknown ID)");
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.toDebugString() + " (unknown ID)");
|
||||
|
||||
default:
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.getRefIdString() + " (unknown type)");
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.toDebugString() + " (unknown type)");
|
||||
}
|
||||
|
||||
mPtr.getRefData().setCount(count);
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace MWWorld
|
|||
res.append(" (");
|
||||
res.append(getTypeDescription());
|
||||
res.append(", ");
|
||||
res.append(getCellRef().getRefId().getRefIdString());
|
||||
res.append(getCellRef().getRefId().toDebugString());
|
||||
res.append(")");
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -950,11 +950,8 @@ namespace MWWorld
|
|||
const ESM::Pathgrid* Store<ESM::Pathgrid>::find(const ESM::RefId& name) const
|
||||
{
|
||||
const ESM::Pathgrid* pathgrid = search(name);
|
||||
if (!pathgrid)
|
||||
{
|
||||
const std::string msg = "Pathgrid in cell '" + name.getRefIdString() + "' not found";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
if (pathgrid == nullptr)
|
||||
throw std::runtime_error("Pathgrid in cell " + name.toDebugString() + " is not found");
|
||||
return pathgrid;
|
||||
}
|
||||
const ESM::Pathgrid* Store<ESM::Pathgrid>::search(const ESM::Cell& cell) const
|
||||
|
|
|
@ -729,7 +729,7 @@ namespace MWWorld
|
|||
Ptr ret = searchPtr(name, activeOnly);
|
||||
if (!ret.isEmpty())
|
||||
return ret;
|
||||
std::string error = "failed to find an instance of object '" + name.getRefIdString() + "'";
|
||||
std::string error = "Failed to find an instance of object " + name.toDebugString();
|
||||
if (activeOnly)
|
||||
error += " in active cells";
|
||||
throw std::runtime_error(error);
|
||||
|
@ -1146,12 +1146,12 @@ namespace MWWorld
|
|||
MWWorld::Ptr newPtr = ptr;
|
||||
|
||||
if (!isPlayer && !currCell)
|
||||
throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId().getRefIdString()
|
||||
+ "\" to another cell: current cell is nullptr");
|
||||
throw std::runtime_error("Can not move actor " + ptr.getCellRef().getRefId().toDebugString()
|
||||
+ " to another cell: current cell is nullptr");
|
||||
|
||||
if (!newCell)
|
||||
throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId().getRefIdString()
|
||||
+ "\" to another cell: new cell is nullptr");
|
||||
throw std::runtime_error("Can not move actor " + ptr.getCellRef().getRefId().toDebugString()
|
||||
+ " to another cell: new cell is nullptr");
|
||||
|
||||
if (currCell != newCell)
|
||||
{
|
||||
|
|
|
@ -89,6 +89,20 @@ namespace ESM
|
|||
EXPECT_EQ(lower, upper);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, equalityIsDefinedForStringRefIdAndRefId)
|
||||
{
|
||||
const StringRefId stringRefId("ref_id");
|
||||
const RefId refId = RefId::stringRefId("REF_ID");
|
||||
EXPECT_EQ(stringRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, equalityIsDefinedForFormRefIdAndRefId)
|
||||
{
|
||||
const FormIdRefId formIdRefId(42);
|
||||
const RefId refId = RefId::formIdRefId(42);
|
||||
EXPECT_EQ(formIdRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdIsEqualToItself)
|
||||
{
|
||||
const RefId refId = RefId::stringRefId("ref_id");
|
||||
|
@ -102,6 +116,20 @@ namespace ESM
|
|||
EXPECT_LT(a, b);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, lessThanIsDefinedForStringRefIdAndRefId)
|
||||
{
|
||||
const StringRefId stringRefId("a");
|
||||
const RefId refId = RefId::stringRefId("B");
|
||||
EXPECT_LT(stringRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, lessThanIsDefinedForFormRefIdAndRefId)
|
||||
{
|
||||
const FormIdRefId formIdRefId(13);
|
||||
const RefId refId = RefId::formIdRefId(42);
|
||||
EXPECT_LT(formIdRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdHasCaseInsensitiveHash)
|
||||
{
|
||||
const RefId lower = RefId::stringRefId("a");
|
||||
|
@ -133,6 +161,22 @@ namespace ESM
|
|||
EXPECT_LT(b, c);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdHasStrongOrderWithFormId)
|
||||
{
|
||||
const RefId stringRefId = RefId::stringRefId("a");
|
||||
const RefId formIdRefId = RefId::formIdRefId(42);
|
||||
EXPECT_TRUE(stringRefId < formIdRefId);
|
||||
EXPECT_FALSE(formIdRefId < stringRefId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, formIdRefIdHasStrongOrderWithStringView)
|
||||
{
|
||||
const RefId formIdRefId = RefId::formIdRefId(42);
|
||||
const std::string_view stringView = "42";
|
||||
EXPECT_TRUE(stringView < formIdRefId);
|
||||
EXPECT_FALSE(formIdRefId < stringView);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, canBeUsedAsMapKeyWithLookupByStringView)
|
||||
{
|
||||
const std::map<ESM::RefId, int, std::less<>> map({ { ESM::RefId::stringRefId("a"), 42 } });
|
||||
|
@ -144,5 +188,65 @@ namespace ESM
|
|||
const std::map<std::string, int, std::less<>> map({ { "a", 42 } });
|
||||
EXPECT_EQ(map.count(ESM::RefId::stringRefId("A")), 1);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdIsNotEqualToFormId)
|
||||
{
|
||||
const RefId stringRefId = RefId::stringRefId("\0");
|
||||
const RefId formIdRefId = RefId::formIdRefId(0);
|
||||
EXPECT_NE(stringRefId, formIdRefId);
|
||||
}
|
||||
|
||||
struct ESMRefIdToStringTest : TestWithParam<std::pair<ESM::RefId, std::string>>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ESMRefIdToStringTest, toString)
|
||||
{
|
||||
const RefId& refId = GetParam().first;
|
||||
const std::string& string = GetParam().second;
|
||||
EXPECT_EQ(refId.toString(), string);
|
||||
}
|
||||
|
||||
const std::vector<std::pair<RefId, std::string>> toStringParams = {
|
||||
{ RefId(), std::string() },
|
||||
{ RefId::stringRefId("foo"), "foo" },
|
||||
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), { 'a', 0, -1, '\n', '\t' } },
|
||||
{ RefId::formIdRefId(42), "42" },
|
||||
{ RefId::generated(42), "42" },
|
||||
{ RefId::index(ESM::REC_ARMO, 42), "ARMO, 42" },
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ESMRefIdToString, ESMRefIdToStringTest, ValuesIn(toStringParams));
|
||||
|
||||
struct ESMRefIdToDebugStringTest : TestWithParam<std::pair<ESM::RefId, std::string>>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ESMRefIdToDebugStringTest, toDebugString)
|
||||
{
|
||||
const RefId& refId = GetParam().first;
|
||||
const std::string& debugString = GetParam().second;
|
||||
EXPECT_EQ(refId.toDebugString(), debugString);
|
||||
}
|
||||
|
||||
TEST_P(ESMRefIdToDebugStringTest, toStream)
|
||||
{
|
||||
const RefId& refId = GetParam().first;
|
||||
const std::string& debugString = GetParam().second;
|
||||
std::ostringstream stream;
|
||||
stream << refId;
|
||||
EXPECT_EQ(stream.str(), debugString);
|
||||
}
|
||||
|
||||
const std::vector<std::pair<RefId, std::string>> toDebugStringParams = {
|
||||
{ RefId(), "Empty{}" },
|
||||
{ RefId::stringRefId("foo"), "String{foo}" },
|
||||
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), "String{a\\x0\\xFF\\xA\\x9}" },
|
||||
{ RefId::formIdRefId(42), "FormId{42}" },
|
||||
{ RefId::generated(42), "Generated{42}" },
|
||||
{ RefId::index(ESM::REC_ARMO, 42), "Index{ARMO, 42}" },
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ESMRefIdToDebugString, ESMRefIdToDebugStringTest, ValuesIn(toDebugStringParams));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include <components/esm/fourcc.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm3/loadcont.hpp>
|
||||
#include <components/esm3/loadregn.hpp>
|
||||
#include <components/esm3/loadscpt.hpp>
|
||||
#include <components/esm3/player.hpp>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
@ -9,15 +12,51 @@
|
|||
#include <array>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto tie(const ContItem& value)
|
||||
{
|
||||
return std::tie(value.mCount, value.mItem);
|
||||
}
|
||||
|
||||
auto tie(const ESM::Region::SoundRef& value)
|
||||
{
|
||||
return std::tie(value.mSound, value.mChance);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator==(const ESM::ContItem& lhs, const ESM::ContItem& rhs)
|
||||
{
|
||||
return tie(lhs) == tie(rhs);
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& stream, const ESM::ContItem& value)
|
||||
{
|
||||
return stream << "ESM::ContItem {.mCount = " << value.mCount << ", .mItem = '" << value.mItem << "'}";
|
||||
}
|
||||
|
||||
inline bool operator==(const ESM::Region::SoundRef& lhs, const ESM::Region::SoundRef& rhs)
|
||||
{
|
||||
return tie(lhs) == tie(rhs);
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& stream, const ESM::Region::SoundRef& value)
|
||||
{
|
||||
return stream << "ESM::Region::SoundRef {.mSound = '" << value.mSound << "', .mChance = " << value.mChance
|
||||
<< "}";
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace ::testing;
|
||||
|
||||
constexpr std::array formats = {
|
||||
MaxLimitedSizeStringsFormatVersion,
|
||||
MaxStringRefIdFormatVersion,
|
||||
CurrentSaveGameFormatVersion,
|
||||
};
|
||||
|
||||
|
@ -47,12 +86,41 @@ namespace ESM
|
|||
return stream;
|
||||
}
|
||||
|
||||
template <class T, class = std::void_t<>>
|
||||
struct HasLoad : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void load(ESMReader& reader, T& record)
|
||||
struct HasLoad<T, std::void_t<decltype(std::declval<T>().load(std::declval<ESMReader&>()))>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
auto load(ESMReader& reader, T& record) -> std::enable_if_t<HasLoad<std::decay_t<T>>::value>
|
||||
{
|
||||
record.load(reader);
|
||||
}
|
||||
|
||||
template <class T, class = std::void_t<>>
|
||||
struct HasLoadWithDelete : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct HasLoadWithDelete<T,
|
||||
std::void_t<decltype(std::declval<T>().load(std::declval<ESMReader&>(), std::declval<bool&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
auto load(ESMReader& reader, T& record) -> std::enable_if_t<HasLoadWithDelete<std::decay_t<T>>::value>
|
||||
{
|
||||
bool deleted = false;
|
||||
record.load(reader, deleted);
|
||||
}
|
||||
|
||||
void load(ESMReader& reader, CellRef& record)
|
||||
{
|
||||
bool deleted = false;
|
||||
|
@ -106,6 +174,40 @@ namespace ESM
|
|||
EXPECT_EQ(reader.getDesc(), description);
|
||||
}
|
||||
|
||||
TEST_F(Esm3SaveLoadRecordTest, containerContItemShouldSupportRefIdLongerThan32)
|
||||
{
|
||||
Container record;
|
||||
record.blank();
|
||||
record.mInventory.mList.push_back(ESM::ContItem{ .mCount = 42, .mItem = generateRandomRefId(33) });
|
||||
record.mInventory.mList.push_back(ESM::ContItem{ .mCount = 13, .mItem = generateRandomRefId(33) });
|
||||
Container result;
|
||||
saveAndLoadRecord(record, CurrentSaveGameFormatVersion, result);
|
||||
EXPECT_EQ(result.mInventory.mList, record.mInventory.mList);
|
||||
}
|
||||
|
||||
TEST_F(Esm3SaveLoadRecordTest, regionSoundRefShouldSupportRefIdLongerThan32)
|
||||
{
|
||||
Region record;
|
||||
record.blank();
|
||||
record.mSoundList.push_back(ESM::Region::SoundRef{ .mSound = generateRandomRefId(33), .mChance = 42 });
|
||||
record.mSoundList.push_back(ESM::Region::SoundRef{ .mSound = generateRandomRefId(33), .mChance = 13 });
|
||||
Region result;
|
||||
saveAndLoadRecord(record, CurrentSaveGameFormatVersion, result);
|
||||
EXPECT_EQ(result.mSoundList, record.mSoundList);
|
||||
}
|
||||
|
||||
TEST_F(Esm3SaveLoadRecordTest, scriptSoundRefShouldSupportRefIdLongerThan32)
|
||||
{
|
||||
Script record;
|
||||
record.blank();
|
||||
record.mId = generateRandomRefId(33);
|
||||
record.mData.mNumShorts = 42;
|
||||
Script result;
|
||||
saveAndLoadRecord(record, CurrentSaveGameFormatVersion, result);
|
||||
EXPECT_EQ(result.mId, record.mId);
|
||||
EXPECT_EQ(result.mData.mNumShorts, record.mData.mNumShorts);
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, playerShouldNotChange)
|
||||
{
|
||||
std::minstd_rand random;
|
||||
|
@ -151,6 +253,44 @@ namespace ESM
|
|||
EXPECT_EQ(record.mLastHitObject, result.mLastHitObject);
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, containerShouldNotChange)
|
||||
{
|
||||
Container record;
|
||||
record.blank();
|
||||
record.mId = generateRandomRefId();
|
||||
record.mInventory.mList.push_back(ESM::ContItem{ .mCount = 42, .mItem = generateRandomRefId(32) });
|
||||
record.mInventory.mList.push_back(ESM::ContItem{ .mCount = 13, .mItem = generateRandomRefId(32) });
|
||||
Container result;
|
||||
saveAndLoadRecord(record, GetParam(), result);
|
||||
EXPECT_EQ(result.mId, record.mId);
|
||||
EXPECT_EQ(result.mInventory.mList, record.mInventory.mList);
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, regionShouldNotChange)
|
||||
{
|
||||
Region record;
|
||||
record.blank();
|
||||
record.mId = generateRandomRefId();
|
||||
record.mSoundList.push_back(ESM::Region::SoundRef{ .mSound = generateRandomRefId(32), .mChance = 42 });
|
||||
record.mSoundList.push_back(ESM::Region::SoundRef{ .mSound = generateRandomRefId(32), .mChance = 13 });
|
||||
Region result;
|
||||
saveAndLoadRecord(record, GetParam(), result);
|
||||
EXPECT_EQ(result.mId, record.mId);
|
||||
EXPECT_EQ(result.mSoundList, record.mSoundList);
|
||||
}
|
||||
|
||||
TEST_P(Esm3SaveLoadRecordTest, scriptShouldNotChange)
|
||||
{
|
||||
Script record;
|
||||
record.blank();
|
||||
record.mId = generateRandomRefId(32);
|
||||
record.mData.mNumShorts = 42;
|
||||
Script result;
|
||||
saveAndLoadRecord(record, GetParam(), result);
|
||||
EXPECT_EQ(result.mId, record.mId);
|
||||
EXPECT_EQ(result.mData.mNumShorts, record.mData.mNumShorts);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FormatVersions, Esm3SaveLoadRecordTest, ValuesIn(formats));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -281,6 +281,7 @@ namespace
|
|||
ESM::MaxOldAiPackageFormatVersion,
|
||||
ESM::MaxOldSkillsAndAttributesFormatVersion,
|
||||
ESM::MaxOldCreatureStatsFormatVersion,
|
||||
ESM::MaxStringRefIdFormatVersion,
|
||||
ESM::CurrentSaveGameFormatVersion,
|
||||
};
|
||||
|
||||
|
@ -420,8 +421,10 @@ TYPED_TEST_P(StoreTest, overwrite_test)
|
|||
|
||||
namespace
|
||||
{
|
||||
using namespace ::testing;
|
||||
|
||||
template <class T>
|
||||
struct StoreSaveLoadTest : public ::testing::Test
|
||||
struct StoreSaveLoadTest : public Test
|
||||
{
|
||||
};
|
||||
|
||||
|
@ -445,11 +448,11 @@ namespace
|
|||
using RecordType = TypeParam;
|
||||
|
||||
const int index = 3;
|
||||
ESM::RefId refId;
|
||||
decltype(RecordType::mId) refId;
|
||||
if constexpr (hasIndex<RecordType> && !std::is_same_v<RecordType, ESM::LandTexture>)
|
||||
refId = ESM::RefId::stringRefId(RecordType::indexToId(index));
|
||||
refId = RecordType::indexToRefId(index);
|
||||
else
|
||||
refId = ESM::RefId::stringRefId("foobar");
|
||||
refId = ESM::StringRefId("foobar");
|
||||
|
||||
for (const ESM::FormatVersion formatVersion : formats)
|
||||
{
|
||||
|
@ -473,7 +476,7 @@ namespace
|
|||
MWWorld::ESMStore esmStore;
|
||||
|
||||
reader.open(getEsmFile(record, false, formatVersion), "filename");
|
||||
esmStore.load(reader, &dummyListener, dialogue);
|
||||
ASSERT_NO_THROW(esmStore.load(reader, &dummyListener, dialogue));
|
||||
esmStore.setUp();
|
||||
|
||||
const RecordType* result = nullptr;
|
||||
|
@ -551,7 +554,7 @@ namespace
|
|||
template <class... T>
|
||||
struct AsTestingTypes<std::tuple<T...>>
|
||||
{
|
||||
using Type = testing::Types<T...>;
|
||||
using Type = Types<T...>;
|
||||
};
|
||||
|
||||
using RecordTypes = typename ToRecordTypes<MWWorld::ESMStore::StoreTuple>::Type;
|
||||
|
|
|
@ -85,7 +85,12 @@ add_component_dir (to_utf8
|
|||
to_utf8
|
||||
)
|
||||
|
||||
add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge)
|
||||
add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge
|
||||
formidrefid
|
||||
stringrefid
|
||||
generatedrefid
|
||||
indexrefid
|
||||
)
|
||||
|
||||
add_component_dir(fx pass technique lexer widgets stateupdater)
|
||||
|
||||
|
|
24
components/esm/formidrefid.cpp
Normal file
24
components/esm/formidrefid.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "formidrefid.hpp"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
std::string FormIdRefId::toString() const
|
||||
{
|
||||
return std::to_string(mValue);
|
||||
}
|
||||
|
||||
std::string FormIdRefId::toDebugString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << *this;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, FormIdRefId value)
|
||||
{
|
||||
return stream << "FormId{" << value.mValue << '}';
|
||||
}
|
||||
}
|
52
components/esm/formidrefid.hpp
Normal file
52
components/esm/formidrefid.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_FORMIDREFID_HPP
|
||||
#define OPENMW_COMPONENTS_ESM_FORMIDREFID_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
|
||||
#include <components/esm4/formid.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class FormIdRefId
|
||||
{
|
||||
public:
|
||||
constexpr FormIdRefId() = default;
|
||||
|
||||
constexpr explicit FormIdRefId(ESM4::FormId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
ESM4::FormId getValue() const { return mValue; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
constexpr bool operator==(FormIdRefId rhs) const noexcept { return mValue == rhs.mValue; }
|
||||
|
||||
constexpr bool operator<(FormIdRefId rhs) const noexcept { return mValue < rhs.mValue; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, FormIdRefId value);
|
||||
|
||||
friend struct std::hash<FormIdRefId>;
|
||||
|
||||
private:
|
||||
ESM4::FormId mValue = 0;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::FormIdRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::FormIdRefId value) const noexcept
|
||||
{
|
||||
return std::hash<ESM4::FormId>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
24
components/esm/generatedrefid.cpp
Normal file
24
components/esm/generatedrefid.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "generatedrefid.hpp"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
std::string GeneratedRefId::toString() const
|
||||
{
|
||||
return std::to_string(mValue);
|
||||
}
|
||||
|
||||
std::string GeneratedRefId::toDebugString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << *this;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, GeneratedRefId value)
|
||||
{
|
||||
return stream << "Generated{" << value.mValue << '}';
|
||||
}
|
||||
}
|
49
components/esm/generatedrefid.hpp
Normal file
49
components/esm/generatedrefid.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_GENERATEDREFID_HPP
|
||||
#define OPENMW_COMPONENTS_ESM_GENERATEDREFID_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class GeneratedRefId
|
||||
{
|
||||
public:
|
||||
constexpr explicit GeneratedRefId(std::uint64_t value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr std::uint64_t getValue() const { return mValue; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
constexpr bool operator==(GeneratedRefId rhs) const noexcept { return mValue == rhs.mValue; }
|
||||
|
||||
constexpr bool operator<(GeneratedRefId rhs) const noexcept { return mValue < rhs.mValue; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, GeneratedRefId value);
|
||||
|
||||
friend struct std::hash<GeneratedRefId>;
|
||||
|
||||
private:
|
||||
std::uint64_t mValue;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::GeneratedRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::GeneratedRefId value) const noexcept
|
||||
{
|
||||
return std::hash<std::uint64_t>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
26
components/esm/indexrefid.cpp
Normal file
26
components/esm/indexrefid.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "indexrefid.hpp"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
std::string IndexRefId::toString() const
|
||||
{
|
||||
return ESM::NAME(mRecordType).toString() + ", " + std::to_string(mValue);
|
||||
}
|
||||
|
||||
std::string IndexRefId::toDebugString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << *this;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, IndexRefId value)
|
||||
{
|
||||
return stream << "Index{" << ESM::NAME(value.mRecordType).toStringView() << ", " << value.mValue << '}';
|
||||
}
|
||||
}
|
61
components/esm/indexrefid.hpp
Normal file
61
components/esm/indexrefid.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_INDEXREFID_HPP
|
||||
#define OPENMW_COMPONENTS_ESM_INDEXREFID_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class IndexRefId
|
||||
{
|
||||
public:
|
||||
constexpr explicit IndexRefId(RecNameInts recordType, std::uint32_t value) noexcept
|
||||
: mRecordType(recordType)
|
||||
, mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr RecNameInts getRecordType() const { return mRecordType; }
|
||||
|
||||
constexpr std::uint32_t getValue() const { return mValue; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
friend inline constexpr auto tie(const IndexRefId& value) noexcept
|
||||
{
|
||||
return std::tie(value.mRecordType, value.mValue);
|
||||
}
|
||||
|
||||
constexpr bool operator==(IndexRefId rhs) const noexcept { return tie(*this) == tie(rhs); }
|
||||
|
||||
constexpr bool operator<(IndexRefId rhs) const noexcept { return tie(*this) < tie(rhs); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, IndexRefId value);
|
||||
|
||||
friend struct std::hash<IndexRefId>;
|
||||
|
||||
private:
|
||||
RecNameInts mRecordType;
|
||||
std::uint32_t mValue;
|
||||
};
|
||||
|
||||
static_assert(sizeof(IndexRefId) <= sizeof(std::uint64_t));
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::IndexRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::IndexRefId value) const noexcept
|
||||
{
|
||||
return std::hash<std::uint64_t>{}(static_cast<std::uint64_t>(value.mRecordType) | value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,68 +1,180 @@
|
|||
#include "refid.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "components/misc/strings/algorithm.hpp"
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
bool RefId::operator==(const RefId& rhs) const
|
||||
namespace
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(mId, rhs.mId);
|
||||
const std::string emptyString;
|
||||
|
||||
struct GetRefString
|
||||
{
|
||||
const std::string& operator()(EmptyRefId /*v*/) const { return emptyString; }
|
||||
|
||||
const std::string& operator()(StringRefId v) const { return v.getValue(); }
|
||||
|
||||
template <class T>
|
||||
const std::string& operator()(const T& v) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "RefId is not a string: " << v;
|
||||
throw std::runtime_error(stream.str());
|
||||
}
|
||||
};
|
||||
|
||||
struct IsEqualToString
|
||||
{
|
||||
const std::string_view mRhs;
|
||||
|
||||
bool operator()(StringRefId v) const noexcept { return v == mRhs; }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct IsLessThanString
|
||||
{
|
||||
const std::string_view mRhs;
|
||||
|
||||
bool operator()(StringRefId v) const noexcept { return v < mRhs; }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct IsGreaterThanString
|
||||
{
|
||||
const std::string_view mLhs;
|
||||
|
||||
bool operator()(StringRefId v) const noexcept { return mLhs < v; }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct StartsWith
|
||||
{
|
||||
const std::string_view mPrefix;
|
||||
|
||||
bool operator()(StringRefId v) const { return v.startsWith(mPrefix); }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct EndsWith
|
||||
{
|
||||
const std::string_view mSuffix;
|
||||
|
||||
bool operator()(StringRefId v) const { return v.endsWith(mSuffix); }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct Contains
|
||||
{
|
||||
const std::string_view mSubString;
|
||||
|
||||
bool operator()(StringRefId v) const { return v.contains(mSubString); }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool RefId::operator<(const RefId& rhs) const
|
||||
const RefId RefId::sEmpty = {};
|
||||
|
||||
std::string EmptyRefId::toString() const
|
||||
{
|
||||
return Misc::StringUtils::ciLess(mId, rhs.mId);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool operator<(const RefId& lhs, std::string_view rhs)
|
||||
std::string EmptyRefId::toDebugString() const
|
||||
{
|
||||
return Misc::StringUtils::ciLess(lhs.mId, rhs);
|
||||
return "Empty{}";
|
||||
}
|
||||
|
||||
bool operator<(std::string_view lhs, const RefId& rhs)
|
||||
std::ostream& operator<<(std::ostream& stream, EmptyRefId value)
|
||||
{
|
||||
return Misc::StringUtils::ciLess(lhs, rhs.mId);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const RefId& refId)
|
||||
{
|
||||
os << refId.getRefIdString();
|
||||
return os;
|
||||
}
|
||||
|
||||
RefId RefId::stringRefId(std::string_view id)
|
||||
{
|
||||
RefId newRefId;
|
||||
newRefId.mId = id;
|
||||
return newRefId;
|
||||
}
|
||||
|
||||
RefId RefId::formIdRefId(const ESM4::FormId id)
|
||||
{
|
||||
return ESM::RefId::stringRefId(ESM4::formIdToString(id));
|
||||
return stream << value.toDebugString();
|
||||
}
|
||||
|
||||
bool RefId::operator==(std::string_view rhs) const
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(mId, rhs);
|
||||
return std::visit(IsEqualToString{ rhs }, mValue);
|
||||
}
|
||||
|
||||
bool operator<(RefId lhs, std::string_view rhs)
|
||||
{
|
||||
return std::visit(IsLessThanString{ rhs }, lhs.mValue);
|
||||
}
|
||||
|
||||
bool operator<(std::string_view lhs, RefId rhs)
|
||||
{
|
||||
return std::visit(IsGreaterThanString{ lhs }, rhs.mValue);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, RefId value)
|
||||
{
|
||||
return std::visit([&](auto v) -> std::ostream& { return stream << v; }, value.mValue);
|
||||
}
|
||||
|
||||
RefId RefId::stringRefId(std::string_view value)
|
||||
{
|
||||
if (value.empty())
|
||||
return RefId();
|
||||
return RefId(StringRefId(value));
|
||||
}
|
||||
|
||||
const std::string& RefId::getRefIdString() const
|
||||
{
|
||||
return std::visit(GetRefString{}, mValue);
|
||||
}
|
||||
|
||||
std::string RefId::toString() const
|
||||
{
|
||||
return std::visit([](auto v) { return v.toString(); }, mValue);
|
||||
}
|
||||
|
||||
std::string RefId::toDebugString() const
|
||||
{
|
||||
return std::visit([](auto v) { return v.toDebugString(); }, mValue);
|
||||
}
|
||||
|
||||
bool RefId::startsWith(std::string_view prefix) const
|
||||
{
|
||||
return Misc::StringUtils::ciStartsWith(mId, prefix);
|
||||
return std::visit(StartsWith{ prefix }, mValue);
|
||||
}
|
||||
|
||||
bool RefId::endsWith(std::string_view suffix) const
|
||||
{
|
||||
return std::visit(EndsWith{ suffix }, mValue);
|
||||
}
|
||||
|
||||
bool RefId::contains(std::string_view subString) const
|
||||
{
|
||||
return Misc::StringUtils::ciFind(mId, subString) != std::string_view::npos;
|
||||
return std::visit(Contains{ subString }, mValue);
|
||||
}
|
||||
|
||||
const RefId RefId::sEmpty = {};
|
||||
}
|
||||
|
||||
std::size_t std::hash<ESM::RefId>::operator()(const ESM::RefId& k) const
|
||||
{
|
||||
return Misc::StringUtils::CiHash()(k.getRefIdString());
|
||||
}
|
||||
|
|
|
@ -5,11 +5,41 @@
|
|||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <components/esm4/formid.hpp>
|
||||
#include <components/misc/notnullptr.hpp>
|
||||
|
||||
#include "formidrefid.hpp"
|
||||
#include "generatedrefid.hpp"
|
||||
#include "indexrefid.hpp"
|
||||
#include "stringrefid.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct EmptyRefId
|
||||
{
|
||||
constexpr bool operator==(EmptyRefId /*rhs*/) const { return true; }
|
||||
|
||||
constexpr bool operator<(EmptyRefId /*rhs*/) const { return false; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, EmptyRefId value);
|
||||
};
|
||||
|
||||
enum class RefIdType : std::uint8_t
|
||||
{
|
||||
Empty = 0,
|
||||
SizedString = 1,
|
||||
UnsizedString = 2,
|
||||
FormId = 3,
|
||||
Generated = 4,
|
||||
Index = 5,
|
||||
};
|
||||
|
||||
// RefId is used to represent an Id that identifies an ESM record. These Ids can then be used in
|
||||
// ESM::Stores to find the actual record. These Ids can be serialized/de-serialized, stored on disk and remain
|
||||
// valid. They are used by ESM files, by records to reference other ESM records.
|
||||
|
@ -18,45 +48,113 @@ namespace ESM
|
|||
public:
|
||||
const static RefId sEmpty;
|
||||
|
||||
// The 2 following functions are used to move back and forth between string and RefID. Used for hard coded
|
||||
// RefIds that are as string in the code. For serialization, and display. Using explicit conversions make it
|
||||
// very clear where in the code we need to convert from string to RefId and Vice versa.
|
||||
static RefId stringRefId(std::string_view id);
|
||||
// Constructs RefId from a string using a pointer to a static set of strings.
|
||||
static RefId stringRefId(std::string_view value);
|
||||
|
||||
static RefId formIdRefId(const ESM4::FormId id);
|
||||
// Constructs RefId from ESM4 FormId storing the value in-place.
|
||||
static RefId formIdRefId(ESM4::FormId value) noexcept { return RefId(FormIdRefId(value)); }
|
||||
|
||||
const std::string& getRefIdString() const { return mId; }
|
||||
// Constructs RefId from uint64 storing the value in-place. Should be used for generated records where id is a
|
||||
// global counter.
|
||||
static RefId generated(std::uint64_t value) { return RefId(GeneratedRefId{ value }); }
|
||||
|
||||
bool empty() const { return mId.empty(); }
|
||||
// Constructs RefId from record type and 32bit index storing the value in-place. Should be used for records
|
||||
// identified by index (i.e. ESM3 SKIL).
|
||||
static RefId index(RecNameInts recordType, std::uint32_t value) { return RefId(IndexRefId(recordType, value)); }
|
||||
|
||||
constexpr RefId() = default;
|
||||
|
||||
constexpr RefId(EmptyRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
RefId(StringRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr RefId(FormIdRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr RefId(GeneratedRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr RefId(IndexRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
// Returns a reference to the value of StringRefId if it's the underlying value or throws an exception.
|
||||
const std::string& getRefIdString() const;
|
||||
|
||||
// Returns a string with serialized underlying value.
|
||||
std::string toString() const;
|
||||
|
||||
// Returns a string with serialized underlying value with information about underlying type.
|
||||
std::string toDebugString() const;
|
||||
|
||||
constexpr bool empty() const noexcept { return std::holds_alternative<EmptyRefId>(mValue); }
|
||||
|
||||
// Returns true if underlying value is StringRefId and its underlying std::string starts with given prefix.
|
||||
// Otherwise returns false.
|
||||
bool startsWith(std::string_view prefix) const;
|
||||
|
||||
// Returns true if underlying value is StringRefId and its underlying std::string ends with given suffix.
|
||||
// Otherwise returns false.
|
||||
bool endsWith(std::string_view suffix) const;
|
||||
|
||||
// Returns true if underlying value is StringRefId and its underlying std::string contains given subString.
|
||||
// Otherwise returns false.
|
||||
bool contains(std::string_view subString) const;
|
||||
|
||||
bool operator==(const RefId& rhs) const;
|
||||
friend constexpr bool operator==(const RefId& l, const RefId& r) { return l.mValue == r.mValue; }
|
||||
|
||||
bool operator==(std::string_view rhs) const;
|
||||
|
||||
bool operator<(const RefId& rhs) const;
|
||||
friend constexpr bool operator<(const RefId& l, const RefId& r) { return l.mValue < r.mValue; }
|
||||
|
||||
friend bool operator<(const RefId& lhs, std::string_view rhs);
|
||||
friend bool operator<(RefId lhs, std::string_view rhs);
|
||||
|
||||
friend bool operator<(std::string_view lhs, const RefId& rhs);
|
||||
friend bool operator<(std::string_view lhs, RefId rhs);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const RefId& dt);
|
||||
friend std::ostream& operator<<(std::ostream& stream, RefId value);
|
||||
|
||||
template <class F, class... T>
|
||||
friend constexpr auto visit(F&& f, T&&... v)
|
||||
-> std::enable_if_t<(std::is_same_v<std::decay_t<T>, RefId> && ...),
|
||||
decltype(std::visit(std::forward<F>(f), std::forward<T>(v).mValue...))>
|
||||
{
|
||||
return std::visit(std::forward<F>(f), std::forward<T>(v).mValue...);
|
||||
}
|
||||
|
||||
friend struct std::hash<ESM::RefId>;
|
||||
|
||||
private:
|
||||
std::string mId;
|
||||
std::variant<EmptyRefId, StringRefId, FormIdRefId, GeneratedRefId, IndexRefId> mValue{ EmptyRefId{} };
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::EmptyRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::EmptyRefId /*value*/) const noexcept { return 0; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<ESM::RefId>
|
||||
{
|
||||
std::size_t operator()(const ESM::RefId& k) const;
|
||||
std::size_t operator()(ESM::RefId value) const noexcept
|
||||
{
|
||||
return std::hash<decltype(value.mValue)>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
93
components/esm/stringrefid.cpp
Normal file
93
components/esm/stringrefid.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include "stringrefid.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "components/misc/guarded.hpp"
|
||||
#include "components/misc/strings/algorithm.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using StringsSet = std::unordered_set<std::string, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>;
|
||||
|
||||
const std::string emptyString;
|
||||
|
||||
Misc::NotNullPtr<const std::string> getOrInsertString(std::string_view id)
|
||||
{
|
||||
static Misc::ScopeGuarded<StringsSet> refIds;
|
||||
const auto locked = refIds.lock();
|
||||
auto it = locked->find(id);
|
||||
if (it == locked->end())
|
||||
it = locked->emplace(id).first;
|
||||
return &*it;
|
||||
}
|
||||
}
|
||||
|
||||
StringRefId::StringRefId()
|
||||
: mValue(&emptyString)
|
||||
{
|
||||
}
|
||||
|
||||
StringRefId::StringRefId(std::string_view value)
|
||||
: mValue(getOrInsertString(value))
|
||||
{
|
||||
}
|
||||
|
||||
bool StringRefId::operator==(std::string_view rhs) const noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(*mValue, rhs);
|
||||
}
|
||||
|
||||
bool StringRefId::operator<(StringRefId rhs) const noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciLess(*mValue, *rhs.mValue);
|
||||
}
|
||||
|
||||
bool operator<(StringRefId lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciLess(*lhs.mValue, rhs);
|
||||
}
|
||||
|
||||
bool operator<(std::string_view lhs, StringRefId rhs) noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciLess(lhs, *rhs.mValue);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, StringRefId value)
|
||||
{
|
||||
stream << "String{";
|
||||
for (char c : *value.mValue)
|
||||
if (std::isprint(c) && c != '\t' && c != '\n' && c != '\r')
|
||||
stream << c;
|
||||
else
|
||||
stream << "\\x" << std::hex << std::uppercase << static_cast<unsigned>(static_cast<unsigned char>(c));
|
||||
return stream << '}';
|
||||
}
|
||||
|
||||
std::string StringRefId::toDebugString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << *this;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
bool StringRefId::startsWith(std::string_view prefix) const
|
||||
{
|
||||
return Misc::StringUtils::ciStartsWith(*mValue, prefix);
|
||||
}
|
||||
|
||||
bool StringRefId::endsWith(std::string_view suffix) const
|
||||
{
|
||||
return Misc::StringUtils::ciEndsWith(*mValue, suffix);
|
||||
}
|
||||
|
||||
bool StringRefId::contains(std::string_view subString) const
|
||||
{
|
||||
return Misc::StringUtils::ciFind(*mValue, subString) != std::string_view::npos;
|
||||
}
|
||||
}
|
66
components/esm/stringrefid.hpp
Normal file
66
components/esm/stringrefid.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_STRINGREFID_HPP
|
||||
#define OPENMW_COMPONENTS_ESM_STRINGREFID_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <components/misc/notnullptr.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class StringRefId
|
||||
{
|
||||
public:
|
||||
StringRefId();
|
||||
|
||||
// Constructs StringRefId from string using pointer to a static set of strings.
|
||||
explicit StringRefId(std::string_view value);
|
||||
|
||||
const std::string& getValue() const { return *mValue; }
|
||||
|
||||
std::string toString() const { return *mValue; }
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
bool startsWith(std::string_view prefix) const;
|
||||
|
||||
bool endsWith(std::string_view suffix) const;
|
||||
|
||||
bool contains(std::string_view subString) const;
|
||||
|
||||
bool operator==(StringRefId rhs) const noexcept { return mValue == rhs.mValue; }
|
||||
|
||||
bool operator==(std::string_view rhs) const noexcept;
|
||||
|
||||
// Compares values to provide stable order
|
||||
bool operator<(StringRefId rhs) const noexcept;
|
||||
|
||||
friend bool operator<(StringRefId lhs, std::string_view rhs) noexcept;
|
||||
|
||||
friend bool operator<(std::string_view lhs, StringRefId rhs) noexcept;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, StringRefId value);
|
||||
|
||||
friend struct std::hash<StringRefId>;
|
||||
|
||||
private:
|
||||
Misc::NotNullPtr<const std::string> mValue;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::StringRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::StringRefId value) const noexcept
|
||||
{
|
||||
return std::hash<const std::string*>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
#include "esmreader.hpp"
|
||||
|
||||
#include "readerscache.hpp"
|
||||
#include "savedgame.hpp"
|
||||
|
||||
#include <components/files/conversion.hpp>
|
||||
#include <components/files/openfile.hpp>
|
||||
|
@ -158,6 +159,9 @@ namespace ESM
|
|||
{
|
||||
getSubHeader();
|
||||
|
||||
if (mHeader.mFormatVersion > MaxStringRefIdFormatVersion)
|
||||
return getStringView(mCtx.leftSub);
|
||||
|
||||
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||
// occur in any of the official mods, but MultiMark makes use of
|
||||
// them. For some reason, they break the rules, and contain a byte
|
||||
|
@ -177,7 +181,10 @@ namespace ESM
|
|||
|
||||
RefId ESMReader::getRefId()
|
||||
{
|
||||
return ESM::RefId::stringRefId(getHStringView());
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return ESM::RefId::stringRefId(getHStringView());
|
||||
getSubHeader();
|
||||
return getRefIdImpl(mCtx.leftSub);
|
||||
}
|
||||
|
||||
void ESMReader::skipHString()
|
||||
|
@ -189,7 +196,8 @@ namespace ESM
|
|||
// them. For some reason, they break the rules, and contain a byte
|
||||
// (value 0) even if the header says there is no data. If
|
||||
// Morrowind accepts it, so should we.
|
||||
if (mCtx.leftSub == 0 && hasMoreSubs() && !mEsm->peek())
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion && mCtx.leftSub == 0 && hasMoreSubs()
|
||||
&& !mEsm->peek())
|
||||
{
|
||||
// Skip the following zero byte
|
||||
mCtx.leftRec--;
|
||||
|
@ -378,6 +386,13 @@ namespace ESM
|
|||
return std::string(getStringView(size));
|
||||
}
|
||||
|
||||
RefId ESMReader::getMaybeFixedRefIdSize(std::size_t size)
|
||||
{
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return RefId::stringRefId(getMaybeFixedStringSize(size));
|
||||
return getRefIdImpl(mCtx.leftSub);
|
||||
}
|
||||
|
||||
std::string_view ESMReader::getStringView(std::size_t size)
|
||||
{
|
||||
if (mBuffer.size() <= size)
|
||||
|
@ -403,7 +418,62 @@ namespace ESM
|
|||
|
||||
RefId ESMReader::getRefId(std::size_t size)
|
||||
{
|
||||
return RefId::stringRefId(getStringView(size));
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return ESM::RefId::stringRefId(getStringView(size));
|
||||
return getRefIdImpl(size);
|
||||
}
|
||||
|
||||
RefId ESMReader::getRefIdImpl(std::size_t size)
|
||||
{
|
||||
RefIdType refIdType = RefIdType::Empty;
|
||||
getT(refIdType);
|
||||
|
||||
switch (refIdType)
|
||||
{
|
||||
case RefIdType::Empty:
|
||||
return RefId();
|
||||
case RefIdType::SizedString:
|
||||
{
|
||||
const std::size_t minSize = sizeof(refIdType) + sizeof(StringSizeType);
|
||||
if (size < minSize)
|
||||
fail("Requested RefId record size is too small (" + std::to_string(size) + " < "
|
||||
+ std::to_string(minSize) + ")");
|
||||
StringSizeType storedSize = 0;
|
||||
getT(storedSize);
|
||||
const std::size_t maxSize = size - minSize;
|
||||
if (storedSize > maxSize)
|
||||
fail("RefId string does not fit subrecord size (" + std::to_string(storedSize) + " > "
|
||||
+ std::to_string(maxSize) + ")");
|
||||
return RefId::stringRefId(getStringView(storedSize));
|
||||
}
|
||||
case RefIdType::UnsizedString:
|
||||
if (size < sizeof(refIdType))
|
||||
fail("Requested RefId record size is too small (" + std::to_string(size) + " < "
|
||||
+ std::to_string(sizeof(refIdType)) + ")");
|
||||
return RefId::stringRefId(getStringView(size - sizeof(refIdType)));
|
||||
case RefIdType::FormId:
|
||||
{
|
||||
ESM4::FormId formId{};
|
||||
getT(formId);
|
||||
return RefId::formIdRefId(formId);
|
||||
}
|
||||
case RefIdType::Generated:
|
||||
{
|
||||
std::uint64_t generated{};
|
||||
getExact(&generated, sizeof(std::uint64_t));
|
||||
return RefId::generated(generated);
|
||||
}
|
||||
case RefIdType::Index:
|
||||
{
|
||||
RecNameInts recordType{};
|
||||
getExact(&recordType, sizeof(std::uint32_t));
|
||||
std::uint32_t index{};
|
||||
getExact(&index, sizeof(std::uint32_t));
|
||||
return RefId::index(recordType, index);
|
||||
}
|
||||
}
|
||||
|
||||
fail("Unsupported RefIdType: " + std::to_string(static_cast<unsigned>(refIdType)));
|
||||
}
|
||||
|
||||
[[noreturn]] void ESMReader::fail(const std::string& msg)
|
||||
|
|
|
@ -273,13 +273,17 @@ namespace ESM
|
|||
skip(sizeof(T));
|
||||
}
|
||||
|
||||
void getExact(void* x, int size) { mEsm->read((char*)x, size); }
|
||||
void getExact(void* x, std::size_t size)
|
||||
{
|
||||
mEsm->read(static_cast<char*>(x), static_cast<std::streamsize>(size));
|
||||
}
|
||||
|
||||
void getName(NAME& name) { getT(name); }
|
||||
void getUint(uint32_t& u) { getT(u); }
|
||||
|
||||
std::string getMaybeFixedStringSize(std::size_t size);
|
||||
|
||||
RefId getMaybeFixedRefIdSize(std::size_t size) { return RefId::stringRefId(getMaybeFixedStringSize(size)); }
|
||||
RefId getMaybeFixedRefIdSize(std::size_t size);
|
||||
|
||||
// Read the next 'size' bytes and return them as a string. Converts
|
||||
// them from native encoding to UTF8 in the process.
|
||||
|
@ -315,6 +319,8 @@ namespace ESM
|
|||
|
||||
void clearCtx();
|
||||
|
||||
RefId getRefIdImpl(std::size_t size);
|
||||
|
||||
std::unique_ptr<std::istream> mEsm;
|
||||
|
||||
ESM_Context mCtx;
|
||||
|
|
|
@ -4,10 +4,65 @@
|
|||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/notnullptr.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "formatversion.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <bool sizedString>
|
||||
struct WriteRefId
|
||||
{
|
||||
ESMWriter& mWriter;
|
||||
|
||||
explicit WriteRefId(ESMWriter& writer)
|
||||
: mWriter(writer)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(EmptyRefId /*v*/) const { mWriter.writeT(RefIdType::Empty); }
|
||||
|
||||
void operator()(StringRefId v) const
|
||||
{
|
||||
constexpr StringSizeType maxSize = std::numeric_limits<StringSizeType>::max();
|
||||
if (v.getValue().size() > maxSize)
|
||||
throw std::runtime_error("RefId string size is too long: \"" + v.getValue().substr(0, 64)
|
||||
+ "<...>\" (" + std::to_string(v.getValue().size()) + " > " + std::to_string(maxSize) + ")");
|
||||
if constexpr (sizedString)
|
||||
{
|
||||
mWriter.writeT(RefIdType::SizedString);
|
||||
mWriter.writeT(static_cast<StringSizeType>(v.getValue().size()));
|
||||
}
|
||||
else
|
||||
mWriter.writeT(RefIdType::UnsizedString);
|
||||
mWriter.write(v.getValue().data(), v.getValue().size());
|
||||
}
|
||||
|
||||
void operator()(FormIdRefId v) const
|
||||
{
|
||||
mWriter.writeT(RefIdType::FormId);
|
||||
mWriter.writeT(v.getValue());
|
||||
}
|
||||
|
||||
void operator()(GeneratedRefId v) const
|
||||
{
|
||||
mWriter.writeT(RefIdType::Generated);
|
||||
mWriter.writeT(v.getValue());
|
||||
}
|
||||
|
||||
void operator()(IndexRefId v) const
|
||||
{
|
||||
mWriter.writeT(RefIdType::Generated);
|
||||
mWriter.writeT(v.getRecordType());
|
||||
mWriter.writeT(v.getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ESMWriter::ESMWriter()
|
||||
: mRecords()
|
||||
, mStream(nullptr)
|
||||
|
@ -167,14 +222,18 @@ namespace ESM
|
|||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNRefId(NAME name, const RefId& value)
|
||||
void ESMWriter::writeHNRefId(NAME name, RefId value)
|
||||
{
|
||||
writeHNString(name, value.getRefIdString());
|
||||
startSubRecord(name);
|
||||
writeHRefId(value);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNRefId(NAME name, const RefId& value, std::size_t size)
|
||||
void ESMWriter::writeHNRefId(NAME name, RefId value, std::size_t size)
|
||||
{
|
||||
writeHNString(name, value.getRefIdString(), size);
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return writeHNString(name, value.getRefIdString(), size);
|
||||
writeHNRefId(name, value);
|
||||
}
|
||||
|
||||
void ESMWriter::writeMaybeFixedSizeString(const std::string& data, std::size_t size)
|
||||
|
@ -220,19 +279,30 @@ namespace ESM
|
|||
write("\0", 1);
|
||||
}
|
||||
|
||||
void ESMWriter::writeMaybeFixedSizeRefId(const RefId& value, std::size_t size)
|
||||
void ESMWriter::writeMaybeFixedSizeRefId(RefId value, std::size_t size)
|
||||
{
|
||||
writeMaybeFixedSizeString(value.getRefIdString(), size);
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return writeMaybeFixedSizeString(value.getRefIdString(), size);
|
||||
visit(WriteRefId<true>(*this), value);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHRefId(const RefId& value)
|
||||
void ESMWriter::writeHRefId(RefId value)
|
||||
{
|
||||
writeHString(value.getRefIdString());
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return writeHString(value.getRefIdString());
|
||||
writeRefId(value);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHCRefId(const RefId& value)
|
||||
void ESMWriter::writeHCRefId(RefId value)
|
||||
{
|
||||
writeHCString(value.getRefIdString());
|
||||
if (mHeader.mFormatVersion <= MaxStringRefIdFormatVersion)
|
||||
return writeHCString(value.getRefIdString());
|
||||
writeRefId(value);
|
||||
}
|
||||
|
||||
void ESMWriter::writeRefId(RefId value)
|
||||
{
|
||||
visit(WriteRefId<false>(*this), value);
|
||||
}
|
||||
|
||||
void ESMWriter::writeName(NAME name)
|
||||
|
|
|
@ -47,6 +47,8 @@ namespace ESM
|
|||
// It is a good idea to compare this with the value you wrote into the header (setRecordCount)
|
||||
// It should be the record count you set + 1 (1 additional record for the TES3 header)
|
||||
int getRecordCount() const { return mRecordCount; }
|
||||
|
||||
FormatVersion getFormatVersion() const { return mHeader.mFormatVersion; }
|
||||
void setFormatVersion(FormatVersion value);
|
||||
|
||||
void clearMaster();
|
||||
|
@ -78,24 +80,24 @@ namespace ESM
|
|||
writeHNCString(name, data);
|
||||
}
|
||||
|
||||
void writeHNRefId(NAME name, const RefId& value);
|
||||
void writeHNRefId(NAME name, RefId value);
|
||||
|
||||
void writeHNRefId(NAME name, const RefId& value, std::size_t size);
|
||||
void writeHNRefId(NAME name, RefId value, std::size_t size);
|
||||
|
||||
void writeHNCRefId(NAME name, const RefId& value)
|
||||
void writeHNCRefId(NAME name, RefId value)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHCRefId(value);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void writeHNORefId(NAME name, const RefId& value)
|
||||
void writeHNORefId(NAME name, RefId value)
|
||||
{
|
||||
if (!value.empty())
|
||||
writeHNRefId(name, value);
|
||||
}
|
||||
|
||||
void writeHNOCRefId(NAME name, const RefId& value)
|
||||
void writeHNOCRefId(NAME name, RefId value)
|
||||
{
|
||||
if (!value.empty())
|
||||
writeHNCRefId(name, value);
|
||||
|
@ -165,11 +167,11 @@ namespace ESM
|
|||
void writeHString(const std::string& data);
|
||||
void writeHCString(const std::string& data);
|
||||
|
||||
void writeMaybeFixedSizeRefId(const RefId& value, std::size_t size);
|
||||
void writeMaybeFixedSizeRefId(RefId value, std::size_t size);
|
||||
|
||||
void writeHRefId(const RefId& value);
|
||||
void writeHRefId(RefId refId);
|
||||
|
||||
void writeHCRefId(const RefId& value);
|
||||
void writeHCRefId(RefId refId);
|
||||
|
||||
void writeName(NAME data);
|
||||
|
||||
|
@ -184,6 +186,8 @@ namespace ESM
|
|||
bool mCounting;
|
||||
|
||||
Header mHeader;
|
||||
|
||||
void writeRefId(RefId value);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ namespace ESM
|
|||
inline constexpr FormatVersion MaxOldSkillsAndAttributesFormatVersion = 18;
|
||||
inline constexpr FormatVersion MaxOldCreatureStatsFormatVersion = 19;
|
||||
inline constexpr FormatVersion MaxLimitedSizeStringsFormatVersion = 22;
|
||||
inline constexpr FormatVersion CurrentSaveGameFormatVersion = 23;
|
||||
inline constexpr FormatVersion MaxStringRefIdFormatVersion = 23;
|
||||
inline constexpr FormatVersion CurrentSaveGameFormatVersion = 24;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -73,4 +73,9 @@ namespace ESM
|
|||
mModel.clear();
|
||||
mRace = ESM::RefId();
|
||||
}
|
||||
|
||||
bool isFirstPersonBodyPart(const BodyPart& value)
|
||||
{
|
||||
return value.mId.endsWith("1st");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,5 +72,7 @@ namespace ESM
|
|||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
|
||||
bool isFirstPersonBodyPart(const BodyPart& value);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "loadmgef.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
|
@ -11,156 +9,6 @@ namespace ESM
|
|||
{
|
||||
namespace
|
||||
{
|
||||
static const char* sIds[MagicEffect::Length] = {
|
||||
"WaterBreathing",
|
||||
"SwiftSwim",
|
||||
"WaterWalking",
|
||||
"Shield",
|
||||
"FireShield",
|
||||
"LightningShield",
|
||||
"FrostShield",
|
||||
"Burden",
|
||||
"Feather",
|
||||
"Jump",
|
||||
"Levitate",
|
||||
"SlowFall",
|
||||
"Lock",
|
||||
"Open",
|
||||
"FireDamage",
|
||||
"ShockDamage",
|
||||
"FrostDamage",
|
||||
"DrainAttribute",
|
||||
"DrainHealth",
|
||||
"DrainMagicka",
|
||||
"DrainFatigue",
|
||||
"DrainSkill",
|
||||
"DamageAttribute",
|
||||
"DamageHealth",
|
||||
"DamageMagicka",
|
||||
"DamageFatigue",
|
||||
"DamageSkill",
|
||||
"Poison",
|
||||
"WeaknessToFire",
|
||||
"WeaknessToFrost",
|
||||
"WeaknessToShock",
|
||||
"WeaknessToMagicka",
|
||||
"WeaknessToCommonDisease",
|
||||
"WeaknessToBlightDisease",
|
||||
"WeaknessToCorprusDisease",
|
||||
"WeaknessToPoison",
|
||||
"WeaknessToNormalWeapons",
|
||||
"DisintegrateWeapon",
|
||||
"DisintegrateArmor",
|
||||
"Invisibility",
|
||||
"Chameleon",
|
||||
"Light",
|
||||
"Sanctuary",
|
||||
"NightEye",
|
||||
"Charm",
|
||||
"Paralyze",
|
||||
"Silence",
|
||||
"Blind",
|
||||
"Sound",
|
||||
"CalmHumanoid",
|
||||
"CalmCreature",
|
||||
"FrenzyHumanoid",
|
||||
"FrenzyCreature",
|
||||
"DemoralizeHumanoid",
|
||||
"DemoralizeCreature",
|
||||
"RallyHumanoid",
|
||||
"RallyCreature",
|
||||
"Dispel",
|
||||
"Soultrap",
|
||||
"Telekinesis",
|
||||
"Mark",
|
||||
"Recall",
|
||||
"DivineIntervention",
|
||||
"AlmsiviIntervention",
|
||||
"DetectAnimal",
|
||||
"DetectEnchantment",
|
||||
"DetectKey",
|
||||
"SpellAbsorption",
|
||||
"Reflect",
|
||||
"CureCommonDisease",
|
||||
"CureBlightDisease",
|
||||
"CureCorprusDisease",
|
||||
"CurePoison",
|
||||
"CureParalyzation",
|
||||
"RestoreAttribute",
|
||||
"RestoreHealth",
|
||||
"RestoreMagicka",
|
||||
"RestoreFatigue",
|
||||
"RestoreSkill",
|
||||
"FortifyAttribute",
|
||||
"FortifyHealth",
|
||||
"FortifyMagicka",
|
||||
"FortifyFatigue",
|
||||
"FortifySkill",
|
||||
"FortifyMaximumMagicka",
|
||||
"AbsorbAttribute",
|
||||
"AbsorbHealth",
|
||||
"AbsorbMagicka",
|
||||
"AbsorbFatigue",
|
||||
"AbsorbSkill",
|
||||
"ResistFire",
|
||||
"ResistFrost",
|
||||
"ResistShock",
|
||||
"ResistMagicka",
|
||||
"ResistCommonDisease",
|
||||
"ResistBlightDisease",
|
||||
"ResistCorprusDisease",
|
||||
"ResistPoison",
|
||||
"ResistNormalWeapons",
|
||||
"ResistParalysis",
|
||||
"RemoveCurse",
|
||||
"TurnUndead",
|
||||
"SummonScamp",
|
||||
"SummonClannfear",
|
||||
"SummonDaedroth",
|
||||
"SummonDremora",
|
||||
"SummonAncestralGhost",
|
||||
"SummonSkeletalMinion",
|
||||
"SummonBonewalker",
|
||||
"SummonGreaterBonewalker",
|
||||
"SummonBonelord",
|
||||
"SummonWingedTwilight",
|
||||
"SummonHunger",
|
||||
"SummonGoldenSaint",
|
||||
"SummonFlameAtronach",
|
||||
"SummonFrostAtronach",
|
||||
"SummonStormAtronach",
|
||||
"FortifyAttack",
|
||||
"CommandCreature",
|
||||
"CommandHumanoid",
|
||||
"BoundDagger",
|
||||
"BoundLongsword",
|
||||
"BoundMace",
|
||||
"BoundBattleAxe",
|
||||
"BoundSpear",
|
||||
"BoundLongbow",
|
||||
"ExtraSpell",
|
||||
"BoundCuirass",
|
||||
"BoundHelm",
|
||||
"BoundBoots",
|
||||
"BoundShield",
|
||||
"BoundGloves",
|
||||
"Corprus",
|
||||
"Vampirism",
|
||||
"SummonCenturionSphere",
|
||||
"SunDamage",
|
||||
"StuntedMagicka",
|
||||
|
||||
// Tribunal only
|
||||
"SummonFabricant",
|
||||
|
||||
// Bloodmoon only
|
||||
"SummonWolf",
|
||||
"SummonBear",
|
||||
"SummonBonewolf",
|
||||
"SummonCreature04",
|
||||
"SummonCreature05",
|
||||
};
|
||||
|
||||
const int NumberOfHardcodedFlags = 143;
|
||||
const int HardcodedFlags[NumberOfHardcodedFlags] = { 0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0,
|
||||
0x11d0, 0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0, 0x1f0, 0x11d2, 0x11f0, 0x11d0,
|
||||
|
@ -186,7 +34,7 @@ namespace ESM
|
|||
|
||||
esm.getHNT(mIndex, "INDX");
|
||||
|
||||
mId = ESM::RefId::stringRefId(indexToId(mIndex));
|
||||
mId = indexToRefId(mIndex);
|
||||
|
||||
esm.getHNTSized<36>(mData, "MEDT");
|
||||
if (esm.getFormatVersion() == DefaultFormatVersion)
|
||||
|
@ -592,28 +440,10 @@ namespace ESM
|
|||
mDescription.clear();
|
||||
}
|
||||
|
||||
std::string MagicEffect::indexToId(int index)
|
||||
RefId MagicEffect::indexToRefId(int index)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
stream << "#";
|
||||
|
||||
if (index < 100)
|
||||
{
|
||||
stream << "0";
|
||||
|
||||
if (index < 10)
|
||||
stream << "0";
|
||||
}
|
||||
|
||||
stream << index;
|
||||
|
||||
if (index >= 0 && index < Length)
|
||||
stream << sIds[index];
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
if (index == -1)
|
||||
return RefId();
|
||||
return RefId::index(sRecordId, static_cast<std::uint32_t>(index));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,7 +269,7 @@ namespace ESM
|
|||
Length
|
||||
};
|
||||
|
||||
static std::string indexToId(int index);
|
||||
static RefId indexToRefId(int index);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "loadskil.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
|
@ -133,7 +131,7 @@ namespace ESM
|
|||
|
||||
// create an ID from the index and the name (only used in the editor and likely to change in the
|
||||
// future)
|
||||
mId = ESM::RefId::stringRefId(indexToId(mIndex));
|
||||
mId = indexToRefId(mIndex);
|
||||
}
|
||||
|
||||
void Skill::save(ESMWriter& esm, bool /*isDeleted*/) const
|
||||
|
@ -152,23 +150,10 @@ namespace ESM
|
|||
mDescription.clear();
|
||||
}
|
||||
|
||||
std::string Skill::indexToId(int index)
|
||||
RefId Skill::indexToRefId(int index)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
stream << "#";
|
||||
|
||||
if (index < 10)
|
||||
stream << "0";
|
||||
|
||||
stream << index;
|
||||
|
||||
if (index >= 0 && index < Length)
|
||||
stream << sSkillNameIds[index].substr(6);
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
if (index == -1)
|
||||
return RefId();
|
||||
return RefId::index(sRecordId, static_cast<std::uint32_t>(index));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace ESM
|
|||
void blank();
|
||||
///< Set record to default state (does not touch the ID/index).
|
||||
|
||||
static std::string indexToId(int index);
|
||||
static RefId indexToRefId(int index);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Misc
|
|||
class NotNullPtr
|
||||
{
|
||||
public:
|
||||
NotNullPtr(T* value)
|
||||
constexpr NotNullPtr(T* value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
assert(mValue != nullptr);
|
||||
|
@ -19,11 +19,11 @@ namespace Misc
|
|||
|
||||
NotNullPtr(std::nullptr_t) = delete;
|
||||
|
||||
operator T*() const { return mValue; }
|
||||
constexpr operator T*() const noexcept { return mValue; }
|
||||
|
||||
T* operator->() const { return mValue; }
|
||||
constexpr T* operator->() const noexcept { return mValue; }
|
||||
|
||||
T& operator*() const { return *mValue; }
|
||||
constexpr T& operator*() const noexcept { return *mValue; }
|
||||
|
||||
private:
|
||||
T* mValue;
|
||||
|
|
Loading…
Reference in a new issue