2 using System.Collections.Generic;
4 using System.Runtime.Serialization;
5 using Microsoft.Xna.Framework;
6 using Microsoft.Xna.Framework.Content;
7 using Microsoft.Xna.Framework.Graphics;
12 /// This class will be instantiated by the XNA Framework Content
13 /// Pipeline to read cfmap files from binary .xnb format.
15 public class MapReader : ContentTypeReader<Map>
17 #region Public Exceptions
20 /// This exception is thrown during the loading of a map if any
21 /// part of the map file is inconsistent with the expected format
24 public class ParserException : System.ApplicationException
26 public ParserException() {}
28 public ParserException(string message) :
31 public ParserException(string message, System.Exception inner) :
32 base(message, inner) {}
34 protected ParserException(SerializationInfo info, StreamingContext context) :
35 base(info, context) {}
41 #region Protected Methods
43 protected override Map Read(ContentReader input, Map existingInstance)
45 mInput = new LineReader(input);
47 return new Map(mMetadata, mGrid, mEntities);
53 #region Private Methods
55 void ReadSectionHeaders()
57 mMetadata = new Map.Metadata();
61 string line = mInput.ReadLine();
65 if (!IsLineSignificant(line)) break;
67 string section = Parse.IniSectionHeader(line);
70 if (section == "metadata")
72 line = ReadMetadataSection();
74 else if (section == "maptable")
76 line = ReadMapTableSection();
78 else if (section.Length == 1)
80 line = ReadEntitySection(section[0]);
84 throw new ParserException("Unexpected section on line " + mInput.LineNumber + ": " + section);
89 throw new ParserException("Unexpected text on line " + mInput.LineNumber + ": " + line);
95 string ReadMetadataSection()
99 string line = mInput.ReadLine();
100 if (!IsLineSignificant(line)) continue;
102 string[] pair = Parse.KeyValuePair(line);
105 if (pair[0] == "type")
107 Map.Type type = Parse.Constant<Map.Type>(pair[1]);
108 if (type != default(Map.Type))
110 mMetadata.Type = type;
114 throw new ParserException("Unexpected type on line " + mInput.LineNumber + ": " + pair[1]);
117 else if (pair[0] == "dimensions")
119 Point? dimensions = Parse.Coordinates(pair[1]);
120 if (dimensions != null)
122 mMetadata.GridWidth = dimensions.Value.X;
123 mMetadata.GridHeight = dimensions.Value.Y;
124 if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0)
126 throw new ParserException("Invalid dimensions on line " + mInput.LineNumber + ": " + pair[1]);
131 throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);
134 else if (pair[0] == "tileset")
136 string tileset = Parse.String(pair[1]);
139 mMetadata.Tileset = tileset;
143 throw new ParserException("Unexpected tileset on line " + mInput.LineNumber + ": " + pair[1]);
146 else if (pair[0] == "numplayers")
148 string[] list = Parse.List(pair[1]);
151 foreach (string atom in list)
153 int[] range = Parse.Range(atom);
156 for (int i = range[0]; i <= range[1]; i++)
158 mMetadata.NumPlayers.Add(i);
162 int? integer = Parse.Integer(atom);
165 mMetadata.NumPlayers.Add(integer.Value);
169 throw new ParserException("Unexpected atom on line " + mInput.LineNumber + ": " + atom);
171 if (mMetadata.NumPlayers.Count == 0)
173 throw new ParserException("No numbers given on line " + mInput.LineNumber + ": " + pair[1]);
178 throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);
181 else if (pair[0] == "author")
183 string author = Parse.String(pair[1]);
186 mMetadata.Author = author;
190 throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);
193 else if (pair[0] == "levelname")
195 string level = Parse.String(pair[1]);
198 mMetadata.Name = level;
202 throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);
207 throw new ParserException("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]);
219 string ReadMapTableSection()
221 if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0)
223 throw new ParserException("Unexpected section on line " + mInput.LineNumber +
224 ": You must define the map dimensions before this section.");
227 mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight];
230 for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++)
232 string line = mInput.ReadLine();
234 if (line.Length < mMetadata.GridWidth)
236 throw new ParserException("Unexpected EOL on line " + mInput.LineNumber +
237 ": The number of characters should match the width dimension (" + mMetadata.GridWidth + ").");
240 for (int x = 0; x < mMetadata.GridWidth; x++)
242 mGrid[x, y] = line[x];
246 if (y < mMetadata.GridHeight)
248 throw new ParserException("Unexpected EOF on line " + mInput.LineNumber +
249 ": The number of lines in this section should match the height dimension (" + mMetadata.GridHeight + ").");
255 string ReadEntitySection(char entity)
257 Dictionary<string, string> pairs = new Dictionary<string, string>();
258 mEntities[entity] = pairs;
262 string line = mInput.ReadLine();
264 string[] pair = Parse.KeyValuePair(line);
267 pairs[pair[0]] = pair[1];
279 bool IsLineSignificant(string line)
281 if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false;
288 #region Private Types
291 /// This private class wraps around ContentReader to make it more
292 /// convenient to use it as an input stream reader.
296 ContentReader mInput;
298 int mExpectedNumberOfLines;
300 public LineReader(ContentReader input)
303 mExpectedNumberOfLines = mInput.ReadInt32();
306 public string ReadLine()
309 return mInput.ReadString();
312 public int LineNumber { get { return mLineNumber; } }
314 public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } }
320 #region Private Variables
322 Map.Metadata mMetadata;
324 Dictionary<char, Dictionary<string, string>> mEntities = new Dictionary<char, Dictionary<string, string>>();