diff --git a/Build/InstallerExtensions.dll b/Build/InstallerExtensions.dll index 05f62afa..bfc48f02 100644 Binary files a/Build/InstallerExtensions.dll and b/Build/InstallerExtensions.dll differ diff --git a/Build/QModInstaller.dll b/Build/QModInstaller.dll index 4bd76794..eb8f81c8 100644 Binary files a/Build/QModInstaller.dll and b/Build/QModInstaller.dll differ diff --git a/Build/QModInstaller.xml b/Build/QModInstaller.xml index ecd5c8e4..1e776bd9 100644 --- a/Build/QModInstaller.xml +++ b/Build/QModInstaller.xml @@ -498,6 +498,63 @@ Optional: The exception that needs to be logged. Optional: Whether to show the message on screen or not. + + + Defines a service for parsing version strings. + + + + + The default version zero used when no version could be parsed. + + + + + Returns a new based on the provided string value, with all 4 groups populated. + + The version string to parse. + A new with all empty groups populated with 0. + + + + Checks if the provided version is all zeros. + + The version to check. + True if this matches version 0.0.0.0 + + + + A service that handles parsing values into objects. + + + + + The regex used to sanitize incoming version strings. + ^(((\d+)\.?){0,3}\d+)$ + + + + + The default version zero used when no version could be parsed. + + + + + Returns a new based on the provided string value, with all 4 groups populated. + + The version string to parse. This must match . + A new with all empty groups populated with 0. + + "2.8" will be parsed as "2.8.0.0" + + + + + Checks if the provided version is all zeros. + + The version to check. + True if this matches version 0.0.0.0 + Container class for the entry point diff --git a/Build/QModManager.QMMHarmonyShimmer.dll b/Build/QModManager.QMMHarmonyShimmer.dll index 02db99f0..8ce3d90a 100644 Binary files a/Build/QModManager.QMMHarmonyShimmer.dll and b/Build/QModManager.QMMHarmonyShimmer.dll differ diff --git a/Build/QModManager.QMMLoader.dll b/Build/QModManager.QMMLoader.dll index 6e042b54..d7a82eea 100644 Binary files a/Build/QModManager.QMMLoader.dll and b/Build/QModManager.QMMLoader.dll differ diff --git a/Build/QModManager.QModPluginGenerator.dll b/Build/QModManager.QModPluginGenerator.dll index c4149477..5b1f2efc 100644 Binary files a/Build/QModManager.QModPluginGenerator.dll and b/Build/QModManager.QModPluginGenerator.dll differ diff --git a/Build/QModManager.UnityAudioFixer.dll b/Build/QModManager.UnityAudioFixer.dll index 8c6fbe8f..06462c0a 100644 Binary files a/Build/QModManager.UnityAudioFixer.dll and b/Build/QModManager.UnityAudioFixer.dll differ diff --git a/Build/QModManager.exe b/Build/QModManager.exe index e848b380..ecd29a60 100644 Binary files a/Build/QModManager.exe and b/Build/QModManager.exe differ diff --git a/Build/QModManager_Setup.exe b/Build/QModManager_Setup.exe index 8a475956..a8962d61 100644 Binary files a/Build/QModManager_Setup.exe and b/Build/QModManager_Setup.exe differ diff --git a/Build/VortexPackage.zip b/Build/VortexPackage.zip new file mode 100644 index 00000000..29cfbba6 Binary files /dev/null and b/Build/VortexPackage.zip differ diff --git a/Data/latest-version.txt b/Data/latest-version.txt index fd5e525d..a7393ba1 100644 --- a/Data/latest-version.txt +++ b/Data/latest-version.txt @@ -1 +1 @@ -4.0.0.0 +4.0.1.0 diff --git a/Executable/Properties/AssemblyInfo.cs b/Executable/Properties/AssemblyInfo.cs index b6fa1447..96b88812 100644 --- a/Executable/Properties/AssemblyInfo.cs +++ b/Executable/Properties/AssemblyInfo.cs @@ -12,5 +12,5 @@ [assembly: ComVisible(false)] -[assembly: AssemblyVersion("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")] diff --git a/Installer/Properties/AssemblyInfo.cs b/Installer/Properties/AssemblyInfo.cs index ea27b063..1e0ea24d 100644 --- a/Installer/Properties/AssemblyInfo.cs +++ b/Installer/Properties/AssemblyInfo.cs @@ -14,5 +14,5 @@ [assembly: Guid("8c6c9a0b-80c4-43d2-89f2-749e6f09fdda")] -[assembly: AssemblyVersion("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")] diff --git a/Installer/QModsInstallerScript.iss b/Installer/QModsInstallerScript.iss index 06e2a5c2..6ae67104 100644 --- a/Installer/QModsInstallerScript.iss +++ b/Installer/QModsInstallerScript.iss @@ -5,7 +5,7 @@ #endif #define Name "QModManager" ; The name of the game will be added after it -#define Version "4.0.0" +#define Version "4.0.1" #define Author "QModManager" #define URL "https://github.com/QModManager/QModManager" #define SupportURL "https://discord.gg/UpWuWwq" @@ -70,6 +70,9 @@ Source: "..\Dependencies\cldb.dat"; DestDir: "{app}\BepInEx\patchers\QModManager Source: "..\Build\QModManager.exe"; DestDir: "{app}\BepInEx\patchers\QModManager"; Flags: ignoreversion; ; BepInEx Source: "..\Dependencies\BepInEx\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs replacesameversion sharedfile uninsnosharedfileprompt; + +[Dirs] +Name: "{app}\QMods" [UninstallRun] Filename: "{app}\BepInEx\patchers\QModManager\QModManager.exe"; Parameters: "-u"; diff --git a/QMMHarmonyShimmer/Properties/AssemblyInfo.cs b/QMMHarmonyShimmer/Properties/AssemblyInfo.cs index fe9dc15b..880520a0 100644 --- a/QMMHarmonyShimmer/Properties/AssemblyInfo.cs +++ b/QMMHarmonyShimmer/Properties/AssemblyInfo.cs @@ -33,6 +33,6 @@ // 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("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")] [assembly: NeutralResourcesLanguage("en")] diff --git a/QMMLoader/Properties/AssemblyInfo.cs b/QMMLoader/Properties/AssemblyInfo.cs index 96a2c1d8..a9964a29 100644 --- a/QMMLoader/Properties/AssemblyInfo.cs +++ b/QMMLoader/Properties/AssemblyInfo.cs @@ -33,6 +33,6 @@ // 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("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")] [assembly: NeutralResourcesLanguage("en")] diff --git a/QModManager/API/RequiredQMod.cs b/QModManager/API/RequiredQMod.cs index 62abd100..8339b334 100644 --- a/QModManager/API/RequiredQMod.cs +++ b/QModManager/API/RequiredQMod.cs @@ -1,24 +1,35 @@ namespace QModManager.API { using System; + using QModManager.Utility; /// /// Identifies a required mod and an optional minimum version. /// public class RequiredQMod { - internal RequiredQMod(string id) + internal static IVersionParser VersionParserService { get; set; } = new VersionParser(); + + private RequiredQMod(string id, bool requiresMinVersion, Version version) { this.Id = id; - this.RequiresMinimumVersion = false; - this.MinimumVersion = new Version(); + this.RequiresMinimumVersion = requiresMinVersion; + this.MinimumVersion = version; + } + + internal RequiredQMod(string id) + : this(id, false, VersionParserService.NoVersionParsed) + { } internal RequiredQMod(string id, Version minimumVersion) + : this(id, !VersionParserService.IsAllZeroVersion(minimumVersion), minimumVersion) + { + } + + internal RequiredQMod(string id, string minimumVersion) + : this(id, !string.IsNullOrEmpty(minimumVersion), VersionParserService.GetVersion(minimumVersion)) { - this.Id = id; - this.RequiresMinimumVersion = true; - this.MinimumVersion = minimumVersion; } /// diff --git a/QModManager/Patching/ManifestValidator.cs b/QModManager/Patching/ManifestValidator.cs index a7a734c7..752276e0 100644 --- a/QModManager/Patching/ManifestValidator.cs +++ b/QModManager/Patching/ManifestValidator.cs @@ -12,7 +12,7 @@ internal class ManifestValidator : IManifestValidator { - internal static readonly Regex VersionRegex = new Regex(@"(((\d+)\.?)+)"); + internal static IVersionParser VersionParserService { get; set; } = new VersionParser(); internal static readonly Dictionary ProhibitedModIDs = new Dictionary() { @@ -29,7 +29,7 @@ public void ValidateManifest(QMod mod) if (mod.PatchMethods.Count > 0) return; - Logger.Debug($"Validating mod in '{mod.SubDirectory}'"); + Logger.Debug($"Validating mod '{mod.Id}'"); if (string.IsNullOrEmpty(mod.Id) || string.IsNullOrEmpty(mod.DisplayName) || string.IsNullOrEmpty(mod.Author)) @@ -70,8 +70,7 @@ public void ValidateManifest(QMod mod) try { - if (Version.TryParse(mod.Version, out Version version)) - mod.ParsedVersion = version; + mod.ParsedVersion = VersionParserService.GetVersion(mod.Version); } catch (Exception vEx) { @@ -135,26 +134,27 @@ public void CheckRequiredMods(QMod mod) foreach (KeyValuePair item in mod.VersionDependencies) { string id = item.Key; - string cleanVersion = VersionRegex.Matches(item.Value)?[0]?.Value; + string versionString = item.Value; - if (string.IsNullOrEmpty(cleanVersion)) - { - requiredMods[id] = new RequiredQMod(id); - } - else if (Version.TryParse(cleanVersion, out Version version)) - { - requiredMods[id] = new RequiredQMod(id, version); - } - else if (!requiredMods.ContainsKey(id)) - { - requiredMods[id] = new RequiredQMod(id); - } + Version version = VersionParserService.GetVersion(versionString); + + requiredMods[id] = new RequiredQMod(id, version); mod.RequiredDependencies.Add(id); } } mod.RequiredMods = requiredMods.Values; + if (Logger.DebugLogsEnabled && requiredMods.Count > 0) + { + string msg = $"{mod.Id} has required mods: "; + foreach (var required in requiredMods.Values) + { + msg += $"{required.Id} "; + } + + Logger.Debug(msg); + } } internal ModStatus FindPatchMethods(QMod qMod) diff --git a/QModManager/Patching/QModFactory.cs b/QModManager/Patching/QModFactory.cs index 02ddc1cc..d43f5dce 100644 --- a/QModManager/Patching/QModFactory.cs +++ b/QModManager/Patching/QModFactory.cs @@ -141,6 +141,11 @@ private void ValidateDependencies(List modsToLoad, QMod mod) continue; } + if (dependencyQMod.HasDependencies) + { + ValidateDependencies(modsToLoad, dependencyQMod); + } + if (dependencyQMod.LoadedAssembly == null) { // Dependency hasn't been validated yet diff --git a/QModManager/Properties/AssemblyInfo.cs b/QModManager/Properties/AssemblyInfo.cs index da5a2b04..01b4002b 100644 --- a/QModManager/Properties/AssemblyInfo.cs +++ b/QModManager/Properties/AssemblyInfo.cs @@ -13,8 +13,8 @@ [assembly: ComVisible(false)] -[assembly: AssemblyVersion("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")] [assembly: InternalsVisibleTo("QMMTests")] [assembly: InternalsVisibleTo("QModManager")] diff --git a/QModManager/QModManager.csproj b/QModManager/QModManager.csproj index ec2522b9..221cab9e 100644 --- a/QModManager/QModManager.csproj +++ b/QModManager/QModManager.csproj @@ -183,6 +183,7 @@ + diff --git a/QModManager/Utility/Dialog.cs b/QModManager/Utility/Dialog.cs index 5277c327..23ada2d8 100644 --- a/QModManager/Utility/Dialog.cs +++ b/QModManager/Utility/Dialog.cs @@ -25,7 +25,11 @@ internal class Button internal static readonly Button Disabled = new Button(); internal static readonly Button SeeLog = new Button("See Log", () => { - string logPath = Application.consoleLogPath; + string gameSuffix = "Subnautica"; + if (Patcher.CurrentlyRunningGame == QModGame.BelowZero) + gameSuffix += "Zero"; + + string logPath = Path.Combine(Environment.CurrentDirectory, $"qmodmanager_log-{gameSuffix}.txt"); Logger.Debug($"Opening log file located in: \"{logPath}\""); diff --git a/QModManager/Utility/VersionParser.cs b/QModManager/Utility/VersionParser.cs new file mode 100644 index 00000000..d9249422 --- /dev/null +++ b/QModManager/Utility/VersionParser.cs @@ -0,0 +1,90 @@ +namespace QModManager.Utility +{ + using System; + using System.Text.RegularExpressions; + + /// + /// Defines a service for parsing version strings. + /// + public interface IVersionParser + { + /// + /// The default version zero used when no version could be parsed. + /// + Version NoVersionParsed { get; } + + /// + /// Returns a new based on the provided string value, with all 4 groups populated. + /// + /// The version string to parse. + /// A new with all empty groups populated with 0. + Version GetVersion(string versionString); + + /// + /// Checks if the provided version is all zeros. + /// + /// The version to check. + /// True if this matches version 0.0.0.0 + bool IsAllZeroVersion(Version version); + } + + /// + /// A service that handles parsing values into objects. + /// + public class VersionParser : IVersionParser + { + private static readonly Version VersionZeroSingleton = new Version(0, 0, 0, 0); + + /// + /// The regex used to sanitize incoming version strings. + /// ^(((\d+)\.?){0,3}\d+)$ + /// + public static readonly Regex VersionRegex = new Regex(@"^(((\d+)\.?){0,3}\d+)$"); + + /// + /// The default version zero used when no version could be parsed. + /// + public Version NoVersionParsed => new Version(0, 0, 0, 0); + + /// + /// Returns a new based on the provided string value, with all 4 groups populated. + /// + /// The version string to parse. This must match . + /// A new with all empty groups populated with 0. + /// + /// "2.8" will be parsed as "2.8.0.0" + /// + public Version GetVersion(string versionString) + { + if (!VersionRegex.IsMatch(versionString)) + { + return NoVersionParsed; + } + + versionString = VersionRegex.Matches(versionString)?[0]?.Value; + + int groups = versionString.Split('.').Length; + while (groups++ < 4) + { + versionString += ".0"; + } + + if (Version.TryParse(versionString, out Version parsedVersion)) + { + return parsedVersion; + } + + return NoVersionParsed; + } + + /// + /// Checks if the provided version is all zeros. + /// + /// The version to check. + /// True if this matches version 0.0.0.0 + public bool IsAllZeroVersion(Version version) + { + return version == VersionZeroSingleton; + } + } +} diff --git a/QModPluginEmulator/Properties/AssemblyInfo.cs b/QModPluginEmulator/Properties/AssemblyInfo.cs index d8f0c47b..9f02ec28 100644 --- a/QModPluginEmulator/Properties/AssemblyInfo.cs +++ b/QModPluginEmulator/Properties/AssemblyInfo.cs @@ -33,8 +33,8 @@ // 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("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")] [assembly: NeutralResourcesLanguage("en")] [assembly: InternalsVisibleTo("QModManager.QMMLoader")] diff --git a/QModPluginEmulator/QModPluginGenerator.cs b/QModPluginEmulator/QModPluginGenerator.cs index 897d97b8..a0c33ab9 100644 --- a/QModPluginEmulator/QModPluginGenerator.cs +++ b/QModPluginEmulator/QModPluginGenerator.cs @@ -5,6 +5,7 @@ using Mono.Cecil; using QModManager.API; using QModManager.Patching; +using QModManager.Utility; using System; using System.Collections.Generic; using System.IO; @@ -25,6 +26,8 @@ public static class QModPluginGenerator internal static Dictionary QModPluginInfos; internal static List InitialisedQModPlugins; + internal static IVersionParser VersionParserService { get; set; } = new VersionParser(); + [Obsolete("Should not be used!", true)] public static void Finish() { @@ -100,22 +103,9 @@ public static void TypeLoaderFindPluginTypesPostfix(ref Dictionary>(nameof(PluginInfo.Dependencies)).Value - = pluginInfo.Dependencies.AddItem(new BepInDependency(versionDependency.Key, new Version().ToString())); - } - else if (Version.TryParse(cleanVersion, out Version version)) - { - traverseablePluginInfo.Property>(nameof(PluginInfo.Dependencies)).Value - = pluginInfo.Dependencies.AddItem(new BepInDependency(versionDependency.Key, version.ToString())); - } - else - { - traverseablePluginInfo.Property>(nameof(PluginInfo.Dependencies)).Value - = pluginInfo.Dependencies.AddItem(new BepInDependency(versionDependency.Key, new Version().ToString())); - } + var version = VersionParserService.GetVersion(versionDependency.Value); + traverseablePluginInfo.Property>(nameof(PluginInfo.Dependencies)).Value + = pluginInfo.Dependencies.AddItem(new BepInDependency(versionDependency.Key, version.ToString())); } foreach (var id in mod.LoadAfter) { @@ -125,7 +115,7 @@ public static void TypeLoaderFindPluginTypesPostfix(ref Dictionary>(nameof(PluginInfo.Processes)).Value = new BepInProcess[0]; traverseablePluginInfo.Property>(nameof(PluginInfo.Incompatibilities)).Value = new BepInIncompatibility[0]; - traverseablePluginInfo.Property(nameof(PluginInfo.Metadata)).Value = new BepInPlugin(mod.Id, mod.DisplayName, mod.Version); + traverseablePluginInfo.Property(nameof(PluginInfo.Metadata)).Value = new BepInPlugin(mod.Id, mod.DisplayName, mod.ParsedVersion.ToString()); traverseablePluginInfo.Property("TypeName").Value = typeof(QModPlugin).FullName; traverseablePluginInfo.Property("TargettedBepInExVersion").Value = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(x => x.Name == "BepInEx").Version; diff --git a/Unit Tests/ManifestValidatorTests.cs b/Unit Tests/ManifestValidatorTests.cs index ca0eb365..1adf54ca 100644 --- a/Unit Tests/ManifestValidatorTests.cs +++ b/Unit Tests/ManifestValidatorTests.cs @@ -65,7 +65,7 @@ public void CheckRequiredMods_WhenRequiresVersion_GetExpectedRequiredMod() Assert.AreEqual(1, versionDependentMod.RequiredMods.Count()); - RequiredQMod expected = new RequiredQMod(requiredMod, new Version(requiredVersionString)); + RequiredQMod expected = new RequiredQMod(requiredMod, requiredVersionString); RequiredQMod actual = versionDependentMod.RequiredMods.First(); Assert.AreEqual(requiredMod, actual.Id); @@ -96,7 +96,7 @@ public void CheckRequiredMods_WhenRequirementsRedundant_GetExpectedRequiredMod() // Only a single entry is added to RequiredMods. Assert.AreEqual(1, versionDependentMod.RequiredMods.Count()); - RequiredQMod expected = new RequiredQMod(requiredMod, new Version(requiredVersionString)); + RequiredQMod expected = new RequiredQMod(requiredMod, requiredVersionString); RequiredQMod actual = versionDependentMod.RequiredMods.First(); Assert.AreEqual(requiredMod, actual.Id); @@ -133,7 +133,7 @@ public void CheckRequiredMods_WhenRequirementsMixed_GetExpectedRequiredMods() Assert.AreEqual(expectedA.RequiresMinimumVersion, actualA.RequiresMinimumVersion); Assert.AreEqual(expectedA.MinimumVersion, actualA.MinimumVersion); - RequiredQMod expectedB = new RequiredQMod(requiredModB, new Version(requiredVersionBString)); + RequiredQMod expectedB = new RequiredQMod(requiredModB, requiredVersionBString); RequiredQMod actualB = versionDependentMod.RequiredMods.ElementAt(1); Assert.AreEqual(requiredModB, actualB.Id); diff --git a/Unit Tests/QModFactoryTests.cs b/Unit Tests/QModFactoryTests.cs index 6e86fc02..3fc8a104 100644 --- a/Unit Tests/QModFactoryTests.cs +++ b/Unit Tests/QModFactoryTests.cs @@ -63,7 +63,8 @@ public void CreateModStatusList_WhenMissingVersionDependencies_StatusUpdates(str // Arange var factory = new QModFactory { - Validator = new DummyValidator() + Validator = new DummyValidator(), + BepInExPlugins = new List() }; var earlyErrors = new List @@ -77,7 +78,7 @@ public void CreateModStatusList_WhenMissingVersionDependencies_StatusUpdates(str Status = ModStatus.Success, RequiredMods = new List { - new RequiredQMod(missingOrOutdatedMod, new Version(1, 0, 2)) + new RequiredQMod(missingOrOutdatedMod, "1.0.2") } }; diff --git a/Unit Tests/RequiredQModTests.cs b/Unit Tests/RequiredQModTests.cs new file mode 100644 index 00000000..12e288e7 --- /dev/null +++ b/Unit Tests/RequiredQModTests.cs @@ -0,0 +1,31 @@ +namespace QMMTests +{ + using System; + using NUnit.Framework; + using QModManager.API; + + [TestFixture] + internal class RequiredQModTests + { + [TestCase("2.8")] + [TestCase("2.8.0")] + public void WhenVersionStringIsTrimmed_EqualsExplicitVersion(string trimmedVersionString) + { + var explicitVersion = new RequiredQMod("SMLHelper", "2.8.0.0"); + + var trimmedVersion = new RequiredQMod("SMLHelper", trimmedVersionString); + + Assert.AreEqual(explicitVersion.MinimumVersion, trimmedVersion.MinimumVersion); + } + + [Test] + public void WhenVersionStringIsMissing_EqualsVersionZero() + { + var required = new RequiredQMod("SomeMod"); + + var expectedVersion = new Version(0, 0, 0, 0); + + Assert.AreEqual(expectedVersion, required.MinimumVersion); + } + } +} diff --git a/Unit Tests/Unit Tests.csproj b/Unit Tests/Unit Tests.csproj index f5541850..c497a367 100644 --- a/Unit Tests/Unit Tests.csproj +++ b/Unit Tests/Unit Tests.csproj @@ -44,6 +44,9 @@ ..\Dependencies\Assembly-CSharp.dll + + ..\Dependencies\BepInEx\BepInEx\core\BepInEx.dll + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll @@ -64,6 +67,7 @@ + diff --git a/UnityAudioFixer/Properties/AssemblyInfo.cs b/UnityAudioFixer/Properties/AssemblyInfo.cs index ec37e3b7..7af8921e 100644 --- a/UnityAudioFixer/Properties/AssemblyInfo.cs +++ b/UnityAudioFixer/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // 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("4.0.0.0")] -[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyVersion("4.0.1.0")] +[assembly: AssemblyFileVersion("4.0.1.0")]