public override Map Parse() { string oldCwd = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(Path.GetDirectoryName(AbsolutePath ?? oldCwd)); OnProgressUpdated(this, new ProgressEventArgs(0, "Cleaning up raw input...")); // FIXME: Only strip leading tabs! Or just tabs not within a key or value? string stripped = Raw .Replace("\r", String.Empty) .Replace("\n", String.Empty) .Replace("\t", String.Empty); // Modern Quake sourceports allow for transparency in textures, // indicated by an open curly brace in the texture name. Any open // curly brace that's actually a delimiter will be followed by an // open parenthesis or one or more whitespace characters. Texture // names will instead have ordinary text characters after the brace. List <string> split = stripped.SplitAndKeepDelimiters( $"{OpenDelimiter} ", $"{OpenDelimiter}(", $"{OpenDelimiter}\"", CloseDelimiter).ToList(); split.RemoveAll(s => s.Trim().Length == 0 || s.Trim() == "\""); // The regex patterns above include capture groups to retain some // delimiters in the output, but they end up in the same item in the // list resulting from Split. This ugly loop fixes that. int item = 0; while (item < split.Count) { if (split[item] == "{(") { split[item] = "{"; split[item + 1] = $"({split[item + 1]}"; item++; } else if (split[item] == "{\"") { split[item] = "{"; split[item + 1] = $"\"{split[item + 1]}"; item++; } else { item++; } } OnProgressUpdated(this, new ProgressEventArgs(25, "Creating map objects...")); int i = 0; while (i < split.Count) { int firstBraceIndex = split.IndexOf("{", i); var block = new QuakeBlock(split, firstBraceIndex, Definitions); MapObjects.Add(new QuakeMapObject(block, Definitions, Textures)); i = firstBraceIndex + block.RawLength; } // First build the final worldspawn. int worldspawnIndex = MapObjects.FindIndex(o => o.Definition.ClassName == "worldspawn"); MapObject worldspawn = MapObjects[worldspawnIndex]; foreach (MapObject mo in AllObjects) { Aabb += mo.Aabb; } FixUp(); Directory.SetCurrentDirectory(oldCwd); return(this); }
private void Parse(List <string> rawBlock) { int i = 0; while (i < rawBlock.Count) { string item = rawBlock[i]; if (item.Contains(KeyValDelimiter)) { List <KeyValuePair <string, string> > rawKeyVals = ExtractKeyVals(item); string classname = rawKeyVals.Find(s => s.Key == "classname").Value; foreach (KeyValuePair <string, string> rawKeyVal in rawKeyVals) { Option newOption; if (Definitions.ContainsKey(classname) && Definitions[classname].KeyValsTemplate.ContainsKey(rawKeyVal.Key)) { newOption = new Option(Definitions[classname].KeyValsTemplate[rawKeyVal.Key]); } else { newOption = new Option(); } newOption.Value = rawKeyVal.Value; if (!KeyVals.ContainsKey(rawKeyVal.Key)) { KeyVals.Add(rawKeyVal.Key, newOption); } else { KeyVals[rawKeyVal.Key] = newOption; } } ++i; } // An open delimiter that's the first item in the raw block is just the // start of the top level block; any others indicate children. else if (item == OpenDelimiter && i != 0) { int childOpenBraceIndex = i + 1; var childBlock = new QuakeBlock(rawBlock, childOpenBraceIndex, Definitions); Children.Add(childBlock); i = childBlock.RawStartIndex + childBlock.RawLength; } else if (item.StartsWith("(", StringComparison.OrdinalIgnoreCase)) { string solid = item; int j = i + 1; while (rawBlock[j] != CloseDelimiter) { solid += rawBlock[j]; j++; } Solids.Add(new Solid(ExtractSides(solid))); i += j; } else { i++; } } }