diff --git a/BepInEx.sln b/BepInEx.sln index 9d302f8..377c327 100644 --- a/BepInEx.sln +++ b/BepInEx.sln @@ -27,44 +27,167 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.RuntimeDetour", "su EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Utils", "submodules\MonoMod\MonoMod.Utils\MonoMod.Utils.csproj", "{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInExTests", "BepInExTests\BepInExTests.csproj", "{E7CD429A-D057-48E3-8C51-E5C934E8E07B}" + ProjectSection(ProjectDependencies) = postProject + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9} = {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Test = Debug|Test Legacy|Any CPU = Legacy|Any CPU + Legacy|Test = Legacy|Test + Release|Any CPU = Release|Any CPU + Release|Test = Release|Test v2018|Any CPU = v2018|Any CPU + v2018|Test = v2018|Test EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Any CPU.ActiveCfg = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Any CPU.Build.0 = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Test.ActiveCfg = v2018|x86 + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Test.Build.0 = v2018|x86 {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.Build.0 = Legacy|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Test.ActiveCfg = Legacy|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Test.Build.0 = Legacy|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Any CPU.ActiveCfg = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Any CPU.Build.0 = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Test.ActiveCfg = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Test.Build.0 = v2018|Any CPU {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.ActiveCfg = v2018|Any CPU {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.Build.0 = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Test.ActiveCfg = v2018|Any CPU + {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Test.Build.0 = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Any CPU.ActiveCfg = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Any CPU.Build.0 = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Test.ActiveCfg = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Test.Build.0 = v2018|Any CPU {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.Build.0 = Legacy|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Test.ActiveCfg = Legacy|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Test.Build.0 = Legacy|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Any CPU.ActiveCfg = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Any CPU.Build.0 = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Test.ActiveCfg = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Test.Build.0 = v2018|Any CPU {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.ActiveCfg = v2018|Any CPU {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.Build.0 = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Test.ActiveCfg = v2018|Any CPU + {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Test.Build.0 = v2018|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Any CPU.Build.0 = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Test.ActiveCfg = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Test.Build.0 = Release|Any CPU {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.ActiveCfg = Release|Any CPU {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.Build.0 = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Test.ActiveCfg = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Test.Build.0 = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Any CPU.Build.0 = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Test.ActiveCfg = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Test.Build.0 = Release|Any CPU {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Any CPU.ActiveCfg = Release|Any CPU {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Any CPU.Build.0 = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Test.ActiveCfg = Release|Any CPU + {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Test.Build.0 = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Test.ActiveCfg = Debug|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Test.Build.0 = Debug|Any CPU {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Any CPU.ActiveCfg = Release|Any CPU {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Any CPU.Build.0 = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Test.ActiveCfg = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Test.Build.0 = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Any CPU.Build.0 = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Test.ActiveCfg = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Test.Build.0 = Release|Any CPU {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Any CPU.ActiveCfg = Release|Any CPU {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Any CPU.Build.0 = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Test.ActiveCfg = Release|Any CPU + {54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Test.Build.0 = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Test.ActiveCfg = Debug|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Test.Build.0 = Debug|Any CPU {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Any CPU.ActiveCfg = Release|Any CPU {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Any CPU.Build.0 = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Test.ActiveCfg = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Test.Build.0 = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Any CPU.Build.0 = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Test.ActiveCfg = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Test.Build.0 = Release|Any CPU {A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Any CPU.ActiveCfg = Release|Any CPU {A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Any CPU.Build.0 = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Test.ActiveCfg = Release|Any CPU + {A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Test.Build.0 = Release|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.ActiveCfg = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.Build.0 = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Test.ActiveCfg = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Test.Build.0 = v2018|Any CPU {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Any CPU.Build.0 = Legacy|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Test.ActiveCfg = Legacy|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Test.Build.0 = Legacy|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.ActiveCfg = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.Build.0 = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Test.ActiveCfg = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Test.Build.0 = v2018|Any CPU {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Any CPU.ActiveCfg = v2018|Any CPU {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Any CPU.Build.0 = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Test.ActiveCfg = v2018|Any CPU + {F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Test.Build.0 = v2018|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Test.ActiveCfg = Debug|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Test.Build.0 = Debug|Any CPU {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Any CPU.ActiveCfg = Release|Any CPU {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Any CPU.Build.0 = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Test.ActiveCfg = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Test.Build.0 = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.Build.0 = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Test.ActiveCfg = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Test.Build.0 = Release|Any CPU {D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Any CPU.ActiveCfg = Release|Any CPU {D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Any CPU.Build.0 = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Test.ActiveCfg = Release|Any CPU + {D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Test.Build.0 = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Test.ActiveCfg = Debug|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Test.Build.0 = Debug|Any CPU {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Any CPU.ActiveCfg = Release|Any CPU {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Any CPU.Build.0 = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Test.ActiveCfg = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Test.Build.0 = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.Build.0 = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Test.ActiveCfg = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Test.Build.0 = Release|Any CPU {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Any CPU.ActiveCfg = Release|Any CPU {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Any CPU.Build.0 = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Test.ActiveCfg = Release|Any CPU + {1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Test.Build.0 = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Test.ActiveCfg = Debug|x86 + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Test.Build.0 = Debug|x86 + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Any CPU.ActiveCfg = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Any CPU.Build.0 = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Test.ActiveCfg = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Test.Build.0 = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.Build.0 = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Test.ActiveCfg = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Test.Build.0 = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Any CPU.ActiveCfg = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Any CPU.Build.0 = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Test.ActiveCfg = Release|Any CPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Test.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BepInEx/BepInEx.csproj b/BepInEx/BepInEx.csproj index 5bf383b..ee37d48 100644 --- a/BepInEx/BepInEx.csproj +++ b/BepInEx/BepInEx.csproj @@ -33,11 +33,41 @@ AnyCPU prompt MinimumRecommendedRules.ruleset + ..\bin\BepInEx.xml + + + bin\x86\Legacy\ + DEBUG;TRACE + false + x86 + prompt + MinimumRecommendedRules.ruleset + full + true + + + bin\x86\v2018\ + DEBUG;TRACE;UNITY_2018 + false + x86 + prompt + MinimumRecommendedRules.ruleset + full + true ..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.dll + + ..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Mdb.dll + + + ..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Pdb.dll + + + ..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Rocks.dll + ..\lib\UnityEngine.dll @@ -46,8 +76,11 @@ + + + diff --git a/BepInEx/Configuration/ConfigDefinition.cs b/BepInEx/Configuration/ConfigDefinition.cs index 304332d..ff49c71 100644 --- a/BepInEx/Configuration/ConfigDefinition.cs +++ b/BepInEx/Configuration/ConfigDefinition.cs @@ -2,27 +2,6 @@ namespace BepInEx.Configuration { - public class ConfigDescription - { - public ConfigDescription(string description, Type settingType, object defaultValue) - { - Description = description ?? throw new ArgumentNullException(nameof(description)); - SettingType = settingType ?? throw new ArgumentNullException(nameof(settingType)); - DefaultValue = defaultValue; - - if(defaultValue == null && settingType.IsByRef) - throw new ArgumentException("defaultValue is null while settingType is a value type"); - - if(defaultValue != null && !settingType.IsInstanceOfType(defaultValue)) - throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name); - } - - public string Description { get; } - public Type SettingType { get; } - public object DefaultValue { get; } - //todo value range - } - public class ConfigDefinition : IEquatable { public string Section { get; } @@ -39,7 +18,7 @@ namespace BepInEx.Configuration { if (other == null) return false; return string.Equals(Key, other.Key) - && string.Equals(Section, other.Section); + && string.Equals(Section, other.Section); } public override bool Equals(object obj) @@ -67,5 +46,10 @@ namespace BepInEx.Configuration public static bool operator !=(ConfigDefinition left, ConfigDefinition right) => !Equals(left, right); + + public override string ToString() + { + return Section + " / " + Key; + } } } \ No newline at end of file diff --git a/BepInEx/Configuration/ConfigDescription.cs b/BepInEx/Configuration/ConfigDescription.cs new file mode 100644 index 0000000..f14216f --- /dev/null +++ b/BepInEx/Configuration/ConfigDescription.cs @@ -0,0 +1,21 @@ +using System; + +namespace BepInEx.Configuration +{ + public class ConfigDescription + { + public ConfigDescription(string description) + { + Description = description ?? throw new ArgumentNullException(nameof(description)); + } + + public string Description { get; } + + //todo value range + + public string ToSerializedString() + { + return $"# {Description.Replace("\n", "\n# ")}"; + } + } +} \ No newline at end of file diff --git a/BepInEx/Configuration/ConfigEntry.cs b/BepInEx/Configuration/ConfigEntry.cs new file mode 100644 index 0000000..6ba9900 --- /dev/null +++ b/BepInEx/Configuration/ConfigEntry.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using System.Linq; +using BepInEx.Logging; + +namespace BepInEx.Configuration +{ + public sealed class ConfigEntry + { + internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue) : this(configFile, definition) + { + SetTypeAndDefaultValue(settingType, defaultValue, true); + } + + internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition) + { + ConfigFile = configFile ?? throw new ArgumentNullException(nameof(configFile)); + Definition = definition ?? throw new ArgumentNullException(nameof(definition)); + } + + internal void SetTypeAndDefaultValue(Type settingType, object defaultValue, bool uniqueDefaultValue) + { + if (settingType == null) throw new ArgumentNullException(nameof(settingType)); + + if (settingType == SettingType) + { + if (uniqueDefaultValue) + DefaultValue = defaultValue; + return; + } + + if (SettingType != null) + { + throw new ArgumentException($"Tried to define setting \"{Definition}\" as type {settingType.Name} " + + $"while it was already defined as type {SettingType.Name}. Use the same " + + $"Type for all Wrappers of a single setting."); + } + + if (defaultValue == null && settingType.IsValueType) + throw new ArgumentException("defaultValue is null while settingType is a value type"); + + if (defaultValue != null && !settingType.IsInstanceOfType(defaultValue)) + throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name); + + SettingType = settingType; + DefaultValue = defaultValue; + } + + private object _convertedValue; + private string _serializedValue; + + public ConfigFile ConfigFile { get; } + public ConfigDefinition Definition { get; } + + public ConfigDescription Description { get; internal set; } + + public Type SettingType { get; private set; } + public object DefaultValue { get; private set; } + + /// + /// Is the type of this setting defined, and by extension can of this setting be accessed. + /// Setting is defined when any objects reference it. + /// + public bool IsDefined => SettingType != null; + + /// + /// Can't be used when is false. + /// + public object Value + { + get + { + ProcessSerializedValue(); + + return _convertedValue; + } + set => SetValue(value, true, this); + } + + internal void SetValue(object newValue, bool fireEvent, object sender) + { + bool wasChanged = ProcessSerializedValue(); + wasChanged = wasChanged || !Equals(newValue, _convertedValue); + + if (wasChanged) + { + _convertedValue = newValue; + + if (fireEvent) + OnSettingChanged(sender); + } + } + + public string GetSerializedValue() + { + if (_serializedValue != null) + return _serializedValue; + + if (!IsDefined) + return null; + + return TomlTypeConverter.ConvertToString(Value, SettingType); + } + + public void SetSerializedValue(string newValue, bool fireEvent, object sender) + { + string current = GetSerializedValue(); + if (string.Equals(current, newValue)) return; + + _serializedValue = newValue; + + if (!IsDefined) return; + + if (ProcessSerializedValue()) + { + if (fireEvent) + OnSettingChanged(sender); + } + } + + private bool ProcessSerializedValue() + { + if (!IsDefined) + throw new InvalidOperationException("Can't get the value before the SettingType is specified"); + + if (_serializedValue != null) + { + string value = _serializedValue; + _serializedValue = null; + + if (value != "") + { + try + { + var newValue = TomlTypeConverter.ConvertToValue(value, SettingType); + if (!Equals(newValue, _convertedValue)) + { + _convertedValue = newValue; + return true; + } + return false; + } + catch (Exception e) + { + Logger.Log(LogLevel.Warning, $"Config value of setting \"{Definition}\" could not be " + + $"parsed and will be ignored. Reason: {e.Message}; Value: {value}"); + } + } + } + + if (_convertedValue == null && DefaultValue != null) + { + _convertedValue = DefaultValue; + return true; + } + + return false; + } + + private void OnSettingChanged(object sender) + { + ConfigFile.OnSettingChanged(sender, this); + } + + public void WriteDescription(StreamWriter writer) + { + if (Description != null) + writer.WriteLine(Description.ToSerializedString()); + + if (SettingType != null) + { + writer.WriteLine("# Setting type: " + SettingType.Name); + writer.WriteLine("# Default value: " + DefaultValue); + + // todo acceptable values + + if (SettingType.IsEnum && SettingType.GetCustomAttributes(typeof(FlagsAttribute), true).Any()) + writer.WriteLine("# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)"); + } + } + } +} diff --git a/BepInEx/Configuration/ConfigFile.cs b/BepInEx/Configuration/ConfigFile.cs index 7be36a4..85ae3c1 100644 --- a/BepInEx/Configuration/ConfigFile.cs +++ b/BepInEx/Configuration/ConfigFile.cs @@ -3,7 +3,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; -using System.Text.RegularExpressions; +using System.Text; +using BepInEx.Logging; namespace BepInEx.Configuration { @@ -12,28 +13,30 @@ namespace BepInEx.Configuration /// public class ConfigFile { - private static readonly Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+"); + // Need to be lazy evaluated to not cause problems for unit tests + private static ConfigFile _coreConfig; + internal static ConfigFile CoreConfig => _coreConfig ?? (_coreConfig = new ConfigFile(Paths.BepInExConfigPath, true)); - internal static ConfigFile CoreConfig { get; } = new ConfigFile(Paths.BepInExConfigPath, true); + protected Dictionary Entries { get; } = new Dictionary(); - protected internal Dictionary Cache { get; } = new Dictionary(); + [Obsolete("Use ConfigEntries instead")] + public ReadOnlyCollection ConfigDefinitions => Entries.Keys.ToList().AsReadOnly(); - public ReadOnlyCollection ConfigDefinitions => Cache.Keys.ToList().AsReadOnly(); - - /// - /// An event that is fired every time the config is reloaded. - /// - public event EventHandler ConfigReloaded; + public ReadOnlyCollection ConfigEntries => Entries.Values.ToList().AsReadOnly(); public string ConfigFilePath { get; } /// - /// If enabled, writes the config to disk every time a value is set. + /// If enabled, writes the config to disk every time a value is set. If disabled, you have to manually save or the changes will be lost! /// public bool SaveOnConfigSet { get; set; } = true; public ConfigFile(string configPath, bool saveOnInit) { + if (configPath == null) throw new ArgumentNullException(nameof(configPath)); + + configPath = Path.GetFullPath(configPath); + ConfigFilePath = configPath; if (File.Exists(ConfigFilePath)) @@ -44,9 +47,13 @@ namespace BepInEx.Configuration { Save(); } + + StartWatching(); } - private object _ioLock = new object(); + #region Save/Load + + private readonly object _ioLock = new object(); /// /// Reloads the config from disk. Unsaved changes are lost. @@ -55,9 +62,7 @@ namespace BepInEx.Configuration { lock (_ioLock) { - Dictionary descriptions = Cache.ToDictionary(x => x.Key, x => x.Key.Description); - - string currentSection = ""; + string currentSection = string.Empty; foreach (string rawLine in File.ReadAllLines(ConfigFilePath)) { @@ -81,14 +86,18 @@ namespace BepInEx.Configuration var definition = new ConfigDefinition(currentSection, currentKey); - if (descriptions.ContainsKey(definition)) - definition.Description = descriptions[definition]; + Entries.TryGetValue(definition, out ConfigEntry entry); + if (entry == null) + { + entry = new ConfigEntry(this, definition); + Entries[definition] = entry; + } - Cache[definition] = currentValue; + entry.SetSerializedValue(currentValue, true, this); } - - ConfigReloaded?.Invoke(this, EventArgs.Empty); } + + OnConfigReloaded(); } /// @@ -98,51 +107,177 @@ namespace BepInEx.Configuration { lock (_ioLock) { - if (!Directory.Exists(Paths.ConfigPath)) - Directory.CreateDirectory(Paths.ConfigPath); + StopWatching(); - using (StreamWriter writer = new StreamWriter(File.Create(ConfigFilePath), System.Text.Encoding.UTF8)) - foreach (var sectionKv in Cache.GroupBy(x => x.Key.Section).OrderBy(x => x.Key)) + string directoryName = Path.GetDirectoryName(ConfigFilePath); + if (directoryName != null) Directory.CreateDirectory(directoryName); + + using (var writer = new StreamWriter(File.Create(ConfigFilePath), Encoding.UTF8)) + { + foreach (var sectionKv in Entries.GroupBy(x => x.Key.Section).OrderBy(x => x.Key)) { + // Section heading writer.WriteLine($"[{sectionKv.Key}]"); - foreach (var entryKv in sectionKv) + foreach (var configEntry in sectionKv.Select(x => x.Value)) { writer.WriteLine(); - if (!string.IsNullOrEmpty(entryKv.Key.Description)) - writer.WriteLine($"# {entryKv.Key.Description.Replace("\n", "\n# ")}"); + configEntry.WriteDescription(writer); - writer.WriteLine($"{entryKv.Key.Key} = {entryKv.Value}"); + writer.WriteLine($"{configEntry.Definition.Key} = {configEntry.GetSerializedValue()}"); } writer.WriteLine(); } + } + + StartWatching(); } } - public ConfigWrapper Wrap(ConfigDefinition configDefinition, T defaultValue = default(T)) + #endregion + + #region Wraps + + public ConfigWrapper Wrap(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription = null) { - if (!Cache.ContainsKey(configDefinition)) + if (!TomlTypeConverter.CanConvert(typeof(T))) + throw new ArgumentException($"Type {typeof(T)} is not supported by the config system. Supported types: {string.Join(", ", TomlTypeConverter.GetSupportedTypes().Select(x => x.Name).ToArray())}"); + + Entries.TryGetValue(configDefinition, out var entry); + + if (entry == null) { - Cache.Add(configDefinition, TomlTypeConverter.ConvertToString(defaultValue)); - Save(); + entry = new ConfigEntry(this, configDefinition, typeof(T), defaultValue); + Entries[configDefinition] = entry; } else { - var original = Cache.Keys.First(x => x.Equals(configDefinition)); + entry.SetTypeAndDefaultValue(typeof(T), defaultValue, !Equals(defaultValue, default(T))); + } - if (original.Description != configDefinition.Description) + if (configDescription != null) + { + if (entry.Description != null) + Logger.Log(LogLevel.Warning, $"Tried to add configDescription to setting {configDefinition} when it already had one defined. Only add configDescription once or a random one will be used."); + + entry.Description = configDescription; + } + + return new ConfigWrapper(entry); + } + + [Obsolete("Use other Wrap overloads instead")] + public ConfigWrapper Wrap(string section, string key, string description = null, T defaultValue = default(T)) + => Wrap(new ConfigDefinition(section, key), defaultValue, string.IsNullOrEmpty(description) ? null : new ConfigDescription(description)); + + public ConfigWrapper Wrap(string section, string key, T defaultValue, ConfigDescription configDescription = null) + => Wrap(new ConfigDefinition(section, key), defaultValue, configDescription); + + #endregion + + #region Events + + /// + /// An event that is fired every time the config is reloaded. + /// + public event EventHandler ConfigReloaded; + + /// + /// Fired when one of the settings is changed. + /// + public event EventHandler SettingChanged; + + protected internal void OnSettingChanged(object sender, ConfigEntry changedEntry) + { + if (changedEntry == null) throw new ArgumentNullException(nameof(changedEntry)); + + if (SettingChanged != null) + { + var args = new SettingChangedEventArgs(changedEntry); + + foreach (var callback in SettingChanged.GetInvocationList().Cast>()) { - original.Description = configDefinition.Description; - Save(); + try + { + callback(sender, args); + } + catch (Exception e) + { + Logger.Log(LogLevel.Error, e); + } } } - return new ConfigWrapper(this, configDefinition); + // todo better way to prevent write loop? maybe do some caching? + if (sender != this && SaveOnConfigSet) + Save(); } - public ConfigWrapper Wrap(string section, string key, string description = null, T defaultValue = default(T)) - => Wrap(new ConfigDefinition(section, key, description), defaultValue); + protected void OnConfigReloaded() + { + if (ConfigReloaded != null) + { + foreach (var callback in ConfigReloaded.GetInvocationList().Cast()) + { + try + { + callback(this, EventArgs.Empty); + } + catch (Exception e) + { + Logger.Log(LogLevel.Error, e); + } + } + } + } + + #endregion + + #region File watcher + + private FileSystemWatcher _watcher; + + /// + /// Start watching the config file on disk for changes. + /// + public void StartWatching() + { + lock (_ioLock) + { + if (_watcher != null) return; + + _watcher = new FileSystemWatcher + { + Path = Path.GetDirectoryName(ConfigFilePath) ?? throw new ArgumentException("Invalid config path"), + Filter = Path.GetFileName(ConfigFilePath), + IncludeSubdirectories = false, + NotifyFilter = NotifyFilters.LastWrite, + EnableRaisingEvents = true + }; + + _watcher.Changed += (sender, args) => Reload(); + } + } + + /// + /// Stop watching the config file on disk for changes. + /// + public void StopWatching() + { + lock (_ioLock) + { + _watcher?.Dispose(); + _watcher = null; + } + } + + ~ConfigFile() + { + StopWatching(); + } + + #endregion } -} \ No newline at end of file +} diff --git a/BepInEx/Configuration/ConfigWrapper.cs b/BepInEx/Configuration/ConfigWrapper.cs index e3c1145..8f919f6 100644 --- a/BepInEx/Configuration/ConfigWrapper.cs +++ b/BepInEx/Configuration/ConfigWrapper.cs @@ -2,11 +2,12 @@ namespace BepInEx.Configuration { - public class ConfigWrapper + public sealed class ConfigWrapper { - public ConfigDefinition Definition { get; protected set; } + public ConfigEntry ConfigEntry { get; } - public ConfigFile ConfigFile { get; protected set; } + public ConfigDefinition Definition => ConfigEntry.Definition; + public ConfigFile ConfigFile => ConfigEntry.ConfigFile; /// /// Fired when the setting is changed. Does not detect changes made outside from this object. @@ -15,25 +16,18 @@ namespace BepInEx.Configuration public T Value { - get => TomlTypeConverter.ConvertToValue(ConfigFile.Cache[Definition]); - set - { - ConfigFile.Cache[Definition] = TomlTypeConverter.ConvertToString(value); - - if (ConfigFile.SaveOnConfigSet) - ConfigFile.Save(); - - SettingChanged?.Invoke(this, EventArgs.Empty); - } + get => (T)ConfigEntry.Value; + set => ConfigEntry.SetValue(value, true, this); } - public ConfigWrapper(ConfigFile configFile, ConfigDefinition definition) + internal ConfigWrapper(ConfigEntry configEntry) { - if (!TomlTypeConverter.TypeConverters.ContainsKey(typeof(T))) - throw new ArgumentException("Unsupported config wrapper type"); + ConfigEntry = configEntry ?? throw new ArgumentNullException(nameof(configEntry)); - ConfigFile = configFile; - Definition = definition; + configEntry.ConfigFile.SettingChanged += (sender, args) => + { + if (args.ChangedSetting == configEntry) SettingChanged?.Invoke(sender, args); + }; } } } \ No newline at end of file diff --git a/BepInEx/Configuration/SettingChangedEventArgs.cs b/BepInEx/Configuration/SettingChangedEventArgs.cs new file mode 100644 index 0000000..3767e9b --- /dev/null +++ b/BepInEx/Configuration/SettingChangedEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace BepInEx.Configuration +{ + public sealed class SettingChangedEventArgs : EventArgs + { + public SettingChangedEventArgs(ConfigEntry changedSetting) + { + ChangedSetting = changedSetting; + } + + public ConfigEntry ChangedSetting { get; } + } +} \ No newline at end of file diff --git a/BepInEx/Configuration/TomlTypeConverter.cs b/BepInEx/Configuration/TomlTypeConverter.cs index 25c4f03..8a836a3 100644 --- a/BepInEx/Configuration/TomlTypeConverter.cs +++ b/BepInEx/Configuration/TomlTypeConverter.cs @@ -98,10 +98,8 @@ namespace BepInEx.Configuration }, }; - public static string ConvertToString(T value) + public static string ConvertToString(object value, Type valueType) { - var valueType = typeof(T); - var conv = GetConverter(valueType); if (conv == null) throw new InvalidOperationException($"Cannot convert from type {valueType}"); @@ -111,13 +109,16 @@ namespace BepInEx.Configuration public static T ConvertToValue(string value) { - var valueType = typeof(T); + return (T)ConvertToValue(value, typeof(T)); + } + public static object ConvertToValue(string value, Type valueType) + { var conv = GetConverter(valueType); if (conv == null) throw new InvalidOperationException($"Cannot convert to type {valueType}"); - return (T)conv.ConvertToObject(value, valueType); + return conv.ConvertToObject(value, valueType); } private static TypeConverter GetConverter(Type valueType) diff --git a/BepInExTests/BepInExTests.csproj b/BepInExTests/BepInExTests.csproj new file mode 100644 index 0000000..b9ebf21 --- /dev/null +++ b/BepInExTests/BepInExTests.csproj @@ -0,0 +1,131 @@ + + + + + Debug + AnyCPU + {E7CD429A-D057-48E3-8C51-E5C934E8E07B} + Library + Properties + BepInExTests + BepInExTests + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + + + + 15.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + + + + + + + + {4ffba620-f5ed-47f9-b90c-dad1316fd9b9} + BepInEx + + + + + + + False + + + False + + + False + + + False + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/BepInExTests/Configuration/ConfigFileTests.cs b/BepInExTests/Configuration/ConfigFileTests.cs new file mode 100644 index 0000000..3fde291 --- /dev/null +++ b/BepInExTests/Configuration/ConfigFileTests.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Concurrent; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.IO; +using System.Linq; +using System.Threading; + +namespace BepInEx.Configuration.Tests +{ + [TestClass] + public class ConfigFileTests + { + private static ConcurrentBag _toRemove; + + [ClassInitialize] + public static void Init(TestContext context) + { + _toRemove = new ConcurrentBag(); + } + + [ClassCleanup] + public static void Cleanup() + { + foreach (var configFile in _toRemove) + { + configFile.StopWatching(); + File.Delete(configFile.ConfigFilePath); + } + } + + private static ConfigFile MakeConfig() + { + string configPath = Path.GetTempFileName(); + if (configPath == null) throw new InvalidOperationException("Wtf"); + var config = new ConfigFile(configPath, true); + _toRemove.Add(config); + return config; + } + + [TestMethod] + public void SaveTest() + { + MakeConfig().Save(); + } + + [TestMethod] + public void SaveTestValueChange() + { + var c = MakeConfig(); + + var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + var lines = File.ReadAllLines(c.ConfigFilePath); + Assert.AreEqual(0, lines.Count(x => x.Equals("[Cat]"))); + Assert.AreEqual(0, lines.Count(x => x.Equals("# Test"))); + Assert.AreEqual(0, lines.Count(x => x.Equals("Key = 0"))); + + c.Save(); + lines = File.ReadAllLines(c.ConfigFilePath); + Assert.AreEqual(1, lines.Count(x => x.Equals("[Cat]"))); + Assert.AreEqual(1, lines.Count(x => x.Equals("# Test"))); + Assert.AreEqual(1, lines.Count(x => x.Equals("Key = 0"))); + + w.Value = 69; + lines = File.ReadAllLines(c.ConfigFilePath); + Assert.AreEqual(1, lines.Count(x => x.Equals("[Cat]"))); + Assert.AreEqual(1, lines.Count(x => x.Equals("# Test"))); + Assert.AreEqual(1, lines.Count(x => x.Equals("Key = 69"))); + } + + [TestMethod] + public void AutoSaveTest() + { + var c = MakeConfig(); + c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + + var eventFired = new AutoResetEvent(false); + c.ConfigReloaded += (sender, args) => eventFired.Set(); + + c.Save(); + + Assert.IsFalse(eventFired.WaitOne(200)); + } + + [TestMethod] + public void ReadTest() + { + var c = MakeConfig(); + File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n"); + c.Reload(); + var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + Assert.AreEqual(w.Value, 1); + var w2 = c.Wrap("Cat", "Key2", 0, new ConfigDescription("Test")); + Assert.AreEqual(w2.Value, 0); + } + + [TestMethod] + public void ReadTest2() + { + var c = MakeConfig(); + var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + Assert.AreEqual(w.Value, 0); + + File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey = 1 \n"); + + c.Reload(); + Assert.AreEqual(w.Value, 1); + } + + [TestMethod] + public void FileWatchTest() + { + var c = MakeConfig(); + var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + Assert.AreEqual(w.Value, 0); + + var eventFired = new AutoResetEvent(false); + w.SettingChanged += (sender, args) => eventFired.Set(); + + File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey = 1 \n"); + + Assert.IsTrue(eventFired.WaitOne(500)); + + Assert.AreEqual(w.Value, 1); + } + + [TestMethod] + public void FileWatchTestNoSelfReload() + { + var c = MakeConfig(); + + var eventFired = new AutoResetEvent(false); + c.ConfigReloaded += (sender, args) => eventFired.Set(); + + c.Save(); + + Assert.IsFalse(eventFired.WaitOne(200)); + } + + [TestMethod] + public void EventTestWrapper() + { + var c = MakeConfig(); + var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + + File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n"); + + var eventFired = false; + w.SettingChanged += (sender, args) => eventFired = true; + + c.Reload(); + + Assert.IsTrue(eventFired); + } + + [TestMethod] + public void EventTestReload() + { + var c = MakeConfig(); + var eventFired = false; + + var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test")); + w.SettingChanged += (sender, args) => eventFired = true; + + Assert.IsFalse(eventFired); + + File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n"); + c.Reload(); + + Assert.IsTrue(eventFired); + } + } +} \ No newline at end of file diff --git a/BepInExTests/Properties/AssemblyInfo.cs b/BepInExTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1487903 --- /dev/null +++ b/BepInExTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BepInExTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BepInExTests")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e7cd429a-d057-48e3-8c51-e5c934e8e07b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/BepInExTests/packages.config b/BepInExTests/packages.config new file mode 100644 index 0000000..d2da0eb --- /dev/null +++ b/BepInExTests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file