From e6cf516e12813f1f5f151b3a8e48a756e64b511b Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 11 Feb 2023 00:08:59 +0100 Subject: [PATCH] Support index RefId as pair of record type and std::uint32_t --- apps/opencs/model/doc/document.cpp | 6 +- apps/opencs/model/tools/classcheck.cpp | 4 +- apps/opencs/model/tools/factioncheck.cpp | 4 +- apps/opencs/model/world/columnimp.hpp | 2 +- apps/openmw_test_suite/esm/testrefid.cpp | 2 + apps/openmw_test_suite/mwworld/test_store.cpp | 2 +- components/CMakeLists.txt | 3 +- components/esm/indexrefid.cpp | 26 +++ components/esm/indexrefid.hpp | 61 ++++++ components/esm/refid.hpp | 13 +- components/esm3/esmreader.cpp | 8 + components/esm3/esmwriter.cpp | 7 + components/esm3/loadmgef.cpp | 180 +----------------- components/esm3/loadmgef.hpp | 2 +- components/esm3/loadskil.cpp | 25 +-- components/esm3/loadskil.hpp | 2 +- 16 files changed, 139 insertions(+), 208 deletions(-) create mode 100644 components/esm/indexrefid.cpp create mode 100644 components/esm/indexrefid.hpp diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index ae43953652..f46fb7be97 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -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(); diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 21b0de8e07..e608195a5f 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -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); } } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 094d8bc7f9..fb4c057879 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -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 diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 3cef2fdd00..a24a661cda 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -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& record, const QVariant& data) override diff --git a/apps/openmw_test_suite/esm/testrefid.cpp b/apps/openmw_test_suite/esm/testrefid.cpp index 30817bccad..e809daa12f 100644 --- a/apps/openmw_test_suite/esm/testrefid.cpp +++ b/apps/openmw_test_suite/esm/testrefid.cpp @@ -213,6 +213,7 @@ namespace ESM { 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)); @@ -243,6 +244,7 @@ namespace ESM { 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)); diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 027268e103..a275c09bc3 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -450,7 +450,7 @@ namespace const int index = 3; decltype(RecordType::mId) refId; if constexpr (hasIndex && !std::is_same_v) - refId = ESM::RefId::stringRefId(RecordType::indexToId(index)); + refId = RecordType::indexToRefId(index); else refId = ESM::StringRefId("foobar"); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index cb9e561ab8..f62274d0e9 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -89,7 +89,8 @@ add_component_dir(esm attr common defs esmcommon records util luascripts format formidrefid stringrefid generatedrefid -) + indexrefid + ) add_component_dir(fx pass technique lexer widgets stateupdater) diff --git a/components/esm/indexrefid.cpp b/components/esm/indexrefid.cpp new file mode 100644 index 0000000000..4a71b64662 --- /dev/null +++ b/components/esm/indexrefid.cpp @@ -0,0 +1,26 @@ +#include "indexrefid.hpp" + +#include +#include + +#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 << '}'; + } +} diff --git a/components/esm/indexrefid.hpp b/components/esm/indexrefid.hpp new file mode 100644 index 0000000000..006dfd39ed --- /dev/null +++ b/components/esm/indexrefid.hpp @@ -0,0 +1,61 @@ +#ifndef OPENMW_COMPONENTS_ESM_INDEXREFID_HPP +#define OPENMW_COMPONENTS_ESM_INDEXREFID_HPP + +#include +#include + +#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; + + private: + RecNameInts mRecordType; + std::uint32_t mValue; + }; + + static_assert(sizeof(IndexRefId) <= sizeof(std::uint64_t)); +} + +namespace std +{ + template <> + struct hash + { + std::size_t operator()(ESM::IndexRefId value) const noexcept + { + return std::hash{}(static_cast(value.mRecordType) | value.mValue); + } + }; +} + +#endif diff --git a/components/esm/refid.hpp b/components/esm/refid.hpp index 2f75679048..7c7d9e03fd 100644 --- a/components/esm/refid.hpp +++ b/components/esm/refid.hpp @@ -12,6 +12,7 @@ #include "formidrefid.hpp" #include "generatedrefid.hpp" +#include "indexrefid.hpp" #include "stringrefid.hpp" namespace ESM @@ -36,6 +37,7 @@ namespace ESM 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 @@ -56,6 +58,10 @@ namespace ESM // global counter. static RefId generated(std::uint64_t value) { return RefId(GeneratedRefId{ value }); } + // 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 @@ -78,6 +84,11 @@ namespace ESM { } + 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; @@ -120,7 +131,7 @@ namespace ESM friend struct std::hash; private: - std::variant mValue{ EmptyRefId{} }; + std::variant mValue{ EmptyRefId{} }; }; } diff --git a/components/esm3/esmreader.cpp b/components/esm3/esmreader.cpp index e3b26d7009..20ec9e47b8 100644 --- a/components/esm3/esmreader.cpp +++ b/components/esm3/esmreader.cpp @@ -463,6 +463,14 @@ namespace ESM 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(refIdType))); diff --git a/components/esm3/esmwriter.cpp b/components/esm3/esmwriter.cpp index 1360464fdb..02c7464b33 100644 --- a/components/esm3/esmwriter.cpp +++ b/components/esm3/esmwriter.cpp @@ -53,6 +53,13 @@ namespace ESM 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()); + } }; } diff --git a/components/esm3/loadmgef.cpp b/components/esm3/loadmgef.cpp index 69abbd5903..e681b628f4 100644 --- a/components/esm3/loadmgef.cpp +++ b/components/esm3/loadmgef.cpp @@ -1,7 +1,5 @@ #include "loadmgef.hpp" -#include - #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(index)); } } diff --git a/components/esm3/loadmgef.hpp b/components/esm3/loadmgef.hpp index 6210e3fadc..1f226aa49c 100644 --- a/components/esm3/loadmgef.hpp +++ b/components/esm3/loadmgef.hpp @@ -269,7 +269,7 @@ namespace ESM Length }; - static std::string indexToId(int index); + static RefId indexToRefId(int index); }; } #endif diff --git a/components/esm3/loadskil.cpp b/components/esm3/loadskil.cpp index e80b3576af..c2776bae74 100644 --- a/components/esm3/loadskil.cpp +++ b/components/esm3/loadskil.cpp @@ -1,7 +1,5 @@ #include "loadskil.hpp" -#include - #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(index)); } } diff --git a/components/esm3/loadskil.hpp b/components/esm3/loadskil.hpp index 99a800871d..bacc8534ed 100644 --- a/components/esm3/loadskil.hpp +++ b/components/esm3/loadskil.hpp @@ -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