private static void CheckNeeds(NodeStack stack, PatchContext context, IEnumerable <string> mods, UrlDir gameData) { ConfigNode original = stack.value; for (int i = 0; i < original.values.Count; ++i) { ConfigNode.Value val = original.values[i]; string valname = val.name; try { if (CheckNeeds(ref valname, mods, gameData)) { val.name = valname; } else { original.values.Remove(val); i--; context.progress.NeedsUnsatisfiedValue(context.patchUrl, stack, val.name); } } catch (ArgumentOutOfRangeException e) { context.progress.Exception("ArgumentOutOfRangeException in CheckNeeds for value \"" + val.name + "\"", e); throw; } catch (Exception e) { context.progress.Exception("General Exception in CheckNeeds for value \"" + val.name + "\"", e); throw; } } for (int i = 0; i < original.nodes.Count; ++i) { ConfigNode node = original.nodes[i]; string nodeName = node.name; if (nodeName == null) { context.progress.Error(context.patchUrl, "Error - Node in file " + context.patchUrl.SafeUrl() + " subnode: " + stack.GetPath() + " has config.name == null"); } try { if (CheckNeeds(ref nodeName, mods, gameData)) { node.name = nodeName; CheckNeeds(stack.Push(node), context, mods, gameData); } else { original.nodes.Remove(node); i--; context.progress.NeedsUnsatisfiedNode(context.patchUrl, stack.Push(node)); } } catch (ArgumentOutOfRangeException e) { context.progress.Exception("ArgumentOutOfRangeException in CheckNeeds for node \"" + node.name + "\"", e); throw; } catch (Exception e) { context.progress.Exception("General Exception " + e.GetType().Name + " for node \"" + node.name + "\"", e); throw; } } }
private void ApplyPatches(string stage, IEnumerable <UrlDir.UrlConfig> patches) { logger.Info(stage + " pass"); Activity = "ModuleManager " + stage; foreach (UrlDir.UrlConfig mod in patches) { try { string name = mod.type.RemoveWS(); Command cmd = CommandParser.Parse(name, out string tmp); if (cmd == Command.Insert) { logger.Warning("Warning - Encountered insert node that should not exist at this stage: " + mod.SafeUrl()); continue; } else if (cmd != Command.Edit && cmd != Command.Copy && cmd != Command.Delete) { logger.Warning("Invalid command encountered on a patch: " + mod.SafeUrl()); continue; } string upperName = name.ToUpper(); PatchContext context = new PatchContext(mod, databaseRoot, logger, progress); char[] sep = { '[', ']' }; string condition = ""; if (upperName.Contains(":HAS[")) { int start = upperName.IndexOf(":HAS["); condition = name.Substring(start + 5, name.LastIndexOf(']') - start - 5); name = name.Substring(0, start); } string[] splits = name.Split(sep, 3); string[] patterns = splits.Length > 1 ? splits[1].Split(',', '|') : null; string type = splits[0].Substring(1); bool loop = mod.config.HasNode("MM_PATCH_LOOP"); foreach (UrlDir.UrlFile file in allConfigFiles) { if (cmd == Command.Edit) { foreach (UrlDir.UrlConfig url in file.configs) { if (!IsMatch(url, type, patterns, condition)) { continue; } if (loop) { logger.Info("Looping on " + mod.SafeUrl() + " to " + url.SafeUrl()); } do { progress.ApplyingUpdate(url, mod); url.config = MMPatchLoader.ModifyNode(new NodeStack(url.config), mod.config, context); } while (loop && IsMatch(url, type, patterns, condition)); if (loop) { url.config.RemoveNodes("MM_PATCH_LOOP"); } } } else if (cmd == Command.Copy) { // Avoid checking the new configs we are creating int count = file.configs.Count; for (int i = 0; i < count; i++) { UrlDir.UrlConfig url = file.configs[i]; if (!IsMatch(url, type, patterns, condition)) { continue; } ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(url.config), mod.config, context); if (url.config.HasValue("name") && url.config.GetValue("name") == clone.GetValue("name")) { progress.Error(mod, $"Error - when applying copy {mod.SafeUrl()} to {url.SafeUrl()} - the copy needs to have a different name than the parent (use @name = xxx)"); } else { progress.ApplyingCopy(url, mod); file.AddConfig(clone); } } } else if (cmd == Command.Delete) { int i = 0; while (i < file.configs.Count) { UrlDir.UrlConfig url = file.configs[i]; if (IsMatch(url, type, patterns, condition)) { progress.ApplyingDelete(url, mod); file.configs.RemoveAt(i); } else { i++; } } } else { throw new NotImplementedException("This code should not be reachable"); } } progress.PatchApplied(); } catch (Exception e) { progress.Exception(mod, "Exception while processing node : " + mod.SafeUrl(), e); try { logger.Error("Processed node was\n" + mod.PrettyPrint()); } catch (Exception ex2) { logger.Exception("Exception while attempting to print a node", ex2); } } } }
public static void CheckNeeds(UrlDir gameDatabaseRoot, IEnumerable <string> mods, IPatchProgress progress, IBasicLogger logger) { UrlDir gameData = gameDatabaseRoot.children.First(dir => dir.type == UrlDir.DirectoryType.GameData && dir.name == ""); foreach (UrlDir.UrlConfig mod in gameDatabaseRoot.AllConfigs.ToArray()) { UrlDir.UrlConfig currentMod = mod; try { if (mod.config.name == null) { progress.Error(currentMod, "Error - Node in file " + currentMod.parent.url + " subnode: " + currentMod.type + " has config.name == null"); } UrlDir.UrlConfig newMod; if (currentMod.type.IndexOf(":NEEDS[", StringComparison.OrdinalIgnoreCase) >= 0) { string type = currentMod.type; if (CheckNeeds(ref type, mods, gameData)) { ConfigNode copy = new ConfigNode(type); copy.ShallowCopyFrom(currentMod.config); int index = mod.parent.configs.IndexOf(currentMod); newMod = new UrlDir.UrlConfig(currentMod.parent, copy); mod.parent.configs[index] = newMod; } else { progress.NeedsUnsatisfiedRoot(currentMod); mod.parent.configs.Remove(currentMod); continue; } } else { newMod = currentMod; } // Recursively check the contents PatchContext context = new PatchContext(newMod, gameDatabaseRoot, logger, progress); CheckNeeds(new NodeStack(newMod.config), context, mods, gameData); } catch (Exception ex) { try { mod.parent.configs.Remove(currentMod); } catch (Exception ex2) { logger.Exception("Exception while attempting to ensure config removed", ex2); } try { progress.Exception(mod, "Exception while checking needs on root node :\n" + mod.PrettyPrint(), ex); } catch (Exception ex2) { progress.Exception("Exception while attempting to log an exception", ex2); } } } }