diff --git a/SPID/include/FormData.h b/SPID/include/FormData.h index f9485ce..e2fe3ac 100644 --- a/SPID/include/FormData.h +++ b/SPID/include/FormData.h @@ -113,6 +113,14 @@ namespace Forms }; } + enum LookupOptions : std::uint8_t + { + kNone = 0, + kWhitelistedOnly = 1, + kRequireAll = 1 << 1, + kCreateIfMissing = 1 << 2 + }; + namespace detail { using namespace Lookup; @@ -140,7 +148,7 @@ namespace Forms } template - std::variant get_form_or_mod(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const Path& path, bool whitelistedOnly = false) + std::variant get_form_or_mod(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const Path& path, const LookupOptions options) { Form* form = nullptr; const RE::TESFile* mod = nullptr; @@ -171,7 +179,7 @@ namespace Forms } else { throw KeywordNotFoundException(editorID, false, path); } - } else { + } else if (options & kCreateIfMissing) { const auto factory = RE::IFormFactory::GetConcreteFormFactoryByType(); if (auto keyword = factory ? factory->Create() : nullptr; keyword) { keyword->formEditorID = editorID; @@ -181,6 +189,9 @@ namespace Forms } else { throw KeywordNotFoundException(editorID, true, path); } + } else { + // If creating keyword from this editorID is not allowed, then we simply fail as unknown editorID + throw UnknownEditorIDException(editorID, path); } }; @@ -241,6 +252,8 @@ namespace Forms throw MismatchingFormTypeException(anyForm->GetFormType(), Form::FORMTYPE, editorID, path); } } else { + // If template's Form is a generic TESForm, that means caller doesn't request specific form type, + // as such we'll attempt to create a keyword if options allow it. if constexpr (std::is_same_v) { form = find_or_create_keyword(editorID); } else { @@ -255,7 +268,7 @@ namespace Forms return mod; } - if (whitelistedOnly && form) { + if (options & kWhitelistedOnly && form) { const auto formType = form->GetFormType(); if (FormType::GetWhitelisted(formType)) { return form; @@ -267,9 +280,9 @@ namespace Forms return form; } - inline const RE::TESFile* get_file(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const Path& path) + inline const RE::TESFile* get_file(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const Path& path, const LookupOptions options) { - auto formOrMod = get_form_or_mod(dataHandler, formOrEditorID, path); + auto formOrMod = get_form_or_mod(dataHandler, formOrEditorID, path, options); if (std::holds_alternative(formOrMod)) { return std::get(formOrMod); @@ -279,9 +292,9 @@ namespace Forms } template - Form* get_form(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const Path& path, bool whitelistedOnly = false) + Form* get_form(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const Path& path, const LookupOptions options) { - auto formOrMod = get_form_or_mod
(dataHandler, formOrEditorID, path, whitelistedOnly); + auto formOrMod = get_form_or_mod(dataHandler, formOrEditorID, path, options); if (std::holds_alternative(formOrMod)) { return std::get(formOrMod); @@ -290,7 +303,7 @@ namespace Forms return nullptr; } - inline bool formID_to_form(RE::TESDataHandler* const a_dataHandler, RawFormVec& a_rawFormVec, FormVec& a_formVec, const Path& a_path, bool a_all = false, bool whitelistedOnly = true) + inline bool formID_to_form(RE::TESDataHandler* const a_dataHandler, RawFormVec& a_rawFormVec, FormVec& a_formVec, const Path& a_path, LookupOptions options) { if (a_rawFormVec.empty()) { return true; @@ -298,7 +311,7 @@ namespace Forms for (auto& formOrEditorID : a_rawFormVec) { try { - auto form = get_form_or_mod(a_dataHandler, formOrEditorID, a_path, whitelistedOnly); + auto form = get_form_or_mod(a_dataHandler, formOrEditorID, a_path, options); a_formVec.emplace_back(form); } catch (const UnknownFormIDException& e) { buffered_logger::error("\t\t[{}] Filter [0x{:X}] ({}) SKIP - formID doesn't exist", e.path, e.formID, e.modName.value_or("")); @@ -340,7 +353,7 @@ namespace Forms } } - return !a_all && !a_formVec.empty() || a_formVec.size() == a_rawFormVec.size(); + return (options & kRequireAll) == 0 && a_formVec.size() == a_rawFormVec.size(); } } @@ -601,15 +614,15 @@ void Forms::LookupGenericForm(RE::TESDataHandler* const dataHandler, INI::Data& auto& [formOrEditorID, strings, filterIDs, level, traits, idxOrCount, chance, path] = rawForm; try { - if (auto form = detail::get_form(dataHandler, formOrEditorID, path); form) { + if (auto form = detail::get_form(dataHandler, formOrEditorID, path, LookupOptions::kCreateIfMissing); form) { FormFilters filterForms{}; - bool validEntry = detail::formID_to_form(dataHandler, filterIDs.ALL, filterForms.ALL, path, true); + bool validEntry = detail::formID_to_form(dataHandler, filterIDs.ALL, filterForms.ALL, path, LookupOptions::kRequireAll); if (validEntry) { - validEntry = detail::formID_to_form(dataHandler, filterIDs.NOT, filterForms.NOT, path); + validEntry = detail::formID_to_form(dataHandler, filterIDs.NOT, filterForms.NOT, path, LookupOptions::kWhitelistedOnly); } if (validEntry) { - validEntry = detail::formID_to_form(dataHandler, filterIDs.MATCH, filterForms.MATCH, path); + validEntry = detail::formID_to_form(dataHandler, filterIDs.MATCH, filterForms.MATCH, path, LookupOptions::kWhitelistedOnly); } FilterData filters{ strings, filterForms, level, traits, chance }; diff --git a/SPID/include/LinkedDistribution.h b/SPID/include/LinkedDistribution.h index b054f43..e5ab30a 100644 --- a/SPID/include/LinkedDistribution.h +++ b/SPID/include/LinkedDistribution.h @@ -154,7 +154,7 @@ namespace LinkedDistribution using namespace Forms::Lookup; try { - return Forms::detail::get_form(dataHandler, rawForm.formOrEditorID, rawForm.path); + return Forms::detail::get_form(dataHandler, rawForm.formOrEditorID, rawForm.path, LookupOptions::kCreateIfMissing); } catch (const UnknownFormIDException& e) { buffered_logger::error("\t\t[{}] LinkedForm [0x{:X}] ({}) SKIP - formID doesn't exist", e.path, e.formID, e.modName.value_or("")); } catch (const UnknownPluginException& e) { @@ -232,7 +232,7 @@ namespace LinkedDistribution if (auto form = detail::LookupLinkedForm(dataHandler, rawForm); form) { auto& [formID, scope, parentFormIDs, count, chance, path] = rawForm; FormVec parentForms{}; - if (Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, false, false)) { + if (Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, LookupOptions::kNone)) { Link(form, scope, parentForms, count, chance, path); } } diff --git a/SPID/src/ExclusiveGroups.cpp b/SPID/src/ExclusiveGroups.cpp index 21e37f7..e3a0910 100644 --- a/SPID/src/ExclusiveGroups.cpp +++ b/SPID/src/ExclusiveGroups.cpp @@ -52,8 +52,8 @@ namespace ExclusiveGroups FormVec match{}; FormVec formsNot{}; - if (Forms::detail::formID_to_form(dataHandler, filterIDs.MATCH, match, path, false, false) && - Forms::detail::formID_to_form(dataHandler, filterIDs.NOT, formsNot, path, false, false)) { + if (Forms::detail::formID_to_form(dataHandler, filterIDs.MATCH, match, path, Forms::LookupOptions::kNone) && + Forms::detail::formID_to_form(dataHandler, filterIDs.NOT, formsNot, path, Forms::LookupOptions::kNone)) { for (const auto& form : match) { if (std::holds_alternative(form)) { forms.insert(std::get(form)); diff --git a/SPID/src/LinkedDistribution.cpp b/SPID/src/LinkedDistribution.cpp index b06ed58..48b44de 100644 --- a/SPID/src/LinkedDistribution.cpp +++ b/SPID/src/LinkedDistribution.cpp @@ -125,7 +125,7 @@ namespace LinkedDistribution if (auto form = detail::LookupLinkedForm(dataHandler, rawSpell); form) { auto& [formID, scope, parentFormIDs, idxOrCount, chance, path] = rawSpell; FormVec parentForms{}; - if (!Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, false, false)) { + if (!Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, LookupOptions::kNone)) { continue; } if (const auto spell = form->As(); spell) { @@ -141,7 +141,7 @@ namespace LinkedDistribution if (auto form = detail::LookupLinkedForm(dataHandler, rawForm); form) { auto& [formID, scope, parentFormIDs, idxOrCount, chance, path] = rawForm; FormVec parentForms{}; - if (!Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, false, false)) { + if (!Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, LookupOptions::kNone)) { continue; } // Add to appropriate list. (Note that type inferring doesn't recognize SleepOutfit, Skin or DeathItems)