Ejemplo n.º 1
0
        public ConsoleAppShould()
        {
            _sw = new StringWriter();
            Console.SetOut(_sw);
            LineInformation lineInfoVictoria = new LineInformation();
            LineInformation lineInfoBakerloo = new LineInformation();
            LineInformation lineInfoCircle   = new LineInformation();

            lineInfoVictoria.LineName   = "Victoria";
            lineInfoVictoria.LineStatus = "Good Service";

            lineInfoBakerloo.LineName   = "Bakerloo";
            lineInfoBakerloo.LineStatus = "Good Service";

            lineInfoCircle.LineName   = "Circle";
            lineInfoCircle.LineStatus = "Good Service";


            _mockDataOfApi = new List <LineInformation>
            {
                lineInfoVictoria, lineInfoBakerloo, lineInfoCircle
            };

            _mockOfApi = new Mock <ITFLAPIClient>();
        }
Ejemplo n.º 2
0
		// --- visual-basic-compatible number parsing (int32) ---
		
		private static int GetInt32FromArgument(int argumentIndex, string argumentName, int defaultValue, LineInformation lineInfo) {
			int value;
			if (TryGetInt32FromArgument(argumentIndex, argumentName, int.MinValue, int.MaxValue, defaultValue, true, lineInfo, out value)) {
				return value;
			} else {
				return defaultValue;
			}
		}
Ejemplo n.º 3
0
		// --- validation functions ---
		
		private static void CheckMeshBuilderPresence(string command, LineInformation lineInfo, ref bool meshBuilderPresent) {
			if (!meshBuilderPresent) {
				string meshBuilder = lineInfo.FileInfo.IsB3d ? "[MeshBuilder]" : "CreateMeshBuilder";
				string text = meshBuilder + " is required before " + command + " can be used on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
				lineInfo.FileInfo.Logger.Add(text);
				meshBuilderPresent = true;
			}
		}
Ejemplo n.º 4
0
        /// <summary>
        /// Add entry to Lines Locations list
        /// </summary>
        /// <param name="iLine">Line Number</param>
        /// <param name="iAddress">Address related to that line</param>
        public void AddEntry(int iLine, int iAddress)
        {
            if (linesHash.ContainsKey(iLine))
            {
                throw new PanicException();
            }

            LineInformation newLine = new LineInformation(iLine, iAddress, -1);

            linesHash[iLine] = newLine;

            // Assuming the inserted line is a line with the largest index
            iMaxLine = iLine;
        }
Ejemplo n.º 5
0
		private static bool CheckCommand(string actual, string b3dCommand, string csvCommand, LineInformation lineInfo) {
			if (lineInfo.FileInfo.IsB3d) {
				if (!string.Equals(actual, b3dCommand, StringComparison.OrdinalIgnoreCase)) {
					string text = actual + " should be " + b3dCommand + " in B3D files on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
					return false;
				}
			} else {
				if (!string.Equals(actual, csvCommand, StringComparison.OrdinalIgnoreCase)) {
					string text = actual + " should be " + csvCommand + " in CSV files on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
					return false;
				}
			}
			return true;
		}
Ejemplo n.º 6
0
		private static bool CheckArgumentCount(string command, int actual, int lower, int upper, LineInformation lineInfo) {
			if (actual < lower | actual > upper) {
				if (lower == upper) {
					string text = command + " expects " + lower.ToString() + (lower == 1 ? " argument" : " arguments") + " but " + actual.ToString() + (actual == 1 ? " argument was" : " arguments were") + " found on line " + (lineInfo.LineNumber).ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
				} else if (lower == 0) {
					string text = command + " expects at most " + upper.ToString() + (upper == 1 ? " argument" : " arguments") + " but " + actual.ToString() + (actual == 1 ? " argument was" : " arguments were") + " found on line " + (lineInfo.LineNumber).ToString() + " in \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
				} else if (upper == int.MaxValue) {
					string text = command + " expects at least " + lower.ToString() + (lower == 1 ? " argument" : " arguments") + " but " + actual.ToString() + (actual == 1 ? " argument was" : " arguments were") + " found on line " + (lineInfo.LineNumber).ToString() + " in \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
				} else {
					string text = command + " expects between " + lower.ToString() + " and " + upper.ToString() + " arguments but " + actual.ToString() + (actual == 1 ? " argument was" : " arguments were") + " found on line " + (lineInfo.LineNumber).ToString() + " in \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
				}
				return false;
			} else {
				return true;
			}
		}
Ejemplo n.º 7
0
		private static bool TryGetInt32FromArgument(int argumentIndex, string argumentName, int minValue, int maxValue, int defaultValue, bool allowMissingArgument, LineInformation lineInfo, out int value) {
			if (argumentIndex >= 0 && argumentIndex < lineInfo.ArgumentCount && lineInfo.Arguments[argumentIndex].Length != 0) {
				int success = TryParseInt32Vb(lineInfo.Arguments[argumentIndex], out value);
				if (success == 0) {
					string text = "\"" + lineInfo.Arguments[argumentIndex] + "\" as argument " + (argumentIndex + 1).ToString() + " (" + argumentName + ") to command " + lineInfo.Command + " is not a valid integer on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
					value = defaultValue;
					return false;
				}
				if (lineInfo.FileInfo.StrictParsing & success == -1) {
					string text = "\"" + lineInfo.Arguments[argumentIndex] + "\" as argument " + (argumentIndex + 1).ToString() + " (" + argumentName + ") to command " + lineInfo.Command + " is a malformed integer and will be treated as " + value.ToString() + " on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
					lineInfo.FileInfo.Logger.Add(text);
				}
				if (value < minValue | value > maxValue) {
					if (minValue == int.MinValue) {
						value = maxValue;
						string text = "\"" + lineInfo.Arguments[argumentIndex] + "\" as argument " + (argumentIndex + 1).ToString() + " (" + argumentName + ") to command " + lineInfo.Command + " must be at most " + maxValue.ToString() + " on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
						lineInfo.FileInfo.Logger.Add(text);
					} else if (maxValue == int.MaxValue) {
						value = minValue;
						string text = "\"" + lineInfo.Arguments[argumentIndex] + "\" as argument " + (argumentIndex + 1).ToString() + " (" + argumentName + ") to command " + lineInfo.Command + " must be at least " + minValue.ToString() + " on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
						lineInfo.FileInfo.Logger.Add(text);
					} else {
						value = value < minValue ? minValue : maxValue;
						string text = "\"" + lineInfo.Arguments[argumentIndex] + "\" as argument " + (argumentIndex + 1).ToString() + " (" + argumentName + ") to command " + lineInfo.Command + " must be between " + minValue.ToString() + " and " + maxValue.ToString() + " on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
						lineInfo.FileInfo.Logger.Add(text);
					}
					return false;
				}
				return true;
			} else if (allowMissingArgument) {
				value = defaultValue;
				return true;
			} else {
				string text = "Argument " + (argumentIndex + 1).ToString() + " (" + argumentName + ") to command " + lineInfo.Command + " is missing on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
				lineInfo.FileInfo.Logger.Add(text);
				value = defaultValue;
				return false;
			}
		}
Ejemplo n.º 8
0
        public SourceInformation Source(TreeNode node)
        {
            SortedDictionary <int, float> metricOnLine;
            var sourceLocation = this.GetSourceLocation(node.BackingNode, node.BackingNode.Name, out metricOnLine);

            if (sourceLocation != null)
            {
                var       sourceFile = sourceLocation.SourceFile;
                FieldInfo fi         = typeof(SourceFile).GetField("m_symbolModule", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fi != null)
                {
                    PropertyInfo pi = typeof(SymbolModule).GetProperty("PdbForSourceServer", BindingFlags.NonPublic | BindingFlags.Instance);
                    if (pi != null)
                    {
                        MethodInfo mi = typeof(SymbolModule).GetMethod("GetSrcSrvStream", BindingFlags.NonPublic | BindingFlags.Instance);
                        if (mi != null)
                        {
                            string srcStream = mi.Invoke(pi.GetValue(fi.GetValue(sourceFile)), null) as string;
                            if (srcStream != null)
                            {
                                string[] lines = srcStream.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
                                foreach (var line in lines)
                                {
                                    // TFS DevDiv support
                                    if (line.StartsWith(sourceFile.BuildTimeFilePath))
                                    {
                                        var split = line.Split('*');

                                        var sourceInfo = new SourceInformation
                                        {
                                            Url     = "http://vstfdevdiv:8080/DevDiv2/DevDiv/_versionControl/changeset/" + split[3].Trim() + "#path=" + split[2].Trim() + "&_a=contents",
                                            Type    = "Url",
                                            Summary = metricOnLine.Select(m => new LineInformation {
                                                Metric = m.Value, LineNumber = m.Key + 1, Line = string.Empty
                                            }).ToList()
                                        };

                                        return(sourceInfo);
                                    }
                                }

                                // support for source depot?
                                return(null);
                            }
                        }
                    }
                }

                // Source Database Format
                {
                    var sdbFiles = Directory.GetFiles(this.reader.SourcePath, "*.sdb", SearchOption.AllDirectories);
                    SourceInformation sourceInfo = null;

                    foreach (var sdbFile in sdbFiles)
                    {
                        using (ZipArchive archive = ZipFile.OpenRead(sdbFile))
                        {
                            foreach (ZipArchiveEntry entry in archive.Entries)
                            {
                                if (sourceFile.BuildTimeFilePath.EndsWith(entry.FullName, StringComparison.OrdinalIgnoreCase))
                                {
                                    int i = 0;

                                    var summaryLineMetrics = new List <LineInformation>();
                                    var linemetrics        = new List <LineInformation>();
                                    sourceInfo         = new SourceInformation();
                                    sourceInfo.Lines   = linemetrics;
                                    sourceInfo.Summary = summaryLineMetrics;
                                    sourceInfo.Type    = "Lines";

                                    using (var sr = new StreamReader(entry.Open()))
                                    {
                                        string line;
                                        while ((line = sr.ReadLine()) != null)
                                        {
                                            i++;
                                            float value = 0;
                                            if (metricOnLine.TryGetValue(i, out value))
                                            {
                                                var lineInfo = new LineInformation {
                                                    LineNumber = i, Metric = value, Line = line
                                                };
                                                summaryLineMetrics.Add(lineInfo);
                                                linemetrics.Add(lineInfo);
                                            }
                                            else
                                            {
                                                linemetrics.Add(new LineInformation {
                                                    LineNumber = i, Metric = value, Line = line
                                                });
                                            }
                                        }
                                    }

                                    break;
                                }
                            }
                        }
                    }

                    return(sourceInfo);
                }
            }

            return(null);
        }
Ejemplo n.º 9
0
		// --- visual-basic-compatible number parsing (double) ---
		
		private static double GetDoubleFromArgument(int argumentIndex, string argumentName, double defaultValue, LineInformation lineInfo) {
			double value;
			if (TryGetDoubleFromArgument(argumentIndex, argumentName, double.MinValue, double.MaxValue, defaultValue, true, lineInfo, out value)) {
				return value;
			} else {
				return defaultValue;
			}
		}
Ejemplo n.º 10
0
		public override Mesh Load(string file, MeshDecodingOptions options) {
			const bool strictParsing = true;
			
			// --- preparations ---
			bool isB3d = file.EndsWith(".b3d", StringComparison.OrdinalIgnoreCase);
			char commandArgumentSeparator = isB3d ? ' ' : ',';
			FileInformation fileInfo = new FileInformation(isB3d, file, strictParsing, options.Logger);
			List<MeshBuilder> meshBuilders = new List<MeshBuilder>();
			MeshBuilder currentMeshBuilder = new MeshBuilder();
			bool meshBuilderPresent = false;
			
			// --- line by line ---
			string[] lines = File.ReadAllLines(file, Encoding.UTF8);
			for (int l = 0; l < lines.Length; l++) {
				string line = lines[l];
				
				// --- trim comments and whitespace ---
				int semicolon = line.IndexOf(';');
				if (semicolon >= 0) {
					line = line.Substring(0, semicolon).Trim();
				} else {
					line = line.Trim();
				}
				if (line.Length != 0) {
					
					// --- split into command and argument sequence ---
					string command;
					string argumentSequence;
					int separator = line.IndexOf(commandArgumentSeparator);
					if (separator >= 0) {
						command = line.Substring(0, separator).TrimEnd();
						argumentSequence = line.Substring(separator + 1).TrimStart();
					} else {
						command = line;
						argumentSequence = string.Empty;
					}
					
					// --- handle malformed commands ---
					if (command.Length != 0) {
						if (isB3d) {
							int comma = command.IndexOf(',');
							if (comma >= 0) {
								argumentSequence = command.Substring(comma + 1).TrimStart() + ',' + argumentSequence;
								command = command.Substring(0, comma).TrimEnd();
								if (strictParsing) {
									string text = "Command \"" + command + "\" must be separated from its arguments by a space in B3D files on line " + (l + 1).ToString() + " in file \"" + file + "\".";
									options.Logger.Add(text);
								}
							}
						} else {
							int space = command.IndexOf(' ');
							if (space >= 0) {
								argumentSequence = command.Substring(space + 1).TrimStart() + ',' + argumentSequence;
								command = command.Substring(0, space).TrimEnd();
								if (strictParsing) {
									string text = "Command \"" + command + "\" must be separated from its arguments by a comma in CSV files on line " + (l + 1).ToString() + " in file \"" + file + "\".";
									options.Logger.Add(text);
								}
							}
						}
					}
					if (command.Length != 0) {
						
						// --- split into arguments and trim empty arguments from the end ---
						string[] arguments = argumentSequence.Split(',');
						for (int a = 0; a < arguments.Length; a++) {
							arguments[a] = arguments[a].Trim();
						}
						int argumentCount = 0;
						for (int a = arguments.Length - 1; a >= 0; a--) {
							if (arguments[a].Length != 0) {
								argumentCount = a + 1;
								break;
							}
						}

						// --- process commands ---
						LineInformation lineInfo = new LineInformation(command, arguments, argumentCount, l + 1, fileInfo);
						string commandLower = command.ToLowerInvariant();
						switch (commandLower) {
							case "[meshbuilder]":
							case "createmeshbuilder":
								{
									if (strictParsing) {
										CheckCommand(command, "[MeshBuilder]", "CreateMeshBuilder", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 0, lineInfo);
									}
									meshBuilders.Add(currentMeshBuilder);
									currentMeshBuilder = new MeshBuilder();
									meshBuilderPresent = true;
								}
								break;
							case "vertex":
							case "addvertex":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "Vertex", "AddVertex", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 8, lineInfo);
									}
									double positionX = GetDoubleFromArgument(0, "positionX", 0.0, lineInfo);
									double positionY = GetDoubleFromArgument(1, "positionY", 0.0, lineInfo);
									double positionZ = GetDoubleFromArgument(2, "positionZ", 0.0, lineInfo);
									double normalX   = GetDoubleFromArgument(3, "normalX",   0.0, lineInfo);
									double normalY   = GetDoubleFromArgument(4, "normalY",   0.0, lineInfo);
									double normalZ   = GetDoubleFromArgument(5, "normalZ",   0.0, lineInfo);
									double textureX  = GetDoubleFromArgument(6, "textureX",  0.0, lineInfo);
									double textureY  = GetDoubleFromArgument(7, "textureY",  0.0, lineInfo);
									MeshBuilderVertex vertex = new MeshBuilderVertex(new Vector3d(positionX, positionY, positionZ), new Vector3d(normalX, normalY, normalZ), new Vector2d(textureX, textureY));
									currentMeshBuilder.Vertices.Add(vertex);
								}
								break;
							case "face":
							case "face2":
							case "addface":
							case "addface2":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
									}
									bool face2 = commandLower[commandLower.Length - 1] == '2';
									if (strictParsing) {
										string suffix = face2 ? "2" : string.Empty;
										CheckCommand(command, "Face" + suffix, "AddFace" + suffix, lineInfo);
									}
									bool success = CheckArgumentCount(command, argumentCount, 3, int.MaxValue, lineInfo);
									int[] indices = new int[argumentCount];
									for (int i = 0; i < argumentCount; i++) {
										if (!TryGetInt32FromArgument(i, "vertex" + (i + 1).ToString(), 0, currentMeshBuilder.Vertices.Count - 1, -1, false, lineInfo, out indices[i])) {
											success = false;
										}
									}
									if (success) {
										currentMeshBuilder.Faces.Add(new MeshBuilderFace(0, indices, lineInfo.LineNumber));
										if (face2) {
											Array.Reverse(indices);
											MeshBuilderFace face = new MeshBuilderFace(0, indices, lineInfo.LineNumber);
											face.Normals = MeshBuilderFaceNormals.Inverse;
											currentMeshBuilder.Faces.Add(face);
										}
									}
								}
								break;
							case "cube":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckArgumentCount(command, argumentCount, 0, 3, lineInfo);
									}
									double x = GetDoubleFromArgument(0, "x", 0.0, lineInfo);
									double y = GetDoubleFromArgument(1, "y", x,   lineInfo);
									double z = GetDoubleFromArgument(2, "z", x,   lineInfo);
									currentMeshBuilder.AddCube(x, y, z, lineInfo.LineNumber);
								}
								break;
							case "cylinder":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckArgumentCount(command, argumentCount, 1, 4, lineInfo);
									}
									int numVertices;
									bool success = TryGetInt32FromArgument(0, "numVertices", 2, int.MaxValue, 0, false, lineInfo, out numVertices);
									double upper  = GetDoubleFromArgument(1, "upper",  0.0, lineInfo);
									double lower  = GetDoubleFromArgument(2, "lower",  0.0, lineInfo);
									double height = GetDoubleFromArgument(3, "height", 0.0, lineInfo);
									if (success) {
										currentMeshBuilder.AddCylinder(numVertices, upper, lower, height, lineInfo.LineNumber);
									}
								}
								break;
							case "[texture]":
							case "generatenormals":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "[Texture]", "GenerateNormals", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 0, lineInfo);
									}
								}
								break;
							case "translate":
							case "translateall":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckArgumentCount(command, argumentCount, 0, 3, lineInfo);
									}
									double x = GetDoubleFromArgument(0, "x", 0.0, lineInfo);
									double y = GetDoubleFromArgument(1, "y", 0.0, lineInfo);
									double z = GetDoubleFromArgument(2, "z", 0.0, lineInfo);
									Vector3d offset = new Vector3d(x, y, z);
									currentMeshBuilder.Translate(offset);
									if (commandLower == "translateall") {
										foreach (MeshBuilder meshBuilder in meshBuilders) {
											meshBuilder.Translate(offset);
										}
									}
								}
								break;
							case "scale":
							case "scaleall":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckArgumentCount(command, argumentCount, 0, 3, lineInfo);
									}
									double x = GetDoubleFromArgument(0, "x", 1.0, lineInfo);
									double y = GetDoubleFromArgument(1, "y", 1.0, lineInfo);
									double z = GetDoubleFromArgument(2, "z", 1.0, lineInfo);
									Vector3d factor = new Vector3d(x, y, z);
									currentMeshBuilder.Scale(factor);
									if (commandLower == "scaleall") {
										foreach (MeshBuilder meshBuilder in meshBuilders) {
											meshBuilder.Scale(factor);
										}
									}
								}
								break;
							case "rotate":
							case "rotateall":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckArgumentCount(command, argumentCount, 0, 4, lineInfo);
									}
									double x = GetDoubleFromArgument(0, "x",     0.0, lineInfo);
									double y = GetDoubleFromArgument(1, "y",     0.0, lineInfo);
									double z = GetDoubleFromArgument(2, "z",     0.0, lineInfo);
									double a = GetDoubleFromArgument(3, "angle", 0.0, lineInfo) * (Math.PI / 180.0);
									Vector3d direction = Vector3d.Normalize(new Vector3d(x, y, z));
									if (direction.IsZero()) {
										direction = Vector3d.Right;
									}
									Vector2d angle = new Vector2d(Math.Cos(a), Math.Sin(a));
									currentMeshBuilder.Rotate(direction, angle);
									if (commandLower == "rotateall") {
										foreach (MeshBuilder meshBuilder in meshBuilders) {
											meshBuilder.Rotate(direction, angle);
										}
									}
								}
								break;
							case "shear":
							case "shearall":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckArgumentCount(command, argumentCount, 0, 7, lineInfo);
									}
									double dx = GetDoubleFromArgument(0, "dx",    0.0, lineInfo);
									double dy = GetDoubleFromArgument(1, "dy",    0.0, lineInfo);
									double dz = GetDoubleFromArgument(2, "dz",    0.0, lineInfo);
									double sx = GetDoubleFromArgument(3, "sx",    0.0, lineInfo);
									double sy = GetDoubleFromArgument(4, "sy",    0.0, lineInfo);
									double sz = GetDoubleFromArgument(5, "sz",    0.0, lineInfo);
									double r  = GetDoubleFromArgument(6, "ratio", 0.0, lineInfo);
									Vector3d direction = Vector3d.Normalize(new Vector3d(dx, dy, dz));
									Vector3d shift     = Vector3d.Normalize(new Vector3d(sx, sy, sz));
									currentMeshBuilder.Shear(direction, shift, r);
									if (commandLower == "shearall") {
										foreach (MeshBuilder meshBuilder in meshBuilders) {
											meshBuilder.Shear(direction, shift, r);
										}
									}
								}
								break;
							case "color":
							case "setcolor":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "Color", "SetColor", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 4, lineInfo);
									}
									double r, g, b, a;
									TryGetDoubleFromArgument(0, "red",   0.0, 255.0, 255.0, true, lineInfo, out r);
									TryGetDoubleFromArgument(1, "green", 0.0, 255.0, 255.0, true, lineInfo, out g);
									TryGetDoubleFromArgument(2, "blue",  0.0, 255.0, 255.0, true, lineInfo, out b);
									TryGetDoubleFromArgument(3, "alpha", 0.0, 255.0, 255.0, true, lineInfo, out a);
									Vector3f color = new Vector3f((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f);
									for (int i = 0; i < currentMeshBuilder.Faces.Count; i++) {
										currentMeshBuilder.Faces[i].ReflectiveColor = color;
										currentMeshBuilder.Faces[i].Alpha = (float)a / 255.0f;
									}
								}
								break;
							case "emissivecolor":
							case "setemissivecolor":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "EmissiveColor", "SetEmissiveColor", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 3, lineInfo);
									}
									double r, g, b;
									TryGetDoubleFromArgument(0, "red",   0.0, 255.0, 0.0, true, lineInfo, out r);
									TryGetDoubleFromArgument(1, "green", 0.0, 255.0, 0.0, true, lineInfo, out g);
									TryGetDoubleFromArgument(2, "blue",  0.0, 255.0, 0.0, true, lineInfo, out b);
									Vector3f color = new Vector3f((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f);
									for (int i = 0; i < currentMeshBuilder.Faces.Count; i++) {
										currentMeshBuilder.Faces[i].EmissiveColor = color;
									}
								}
								break;
							case "blendmode":
							case "setblendmode":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "BlendMode", "SetBlendMode", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 3, lineInfo);
									}
									string blendMode = argumentCount >= 1 && arguments[0].Length != 0 ? arguments[0] : "normal";
									double glowHalfDistance;
									TryGetDoubleFromArgument(1, "glowHalfDistance", 0.0, double.MaxValue, 0.0, true, lineInfo, out glowHalfDistance);
									string glowAttenuationMode = argumentCount >= 3 && arguments[2].Length != 0 ? arguments[2] : "divideexponent2";
									switch (blendMode.ToLowerInvariant()) {
										case "normal":
											break;
										case "additive":
											break;
										default:
											string text = "\"" + blendMode + "\" as argument 1 to command " + lineInfo.Command + " is not a valid blend mode on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
											options.Logger.Add(text);
											break;
									}
									switch (glowAttenuationMode.ToLowerInvariant()) {
										case "divideexponent2":
											break;
										case "divideexponent4":
											break;
										default:
											string text = "\"" + blendMode + "\" as argument 3 to command " + lineInfo.Command + " is not a valid glow attenuation mode on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
											options.Logger.Add(text);
											break;
									}
									
									// TODO: Not implemented.
									
								}
								break;
							case "load":
							case "loadtexture":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "Load", "LoadTexture", lineInfo);
										CheckArgumentCount(command, argumentCount, 1, 2, lineInfo);
									}
									string daytimeTexture = null;
									string nighttimeTexture = null;
									if (argumentCount >= 1 && arguments[0].Length != 0) {
										daytimeTexture = arguments[0];
									}
									if (argumentCount >= 2 && arguments[1].Length != 0) {
										nighttimeTexture = arguments[1];
									}
									if (daytimeTexture != null) {
										currentMeshBuilder.DaytimeTexture = Platform.CombineFile(Path.GetDirectoryName(file), daytimeTexture);
									}
									
									// TODO: Nighttime textures.
									
								}
								break;
							case "transparent":
							case "setdecaltransparentcolor":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "Transparent", "SetDecalTransparentColor", lineInfo);
										CheckArgumentCount(command, argumentCount, 0, 3, lineInfo);
									}
									int r, g, b;
									TryGetInt32FromArgument(0, "red",   0, 255, 0, true, lineInfo, out r);
									TryGetInt32FromArgument(1, "green", 0, 255, 0, true, lineInfo, out g);
									TryGetInt32FromArgument(2, "blue",  0, 255, 0, true, lineInfo, out b);
									currentMeshBuilder.TransparentColor = new Vector3b((byte)r, (byte)g, (byte)b);
									currentMeshBuilder.TransparentColorUsed = true;
								}
								break;
							case "coordinates":
							case "settexturecoordinates":
								{
									if (strictParsing) {
										CheckMeshBuilderPresence(command, lineInfo, ref meshBuilderPresent);
										CheckCommand(command, "Coordinates", "SetTextureCoordinates", lineInfo);
										CheckArgumentCount(command, argumentCount, 1, 3, lineInfo);
									}
									int v = -1;
									bool success;
									if (currentMeshBuilder.Vertices.Count == 0) {
										string text = lineInfo.Command + " cannot be used at this point because no vertices were defined in the mesh builder on line " + lineInfo.LineNumber.ToString() + " in file \"" + lineInfo.FileInfo.File + "\".";
										options.Logger.Add(text);
										success = false;
									} else {
										success = TryGetInt32FromArgument(0, "vertex", 0, currentMeshBuilder.Vertices.Count - 1, 0, false, lineInfo, out v);
									}
									double x = GetDoubleFromArgument(1, "x", 0.0, lineInfo);
									double y = GetDoubleFromArgument(2, "y", 0.0, lineInfo);
									if (success) {
										currentMeshBuilder.Vertices[v].Texture = new Vector2d(x, y);
									}
								}
								break;
							default:
								{
									string text = "Unsupported command \"" + command + "\" found on line " + (l + 1).ToString() + " in file \"" + file + "\".";
									options.Logger.Add(text);
								}
								break;
						}
					}
				}
			}
			meshBuilders.Add(currentMeshBuilder);
			currentMeshBuilder = null;
			
			// --- assemble mesh ---
			Dictionary<Vertex, int> hashedVertices = new Dictionary<Vertex, int>();
			Dictionary<Material, int> hashedMaterials = new Dictionary<Material, int>();
			List<Vertex> vertices = new List<Vertex>();
			List<Material> materials = new List<Material>();
			List<Face> faces = new List<Face>();
			foreach (MeshBuilder meshBuilder in meshBuilders) {
				foreach (MeshBuilderFace meshBuilderFace in meshBuilder.Faces) {
					
					// --- generate normal ---
					Vector3d normal;
					{
						Vector3d a = meshBuilder.Vertices[meshBuilderFace.Vertices[0]].Position;
						Vector3d b = meshBuilder.Vertices[meshBuilderFace.Vertices[1]].Position;
						Vector3d c = meshBuilder.Vertices[meshBuilderFace.Vertices[2]].Position;
						normal = Vector3d.Normalize(Vector3d.Cross(b - a, c - a));
						if (normal.IsZero()) {
							normal = Vector3d.Up;
						}
					}
					
					// --- vertices ---
					Vertex[] v = new Vertex[meshBuilderFace.Vertices.Length];
					for (int i = 0; i < meshBuilderFace.Vertices.Length; i++) {
						MeshBuilderVertex meshBuilderVertex = meshBuilder.Vertices[meshBuilderFace.Vertices[i]];
						v[i] = new Vertex((Vector3f)meshBuilderVertex.Position, (Vector3f)meshBuilderVertex.Normal, (Vector2f)meshBuilderVertex.Texture);
						if (meshBuilderFace.Normals == MeshBuilderFaceNormals.Generate || meshBuilderVertex.Normal.IsZero()) {
							v[i].Normal = (Vector3f)normal;
						} else if (meshBuilderFace.Normals == MeshBuilderFaceNormals.Inverse) {
							v[i].Normal = (Vector3f)(-meshBuilderVertex.Normal);
						}
					}
					int[] vertexIndices = new int[v.Length];
					for (int i = 0; i < v.Length; i++) {
						if (!hashedVertices.TryGetValue(v[i], out vertexIndices[i])) {
							vertexIndices[i] = vertices.Count;
							vertices.Add(v[i]);
							hashedVertices.Add(v[i], vertexIndices[i]);
						}
					}
					
					// --- material ---
					Material material = new Material();
					material.Alpha = meshBuilderFace.Alpha;
					material.EmissiveColor = meshBuilderFace.EmissiveColor;
					material.ReflectiveColor = meshBuilderFace.ReflectiveColor;
					if (meshBuilder.DaytimeTexture != null) {
						if (meshBuilder.TransparentColorUsed) {
							material.Texture = options.Manager.Add(meshBuilder.DaytimeTexture, meshBuilder.TransparentColor);
						} else {
							material.Texture = options.Manager.Add(meshBuilder.DaytimeTexture);
						}
					}
					int materialIndex;
					if (!hashedMaterials.TryGetValue(material, out materialIndex)) {
						materialIndex = materials.Count;
						materials.Add(material);
						hashedMaterials.Add(material, materialIndex);
					}
					
					// --- face ---
					Face face = new Face(vertexIndices, materialIndex, meshBuilderFace.LineNumber);
					faces.Add(face);
					
				}
			}
			return new Mesh(vertices.ToArray(), materials.ToArray(), faces.ToArray());
		}