using System; using System.Collections.Generic; using System.Text; using Substrate.Core; using Substrate.Nbt; using System.IO; namespace Substrate.Data { /// /// Represents the complete data of a Map item. /// public class Map : INbtObject, ICopyable { private static SchemaNodeCompound _schema = new SchemaNodeCompound() { new SchemaNodeCompound("data") { new SchemaNodeScaler("scale", TagType.TAG_BYTE), new SchemaNodeScaler("dimension", TagType.TAG_BYTE), new SchemaNodeScaler("height", TagType.TAG_SHORT), new SchemaNodeScaler("width", TagType.TAG_SHORT), new SchemaNodeScaler("xCenter", TagType.TAG_INT), new SchemaNodeScaler("zCenter", TagType.TAG_INT), new SchemaNodeArray("colors"), }, }; private TagNodeCompound _source; private NbtWorld _world; private int _id; private byte _scale; private byte _dimension; private short _height; private short _width; private int _x; private int _z; private byte[] _colors; /// /// Creates a new default object. /// public Map () { _scale = 3; _dimension = 0; _height = 128; _width = 128; _colors = new byte[_width * _height]; } /// /// Creates a new object with copied data. /// /// A to copy data from. protected Map (Map p) { _world = p._world; _id = p._id; _scale = p._scale; _dimension = p._dimension; _height = p._height; _width = p._width; _x = p._x; _z = p._z; _colors = new byte[_width * _height]; if (p._colors != null) { p._colors.CopyTo(_colors, 0); } } /// /// Gets or sets the id value associated with this map. /// public int Id { get { return _id; } set { if (_id < 0 || _id >= 65536) { throw new ArgumentOutOfRangeException("value", value, "Map Ids must be in the range [0, 65535]."); } _id = value; } } /// /// Gets or sets the scale of the map. Acceptable values are 0 (1:1) to 4 (1:16). /// public int Scale { get { return _scale; } set { _scale = (byte)value; } } /// /// Gets or sets the (World) Dimension of the map. /// public int Dimension { get { return _dimension; } set { _dimension = (byte)value; } } /// /// Gets or sets the height of the map. /// /// If the new height dimension is different, the map's color data will be reset. /// Thrown if the new height value is zero or negative. public int Height { get { return _height; } set { if (value <= 0) { throw new ArgumentOutOfRangeException("value", "Height must be a positive number"); } if (_height != value) { _height = (short)value; _colors = new byte[_width * _height]; } } } /// /// Gets or sets the width of the map. /// /// If the new width dimension is different, the map's color data will be reset. /// Thrown if the new width value is zero or negative. public int Width { get { return _width; } set { if (value <= 0) { throw new ArgumentOutOfRangeException("value", "Width must be a positive number"); } if (_width != value) { _width = (short)value; _colors = new byte[_width * _height]; } } } /// /// Gets or sets the global X-coordinate that this map is centered on, in blocks. /// public int X { get { return _x; } set { _x = value; } } /// /// Gets or sets the global Z-coordinate that this map is centered on, in blocks. /// public int Z { get { return _z; } set { _z = value; } } /// /// Gets the raw byte array of the map's color index values. /// public byte[] Colors { get { return _colors; } } /// /// Gets or sets a color index value within the map's internal colors bitmap. /// /// The X-coordinate to get or set. /// The Z-coordinate to get or set. /// Thrown when the X- or Z-coordinates exceed the map dimensions. public byte this[int x, int z] { get { if (x < 0 || x >= _width || z < 0 || z >= _height) { throw new IndexOutOfRangeException(); } return _colors[x + _width * z]; } set { if (x < 0 || x >= _width || z < 0 || z >= _height) { throw new IndexOutOfRangeException(); } _colors[x + _width * z] = value; } } /// /// Saves a object to disk as a standard compressed NBT stream. /// /// True if the map was saved; false otherwise. /// Thrown when an error is encountered writing out the level. public bool Save () { if (_world == null) { return false; } try { string path = Path.Combine(_world.Path, _world.DataDirectory); NBTFile nf = new NBTFile(Path.Combine(path, "map_" + _id + ".dat")); Stream zipstr = nf.GetDataOutputStream(); if (zipstr == null) { NbtIOException nex = new NbtIOException("Failed to initialize compressed NBT stream for output"); nex.Data["Map"] = this; throw nex; } new NbtTree(BuildTree() as TagNodeCompound).WriteTo(zipstr); zipstr.Close(); return true; } catch (Exception ex) { Exception mex = new Exception("Could not save map file.", ex); // TODO: Exception Type mex.Data["Map"] = this; throw mex; } } #region INBTObject Members /// /// Attempt to load a Map subtree into the without validation. /// /// The root node of a Map subtree. /// The returns itself on success, or null if the tree was unparsable. public virtual Map LoadTree (TagNode tree) { TagNodeCompound dtree = tree as TagNodeCompound; if (dtree == null) { return null; } TagNodeCompound ctree = dtree["data"].ToTagCompound(); _scale = ctree["scale"].ToTagByte(); _dimension = ctree["dimension"].ToTagByte(); _height = ctree["height"].ToTagShort(); _width = ctree["width"].ToTagShort(); _x = ctree["xCenter"].ToTagInt(); _z = ctree["zCenter"].ToTagInt(); _colors = ctree["colors"].ToTagByteArray(); _source = ctree.Copy() as TagNodeCompound; return this; } /// /// Attempt to load a Map subtree into the with validation. /// /// The root node of a Map subtree. /// The returns itself on success, or null if the tree failed validation. public virtual Map LoadTreeSafe (TagNode tree) { if (!ValidateTree(tree)) { return null; } Map map = LoadTree(tree); if (map != null) { if (map._colors.Length != map._width * map._height) { throw new Exception("Unexpected length of colors byte array in Map"); // TODO: Expception Type } } return map; } /// /// Builds a Map subtree from the current data. /// /// The root node of a Map subtree representing the current data. public virtual TagNode BuildTree () { TagNodeCompound data = new TagNodeCompound(); data["scale"] = new TagNodeByte(_scale); data["dimension"] = new TagNodeByte(_dimension); data["height"] = new TagNodeShort(_height); data["width"] = new TagNodeShort(_width); data["xCenter"] = new TagNodeInt(_x); data["zCenter"] = new TagNodeInt(_z); data["colors"] = new TagNodeByteArray(_colors); if (_source != null) { data.MergeFrom(_source); } TagNodeCompound tree = new TagNodeCompound(); tree.Add("data", data); return tree; } /// /// Validate a Map subtree against a schema defintion. /// /// The root node of a Map subtree. /// Status indicating whether the tree was valid against the internal schema. public virtual bool ValidateTree (TagNode tree) { return new NbtVerifier(tree, _schema).Verify(); } #endregion #region ICopyable Members /// /// Creates a deep-copy of the . /// /// A deep-copy of the . public virtual Map Copy () { return new Map(this); } #endregion } }