diff --git a/SPID/cmake/headerlist.cmake b/SPID/cmake/headerlist.cmake
index 9d8fa86..e233a89 100644
--- a/SPID/cmake/headerlist.cmake
+++ b/SPID/cmake/headerlist.cmake
@@ -8,6 +8,7 @@ set(headers ${headers}
include/ExclusiveGroups.h
include/FormData.h
include/KeywordDependencies.h
+ include/LinkedDistribution.h
include/LogBuffer.h
include/LookupConfigs.h
include/LookupFilters.h
diff --git a/SPID/cmake/sourcelist.cmake b/SPID/cmake/sourcelist.cmake
index fc75060..c78269e 100644
--- a/SPID/cmake/sourcelist.cmake
+++ b/SPID/cmake/sourcelist.cmake
@@ -6,6 +6,7 @@ set(sources ${sources}
src/ExclusiveGroups.cpp
src/FormData.cpp
src/KeywordDependencies.cpp
+ src/LinkedDistribution.cpp
src/LogBuffer.cpp
src/LookupConfigs.cpp
src/LookupFilters.cpp
diff --git a/SPID/include/ExclusiveGroups.h b/SPID/include/ExclusiveGroups.h
index 0fc97e8..4ea6043 100644
--- a/SPID/include/ExclusiveGroups.h
+++ b/SPID/include/ExclusiveGroups.h
@@ -15,15 +15,15 @@ namespace ExclusiveGroups
///
/// As a result this method configures Manager with discovered valid exclusive groups.
///
- ///
- /// A raw exclusive group entries that should be processed/
+ /// A DataHandler that will perform the actual lookup.
+ /// A raw exclusive group entries that should be processed.
void LookupExclusiveGroups(RE::TESDataHandler* const dataHandler, INI::ExclusiveGroupsVec& rawExclusiveGroups);
///
/// Gets a set of all forms that are in the same exclusive group as the given form.
/// Note that a form can appear in multiple exclusive groups, all of those groups are returned.
///
- ///
+ /// A form for which mutually exclusive forms will be returned.
/// A union of all groups that contain a given form.
std::unordered_set MutuallyExclusiveFormsForForm(RE::TESForm* form) const;
diff --git a/SPID/include/LinkedDistribution.h b/SPID/include/LinkedDistribution.h
new file mode 100644
index 0000000..ba965c2
--- /dev/null
+++ b/SPID/include/LinkedDistribution.h
@@ -0,0 +1,79 @@
+#pragma once
+#include "FormData.h"
+
+namespace LinkedDistribution
+{
+ namespace INI {
+
+ struct RawLinkedItem
+ {
+ FormOrEditorID rawForm{};
+
+ /// Raw filters in RawLinkedItem only use MATCH, there is no meaning for ALL or NOT, so they are ignored.
+ Filters rawFormFilters{};
+
+ RandomCount count{ 1, 1 };
+ Chance chance{ 100 };
+
+ std::string path{};
+ };
+
+ using LinkedItemsVec = std::vector;
+
+ inline LinkedItemsVec linkedItems{};
+
+ namespace Parser
+ {
+ bool TryParse(const std::string& a_key, const std::string& a_value, const std::string& a_path);
+ }
+ }
+
+ template
+ using DataSet = std::set>;
+
+ template
+ using LinkedForms = std::unordered_map>;
+
+ class Manager : public ISingleton
+ {
+ private:
+ template
+ const DataSet