diff --git a/Substrate/SubstrateCS/Source/Nbt/INBTObject.cs b/Substrate/SubstrateCS/Source/Nbt/INBTObject.cs
new file mode 100644
index 0000000..d5df28b
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/INBTObject.cs
@@ -0,0 +1,38 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// Defines methods for loading or extracting an NBT tree.
+ ///
+ /// Object type that supports this interface.
+ public interface INBTObject
+ {
+ ///
+ /// Attempt to load an NBT tree into the object without validation.
+ ///
+ /// The root node of an NBT tree.
+ /// The object returns itself on success, or null if the tree was unparsable.
+ T LoadTree (TagNode tree);
+
+ ///
+ /// Attempt to load an NBT tree into the object with validation.
+ ///
+ /// The root node of an NBT tree.
+ /// The object returns itself on success, or null if the tree failed validation.
+ T LoadTreeSafe (TagNode tree);
+
+ ///
+ /// Builds an NBT tree from the object's data.
+ ///
+ /// The root node of an NBT tree representing the object's data.
+ TagNode BuildTree ();
+
+ ///
+ /// Validate an NBT tree, usually against an object-supplied schema.
+ ///
+ /// The root node of an NBT tree.
+ /// Status indicating whether the tree was valid for this object.
+ bool ValidateTree (TagNode tree);
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/JSONSerializer.cs b/Substrate/SubstrateCS/Source/Nbt/JSONSerializer.cs
new file mode 100644
index 0000000..44a8578
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/JSONSerializer.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Substrate.Core;
+
+namespace Substrate.Nbt
+{
+ public class JSONSerializer
+ {
+ public static string Serialize (TagNode tag)
+ {
+ return Serialize(tag, 0);
+ }
+
+ public static string Serialize (TagNode tag, int level)
+ {
+ StringBuilder str = new StringBuilder();
+
+ if (tag.GetTagType() == TagType.TAG_COMPOUND) {
+ SerializeCompound(tag as TagNodeCompound, str, level);
+ }
+ else if (tag.GetTagType() == TagType.TAG_LIST) {
+ SerializeList(tag as TagNodeList, str, level);
+ }
+ else {
+ SerializeScaler(tag, str);
+ }
+
+ return str.ToString();
+ }
+
+ private static void SerializeCompound (TagNodeCompound tag, StringBuilder str, int level)
+ {
+ if (tag.Count == 0) {
+ str.Append("{ }");
+ return;
+ }
+
+ str.AppendLine();
+ AddLine(str, "{", level);
+
+ IEnumerator> en = tag.GetEnumerator();
+ bool first = true;
+ while (en.MoveNext()) {
+ if (!first) {
+ str.Append(",");
+ str.AppendLine();
+ }
+
+ KeyValuePair item = en.Current;
+ Add(str, "\"" + item.Key + "\": ", level + 1);
+
+ if (item.Value.GetTagType() == TagType.TAG_COMPOUND) {
+ SerializeCompound(item.Value as TagNodeCompound, str, level + 1);
+ }
+ else if (item.Value.GetTagType() == TagType.TAG_LIST) {
+ SerializeList(item.Value as TagNodeList, str, level + 1);
+ }
+ else {
+ SerializeScaler(item.Value, str);
+ }
+
+ first = false;
+ }
+
+ str.AppendLine();
+ Add(str, "}", level);
+ }
+
+ private static void SerializeList (TagNodeList tag, StringBuilder str, int level)
+ {
+ if (tag.Count == 0) {
+ str.Append("[ ]");
+ return;
+ }
+
+ str.AppendLine();
+ AddLine(str, "[", level);
+
+ IEnumerator en = tag.GetEnumerator();
+ bool first = true;
+ while (en.MoveNext()) {
+ if (!first) {
+ str.Append(",");
+ }
+
+ TagNode item = en.Current;
+
+ if (item.GetTagType() == TagType.TAG_COMPOUND) {
+ SerializeCompound(item as TagNodeCompound, str, level + 1);
+ }
+ else if (item.GetTagType() == TagType.TAG_LIST) {
+ SerializeList(item as TagNodeList, str, level + 1);
+ }
+ else {
+ if (!first) {
+ str.AppendLine();
+ }
+ Indent(str, level + 1);
+ SerializeScaler(item, str);
+ }
+
+
+ first = false;
+ }
+
+ str.AppendLine();
+ Add(str, "]", level);
+ }
+
+ private static void SerializeScaler (TagNode tag, StringBuilder str)
+ {
+ switch (tag.GetTagType()) {
+ case TagType.TAG_STRING:
+ str.Append("\"" + tag.ToTagString().Data + "\"");
+ break;
+
+ case TagType.TAG_BYTE:
+ str.Append(tag.ToTagByte().Data);
+ break;
+
+ case TagType.TAG_SHORT:
+ str.Append(tag.ToTagShort().Data);
+ break;
+
+ case TagType.TAG_INT:
+ str.Append(tag.ToTagInt().Data);
+ break;
+
+ case TagType.TAG_LONG:
+ str.Append(tag.ToTagLong().Data);
+ break;
+
+ case TagType.TAG_FLOAT:
+ str.Append(tag.ToTagFloat().Data);
+ break;
+
+ case TagType.TAG_DOUBLE:
+ str.Append(tag.ToTagDouble().Data);
+ break;
+
+ case TagType.TAG_BYTE_ARRAY:
+ str.Append(Convert.ToBase64String(tag.ToTagByteArray().Data));
+ /*if (tag.ToTagByteArray().Length == (16 * 16 * 128 / 2)) {
+ str.Append(Base16.Encode(tag.ToTagByteArray().Data, 1));
+ }
+ else {
+ str.Append(Base16.Encode(tag.ToTagByteArray().Data, 2));
+ }*/
+ break;
+ }
+ }
+
+ private static void AddLine (StringBuilder str, string line, int level)
+ {
+ Indent(str, level);
+ str.AppendLine(line);
+ }
+
+ private static void Add (StringBuilder str, string line, int level)
+ {
+ Indent(str, level);
+ str.Append(line);
+ }
+
+ private static void Indent (StringBuilder str, int count)
+ {
+ for (int i = 0; i < count; i++) {
+ str.Append("\t");
+ }
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/NBT.cs b/Substrate/SubstrateCS/Source/Nbt/NBT.cs
new file mode 100644
index 0000000..8192c15
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/NBT.cs
@@ -0,0 +1,534 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.IO.Compression;
+using Substrate.Core;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// Contains the root node of an NBT tree and handles IO of tree nodes.
+ ///
+ ///
+ /// NBT, or Named Byte Tag, is a tree-based data structure for storing most Minecraft data.
+ /// NBT_Tree is more of a helper class for NBT trees that handles reading and writing nodes to data streams.
+ /// Most of the API takes a TagValue or derived node as the root of the tree, rather than an NBT_Tree object itself.
+ ///
+ public class NbtTree : ICopyable
+ {
+ private Stream _stream = null;
+ private TagNodeCompound _root = null;
+
+ private static TagNodeNull _nulltag = new TagNodeNull();
+
+ ///
+ /// Gets the root node of this tree.
+ ///
+ public TagNodeCompound Root
+ {
+ get { return _root; }
+ }
+
+ ///
+ /// Constructs a wrapper around a new NBT tree with an empty root node.
+ ///
+ public NbtTree ()
+ {
+ _root = new TagNodeCompound();
+ }
+
+ ///
+ /// Constructs a wrapper around another NBT tree.
+ ///
+ /// The root node of an NBT tree.
+ public NbtTree (TagNodeCompound tree)
+ {
+ _root = tree;
+ }
+
+ ///
+ /// Constructs and wrapper around a new NBT tree parsed from a source data stream.
+ ///
+ /// An open, readable data stream containing NBT data.
+ public NbtTree (Stream s)
+ {
+ ReadFrom(s);
+ }
+
+ ///
+ /// Rebuild the internal NBT tree from a source data stream.
+ ///
+ /// An open, readable data stream containing NBT data.
+ public void ReadFrom (Stream s)
+ {
+ if (s != null) {
+ _stream = s;
+ _root = ReadRoot();
+ _stream = null;
+ }
+ }
+
+ ///
+ /// Writes out the internal NBT tree to a destination data stream.
+ ///
+ /// An open, writable data stream.
+ public void WriteTo (Stream s)
+ {
+ if (s != null) {
+ _stream = s;
+
+ if (_root != null) {
+ WriteTag("", _root);
+ }
+
+ _stream = null;
+ }
+ }
+
+ private TagNode ReadValue (TagType type)
+ {
+ switch (type) {
+ case TagType.TAG_END:
+ return null;
+
+ case TagType.TAG_BYTE:
+ return ReadByte();
+
+ case TagType.TAG_SHORT:
+ return ReadShort();
+
+ case TagType.TAG_INT:
+ return ReadInt();
+
+ case TagType.TAG_LONG:
+ return ReadLong();
+
+ case TagType.TAG_FLOAT:
+ return ReadFloat();
+
+ case TagType.TAG_DOUBLE:
+ return ReadDouble();
+
+ case TagType.TAG_BYTE_ARRAY:
+ return ReadByteArray();
+
+ case TagType.TAG_STRING:
+ return ReadString();
+
+ case TagType.TAG_LIST:
+ return ReadList();
+
+ case TagType.TAG_COMPOUND:
+ return ReadCompound();
+ }
+
+ throw new Exception();
+ }
+
+ private TagNode ReadByte ()
+ {
+ int gzByte = _stream.ReadByte();
+ if (gzByte == -1) {
+ throw new NBTException(NBTException.MSG_GZIP_ENDOFSTREAM);
+ }
+
+ TagNodeByte val = new TagNodeByte((byte)gzByte);
+
+ return val;
+ }
+
+ private TagNode ReadShort ()
+ {
+ byte[] gzBytes = new byte[2];
+ _stream.Read(gzBytes, 0, 2);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ TagNodeShort val = new TagNodeShort(BitConverter.ToInt16(gzBytes, 0));
+
+ return val;
+ }
+
+ private TagNode ReadInt ()
+ {
+ byte[] gzBytes = new byte[4];
+ _stream.Read(gzBytes, 0, 4);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ TagNodeInt val = new TagNodeInt(BitConverter.ToInt32(gzBytes, 0));
+
+ return val;
+ }
+
+ private TagNode ReadLong ()
+ {
+ byte[] gzBytes = new byte[8];
+ _stream.Read(gzBytes, 0, 8);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ TagNodeLong val = new TagNodeLong(BitConverter.ToInt64(gzBytes, 0));
+
+ return val;
+ }
+
+ private TagNode ReadFloat ()
+ {
+ byte[] gzBytes = new byte[4];
+ _stream.Read(gzBytes, 0, 4);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ TagNodeFloat val = new TagNodeFloat(BitConverter.ToSingle(gzBytes, 0));
+
+ return val;
+ }
+
+ private TagNode ReadDouble ()
+ {
+ byte[] gzBytes = new byte[8];
+ _stream.Read(gzBytes, 0, 8);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ TagNodeDouble val = new TagNodeDouble(BitConverter.ToDouble(gzBytes, 0));
+
+ return val;
+ }
+
+ private TagNode ReadByteArray ()
+ {
+ byte[] lenBytes = new byte[4];
+ _stream.Read(lenBytes, 0, 4);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(lenBytes);
+ }
+
+ int length = BitConverter.ToInt32(lenBytes, 0);
+ if (length < 0) {
+ throw new NBTException(NBTException.MSG_READ_NEG);
+ }
+
+ byte[] data = new byte[length];
+ _stream.Read(data, 0, length);
+
+ TagNodeByteArray val = new TagNodeByteArray(data);
+
+ return val;
+ }
+
+ private TagNode ReadString ()
+ {
+ byte[] lenBytes = new byte[2];
+ _stream.Read(lenBytes, 0, 2);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(lenBytes);
+ }
+
+ short len = BitConverter.ToInt16(lenBytes, 0);
+ if (len < 0) {
+ throw new NBTException(NBTException.MSG_READ_NEG);
+ }
+
+ byte[] strBytes = new byte[len];
+ _stream.Read(strBytes, 0, len);
+
+ System.Text.Encoding str = Encoding.GetEncoding(28591);
+
+ TagNodeString val = new TagNodeString(str.GetString(strBytes));
+
+ return val;
+ }
+
+ private TagNode ReadList ()
+ {
+ int gzByte = _stream.ReadByte();
+ if (gzByte == -1) {
+ throw new NBTException(NBTException.MSG_GZIP_ENDOFSTREAM);
+ }
+
+ TagNodeList val = new TagNodeList((TagType)gzByte);
+ if (val.ValueType > (TagType)Enum.GetValues(typeof(TagType)).GetUpperBound(0)) {
+ throw new NBTException(NBTException.MSG_READ_TYPE);
+ }
+
+ byte[] lenBytes = new byte[4];
+ _stream.Read(lenBytes, 0, 4);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(lenBytes);
+ }
+
+ int length = BitConverter.ToInt32(lenBytes, 0);
+ if (length < 0) {
+ throw new NBTException(NBTException.MSG_READ_NEG);
+ }
+
+ for (int i = 0; i < length; i++) {
+ val.Add(ReadValue(val.ValueType));
+ }
+
+ return val;
+ }
+
+ private TagNode ReadCompound ()
+ {
+ TagNodeCompound val = new TagNodeCompound();
+
+ while (ReadTag(val)) ;
+
+ return val;
+ }
+
+ private TagNodeCompound ReadRoot ()
+ {
+ TagType type = (TagType)_stream.ReadByte();
+ if (type == TagType.TAG_COMPOUND) {
+ ReadString(); // name
+ return ReadValue(type) as TagNodeCompound;
+ }
+
+ return null;
+ }
+
+ private bool ReadTag (TagNodeCompound parent)
+ {
+ //NBT_Tag tag = new NBT_Tag();
+
+ TagType type = (TagType)_stream.ReadByte();
+ if (type != TagType.TAG_END) {
+ string name = ReadString().ToTagString().Data;
+ parent[name] = ReadValue(type);
+ return true;
+ }
+
+ return false;
+
+ //tag.Value = ReadValue(type);
+
+ //return tag;
+ }
+
+ private void WriteValue (TagNode val)
+ {
+ switch (val.GetTagType()) {
+ case TagType.TAG_END:
+ break;
+
+ case TagType.TAG_BYTE:
+ WriteByte(val.ToTagByte());
+ break;
+
+ case TagType.TAG_SHORT:
+ WriteShort(val.ToTagShort());
+ break;
+
+ case TagType.TAG_INT:
+ WriteInt(val.ToTagInt());
+ break;
+
+ case TagType.TAG_LONG:
+ WriteLong(val.ToTagLong());
+ break;
+
+ case TagType.TAG_FLOAT:
+ WriteFloat(val.ToTagFloat());
+ break;
+
+ case TagType.TAG_DOUBLE:
+ WriteDouble(val.ToTagDouble());
+ break;
+
+ case TagType.TAG_BYTE_ARRAY:
+ WriteByteArray(val.ToTagByteArray());
+ break;
+
+ case TagType.TAG_STRING:
+ WriteString(val.ToTagString());
+ break;
+
+ case TagType.TAG_LIST:
+ WriteList(val.ToTagList());
+ break;
+
+ case TagType.TAG_COMPOUND:
+ WriteCompound(val.ToTagCompound());
+ break;
+ }
+ }
+
+ private void WriteByte (TagNodeByte val)
+ {
+ _stream.WriteByte(val.Data);
+ }
+
+ private void WriteShort (TagNodeShort val)
+ {
+ byte[] gzBytes = BitConverter.GetBytes(val.Data);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ _stream.Write(gzBytes, 0, 2);
+ }
+
+ private void WriteInt (TagNodeInt val)
+ {
+ byte[] gzBytes = BitConverter.GetBytes(val.Data);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ _stream.Write(gzBytes, 0, 4);
+ }
+
+ private void WriteLong (TagNodeLong val)
+ {
+ byte[] gzBytes = BitConverter.GetBytes(val.Data);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ _stream.Write(gzBytes, 0, 8);
+ }
+
+ private void WriteFloat (TagNodeFloat val)
+ {
+ byte[] gzBytes = BitConverter.GetBytes(val.Data);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ _stream.Write(gzBytes, 0, 4);
+ }
+
+ private void WriteDouble (TagNodeDouble val)
+ {
+ byte[] gzBytes = BitConverter.GetBytes(val.Data);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(gzBytes);
+ }
+
+ _stream.Write(gzBytes, 0, 8);
+ }
+
+ private void WriteByteArray (TagNodeByteArray val)
+ {
+ byte[] lenBytes = BitConverter.GetBytes(val.Length);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(lenBytes);
+ }
+
+ _stream.Write(lenBytes, 0, 4);
+ _stream.Write(val.Data, 0, val.Length);
+ }
+
+ private void WriteString (TagNodeString val)
+ {
+ byte[] lenBytes = BitConverter.GetBytes((short)val.Length);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(lenBytes);
+ }
+
+ _stream.Write(lenBytes, 0, 2);
+
+ System.Text.Encoding str = Encoding.GetEncoding(28591);
+ byte[] gzBytes = str.GetBytes(val.Data);
+
+ _stream.Write(gzBytes, 0, gzBytes.Length);
+ }
+
+ private void WriteList (TagNodeList val)
+ {
+ byte[] lenBytes = BitConverter.GetBytes(val.Count);
+
+ if (BitConverter.IsLittleEndian) {
+ Array.Reverse(lenBytes);
+ }
+
+ _stream.WriteByte((byte)val.ValueType);
+ _stream.Write(lenBytes, 0, 4);
+
+ foreach (TagNode v in val) {
+ WriteValue(v);
+ }
+ }
+
+ private void WriteCompound (TagNodeCompound val)
+ {
+ foreach (KeyValuePair item in val) {
+ WriteTag(item.Key, item.Value);
+ }
+
+ WriteTag(null, _nulltag);
+ }
+
+ private void WriteTag (string name, TagNode val)
+ {
+ _stream.WriteByte((byte)val.GetTagType());
+
+ if (val.GetTagType() != TagType.TAG_END) {
+ WriteString(name);
+ WriteValue(val);
+ }
+ }
+
+ #region ICopyable Members
+
+ ///
+ /// Creates a deep copy of the NBT_Tree and underlying nodes.
+ ///
+ /// A new NBT_tree.
+ public NbtTree Copy ()
+ {
+ NbtTree tree = new NbtTree();
+ tree._root = _root.Copy() as TagNodeCompound;
+
+ return tree;
+ }
+
+ #endregion
+ }
+
+ // TODO: Revise exceptions?
+ public class NBTException : Exception
+ {
+ public const String MSG_GZIP_ENDOFSTREAM = "Gzip Error: Unexpected end of stream";
+
+ public const String MSG_READ_NEG = "Read Error: Negative length";
+ public const String MSG_READ_TYPE = "Read Error: Invalid value type";
+
+ public NBTException () { }
+
+ public NBTException (String msg) : base(msg) { }
+
+ public NBTException (String msg, Exception innerException) : base(msg, innerException) { }
+ }
+
+ public class InvalidNBTObjectException : Exception { }
+
+ public class InvalidTagException : Exception { }
+
+ public class InvalidValueException : Exception { }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/NBTVerifier.cs b/Substrate/SubstrateCS/Source/Nbt/NBTVerifier.cs
new file mode 100644
index 0000000..6623eec
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/NBTVerifier.cs
@@ -0,0 +1,386 @@
+using System;
+using System.Collections.Generic;
+using Substrate.Core;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// Indicates how an event processor should respond to returning event handler.
+ ///
+ public enum TagEventCode
+ {
+ ///
+ /// The event processor should process the next event in the chian.
+ ///
+ NEXT,
+
+ ///
+ /// The event processor should ignore the verification failure and stop processing any remaining events.
+ ///
+ PASS,
+
+ ///
+ /// The event processor should fail and stop processing any remaining events.
+ ///
+ FAIL,
+ }
+
+ ///
+ /// Event arguments for failure events.
+ ///
+ public class TagEventArgs : EventArgs
+ {
+ private string _tagName;
+ private TagNode _parent;
+ private TagNode _tag;
+ private SchemaNode _schema;
+
+ ///
+ /// Gets the expected name of the referenced by this event.
+ ///
+ public string TagName
+ {
+ get { return _tagName; }
+ }
+
+ ///
+ /// Gets the parent of the referenced by this event, if it exists.
+ ///
+ public TagNode Parent
+ {
+ get { return _parent; }
+ }
+
+ ///
+ /// Gets the referenced by this event.
+ ///
+ public TagNode Tag
+ {
+ get { return _tag; }
+ }
+
+ ///
+ /// Gets the corresponding to the referenced by this event.
+ ///
+ public SchemaNode Schema
+ {
+ get { return _schema; }
+ }
+
+ ///
+ /// Constructs a new event argument set.
+ ///
+ /// The expected name of a .
+ public TagEventArgs (string tagName)
+ : base()
+ {
+ _tagName = tagName;
+ }
+
+ ///
+ /// Constructs a new event argument set.
+ ///
+ /// The expected name of a .
+ /// The involved in this event.
+ public TagEventArgs (string tagName, TagNode tag)
+ : base()
+ {
+ _tag = tag;
+ _tagName = tagName;
+ }
+
+ ///
+ /// Constructs a new event argument set.
+ ///
+ /// The corresponding to the involved in this event.
+ /// The involved in this event.
+ public TagEventArgs (SchemaNode schema, TagNode tag)
+ : base()
+ {
+ _tag = tag;
+ _schema = schema;
+ }
+ }
+
+ ///
+ /// An event handler for intercepting and responding to verification failures of NBT trees.
+ ///
+ /// Information relating to a verification event.
+ /// A determining how the event processor should respond.
+ public delegate TagEventCode VerifierEventHandler (TagEventArgs eventArgs);
+
+ ///
+ /// Verifies the integrity of an NBT tree against a schema definition.
+ ///
+ public class NBTVerifier
+ {
+ private TagNode _root;
+ private SchemaNode _schema;
+
+ ///
+ /// An event that gets fired whenever an expected is not found.
+ ///
+ public static event VerifierEventHandler MissingTag;
+
+ ///
+ /// An event that gets fired whenever an expected is of the wrong type and cannot be cast.
+ ///
+ public static event VerifierEventHandler InvalidTagType;
+
+ ///
+ /// An event that gets fired whenever an expected has a value that violates the schema.
+ ///
+ public static event VerifierEventHandler InvalidTagValue;
+
+ private Dictionary _scratch = new Dictionary();
+
+ ///
+ /// Constructs a new object for a given NBT tree and schema.
+ ///
+ /// A representing the root of an NBT tree.
+ /// A representing the root of a schema definition for the NBT tree.
+ public NBTVerifier (TagNode root, SchemaNode schema)
+ {
+ _root = root;
+ _schema = schema;
+ }
+
+ ///
+ /// Invokes the verifier.
+ ///
+ /// Status indicating whether the NBT tree is valid for the given schema.
+ public virtual bool Verify ()
+ {
+ return Verify(null, _root, _schema);
+ }
+
+ private bool Verify (TagNode parent, TagNode tag, SchemaNode schema)
+ {
+ if (tag == null) {
+ return OnMissingTag(new TagEventArgs(schema.Name));
+ }
+
+ SchemaNodeScaler scaler = schema as SchemaNodeScaler;
+ if (scaler != null) {
+ return VerifyScaler(tag, scaler);
+ }
+
+ SchemaNodeString str = schema as SchemaNodeString;
+ if (str != null) {
+ return VerifyString(tag, str);
+ }
+
+ SchemaNodeArray array = schema as SchemaNodeArray;
+ if (array != null) {
+ return VerifyArray(tag, array);
+ }
+
+ SchemaNodeList list = schema as SchemaNodeList;
+ if (list != null) {
+ return VerifyList(tag, list);
+ }
+
+ SchemaNodeCompound compound = schema as SchemaNodeCompound;
+ if (compound != null) {
+ return VerifyCompound(tag, compound);
+ }
+
+ return OnInvalidTagType(new TagEventArgs(schema.Name, tag));
+ }
+
+ private bool VerifyScaler (TagNode tag, SchemaNodeScaler schema)
+ {
+ if (!tag.IsCastableTo(schema.Type)) {
+ if (!OnInvalidTagType(new TagEventArgs(schema.Name, tag))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private bool VerifyString (TagNode tag, SchemaNodeString schema)
+ {
+ TagNodeString stag = tag as TagNodeString;
+ if (stag == null) {
+ if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+ if (schema.Length > 0 && stag.Length > schema.Length) {
+ if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+ if (schema.Value != null && stag.Data != schema.Value) {
+ if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ private bool VerifyArray (TagNode tag, SchemaNodeArray schema)
+ {
+ TagNodeByteArray atag = tag as TagNodeByteArray;
+ if (atag == null) {
+ if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+ if (schema.Length > 0 && atag.Length != schema.Length) {
+ if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private bool VerifyList (TagNode tag, SchemaNodeList schema)
+ {
+ TagNodeList ltag = tag as TagNodeList;
+ if (ltag == null) {
+ if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+ if (ltag.Count > 0 && ltag.ValueType != schema.Type) {
+ if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+ if (schema.Length > 0 && ltag.Count != schema.Length) {
+ if (!OnInvalidTagValue(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+
+ // Patch up empty lists
+ //if (schema.Length == 0) {
+ // tag = new NBT_List(schema.Type);
+ //}
+
+ bool pass = true;
+
+ // If a subschema is set, test all items in list against it
+
+ if (schema.SubSchema != null) {
+ foreach (TagNode v in ltag) {
+ pass = Verify(tag, v, schema.SubSchema) && pass;
+ }
+ }
+
+ return pass;
+ }
+
+ private bool VerifyCompound (TagNode tag, SchemaNodeCompound schema)
+ {
+ TagNodeCompound ctag = tag as TagNodeCompound;
+ if (ctag == null) {
+ if (!OnInvalidTagType(new TagEventArgs(schema, tag))) {
+ return false;
+ }
+ }
+
+ bool pass = true;
+
+ foreach (SchemaNode node in schema) {
+ TagNode value;
+ ctag.TryGetValue(node.Name, out value);
+
+ if (value == null) {
+ if ((node.Options & SchemaOptions.CREATE_ON_MISSING) == SchemaOptions.CREATE_ON_MISSING) {
+ _scratch[node.Name] = node.BuildDefaultTree();
+ continue;
+ }
+ else if ((node.Options & SchemaOptions.OPTIONAL) == SchemaOptions.OPTIONAL) {
+ continue;
+ }
+ }
+
+ pass = Verify(tag, value, node) && pass;
+ }
+
+ foreach (KeyValuePair item in _scratch) {
+ ctag[item.Key] = item.Value;
+ }
+
+ _scratch.Clear();
+
+ return pass;
+ }
+
+ #region Event Handlers
+
+ ///
+ /// Processes registered events for whenever an expected is not found.
+ ///
+ /// Arguments for this event.
+ /// Status indicating whether this event can be ignored.
+ protected virtual bool OnMissingTag (TagEventArgs e)
+ {
+ if (MissingTag != null) {
+ foreach (VerifierEventHandler func in MissingTag.GetInvocationList()) {
+ TagEventCode code = func(e);
+ switch (code) {
+ case TagEventCode.FAIL:
+ return false;
+ case TagEventCode.PASS:
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Processes registered events for whenever an expected is of the wrong type and cannot be cast.
+ ///
+ /// Arguments for this event.
+ /// Status indicating whether this event can be ignored.
+ protected virtual bool OnInvalidTagType (TagEventArgs e)
+ {
+ if (InvalidTagType != null) {
+ foreach (VerifierEventHandler func in InvalidTagType.GetInvocationList()) {
+ TagEventCode code = func(e);
+ switch (code) {
+ case TagEventCode.FAIL:
+ return false;
+ case TagEventCode.PASS:
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Processes registered events for whenever an expected has a value that violates the schema.
+ ///
+ /// Arguments for this event.
+ /// Status indicating whether this event can be ignored.
+ protected virtual bool OnInvalidTagValue (TagEventArgs e)
+ {
+ if (InvalidTagValue != null) {
+ foreach (VerifierEventHandler func in InvalidTagValue.GetInvocationList()) {
+ TagEventCode code = func(e);
+ switch (code) {
+ case TagEventCode.FAIL:
+ return false;
+ case TagEventCode.PASS:
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaNode.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaNode.cs
new file mode 100644
index 0000000..e8d34ce
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaNode.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A node in an NBT schema definition, used to define what values are considered valid for a given NBT node.
+ ///
+ public abstract class SchemaNode
+ {
+ private string _name;
+ private SchemaOptions _options;
+
+ ///
+ /// Gets the name of an expected NBT node.
+ ///
+ public string Name
+ {
+ get { return _name; }
+ }
+
+ ///
+ /// Gets additional schema options defined for this node.
+ ///
+ public SchemaOptions Options
+ {
+ get { return _options; }
+ }
+
+ ///
+ /// Constructs a new representing a named .
+ ///
+ /// The name of the corresponding .
+ protected SchemaNode (string name)
+ {
+ _name = name;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ protected SchemaNode (string name, SchemaOptions options)
+ {
+ _name = name;
+ _options = options;
+ }
+
+ ///
+ /// Construct a sensible default NBT tree representative of this schema node.
+ ///
+ /// A that is valid for this schema node.
+ public virtual TagNode BuildDefaultTree ()
+ {
+ return null;
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaNodeArray.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeArray.cs
new file mode 100644
index 0000000..9d3f354
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeArray.cs
@@ -0,0 +1,81 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A concrete representing a .
+ ///
+ public sealed class SchemaNodeArray : SchemaNode
+ {
+ private int _length;
+
+ ///
+ /// Gets the expected length of the corresponding byte array.
+ ///
+ public int Length
+ {
+ get { return _length; }
+ }
+
+ ///
+ /// Indicates whether there is an expected length of the corresponding byte array.
+ ///
+ public bool HasExpectedLength
+ {
+ get { return _length > 0; }
+ }
+
+ ///
+ /// Constructs a new representing a named .
+ ///
+ /// The name of the corresponding .
+ public SchemaNodeArray (string name)
+ : base(name)
+ {
+ _length = 0;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeArray (string name, SchemaOptions options)
+ : base(name, options)
+ {
+ _length = 0;
+ }
+
+ ///
+ /// Constructs a new representing a named with expected length .
+ ///
+ /// The name of the corresponding .
+ /// The expected length of corresponding byte array.
+ public SchemaNodeArray (string name, int length)
+ : base(name)
+ {
+ _length = length;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The expected length of corresponding byte array.
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeArray (string name, int length, SchemaOptions options)
+ : base(name, options)
+ {
+ _length = length;
+ }
+
+ ///
+ /// Constructs a default satisfying the constraints of this node.
+ ///
+ /// A with a sensible default value.
+ public override TagNode BuildDefaultTree ()
+ {
+ return new TagNodeByteArray(new byte[_length]);
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaNodeCompound.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeCompound.cs
new file mode 100644
index 0000000..a7670f9
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeCompound.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A concrete representing a .
+ ///
+ public sealed class SchemaNodeCompound : SchemaNode, ICollection
+ {
+ private List _subnodes;
+
+ #region ICollection Members
+
+ ///
+ /// Adds a as a child of this node.
+ ///
+ /// The to add.
+ public void Add (SchemaNode item)
+ {
+ _subnodes.Add(item);
+ }
+
+ ///
+ /// Removes all objects from the node.
+ ///
+ public void Clear ()
+ {
+ _subnodes.Clear();
+ }
+
+ ///
+ /// Checks if a is a child of this node.
+ ///
+ /// The to check for existance.
+ /// Status indicating if the exists as a child of this node.
+ public bool Contains (SchemaNode item)
+ {
+ return _subnodes.Contains(item);
+ }
+
+ ///
+ /// Copies all child objects of this node to a compatible one-dimensional array, starting at the specified index of the target array.
+ ///
+ /// The one-dimensional that is the destination of the subnodes copied. The Array must have zero-based indexing.
+ /// The zero-based index in at which copying begins.
+ public void CopyTo (SchemaNode[] array, int arrayIndex)
+ {
+ _subnodes.CopyTo(array, arrayIndex);
+ }
+
+ ///
+ /// Gets the number of child objects in this node.
+ ///
+ public int Count
+ {
+ get { return _subnodes.Count; }
+ }
+
+ ///
+ /// Gets a value indicating whether the node is readonly.
+ ///
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Removes the first occurance of a from this node.
+ ///
+ /// The to remove.
+ /// Status indicating whether a was removed.
+ public bool Remove (SchemaNode item)
+ {
+ return _subnodes.Remove(item);
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Iterates through all of the objects in this .
+ ///
+ /// An enumerator for this node.
+ public IEnumerator GetEnumerator ()
+ {
+ return _subnodes.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Iterates through all of the objects in this .
+ ///
+ /// An enumerator for this node.
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return _subnodes.GetEnumerator();
+ }
+
+ #endregion
+
+ ///
+ /// Constructs a new representing a root .
+ ///
+ public SchemaNodeCompound ()
+ : base("")
+ {
+ _subnodes = new List();
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeCompound (SchemaOptions options)
+ : base("", options)
+ {
+ _subnodes = new List();
+ }
+
+ ///
+ /// Constructs a new representing a named .
+ ///
+ /// The name of the corresponding .
+ public SchemaNodeCompound (string name)
+ : base(name)
+ {
+ _subnodes = new List();
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeCompound (string name, SchemaOptions options)
+ : base(name, options)
+ {
+ _subnodes = new List();
+ }
+
+ ///
+ /// Constructs a new representing a named matching the given schema.
+ ///
+ /// The name of the corresponding .
+ /// A representing a schema to verify against the corresponding .
+ public SchemaNodeCompound (string name, SchemaNode subschema)
+ : base(name)
+ {
+ _subnodes = new List();
+
+ SchemaNodeCompound schema = subschema as SchemaNodeCompound;
+ if (schema == null) {
+ return;
+ }
+
+ foreach (SchemaNode node in schema._subnodes) {
+ _subnodes.Add(node);
+ }
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// A representing a schema to verify against the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeCompound (string name, SchemaNode subschema, SchemaOptions options)
+ : base(name, options)
+ {
+ _subnodes = new List();
+
+ SchemaNodeCompound schema = subschema as SchemaNodeCompound;
+ if (schema == null) {
+ return;
+ }
+
+ foreach (SchemaNode node in schema._subnodes) {
+ _subnodes.Add(node);
+ }
+ }
+
+ ///
+ /// Copies all the elements of this into .
+ ///
+ /// The destination to copy elements into.
+ /// A reference to .
+ public SchemaNodeCompound MergeInto (SchemaNodeCompound tree)
+ {
+ foreach (SchemaNode node in _subnodes) {
+ SchemaNode f = tree._subnodes.Find(n => n.Name == node.Name);
+ if (f != null) {
+ continue;
+ }
+ tree.Add(node);
+ }
+
+ return tree;
+ }
+
+ ///
+ /// Constructs a default satisfying the constraints of this node.
+ ///
+ /// A with a sensible default value. A default child is created for every contained in this .
+ public override TagNode BuildDefaultTree ()
+ {
+ TagNodeCompound list = new TagNodeCompound();
+ foreach (SchemaNode node in _subnodes) {
+ list[node.Name] = node.BuildDefaultTree();
+ }
+
+ return list;
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaNodeList.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeList.cs
new file mode 100644
index 0000000..d7bf6e2
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeList.cs
@@ -0,0 +1,172 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A concrete representing a .
+ ///
+ public sealed class SchemaNodeList : SchemaNode
+ {
+ private TagType _type;
+ private int _length;
+ private SchemaNode _subschema;
+
+ ///
+ /// Gets the expected number of items contained in the corresponding .
+ ///
+ public int Length
+ {
+ get { return _length; }
+ }
+
+ ///
+ /// Gets the expected of the items contained in the corresponding .
+ ///
+ public TagType Type
+ {
+ get { return _type; }
+ }
+
+ ///
+ /// Gets a representing a schema that items contained in the corresponding should be verified against.
+ ///
+ public SchemaNode SubSchema
+ {
+ get { return _subschema; }
+ }
+
+ ///
+ /// Indicates whether there is an expected number of items of the corresponding .
+ ///
+ public bool HasExpectedLength
+ {
+ get { return _length > 0; }
+ }
+
+ ///
+ /// Constructs a new representing a named containing items of type .
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ public SchemaNodeList (string name, TagType type)
+ : base(name)
+ {
+ _type = type;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeList (string name, TagType type, SchemaOptions options)
+ : base(name, options)
+ {
+ _type = type;
+ }
+
+ ///
+ /// Constructs a new representing a named containing items of type .
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// The number of items contained in the corresponding .
+ public SchemaNodeList (string name, TagType type, int length)
+ : base(name)
+ {
+ _type = type;
+ _length = length;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// The number of items contained in the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeList (string name, TagType type, int length, SchemaOptions options)
+ : base(name, options)
+ {
+ _type = type;
+ _length = length;
+ }
+
+ ///
+ /// Constructs a new representing a named containing items of type matching the given schema.
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// A representing a schema to verify against items contained in the corresponding .
+ public SchemaNodeList (string name, TagType type, SchemaNode subschema)
+ : base(name)
+ {
+ _type = type;
+ _subschema = subschema;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// A representing a schema to verify against items contained in the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeList (string name, TagType type, SchemaNode subschema, SchemaOptions options)
+ : base(name, options)
+ {
+ _type = type;
+ _subschema = subschema;
+ }
+
+ ///
+ /// Constructs a new representing a named containing items of type matching the given schema.
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// The number of items contained in the corresponding .
+ /// A representing a schema to verify against items contained in the corresponding .
+ public SchemaNodeList (string name, TagType type, int length, SchemaNode subschema)
+ : base(name)
+ {
+ _type = type;
+ _length = length;
+ _subschema = subschema;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The type of items contained in the corresponding .
+ /// The number of items contained in the corresponding .
+ /// A representing a schema to verify against items contained in the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeList (string name, TagType type, int length, SchemaNode subschema, SchemaOptions options)
+ : base(name, options)
+ {
+ _type = type;
+ _length = length;
+ _subschema = subschema;
+ }
+
+ ///
+ /// Constructs a default satisfying the constraints of this node.
+ ///
+ /// A with a sensible default value. If a length is specified, default child objects of the necessary type will be created and added to the .
+ public override TagNode BuildDefaultTree ()
+ {
+ if (_length == 0) {
+ return new TagNodeList(_type);
+ }
+
+ TagNodeList list = new TagNodeList(_type);
+ for (int i = 0; i < _length; i++) {
+ list.Add(_subschema.BuildDefaultTree());
+ }
+
+ return list;
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaNodeScaler.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeScaler.cs
new file mode 100644
index 0000000..b156c8d
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeScaler.cs
@@ -0,0 +1,75 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A concrete representing a scaler-type .
+ ///
+ public sealed class SchemaNodeScaler : SchemaNode
+ {
+ private TagType _type;
+
+ ///
+ /// Gets the scaler that this node represents.
+ ///
+ public TagType Type
+ {
+ get { return _type; }
+ }
+
+ ///
+ /// Constructs a new representing a named and of type .
+ ///
+ /// The name of the corresponding .
+ /// The type of the corresponding , restricted to scaler types.
+ public SchemaNodeScaler (string name, TagType type)
+ : base(name)
+ {
+ _type = type;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The type of the corresponding , restricted to scaler types.
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeScaler (string name, TagType type, SchemaOptions options)
+ : base(name, options)
+ {
+ _type = type;
+ }
+
+ ///
+ /// Constructs a default according to the this node represents.
+ ///
+ /// A with a sensible default value.
+ public override TagNode BuildDefaultTree ()
+ {
+ switch (_type) {
+ case TagType.TAG_STRING:
+ return new TagNodeString();
+
+ case TagType.TAG_BYTE:
+ return new TagNodeByte();
+
+ case TagType.TAG_SHORT:
+ return new TagNodeShort();
+
+ case TagType.TAG_INT:
+ return new TagNodeInt();
+
+ case TagType.TAG_LONG:
+ return new TagNodeLong();
+
+ case TagType.TAG_FLOAT:
+ return new TagNodeFloat();
+
+ case TagType.TAG_DOUBLE:
+ return new TagNodeDouble();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaNodeString.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeString.cs
new file mode 100644
index 0000000..54c1f9b
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaNodeString.cs
@@ -0,0 +1,116 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A concrete representing a .
+ ///
+ public sealed class SchemaNodeString : SchemaNode
+ {
+ private string _value = "";
+ private int _length;
+
+ ///
+ /// Gets the maximum length of a valid string.
+ ///
+ public int Length
+ {
+ get { return _length; }
+ }
+
+ ///
+ /// Gets the expected value of a valid string.
+ ///
+ /// A must be set to this value to be considered valid.
+ public string Value
+ {
+ get { return _value; }
+ }
+
+ ///
+ /// Indicates whether there is a maximum-length constraint on strings in this node.
+ ///
+ public bool HasMaxLength
+ {
+ get { return _length > 0; }
+ }
+
+ ///
+ /// Constructs a new representing a named .
+ ///
+ /// The name of the corresponding .
+ public SchemaNodeString (string name)
+ : base(name)
+ {
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeString (string name, SchemaOptions options)
+ : base(name, options)
+ {
+ }
+
+ ///
+ /// Constructs a new representing a named set to .
+ ///
+ /// The name of the corresponding .
+ /// The value that the corresponding must be set to.
+ public SchemaNodeString (string name, string value)
+ : base(name)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The value that the corresponding must be set to.
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeString (string name, string value, SchemaOptions options)
+ : base(name, options)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Constructs a new representing a named with maximum length .
+ ///
+ /// The name of the corresponding .
+ /// The maximum length of strings in the corresponding .
+ public SchemaNodeString (string name, int length)
+ : base(name)
+ {
+ _length = length;
+ }
+
+ ///
+ /// Constructs a new with additional options.
+ ///
+ /// The name of the corresponding .
+ /// The maximum length of strings in the corresponding .
+ /// One or more option flags modifying the processing of this node.
+ public SchemaNodeString (string name, int length, SchemaOptions options)
+ : base(name, options)
+ {
+ _length = length;
+ }
+
+ ///
+ /// Constructs a default satisfying the constraints of this node.
+ ///
+ /// A with a sensible default value. If this node represents a particular string, the constructed will be set to that string.
+ public override TagNode BuildDefaultTree ()
+ {
+ if (_value.Length > 0) {
+ return new TagNodeString(_value);
+ }
+
+ return new TagNodeString();
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/SchemaOptions.cs b/Substrate/SubstrateCS/Source/Nbt/SchemaOptions.cs
new file mode 100644
index 0000000..0b5eb10
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/SchemaOptions.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// Additional options that modify the processing of a .
+ ///
+ [Flags]
+ public enum SchemaOptions
+ {
+ ///
+ /// Any with this option will not throw an error if the corresponding is missing.
+ ///
+ OPTIONAL = 0x1,
+
+ ///
+ /// If a cannot be found for a marked with this option, a sensible default will be created and inserted into the tree.
+ ///
+ CREATE_ON_MISSING = 0x2,
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNode.cs b/Substrate/SubstrateCS/Source/Nbt/TagNode.cs
new file mode 100644
index 0000000..05471fc
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNode.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Substrate.Core;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An abstract base class representing a node in an NBT tree.
+ ///
+ public abstract class TagNode : ICopyable
+ {
+ ///
+ /// Convert this node to a null tag type if supported.
+ ///
+ /// A new null node.
+ public virtual TagNodeNull ToTagNull ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a byte tag type if supported.
+ ///
+ /// A new byte node.
+ public virtual TagNodeByte ToTagByte ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a short tag type if supported.
+ ///
+ /// A new short node.
+ public virtual TagNodeShort ToTagShort ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to an int tag type if supported.
+ ///
+ /// A new int node.
+ public virtual TagNodeInt ToTagInt ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a long tag type if supported.
+ ///
+ /// A new long node.
+ public virtual TagNodeLong ToTagLong ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a float tag type if supported.
+ ///
+ /// A new float node.
+ public virtual TagNodeFloat ToTagFloat ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a double tag type if supported.
+ ///
+ /// A new double node.
+ public virtual TagNodeDouble ToTagDouble ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a byte array tag type if supported.
+ ///
+ /// A new byte array node.
+ public virtual TagNodeByteArray ToTagByteArray ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a string tag type if supported.
+ ///
+ /// A new string node.
+ public virtual TagNodeString ToTagString ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a list tag type if supported.
+ ///
+ /// A new list node.
+ public virtual TagNodeList ToTagList ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Convert this node to a compound tag type if supported.
+ ///
+ /// A new compound node.
+ public virtual TagNodeCompound ToTagCompound ()
+ {
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Gets the underlying tag type of the node.
+ ///
+ /// An NBT tag type.
+ public virtual TagType GetTagType ()
+ {
+ return TagType.TAG_END;
+ }
+
+ ///
+ /// Checks if this node is castable to another node of a given tag type.
+ ///
+ /// An NBT tag type.
+ /// Status indicating whether this object could be cast to a node type represented by the given tag type.
+ public virtual bool IsCastableTo (TagType type)
+ {
+ return type == GetTagType();
+ }
+
+ ///
+ /// Makes a deep copy of the NBT node.
+ ///
+ /// A new NBT node.
+ public virtual TagNode Copy ()
+ {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeByte.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeByte.cs
new file mode 100644
index 0000000..ee483ed
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeByte.cs
@@ -0,0 +1,161 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a signed byte tag type.
+ ///
+ public sealed class TagNodeByte : TagNode
+ {
+ private byte _data = 0;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeByte ToTagByte ()
+ {
+ return this;
+ }
+
+ ///
+ /// Converts the node to a new short node.
+ ///
+ /// A short node representing the same data.
+ public override TagNodeShort ToTagShort ()
+ {
+ return new TagNodeShort(_data);
+ }
+
+ ///
+ /// Converts the node to a new int node.
+ ///
+ /// An int node representing the same data.
+ public override TagNodeInt ToTagInt ()
+ {
+ return new TagNodeInt(_data);
+ }
+
+ ///
+ /// Converts the node to a new long node.
+ ///
+ /// A long node representing the same data.
+ public override TagNodeLong ToTagLong ()
+ {
+ return new TagNodeLong(_data);
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_BYTE tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_BYTE;
+ }
+
+ ///
+ /// Checks if the node is castable to another node of a given tag type.
+ ///
+ /// An NBT tag type.
+ /// Status indicating whether this object could be cast to a node type represented by the given tag type.
+ public override bool IsCastableTo (TagType type)
+ {
+ return (type == TagType.TAG_BYTE ||
+ type == TagType.TAG_SHORT ||
+ type == TagType.TAG_INT ||
+ type == TagType.TAG_LONG);
+ }
+
+ ///
+ /// Gets or sets a byte of tag data.
+ ///
+ public byte Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Constructs a new byte node with a data value of 0.
+ ///
+ public TagNodeByte () { }
+
+ ///
+ /// Constructs a new byte node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeByte (byte d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new byte node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeByte(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system byte to a byte node representing the same value.
+ ///
+ /// A byte value.
+ /// A new byte node containing the given value.
+ public static implicit operator TagNodeByte (byte b)
+ {
+ return new TagNodeByte(b);
+ }
+
+ ///
+ /// Converts a byte node to a system byte representing the same value.
+ ///
+ /// A byte node.
+ /// A system byte set to the node's data value.
+ public static implicit operator byte (TagNodeByte b)
+ {
+ return b._data;
+ }
+
+ ///
+ /// Converts a byte node to a system short representing the same value.
+ ///
+ /// A byte node.
+ /// A system short set to the node's data value.
+ public static implicit operator short (TagNodeByte b)
+ {
+ return b._data;
+ }
+
+ ///
+ /// Converts a byte node to a system int representing the same value.
+ ///
+ /// A byte node.
+ /// A system int set to the node's data value.
+ public static implicit operator int (TagNodeByte b)
+ {
+ return b._data;
+ }
+
+ ///
+ /// Converts a byte node to a system long representing the same value.
+ ///
+ /// A byte node.
+ /// A system long set to the node's data value.
+ public static implicit operator long (TagNodeByte b)
+ {
+ return b._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeByteArray.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeByteArray.cs
new file mode 100644
index 0000000..a39347c
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeByteArray.cs
@@ -0,0 +1,113 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing an unsigned byte array tag type.
+ ///
+ public sealed class TagNodeByteArray : TagNode
+ {
+ private byte[] _data = null;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeByteArray ToTagByteArray ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_BYTE_ARRAY tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_BYTE_ARRAY;
+ }
+
+ ///
+ /// Gets or sets a byte array of tag data.
+ ///
+ public byte[] Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Gets the length of the stored byte array.
+ ///
+ public int Length
+ {
+ get { return _data.Length; }
+ }
+
+ ///
+ /// Constructs a new byte array node with a null data value.
+ ///
+ public TagNodeByteArray () { }
+
+ ///
+ /// Constructs a new byte array node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeByteArray (byte[] d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new byte array node representing the same data.
+ public override TagNode Copy ()
+ {
+ byte[] arr = new byte[_data.Length];
+ _data.CopyTo(arr, 0);
+
+ return new TagNodeByteArray(arr);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Gets or sets a single byte at the specified index.
+ ///
+ /// Valid index within stored byte array.
+ /// The byte value at the given index of the stored byte array.
+ public byte this[int index]
+ {
+ get { return _data[index]; }
+ set { _data[index] = value; }
+ }
+
+ ///
+ /// Converts a system byte array to a byte array node representing the same data.
+ ///
+ /// A byte array.
+ /// A new byte array node containing the given value.
+ public static implicit operator TagNodeByteArray (byte[] b)
+ {
+ return new TagNodeByteArray(b);
+ }
+
+ ///
+ /// Converts a byte array node to a system byte array representing the same data.
+ ///
+ /// A byte array node.
+ /// A system byte array set to the node's data.
+ public static implicit operator byte[] (TagNodeByteArray b)
+ {
+ return b._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeCompound.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeCompound.cs
new file mode 100644
index 0000000..11b716a
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeCompound.cs
@@ -0,0 +1,265 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a compound tag type containing other nodes.
+ ///
+ public sealed class TagNodeCompound : TagNode, IDictionary
+ {
+ private Dictionary _tags;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeCompound ToTagCompound ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_STRING tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_COMPOUND;
+ }
+
+ ///
+ /// Gets the number of subnodes contained in node.
+ ///
+ public int Count
+ {
+ get { return _tags.Count; }
+ }
+
+ ///
+ /// Constructs a new empty compound node.
+ ///
+ public TagNodeCompound ()
+ {
+ _tags = new Dictionary();
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new compound node containing new subnodes representing the same data.
+ public override TagNode Copy ()
+ {
+ TagNodeCompound list = new TagNodeCompound();
+ foreach (KeyValuePair item in _tags) {
+ list[item.Key] = item.Value.Copy();
+ }
+ return list;
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _tags.ToString();
+ }
+
+ #region IDictionary Members
+
+ ///
+ /// Adds a named subnode to the set.
+ ///
+ /// The name of the subnode.
+ /// The subnode to add.
+ /// is null.
+ /// A subnode with the same key already exists in the set.
+ public void Add (string key, TagNode value)
+ {
+ _tags.Add(key, value);
+ }
+
+ ///
+ /// Checks if a subnode exists in the set with the specified name.
+ ///
+ /// The name of a subnode to check.
+ /// Status indicating whether a subnode with the specified name exists.
+ /// is null.
+ public bool ContainsKey (string key)
+ {
+ return _tags.ContainsKey(key);
+ }
+
+ ///
+ /// Gets a collection containing all the names of subnodes in this set.
+ ///
+ public ICollection Keys
+ {
+ get { return _tags.Keys; }
+ }
+
+ ///
+ /// Removes a subnode with the specified name.
+ ///
+ /// The name of the subnode to remove.
+ /// Status indicating whether a subnode was removed.
+ /// is null.
+ public bool Remove (string key)
+ {
+ return _tags.Remove(key);
+ }
+
+ ///
+ /// Gets the subnode associated with the given name.
+ ///
+ /// The name of the subnode to get.
+ /// When the function returns, contains the subnode assicated with the specified key. If no subnode was found, contains a default value.
+ /// Status indicating whether a subnode was found.
+ /// is null.
+ public bool TryGetValue (string key, out TagNode value)
+ {
+ return _tags.TryGetValue(key, out value);
+ }
+
+ ///
+ /// Gets a collection containing all the subnodes in this set.
+ ///
+ public ICollection Values
+ {
+ get { return _tags.Values; }
+ }
+
+ ///
+ /// Gets or sets the subnode with the associated name.
+ ///
+ /// The name of the subnode to get or set.
+ /// is null.
+ /// The property is retrieved and key does not exist in the collection.
+ public TagNode this[string key]
+ {
+ get
+ {
+ return _tags[key];
+ }
+ set
+ {
+ _tags[key] = value;
+ }
+ }
+
+ #endregion
+
+ #region ICollection> Members
+
+ ///
+ /// Adds a subnode to the to the set with the specified name.
+ ///
+ /// The structure representing the key and subnode to add to the set.
+ /// The key of is null.
+ /// A subnode with the same key already exists in the set.
+ public void Add (KeyValuePair item)
+ {
+ _tags.Add(item.Key, item.Value);
+ }
+
+ ///
+ /// Removes all of the subnodes from this node.
+ ///
+ public void Clear ()
+ {
+ _tags.Clear();
+ }
+
+ ///
+ /// Checks if a specific subnode with a specific name is contained in the set.
+ ///
+ /// The structure representing the key and subnode to look for.
+ /// Status indicating if the subnode and key combination exists in the set.
+ public bool Contains (KeyValuePair item)
+ {
+ TagNode value;
+ if (!_tags.TryGetValue(item.Key, out value)) {
+ return false;
+ }
+ return value == item.Value;
+ }
+
+ ///
+ /// Copies the elements of the to an array of type , starting at the specified array index.
+ ///
+ /// The one-dimensional that is the destination of the subnodes copied. The Array must have zero-based indexing.
+ /// The zero-based index in at which copying begins.
+ /// is null.
+ /// is less than 0.
+ /// The number of elements in the source is greater than the available space from to the end of the destination .
+ public void CopyTo (KeyValuePair[] array, int arrayIndex)
+ {
+ if (array == null) {
+ throw new ArgumentNullException();
+ }
+ if (arrayIndex < 0) {
+ throw new ArgumentOutOfRangeException();
+ }
+ if (array.Length - arrayIndex < _tags.Count) {
+ throw new ArgumentException();
+ }
+
+ foreach (KeyValuePair item in _tags) {
+ array[arrayIndex] = item;
+ arrayIndex++;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the node is readonly.
+ ///
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Removes the specified key and subnode combination from the set.
+ ///
+ /// The structure representing the key and value to remove from the set.
+ /// Status indicating whether a subnode was removed.
+ public bool Remove (KeyValuePair item)
+ {
+ if (Contains(item)) {
+ _tags.Remove(item.Key);
+ return true;
+ }
+ return false;
+ }
+
+ #endregion
+
+ #region IEnumerable> Members
+
+ ///
+ /// Returns an enumerator that iterates through all of the subnodes in the set.
+ ///
+ /// An enumerator for this node.
+ public IEnumerator> GetEnumerator ()
+ {
+ return _tags.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Returns an enumerator that iterates through all of the subnodes in the set.
+ ///
+ /// An enumerator for this node.
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return _tags.GetEnumerator();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeDouble.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeDouble.cs
new file mode 100644
index 0000000..8cdbbb3
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeDouble.cs
@@ -0,0 +1,101 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a double-precision floating point tag type.
+ ///
+ public sealed class TagNodeDouble : TagNode
+ {
+ private double _data = 0;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeDouble ToTagDouble ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_DOUBLE tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_DOUBLE;
+ }
+
+ ///
+ /// Gets or sets a double of tag data.
+ ///
+ public double Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Constructs a new double node with a data value of 0.0.
+ ///
+ public TagNodeDouble () { }
+
+ ///
+ /// Constructs a new double node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeDouble (double d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new double node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeDouble(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system float to a double node representing the same value.
+ ///
+ /// A float value.
+ /// A new double node containing the given value.
+ public static implicit operator TagNodeDouble (float f)
+ {
+ return new TagNodeDouble(f);
+ }
+
+ ///
+ /// Converts a system double to a double node representing the same value.
+ ///
+ /// A double value.
+ /// A new double node containing the given value.
+ public static implicit operator TagNodeDouble (double d)
+ {
+ return new TagNodeDouble(d);
+ }
+
+ ///
+ /// Converts a double node to a system double representing the same value.
+ ///
+ /// A double node.
+ /// A system double set to the node's data value.
+ public static implicit operator double (TagNodeDouble d)
+ {
+ return d._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeFloat.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeFloat.cs
new file mode 100644
index 0000000..bce9c20
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeFloat.cs
@@ -0,0 +1,121 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a single-precision floating point tag type.
+ ///
+ public sealed class TagNodeFloat : TagNode
+ {
+ private float _data = 0;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeFloat ToTagFloat ()
+ {
+ return this;
+ }
+
+ ///
+ /// Converts the node to a new double node.
+ ///
+ /// A double node representing the same data.
+ public override TagNodeDouble ToTagDouble ()
+ {
+ return new TagNodeDouble(_data);
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_FLOAT tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_FLOAT;
+ }
+
+ ///
+ /// Checks if the node is castable to another node of a given tag type.
+ ///
+ /// An NBT tag type.
+ /// Status indicating whether this object could be cast to a node type represented by the given tag type.
+ public override bool IsCastableTo (TagType type)
+ {
+ return (type == TagType.TAG_FLOAT ||
+ type == TagType.TAG_DOUBLE);
+ }
+
+ ///
+ /// Gets or sets a float of tag data.
+ ///
+ public float Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Constructs a new float node with a data value of 0.0.
+ ///
+ public TagNodeFloat () { }
+
+ ///
+ /// Constructs a new float node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeFloat (float d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new float node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeFloat(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system float to a float node representing the same value.
+ ///
+ /// A float value.
+ /// A new float node containing the given value.
+ public static implicit operator TagNodeFloat (float f)
+ {
+ return new TagNodeFloat(f);
+ }
+
+ ///
+ /// Converts a float node to a system float representing the same value.
+ ///
+ /// A float node.
+ /// A system float set to the node's data value.
+ public static implicit operator float (TagNodeFloat f)
+ {
+ return f._data;
+ }
+
+ ///
+ /// Converts a float node to a system double representing the same value.
+ ///
+ /// A float node.
+ /// A system double set to the node's data value.
+ public static implicit operator double (TagNodeFloat f)
+ {
+ return f._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeInt.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeInt.cs
new file mode 100644
index 0000000..a1bbbcc
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeInt.cs
@@ -0,0 +1,141 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a signed int tag type.
+ ///
+ public sealed class TagNodeInt : TagNode
+ {
+ private int _data = 0;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeInt ToTagInt ()
+ {
+ return this;
+ }
+
+ ///
+ /// Converts the node to a new long node.
+ ///
+ /// A long node representing the same data.
+ public override TagNodeLong ToTagLong ()
+ {
+ return new TagNodeLong(_data);
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_INT tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_INT;
+ }
+
+ ///
+ /// Checks if the node is castable to another node of a given tag type.
+ ///
+ /// An NBT tag type.
+ /// Status indicating whether this object could be cast to a node type represented by the given tag type.
+ public override bool IsCastableTo (TagType type)
+ {
+ return (type == TagType.TAG_INT ||
+ type == TagType.TAG_LONG);
+ }
+
+ ///
+ /// Gets or sets an int of tag data.
+ ///
+ public int Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Constructs a new int node with a data value of 0.
+ ///
+ public TagNodeInt () { }
+
+ ///
+ /// Constructs a new int node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeInt (int d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new int node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeInt(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system byte to an int node representing the same value.
+ ///
+ /// A byte value.
+ /// A new int node containing the given value.
+ public static implicit operator TagNodeInt (byte b)
+ {
+ return new TagNodeInt(b);
+ }
+
+ ///
+ /// Converts a system short to an int node representing the same value.
+ ///
+ /// A short value.
+ /// A new int node containing the given value.
+ public static implicit operator TagNodeInt (short s)
+ {
+ return new TagNodeInt(s);
+ }
+
+ ///
+ /// Converts a system int to an int node representing the same value.
+ ///
+ /// An int value.
+ /// A new int node containing the given value.
+ public static implicit operator TagNodeInt (int i)
+ {
+ return new TagNodeInt(i);
+ }
+
+ ///
+ /// Converts an int node to a system int representing the same value.
+ ///
+ /// An int node.
+ /// A system int set to the node's data value.
+ public static implicit operator int (TagNodeInt i)
+ {
+ return i._data;
+ }
+
+ ///
+ /// Converts an int node to a system long representing the same value.
+ ///
+ /// An int node.
+ /// A system long set to the node's data value.
+ public static implicit operator long (TagNodeInt i)
+ {
+ return i._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeList.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeList.cs
new file mode 100644
index 0000000..8cbc01e
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeList.cs
@@ -0,0 +1,264 @@
+using System;
+using System.Collections.Generic;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a list tag type containing other nodes.
+ ///
+ ///
+ /// A list node contains 0 or more nodes of the same type. The nodes are unnamed
+ /// but can be accessed by sequential index.
+ ///
+ public sealed class TagNodeList : TagNode, IList
+ {
+ private TagType _type = TagType.TAG_END;
+
+ private List _items = null;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeList ToTagList ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_STRING tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_LIST;
+ }
+
+ ///
+ /// Gets the number of subnodes contained in the list.
+ ///
+ public int Count
+ {
+ get { return _items.Count; }
+ }
+
+ ///
+ /// Gets the tag type of the subnodes contained in the list.
+ ///
+ public TagType ValueType
+ {
+ get { return _type; }
+ }
+
+ ///
+ /// Constructs a new empty list node.
+ ///
+ /// The tag type of the list's subnodes.
+ public TagNodeList (TagType type)
+ {
+ _type = type;
+ _items = new List();
+ }
+
+ ///
+ /// Constructs a new list node from a list of nodes.
+ ///
+ /// The tag type of the list's subnodes.
+ /// A list containing node objects matching the type parameter.
+ public TagNodeList (TagType type, List items)
+ {
+ _type = type;
+ _items = items;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new list node containing new subnodes representing the same data.
+ public override TagNode Copy ()
+ {
+ TagNodeList list = new TagNodeList(_type);
+ foreach (TagNode item in _items) {
+ list.Add(item.Copy());
+ }
+ return list;
+ }
+
+ ///
+ /// Retrieves all the subnodes that match the conditions defined by the specified predicate.
+ ///
+ /// The delegate that defines the conditions of the subnode to search for.
+ /// A list of all subnodes matching the predicate.
+ public List FindAll (Predicate match)
+ {
+ return _items.FindAll(match);
+ }
+
+ ///
+ /// Removes all subnodes that match the conditions defined by the specified predicate.
+ ///
+ /// The delegate that defines the conditions of the subnode to search for.
+ /// The number of subnodes removed from the node.
+ public int RemoveAll (Predicate match)
+ {
+ return _items.RemoveAll(match);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _items.ToString();
+ }
+
+ #region IList Members
+
+ ///
+ /// Searches for the specified subnode and returns the zero-based index of the first occurrence within the entire node's list.
+ ///
+ /// The subnode to locate.
+ /// The zero-based index of the subnode within the node's list if found, or -1 otherwise.
+ public int IndexOf (TagNode item)
+ {
+ return _items.IndexOf(item);
+ }
+
+ ///
+ /// Inserts a subnode into the node's list at the specified index.
+ ///
+ /// The zero-based index at which the subnode should be inserted.
+ /// The subnode to insert.
+ /// Thrown when a subnode being inserted has the wrong tag type.
+ public void Insert (int index, TagNode item)
+ {
+ if (item.GetTagType() != _type) {
+ throw new ArgumentException("The tag type of item is invalid for this node");
+ }
+ _items.Insert(index, item);
+ }
+
+ ///
+ /// Removes the subnode from the node's list at the specified index.
+ ///
+ /// The zero-based index to remove a subnode at.
+ public void RemoveAt (int index)
+ {
+ _items.RemoveAt(index);
+ }
+
+ ///
+ /// Gets or sets the subnode in the node's list at the specified index.
+ ///
+ /// The zero-based index to get or set from.
+ /// The subnode at the specified index.
+ /// Thrown when a subnode being assigned has the wrong tag type.
+ public TagNode this[int index]
+ {
+ get
+ {
+ return _items[index];
+ }
+ set
+ {
+ if (value.GetTagType() != _type) {
+ throw new ArgumentException("The tag type of the assigned subnode is invalid for this node");
+ }
+ _items[index] = value;
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ ///
+ /// Adds a subnode to the end of the node's list.
+ ///
+ /// The subnode to add.
+ /// Thrown when a subnode being added has the wrong tag type.
+ public void Add (TagNode item)
+ {
+ if (item.GetTagType() != _type) {
+ throw new ArgumentException("The tag type of item is invalid for this node");
+ }
+
+ _items.Add(item);
+ }
+
+ ///
+ /// Removes all subnode's from the node's list.
+ ///
+ public void Clear ()
+ {
+ _items.Clear();
+ }
+
+ ///
+ /// Checks if a subnode is contained within the node's list.
+ ///
+ /// The subnode to check for existance.
+ /// Status indicating if the subnode exists in the node's list.
+ public bool Contains (TagNode item)
+ {
+ return _items.Contains(item);
+ }
+
+ ///
+ /// Copies the entire node's list to a compatible one-dimensional array, starting at the specified index of the target array.
+ ///
+ /// The one-dimensional that is the destination of the subnodes copied. The Array must have zero-based indexing.
+ /// The zero-based index in at which copying begins.
+ public void CopyTo (TagNode[] array, int arrayIndex)
+ {
+ _items.CopyTo(array, arrayIndex);
+ }
+
+ ///
+ /// Gets a value indicating whether the node is readonly.
+ ///
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Removes the first occurance of a subnode from the node's list.
+ ///
+ /// The subnode to remove.
+ /// Status indicating whether a subnode was removed.
+ public bool Remove (TagNode item)
+ {
+ return _items.Remove(item);
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Returns an enumerator that iterates through all of the subnodes in the node's list.
+ ///
+ /// An enumerator for this node.
+ public IEnumerator GetEnumerator ()
+ {
+ return _items.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ ///
+ /// Returns an enumerator that iterates through all of the subnodes in the node's list.
+ ///
+ /// An enumerator for this node.
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return _items.GetEnumerator();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeLong.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeLong.cs
new file mode 100644
index 0000000..5d6bad3
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeLong.cs
@@ -0,0 +1,121 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a signed long tag type.
+ ///
+ public sealed class TagNodeLong : TagNode
+ {
+ private long _data = 0;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeLong ToTagLong ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_LONG tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_LONG;
+ }
+
+ ///
+ /// Gets or sets a long of tag data.
+ ///
+ public long Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Constructs a new long node with a data value of 0.
+ ///
+ public TagNodeLong () { }
+
+ ///
+ /// Constructs a new long node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeLong (long d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new long node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeLong(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system byte to a long node representing the same value.
+ ///
+ /// A byte value.
+ /// A new long node containing the given value.
+ public static implicit operator TagNodeLong (byte b)
+ {
+ return new TagNodeLong(b);
+ }
+
+ ///
+ /// Converts a system shprt to a long node representing the same value.
+ ///
+ /// A short value.
+ /// A new long node containing the given value.
+ public static implicit operator TagNodeLong (short s)
+ {
+ return new TagNodeLong(s);
+ }
+
+ ///
+ /// Converts a system int to a long node representing the same value.
+ ///
+ /// An int value.
+ /// A new long node containing the given value.
+ public static implicit operator TagNodeLong (int i)
+ {
+ return new TagNodeLong(i);
+ }
+
+ ///
+ /// Converts a system long to a long node representing the same value.
+ ///
+ /// A long value.
+ /// A new long node containing the given value.
+ public static implicit operator TagNodeLong (long l)
+ {
+ return new TagNodeLong(l);
+ }
+
+ ///
+ /// Converts a long node to a system long representing the same value.
+ ///
+ /// A long node.
+ /// A system long set to the node's data value.
+ public static implicit operator long (TagNodeLong l)
+ {
+ return l._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeNull.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeNull.cs
new file mode 100644
index 0000000..bc1b496
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeNull.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a null tag type.
+ ///
+ public sealed class TagNodeNull : TagNode
+ {
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeNull ToTagNull ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_END tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_END;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new null node.
+ public override TagNode Copy ()
+ {
+ return new TagNodeNull();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeShort.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeShort.cs
new file mode 100644
index 0000000..3343e08
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeShort.cs
@@ -0,0 +1,151 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a signed short tag type.
+ ///
+ public sealed class TagNodeShort : TagNode
+ {
+ private short _data = 0;
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeShort ToTagShort ()
+ {
+ return this;
+ }
+
+ ///
+ /// Converts the node to a new int node.
+ ///
+ /// An int node representing the same data.
+ public override TagNodeInt ToTagInt ()
+ {
+ return new TagNodeInt(_data);
+ }
+
+ ///
+ /// Converts the node to a new long node.
+ ///
+ /// A long node representing the same data.
+ public override TagNodeLong ToTagLong ()
+ {
+ return new TagNodeLong(_data);
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_SHORT tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_SHORT;
+ }
+
+ ///
+ /// Checks if the node is castable to another node of a given tag type.
+ ///
+ /// An NBT tag type.
+ /// Status indicating whether this object could be cast to a node type represented by the given tag type.
+ public override bool IsCastableTo (TagType type)
+ {
+ return (type == TagType.TAG_SHORT ||
+ type == TagType.TAG_INT ||
+ type == TagType.TAG_LONG);
+ }
+
+ ///
+ /// Gets or sets a short of tag data.
+ ///
+ public short Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Constructs a new short node with a data value of 0.
+ ///
+ public TagNodeShort () { }
+
+ ///
+ /// Constructs a new short node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeShort (short d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new short node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeShort(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system byte to a short node representing the same value.
+ ///
+ /// A byte value.
+ /// A new short node containing the given value.
+ public static implicit operator TagNodeShort (byte b)
+ {
+ return new TagNodeShort(b);
+ }
+
+ ///
+ /// Converts a system short to a short node representing the same value.
+ ///
+ /// A short value.
+ /// A new short node containing the given value.
+ public static implicit operator TagNodeShort (short s)
+ {
+ return new TagNodeShort(s);
+ }
+
+ ///
+ /// Converts a short node to a system short representing the same value.
+ ///
+ /// A short node.
+ /// A system short set to the node's data value.
+ public static implicit operator short (TagNodeShort s)
+ {
+ return s._data;
+ }
+
+ ///
+ /// Converts a short node to a system int representing the same value.
+ ///
+ /// A short node.
+ /// A system int set to the node's data value.
+ public static implicit operator int (TagNodeShort s)
+ {
+ return s._data;
+ }
+
+ ///
+ /// Converts a short node to a system long representing the same value.
+ ///
+ /// A short node.
+ /// A system long set to the node's data value.
+ public static implicit operator long (TagNodeShort s)
+ {
+ return s._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagNodeString.cs b/Substrate/SubstrateCS/Source/Nbt/TagNodeString.cs
new file mode 100644
index 0000000..539bfc6
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagNodeString.cs
@@ -0,0 +1,99 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// An NBT node representing a string tag type.
+ ///
+ public sealed class TagNodeString : TagNode
+ {
+ private string _data = "";
+
+ ///
+ /// Converts the node to itself.
+ ///
+ /// A reference to itself.
+ public override TagNodeString ToTagString ()
+ {
+ return this;
+ }
+
+ ///
+ /// Gets the tag type of the node.
+ ///
+ /// The TAG_STRING tag type.
+ public override TagType GetTagType ()
+ {
+ return TagType.TAG_STRING;
+ }
+
+ ///
+ /// Gets or sets a string of tag data.
+ ///
+ public string Data
+ {
+ get { return _data; }
+ set { _data = value; }
+ }
+
+ ///
+ /// Gets the length of the stored string.
+ ///
+ public int Length
+ {
+ get { return _data.Length; }
+ }
+
+ ///
+ /// Constructs a new byte array node with an empty string.
+ ///
+ public TagNodeString () { }
+
+ ///
+ /// Constructs a new string node.
+ ///
+ /// The value to set the node's tag data value.
+ public TagNodeString (string d)
+ {
+ _data = d;
+ }
+
+ ///
+ /// Makes a deep copy of the node.
+ ///
+ /// A new string node representing the same data.
+ public override TagNode Copy ()
+ {
+ return new TagNodeString(_data);
+ }
+
+ ///
+ /// Gets a string representation of the node's data.
+ ///
+ /// String representation of the node's data.
+ public override string ToString ()
+ {
+ return _data.ToString();
+ }
+
+ ///
+ /// Converts a system string to a string node representing the same data.
+ ///
+ /// A string.
+ /// A new string node containing the given value.
+ public static implicit operator TagNodeString (string s)
+ {
+ return new TagNodeString(s);
+ }
+
+ ///
+ /// Converts a string node to a system string representing the same data.
+ ///
+ /// A string node.
+ /// A system string set to the node's data.
+ public static implicit operator string (TagNodeString s)
+ {
+ return s._data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/TagType.cs b/Substrate/SubstrateCS/Source/Nbt/TagType.cs
new file mode 100644
index 0000000..e48a0e6
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/TagType.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// Defines the type of an NBT tag.
+ ///
+ public enum TagType
+ {
+ ///
+ /// A null tag, used to terminate lists.
+ ///
+ TAG_END = 0,
+
+ ///
+ /// A tag containing an 8-bit signed integer.
+ ///
+ TAG_BYTE = 1,
+
+ ///
+ /// A tag containing a 16-bit signed integer.
+ ///
+ TAG_SHORT = 2,
+
+ ///
+ /// A tag containing a 32-bit signed integer.
+ ///
+ TAG_INT = 3,
+
+ ///
+ /// A tag containing a 64-bit signed integer.
+ ///
+ TAG_LONG = 4,
+
+ ///
+ /// A tag containing a 32-bit (single precision) floating-point value.
+ ///
+ TAG_FLOAT = 5,
+
+ ///
+ /// A tag containing a 64-bit (double precision) floating-point value.
+ ///
+ TAG_DOUBLE = 6,
+
+ ///
+ /// A tag containing an array of unsigned 8-bit byte values.
+ ///
+ TAG_BYTE_ARRAY = 7,
+
+ ///
+ /// A tag containing a string of text.
+ ///
+ TAG_STRING = 8,
+
+ ///
+ /// A tag containing a sequential list of tags, where all tags of of the same type.
+ ///
+ TAG_LIST = 9,
+
+ ///
+ /// A tag containing a key-value store of tags, where each tag can be of any type.
+ ///
+ TAG_COMPOUND = 10
+ }
+}
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/Nbt/VerifierLogger.cs b/Substrate/SubstrateCS/Source/Nbt/VerifierLogger.cs
new file mode 100644
index 0000000..0f1eaf5
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Nbt/VerifierLogger.cs
@@ -0,0 +1,47 @@
+using System;
+using Substrate.Core;
+
+namespace Substrate.Nbt
+{
+ ///
+ /// A collection of static methods that can be hooked into events for logging NBT errors to the console.
+ ///
+ public static class VerifierLogger
+ {
+ ///
+ /// Logs an occurance of a missing tag error, and advances to the next event in the event chain.
+ ///
+ /// Data about the NBT node being verified.
+ /// A indicating whether event processing should pass, fail, or advance.
+ public static TagEventCode MissingTagHandler (TagEventArgs e)
+ {
+ Console.WriteLine("Missing Tag Error: '{0}'", e.TagName);
+
+ return TagEventCode.NEXT;
+ }
+
+ ///
+ /// Logs an occurance of an invalid tag type error, and advances to the next event in the event chain.
+ ///
+ /// Data about the NBT node being verified.
+ /// A indicating whether event processing should pass, fail, or advance.
+ public static TagEventCode InvalidTagTypeHandler (TagEventArgs e)
+ {
+ Console.WriteLine("Invalid Tag Type Error: '{0}' has type '{1}', expected '{2}'", e.TagName, e.Tag.GetTagType(), e.Schema.ToString());
+
+ return TagEventCode.NEXT;
+ }
+
+ ///
+ /// Logs an occurance of an invalid tag value error, and advances to the next event in the event chain.
+ ///
+ /// Data about the NBT node being verified.
+ /// A indicating whether event processing should pass, fail, or advance.
+ public static TagEventCode InvalidTagValueHandler (TagEventArgs e)
+ {
+ Console.WriteLine("Invalid Tag Value Error: '{0}' of type '{1}' is set to invalid value '{2}'", e.TagName, e.Tag.GetTagType(), e.Tag.ToString());
+
+ return TagEventCode.NEXT;
+ }
+ }
+}
\ No newline at end of file