public static PatchList SortAndExtractPatches(UrlDir databaseRoot, IEnumerable <string> modList, IPatchProgress progress)
        {
            PatchList list = new PatchList(modList);

            // Have to convert to an array because we will be removing patches
            foreach (UrlDir.UrlConfig url in databaseRoot.AllConfigs.ToArray())
            {
                try
                {
                    if (!url.type.IsBracketBalanced())
                    {
                        progress.Error(url, "Error - node name does not have balanced brackets (or a space - if so replace with ?):\n" + url.SafeUrl());
                        url.parent.configs.Remove(url);
                        continue;
                    }

                    Command command = CommandParser.Parse(url.type, out _);;

                    Match firstMatch  = firstRegex.Match(url.type);
                    Match finalMatch  = finalRegex.Match(url.type);
                    Match beforeMatch = beforeRegex.Match(url.type);
                    Match forMatch    = forRegex.Match(url.type);
                    Match afterMatch  = afterRegex.Match(url.type);

                    int matchCount = 0;

                    if (firstMatch.Success)
                    {
                        matchCount++;
                    }
                    if (finalMatch.Success)
                    {
                        matchCount++;
                    }
                    if (beforeMatch.Success)
                    {
                        matchCount++;
                    }
                    if (forMatch.Success)
                    {
                        matchCount++;
                    }
                    if (afterMatch.Success)
                    {
                        matchCount++;
                    }

                    if (firstMatch.NextMatch().Success)
                    {
                        matchCount++;
                    }
                    if (finalMatch.NextMatch().Success)
                    {
                        matchCount++;
                    }
                    if (beforeMatch.NextMatch().Success)
                    {
                        matchCount++;
                    }
                    if (forMatch.NextMatch().Success)
                    {
                        matchCount++;
                    }
                    if (afterMatch.NextMatch().Success)
                    {
                        matchCount++;
                    }

                    bool error = false;

                    if (command == Command.Insert && matchCount > 0)
                    {
                        progress.Error(url, $"Error - pass specifier detected on an insert node (not a patch): {url.SafeUrl()}");
                        error = true;
                    }
                    else if (command == Command.Replace)
                    {
                        progress.Error(url, $"Error - replace command (%) is not valid on a root node: {url.SafeUrl()}");
                        error = true;
                    }
                    else if (command == Command.Create)
                    {
                        progress.Error(url, $"Error - create command (&) is not valid on a root node: {url.SafeUrl()}");
                        error = true;
                    }
                    else if (command == Command.Rename)
                    {
                        progress.Error(url, $"Error - rename command (|) is not valid on a root node: {url.SafeUrl()}");
                        error = true;
                    }
                    else if (command == Command.Paste)
                    {
                        progress.Error(url, $"Error - paste command (#) is not valid on a root node: {url.SafeUrl()}");
                        error = true;
                    }
                    else if (command == Command.Special)
                    {
                        progress.Error(url, $"Error - special command (*) is not valid on a root node: {url.SafeUrl()}");
                        error = true;
                    }

                    if (matchCount > 1)
                    {
                        progress.Error(url, $"Error - more than one pass specifier on a node: {url.SafeUrl()}");
                        error = true;
                    }
                    if (beforeMatch.Success && !beforeMatch.Groups[1].Success)
                    {
                        progress.Error(url, "Error - malformed :BEFORE patch specifier detected: " + url.SafeUrl());
                        error = true;
                    }
                    if (forMatch.Success && !forMatch.Groups[1].Success)
                    {
                        progress.Error(url, "Error - malformed :FOR patch specifier detected: " + url.SafeUrl());
                        error = true;
                    }
                    if (afterMatch.Success && !afterMatch.Groups[1].Success)
                    {
                        progress.Error(url, "Error - malformed :AFTER patch specifier detected: " + url.SafeUrl());
                        error = true;
                    }
                    if (error)
                    {
                        url.parent.configs.Remove(url);
                        continue;
                    }

                    if (command == Command.Insert)
                    {
                        continue;
                    }

                    url.parent.configs.Remove(url);

                    Match theMatch = null;
                    List <UrlDir.UrlConfig> thePass = null;
                    bool modNotFound = false;

                    if (firstMatch.Success)
                    {
                        theMatch = firstMatch;
                        thePass  = list.firstPatches;
                    }
                    else if (finalMatch.Success)
                    {
                        theMatch = finalMatch;
                        thePass  = list.finalPatches;
                    }
                    else if (beforeMatch.Success)
                    {
                        if (CheckMod(beforeMatch, list.modPasses, out string theMod))
                        {
                            theMatch = beforeMatch;
                            thePass  = list.modPasses[theMod].beforePatches;
                        }
                        else
                        {
                            modNotFound = true;
                            progress.NeedsUnsatisfiedBefore(url);
                        }
                    }
                    else if (forMatch.Success)
                    {
                        if (CheckMod(forMatch, list.modPasses, out string theMod))
                        {
                            theMatch = forMatch;
                            thePass  = list.modPasses[theMod].forPatches;
                        }
                        else
                        {
                            modNotFound = true;
                            progress.NeedsUnsatisfiedFor(url);
                        }
                    }
                    else if (afterMatch.Success)
                    {
                        if (CheckMod(afterMatch, list.modPasses, out string theMod))
                        {
                            theMatch = afterMatch;
                            thePass  = list.modPasses[theMod].afterPatches;
                        }
                        else
                        {
                            modNotFound = true;
                            progress.NeedsUnsatisfiedAfter(url);
                        }
                    }
                    else
                    {
                        thePass = list.legacyPatches;
                    }

                    if (modNotFound)
                    {
                        continue;
                    }

                    UrlDir.UrlConfig newUrl = url;
                    if (theMatch != null)
                    {
                        string     newName = url.type.Remove(theMatch.Index, theMatch.Length);
                        ConfigNode newNode = new ConfigNode(newName)
                        {
                            id = url.config.id
                        };
                        newNode.ShallowCopyFrom(url.config);
                        newUrl = new UrlDir.UrlConfig(url.parent, newNode);
                    }

                    thePass.Add(newUrl);
                    progress.PatchAdded();
                }
                catch (Exception e)
                {
                    progress.Exception(url, $"Exception while parsing pass for config: {url.SafeUrl()}", e);
                }
            }

            return(list);
        }