internal void UnloadMod(Mod mod) { if (!ModUtilities.IsOnMainMenu) { throw new InvalidOperationException("Must be in main menu to unload a mod!"); } PatchRegistry.UndoPatchesForMod(mod); foreach (var key in new List <string>(ComponentRegistry.Registry.Keys)) { var value = ComponentRegistry.Registry[key]; if (value.Mod == mod) { ComponentRegistry.Registry.Remove(key); } } _Mods.Remove(mod); }
private void LoadConfiguration(string configFilePath) { XPathNavigator navigator = GetNavigator(configFilePath); string overallTimeoutString = navigator.SelectSingleNode(OverallTimeoutValueXPath).Value; if (string.IsNullOrEmpty(overallTimeoutString) || (!int.TryParse(overallTimeoutString, out _overallTimeoutSeconds))) { LogUtils.WriteTrace(DateTime.UtcNow, "OverallTimeout value is not defined or is not a valid integer value. OverallTimeout = " + (string.IsNullOrEmpty(overallTimeoutString) ? "null" : overallTimeoutString)); throw new InvalidOperationException( "OverallTimeout value is not defined or is not a valid integer value. OverallTimeout = " + (string.IsNullOrEmpty(overallTimeoutString) ? "null" : overallTimeoutString)); } if (_overallTimeoutSeconds < 0) { LogUtils.WriteTrace(DateTime.UtcNow, "OverallTimeout value cannot be less than 0. OverallTimeout = " + overallTimeoutString); throw new InvalidOperationException( "OverallTimeout value cannot be less than 0. OverallTimeout = " + overallTimeoutString); } // Loop through all patches XPathNodeIterator nodeIterator = navigator.Select(PatchXPath); while (nodeIterator.MoveNext()) { Patch patch = new Patch(); // Read name if (nodeIterator.Current.SelectSingleNode("@name") == null) { LogUtils.WriteTrace(DateTime.UtcNow, "name attribute needed for Patch element"); throw new InvalidOperationException("name attribute needed for Patch element"); } patch.Name = nodeIterator.Current.SelectSingleNode("@name").Value.Trim(); if (string.IsNullOrEmpty(patch.Name)) { LogUtils.WriteTrace(DateTime.UtcNow, "name attribute can't be empty for Patch element"); throw new InvalidOperationException("name attribute can't be empty for Patch element"); } if (_patchMap.ContainsKey(patch.Name)) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "Patch '{0}' is defined in multiple places.", patch.Name)); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Patch '{0}' is defined in multiple places.", patch.Name)); } // Read OS versions the patch applies to and check whether it is applicable to current OS version if (nodeIterator.Current.SelectSingleNode("@appliesToOsVersion") != null) { string osVersionAppliesTo = string.Empty; try { bool invalidVersionFormat = false; osVersionAppliesTo = nodeIterator.Current.SelectSingleNode("@appliesToOsVersion").Value.Trim(); Version currentOsVersion = Environment.OSVersion.Version; char[] versionDelimiter = new char[] { '.' }; string[] versionComponents = osVersionAppliesTo.Split(versionDelimiter); string[] currentOsVersionComponents = currentOsVersion.ToString().Split(versionDelimiter); // OS version the patch applies to should have four components: major.minor.build.revision if (versionComponents.Length != 4) { invalidVersionFormat = true; } else { // Each version component should be an integer or a wildcard character '*' foreach (string t in versionComponents) { int versionComponent; if (t.Equals("*", StringComparison.OrdinalIgnoreCase) || int.TryParse(t, out versionComponent)) { continue; } invalidVersionFormat = true; break; } } if (invalidVersionFormat) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format( @"PathUtil: Invalid version format for OS version patch {0} applies to : {1}; the patch will be installed", patch.Name, osVersionAppliesTo)); } else { bool skipPatch = false; for (int i = 0; i < Math.Min(versionComponents.Length, currentOsVersionComponents.Length); i++) { if (!versionComponents[i].Equals("*", StringComparison.OrdinalIgnoreCase) && !versionComponents[i].Equals(currentOsVersionComponents[i], StringComparison.OrdinalIgnoreCase)) { skipPatch = true; } } // If the patch is not applicable to current OS version, skip it if (skipPatch) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format( @"PatchUtil: Patch {0} will be skipped since it is only applicable to OS version {1} while current OS version is {2}", patch.Name, osVersionAppliesTo, currentOsVersion)); continue; } } } catch (Exception e) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format( @"PathUtil: Exception occurred when checking whether patch {0} (applicable to OS version {1}) is applicable to current OS version, skip version checking and continue installing : {2}", patch.Name, osVersionAppliesTo, e)); } } // Read command line. if (nodeIterator.Current.SelectSingleNode(CommandLineXPath) == null) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "Command value attribute needed for Patch '{0}'.", patch.Name)); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Command value attribute needed for Patch '{0}'.", patch.Name)); } patch.CommandLine = nodeIterator.Current.SelectSingleNode(CommandLineXPath).Value.Trim(); if (string.IsNullOrEmpty(patch.CommandLine)) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "Command value can't be empty for Patch '{0}'.", patch.Name)); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Command value can't be empty for Patch '{0}'.", patch.Name)); } // Read log path (optional element). patch.LogFolderPath = null; if (nodeIterator.Current.SelectSingleNode(LogFolderPathXPath) != null) { string logFolderPath = nodeIterator.Current.SelectSingleNode(LogFolderPathXPath).Value.Trim(); if (!string.IsNullOrEmpty(logFolderPath)) { patch.LogFolderPath = logFolderPath; } } // Read registry settings List <PatchRegistry> registrySettings = new List <PatchRegistry>(); XPathNodeIterator iterator = nodeIterator.Current.Select(PatchRegistryXPath); while (iterator.MoveNext()) { string keyName = ReadPatchRegistryAttribute(iterator, patch.Name, RegistryKeyNameAttribute, true); string valueName = ReadPatchRegistryAttribute(iterator, patch.Name, RegistryValueNameAttribute, true); string expectedValue = ReadPatchRegistryAttribute(iterator, patch.Name, RegistryExpectedValueAttribute, false); PatchRegistry registrySetting = new PatchRegistry { RegistryKeyName = keyName, RegistryValueName = valueName, ExpectedValue = expectedValue, }; registrySettings.Add(registrySetting); } // We need at least one registry setting to confirm the patch is installed. if (!registrySettings.Any()) { LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "There is no registry setting found for Patch '{0}'", patch.Name)); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "There is no registry setting found for Patch '{0}'", patch.Name)); } patch.RegistrySettings = registrySettings; _patchMap.Add(patch.Name, patch); } }