void ParsePatch(Entity entity, ref int line, List<string> lines)
        {
            //++line;
            string defLine = lines[line].ToLower();
            Patch patch = new Patch(defLine.Contains("mesh"));
            bool tFormat = defLine.Contains("mesh") || defLine.Contains("curve");

            // Skip the definition line, skip the {
            line += 2;

            if (!tFormat)
            {
                // Get the texture name
                patch.TextureName = lines[line].Trim();
                ++line;
                // Get the patch dimensions
                string[] sizeTerms = lines[line].Split(SPLIT_CHARS, StringSplitOptions.RemoveEmptyEntries);
                patch.Rows = int.Parse(sizeTerms[0]);
                patch.Columns = int.Parse(sizeTerms[1]);
                ++line;
            }
            else
            {
                for (; line < lines.Count; ++line)
                {
                    if (lines[line].Contains("lightmap") || lines[line].Contains("content"))
                        continue;
                    else
                    {
                        if (lines[line].Contains("("))
                        {
                            break;
                        }
                        string[] terms = lines[line].Trim().Split(' ');
                        if (terms.Length == 1)
                            patch.TextureName = terms[0];
                        else if (terms.Length == 4)
                        {
                            patch.Rows = int.Parse(terms[0]);
                            patch.Columns = int.Parse(terms[1]);
                        }
                    }
                }
            }

            int jumpCt = 0;
            for (; line < lines.Count; ++line)
            {
                string text = lines[line];
                string shortLine = lines[line].Trim();

                if (shortLine.Contains("}"))
                {
                    ++jumpCt;
                    if (jumpCt == 2) // Leave after second } encounters { patchDef { "content" } } <--
                        break;
                    continue;
                }
                else
                {
                    string[] terms = shortLine.Split(PATCH_SPLIT, StringSplitOptions.RemoveEmptyEntries);
                    if (tFormat)
                    {
                        if (shortLine.Contains('v'))
                        {
                            PatchVert vert = new PatchVert();
                            vert.Position = Face.ParseVec3(terms, 0);
                            // If we contain a color
                            if (shortLine.Contains('c'))
                            {
                                vert.Color = Face.ParseVec3(terms, 3);
                                vert.Alpha = float.Parse(terms[6]);
                                vert.UV = Face.ParseVec2(terms, 7);
                            }
                            else
                                vert.UV = Face.ParseVec2(terms, 3);
                            patch.FlatVertices.Add(vert);
                        }
                    }
                    else if (terms.Length > 0)
                    {
                        // Id format
                        if (shortLine.Count(c => c == '(') > 0)
                        {
                            List<PatchVert> rowVerts = new List<PatchVert>();
                            // ( (x y z u v) (x y z u v) (x y z u v) )
                            for (int i = 0; i < patch.Columns; ++i)
                            {
                                PatchVert vert = new PatchVert();
                                int startSub = i * 5;
                                // (x y z u v)
                                vert.Position = Face.ParseVec3(terms, startSub);
                                vert.UV = Face.ParseVec2(terms, startSub + 3);
                                rowVerts.Add(vert);
                                patch.FlatVertices.Add(vert);
                            }
                            patch.PatchVertices.Add(rowVerts);
                        }
                    }
                }
            }
            entity.Patches.Add(patch);
        }
        public override BrushMap ReadMap(string inputFile, Dictionary<string, string> settings)
        {
            Console.WriteLine("----------------------------------------------------------------");
            Console.WriteLine(" Quake Reader");
            Console.WriteLine("----------------------------------------------------------------");

            Vector3 scale = new Vector3(1,1,1);

            if (settings.ContainsKey("scale"))
                scale = QMapConverter.Util.MathExt.Vector3FromString(settings["scale"]);

            BrushMap map = new BrushMap();
            List<String> fileData = new List<String>(System.IO.File.ReadLines(inputFile));
            int braceDepth = 0;
            int lineNum = 0;

            Entity currentEntity = null;
            Brush currentBrush = null;
            Patch currentPatch = null;

            using (QMapConverter.Util.ConsoleProgress prog = new QMapConverter.Util.ConsoleProgress("Loading Map", fileData.Count))
            foreach (string line in fileData)
            {
                ++lineNum;
                prog.Increment();
                prog.Write();
                string shortLine = line.Trim();

                // Skip over comment lines
                if (shortLine.StartsWith("//"))
                    continue;

                if (shortLine.CompareTo("{") == 0)
                {
                    ++braceDepth;
                    if (braceDepth == 1)
                        currentEntity = new Entity();
                    else if (braceDepth == 2)
                        currentBrush = new Brush();
                }
                else if (shortLine.CompareTo("}") == 0)
                {
                    --braceDepth;
                    if (braceDepth == 0 && currentEntity != null)
                    {
                        map.Entities.Add(currentEntity);
                        currentEntity = null;
                    }
                    else if (braceDepth == 1 && (currentBrush != null || currentPatch != null) && currentEntity != null)
                    {
                        if (currentBrush != null)
                        {
                            currentEntity.Brushes.Add(currentBrush);
                            currentBrush = null;
                        }
                        else if (currentPatch != null)
                        {
                            currentEntity.Patches.Add(currentPatch);
                            currentPatch = null;
                        }
                    }
                }
                else
                {
                    if (currentBrush != null)
                    {
                        if (shortLine.ToLower().Equals("curve") || shortLine.ToLower().Contains("patchdef") || shortLine.ToLower().Equals("mesh"))
                        {
                            // Cancel the brush, it's either a patch or a mesh
                            currentBrush = null;
                            currentPatch = new Patch(shortLine.ToLower().Equals("mesh"));
                            continue;
                        }
                        //try
                        {
                            if (!shortLine.Contains('('))
                                continue;
                            Face face = new Face(shortLine, scale);
                            currentBrush.Faces.Add(face);
                        }
                        //catch (Exception ex)
                        //{
                        //    Console.WriteLine(String.Format("ERROR: line {0}", lineNum));
                        //    Console.WriteLine(ex.Message);
                        //    Environment.FailFast("Map Parse Error");
                        //    return null;
                        //}
                    }
                    else if (currentPatch != null)
                    {
                        int termCt = shortLine.Split(SPLIT_CHARS, StringSplitOptions.RemoveEmptyEntries).Count();
                        if (currentPatch.Columns == -1 && termCt == 5 || termCt == 4)
                        {
                            string[] terms = shortLine.Split(SPLIT_CHARS, StringSplitOptions.RemoveEmptyEntries);
                            currentPatch.Rows = int.Parse(terms[0]);
                            currentPatch.Columns = int.Parse(terms[1]);
                        }
                        else if (termCt == 1 && currentPatch.TextureName.Length == 0)
                        {
                            currentPatch.TextureName = shortLine;
                        }
                        else
                        {
                            // Characters on which to split
                            char[] PATCH_SPLIT = { ' ', 'v', 't', 'c', '(', ')' };
                            string[] terms = shortLine.Split(PATCH_SPLIT, StringSplitOptions.RemoveEmptyEntries);
                            if (terms.Length > 0)
                            {
                                // Id format
                                if (shortLine.Count(c => c == '(') > 0)
                                {
                                    List<PatchVert> rowVerts = new List<PatchVert>();
                                    // ( (x y z u v) (x y z u v) (x y z u v) )
                                    for (int i = 0; i < currentPatch.Columns; ++i)
                                    {
                                        PatchVert vert = new PatchVert();
                                        int startSub = i * 5;
                                        // (x y z u v)
                                        vert.Position = Face.ParseVec3(terms, startSub);
                                        vert.UV = Face.ParseVec2(terms, startSub + 3);
                                        rowVerts.Add(vert);
                                        currentPatch.FlatVertices.Add(vert);
                                    }
                                    currentPatch.PatchVertices.Add(rowVerts);
                                }
                                // Treyarch format
                                else if (shortLine.Contains('v'))
                                {
                                    PatchVert vert = new PatchVert();
                                    vert.Position = Face.ParseVec3(terms, 0);
                                    // If we contain a color
                                    if (shortLine.Contains('c'))
                                    {
                                        vert.Color = Face.ParseVec3(terms, 3);
                                        vert.Alpha = float.Parse(terms[6]);
                                        vert.UV = Face.ParseVec2(terms, 7);
                                    }
                                    else
                                        vert.UV = Face.ParseVec2(terms, 3);
                                    currentPatch.FlatVertices.Add(vert);
                                }
                            }
                        }
                    }
                    else if (currentEntity != null)
                    {
                        // Brush Primitives - GtkRadiant format
                        if (shortLine.ToLower().Equals("brushdef"))
                        {
                            currentEntity.Kind = EntityType.Brush;
                        }
                        // Patch
                        else if (shortLine.ToLower().Equals("patchdef2") || shortLine.ToLower().Equals("curve"))
                        {
                            currentEntity.Kind = EntityType.Patch;
                        }
                        else if (shortLine.ToLower().Equals("mesh"))
                        {
                            currentEntity.Kind = EntityType.Mesh;
                        }
                        else
                        {
                            Regex regex = new Regex(@"\w+|""[\w\s]*""");
                            List<string> terms = new List<string>(shortLine.Split('"').ToList());
                            for (int i = 0; i < terms.Count; ++i)
                            {
                                if (String.IsNullOrWhiteSpace(terms[i]))
                                {
                                    terms.RemoveAt(i);
                                    --i;
                                }
                            }

                            // Insert KvP properties, if a Value is non-existent, insert 'true' as it's likely a flag
                            if (terms.Count == 2)
                                currentEntity.Properties[terms[0].Replace("\"", "")] = terms[1].Replace("\"", "");
                            else
                                currentEntity.Properties[terms[0].Replace("\"", "")] = "true";
                        }
                    }
                }
            }
            return map;
        }