From ad3f3d336d4936ca4b66d0d757636936597ae53e Mon Sep 17 00:00:00 2001 From: Justin Aquadro Date: Thu, 30 Jun 2011 04:44:46 +0000 Subject: [PATCH] I really messed this transaction up. --- .../SubstrateCS/Source/Nbt/INBTObject.cs | 38 ++ .../SubstrateCS/Source/Nbt/JSONSerializer.cs | 173 ++++++ Substrate/SubstrateCS/Source/Nbt/NBT.cs | 534 ++++++++++++++++++ .../SubstrateCS/Source/Nbt/NBTVerifier.cs | 386 +++++++++++++ .../SubstrateCS/Source/Nbt/SchemaNode.cs | 60 ++ .../SubstrateCS/Source/Nbt/SchemaNodeArray.cs | 81 +++ .../Source/Nbt/SchemaNodeCompound.cs | 219 +++++++ .../SubstrateCS/Source/Nbt/SchemaNodeList.cs | 172 ++++++ .../Source/Nbt/SchemaNodeScaler.cs | 75 +++ .../Source/Nbt/SchemaNodeString.cs | 116 ++++ .../SubstrateCS/Source/Nbt/SchemaOptions.cs | 21 + Substrate/SubstrateCS/Source/Nbt/TagNode.cs | 140 +++++ .../SubstrateCS/Source/Nbt/TagNodeByte.cs | 161 ++++++ .../Source/Nbt/TagNodeByteArray.cs | 113 ++++ .../SubstrateCS/Source/Nbt/TagNodeCompound.cs | 265 +++++++++ .../SubstrateCS/Source/Nbt/TagNodeDouble.cs | 101 ++++ .../SubstrateCS/Source/Nbt/TagNodeFloat.cs | 121 ++++ .../SubstrateCS/Source/Nbt/TagNodeInt.cs | 141 +++++ .../SubstrateCS/Source/Nbt/TagNodeList.cs | 264 +++++++++ .../SubstrateCS/Source/Nbt/TagNodeLong.cs | 121 ++++ .../SubstrateCS/Source/Nbt/TagNodeNull.cs | 37 ++ .../SubstrateCS/Source/Nbt/TagNodeShort.cs | 151 +++++ .../SubstrateCS/Source/Nbt/TagNodeString.cs | 99 ++++ Substrate/SubstrateCS/Source/Nbt/TagType.cs | 65 +++ .../SubstrateCS/Source/Nbt/VerifierLogger.cs | 47 ++ 25 files changed, 3701 insertions(+) create mode 100644 Substrate/SubstrateCS/Source/Nbt/INBTObject.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/JSONSerializer.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/NBT.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/NBTVerifier.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaNode.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaNodeArray.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaNodeCompound.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaNodeList.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaNodeScaler.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaNodeString.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/SchemaOptions.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNode.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeByte.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeByteArray.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeCompound.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeDouble.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeFloat.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeInt.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeList.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeLong.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeNull.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeShort.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagNodeString.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/TagType.cs create mode 100644 Substrate/SubstrateCS/Source/Nbt/VerifierLogger.cs 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