From 16369b68e3a65fc8f7f42a66bb6ba0a9d1276aff Mon Sep 17 00:00:00 2001 From: ForeverZer0 Date: Tue, 31 Aug 2021 01:32:36 -0400 Subject: [PATCH] Implemented generating SNBT from tags --- SharpNBT.Tests/StringifiedTest.cs | 30 ++++++++++ SharpNBT/SNBT/Lexer.cs | 95 +++++++++++++++++++++++++++++++ SharpNBT/Tags/ByteArrayTag.cs | 7 +++ SharpNBT/Tags/ByteTag.cs | 8 +++ SharpNBT/Tags/CompoundTag.cs | 27 +++++++++ SharpNBT/Tags/DoubleTag.cs | 7 +++ SharpNBT/Tags/EndTag.cs | 7 +++ SharpNBT/Tags/FloatTag.cs | 7 +++ SharpNBT/Tags/IntArrayTag.cs | 7 +++ SharpNBT/Tags/IntTag.cs | 7 +++ SharpNBT/Tags/ListTag.cs | 16 +++++- SharpNBT/Tags/LongArrayTag.cs | 7 +++ SharpNBT/Tags/LongTag.cs | 7 +++ SharpNBT/Tags/ShortTag.cs | 7 +++ SharpNBT/Tags/StringTag.cs | 7 +++ SharpNBT/Tags/Tag.cs | 22 +++++++ 16 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 SharpNBT.Tests/StringifiedTest.cs create mode 100644 SharpNBT/SNBT/Lexer.cs diff --git a/SharpNBT.Tests/StringifiedTest.cs b/SharpNBT.Tests/StringifiedTest.cs new file mode 100644 index 0000000..4ec3584 --- /dev/null +++ b/SharpNBT.Tests/StringifiedTest.cs @@ -0,0 +1,30 @@ +using Xunit; +using Xunit.Abstractions; + +namespace SharpNBT.Tests +{ + public class StringifiedTest + { + private readonly ITestOutputHelper output; + + + public StringifiedTest(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void BigOutput() + { + var tag = TestHelper.GetTag("bigtest.nbt", CompressionType.GZip); + output.WriteLine(tag.Stringify(true)); + } + + [Fact] + public void HelloWorldOutput() + { + var tag = TestHelper.GetTag("hello_world.nbt", CompressionType.None); + output.WriteLine(tag.Stringify(true)); + } + } +} \ No newline at end of file diff --git a/SharpNBT/SNBT/Lexer.cs b/SharpNBT/SNBT/Lexer.cs new file mode 100644 index 0000000..c6b40aa --- /dev/null +++ b/SharpNBT/SNBT/Lexer.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace SharpNBT.SNBT +{ + internal enum TokenType + { + None, + CompoundBegin, + CompoundEnd, + Identifier, + String, + Separator, + Comma, + ByteArray, + IntArray, + LongArray, + ListArray, + EndArray, + Float, + Double, + Byte, + Short, + Long, + Int, + WhiteSpace, + Char + } + + internal sealed class LexerRule + { + internal delegate string PostProcessHandler(string input); + + private readonly Regex regex; + + public TokenType Type { get; } + + public LexerRule(TokenType type, string pattern) : this(type, new Regex(pattern, RegexOptions.Multiline | RegexOptions.CultureInvariant)) + { + } + + public LexerRule(TokenType type, Regex regex) + { + Type = type; + this.regex = regex ?? throw new ArgumentNullException(nameof(regex)); + } + + public LexerRule(TokenType type, Regex regex, PostProcessHandler handler) + { + + } + } + + internal class Lexer + { + private static readonly List rules; + + static Lexer() + { + rules = new List + { + new LexerRule(TokenType.CompoundBegin, @"\{[\s]*"), + new LexerRule(TokenType.CompoundEnd, @"[\s]*\}"), + new LexerRule(TokenType.Identifier, "\".+?\"(?=:)"), + new LexerRule(TokenType.Identifier, "'.+?'(?=:) "), + new LexerRule(TokenType.Identifier, "A-Za-z0-9_-]+?(?=:) "), + new LexerRule(TokenType.String, "\".*?\""), + new LexerRule(TokenType.String, "'.*?'"), + new LexerRule(TokenType.Separator, @"[\s]*:[\s]*"), + new LexerRule(TokenType.Comma, @"[\s]*,[\s]*"), + new LexerRule(TokenType.ByteArray, @"\[B;[\s]*?"), + new LexerRule(TokenType.IntArray, @"\[I;[\s]*?"), + new LexerRule(TokenType.LongArray, @"\[L;[\s]*?"), + new LexerRule(TokenType.ListArray, @"\[[\s]*?"), + new LexerRule(TokenType.EndArray, @"[\s]*\]"), + new LexerRule(TokenType.Float, @"-?[0-9]*\.[0-9]+[Ff]"), + new LexerRule(TokenType.Double, @"-?[0-9]*\.[0-9]+[Dd]?"), + new LexerRule(TokenType.Byte, "-?([0-9]+)[Bb]"), + new LexerRule(TokenType.Short, "-?([0-9]+)[Ss]"), + new LexerRule(TokenType.Long, "-?([0-9]+)[Ll]"), + new LexerRule(TokenType.Int, "-?([0-9]+)"), + new LexerRule(TokenType.WhiteSpace, @"[\s]+"), + new LexerRule(TokenType.String, @"[\S]+"), + new LexerRule(TokenType.Char, ".") + }; + } + + public Lexer() + { + + } + } +} \ No newline at end of file diff --git a/SharpNBT/Tags/ByteArrayTag.cs b/SharpNBT/Tags/ByteArrayTag.cs index 151dec6..823c836 100644 --- a/SharpNBT/Tags/ByteArrayTag.cs +++ b/SharpNBT/Tags/ByteArrayTag.cs @@ -57,5 +57,12 @@ namespace SharpNBT var word = Count == 1 ? Strings.WordElement : Strings.WordElements; return $"TAG_Byte_Array({PrettyName}): [{Count} {word}]"; } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}[B;{string.Join(',', this)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/ByteTag.cs b/SharpNBT/Tags/ByteTag.cs index c0deb08..de1eb85 100644 --- a/SharpNBT/Tags/ByteTag.cs +++ b/SharpNBT/Tags/ByteTag.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.Serialization; +using System.Text; using JetBrains.Annotations; namespace SharpNBT @@ -69,5 +70,12 @@ namespace SharpNBT /// The tag represented as a . [CLSCompliant(false)] public static implicit operator sbyte(ByteTag tag) => unchecked((sbyte)tag.Value); + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value}B"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/CompoundTag.cs b/SharpNBT/Tags/CompoundTag.cs index 2485f1e..a0aeb4e 100644 --- a/SharpNBT/Tags/CompoundTag.cs +++ b/SharpNBT/Tags/CompoundTag.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using System.Text; using JetBrains.Annotations; @@ -112,5 +113,31 @@ namespace SharpNBT tag.PrettyPrinted(buffer, level + 1, indent); buffer.AppendLine(space + "}"); } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() + { + var strings = new string[Count]; + for (var i = 0; i < strings.Length; i++) + strings[i] = this[i].Stringify(); + + return $"{StringifyName}{{{string.Join(',', strings)}}}"; + } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// Flag indicating if this is the top-level tag that should be wrapped in braces. + /// This NBT tag in SNBT format. + /// + public string Stringify(bool topLevel) + { + var str = Stringify(); + return topLevel ? $"{{{str}}}" : str; + } } } \ No newline at end of file diff --git a/SharpNBT/Tags/DoubleTag.cs b/SharpNBT/Tags/DoubleTag.cs index e528aa8..d9e1bfa 100644 --- a/SharpNBT/Tags/DoubleTag.cs +++ b/SharpNBT/Tags/DoubleTag.cs @@ -37,5 +37,12 @@ namespace SharpNBT /// The tag to convert. /// The tag represented as a . public static implicit operator double(DoubleTag tag) => tag.Value; + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value:0.0}D"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/EndTag.cs b/SharpNBT/Tags/EndTag.cs index e1089e3..cdd7a75 100644 --- a/SharpNBT/Tags/EndTag.cs +++ b/SharpNBT/Tags/EndTag.cs @@ -24,5 +24,12 @@ namespace SharpNBT { // Do nothing } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => string.Empty; } } \ No newline at end of file diff --git a/SharpNBT/Tags/FloatTag.cs b/SharpNBT/Tags/FloatTag.cs index 6724d41..23dc26a 100644 --- a/SharpNBT/Tags/FloatTag.cs +++ b/SharpNBT/Tags/FloatTag.cs @@ -36,5 +36,12 @@ namespace SharpNBT /// The tag to convert. /// The tag represented as a . public static implicit operator float(FloatTag tag) => tag.Value; + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value:0.0}F"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/IntArrayTag.cs b/SharpNBT/Tags/IntArrayTag.cs index 1e0b031..1709d33 100644 --- a/SharpNBT/Tags/IntArrayTag.cs +++ b/SharpNBT/Tags/IntArrayTag.cs @@ -61,5 +61,12 @@ namespace SharpNBT var word = Count == 1 ? Strings.WordElement : Strings.WordElements; return $"TAG_Int_Array({PrettyName}): [{Count} {word}]"; } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}[I;{string.Join(',', this)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/IntTag.cs b/SharpNBT/Tags/IntTag.cs index 05dca84..0538c97 100644 --- a/SharpNBT/Tags/IntTag.cs +++ b/SharpNBT/Tags/IntTag.cs @@ -64,5 +64,12 @@ namespace SharpNBT /// The tag represented as a . [CLSCompliant(false)] public static implicit operator uint(IntTag tag) => unchecked((uint)tag.Value); + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value}"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/ListTag.cs b/SharpNBT/Tags/ListTag.cs index e8c12c3..5a51db6 100644 --- a/SharpNBT/Tags/ListTag.cs +++ b/SharpNBT/Tags/ListTag.cs @@ -72,7 +72,7 @@ namespace SharpNBT tag.PrettyPrinted(buffer, level + 1, indent); buffer.AppendLine(space + "}"); } - + /// /// Retrieves a "pretty-printed" multiline string representing the complete tree structure of the tag. /// @@ -85,5 +85,19 @@ namespace SharpNBT PrettyPrinted(buffer, 0, indent); return buffer.ToString(); } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() + { + var strings = new string[Count]; + for (var i = 0; i < strings.Length; i++) + strings[i] = this[i].Stringify(); + + return $"{StringifyName}[{string.Join(',', strings)}]"; + } } } \ No newline at end of file diff --git a/SharpNBT/Tags/LongArrayTag.cs b/SharpNBT/Tags/LongArrayTag.cs index c834092..f7c3178 100644 --- a/SharpNBT/Tags/LongArrayTag.cs +++ b/SharpNBT/Tags/LongArrayTag.cs @@ -60,5 +60,12 @@ namespace SharpNBT var word = Count == 1 ? Strings.WordElement : Strings.WordElements; return $"TAG_Long_Array({PrettyName}): [{Count} {word}]"; } + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}[L;{string.Join(',', this)}]"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/LongTag.cs b/SharpNBT/Tags/LongTag.cs index 3bd772e..0e34115 100644 --- a/SharpNBT/Tags/LongTag.cs +++ b/SharpNBT/Tags/LongTag.cs @@ -64,5 +64,12 @@ namespace SharpNBT /// The tag represented as a . [CLSCompliant(false)] public static implicit operator ulong(LongTag tag) => unchecked((ulong)tag.Value); + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value}L"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/ShortTag.cs b/SharpNBT/Tags/ShortTag.cs index 1b1cc4e..2e404e7 100644 --- a/SharpNBT/Tags/ShortTag.cs +++ b/SharpNBT/Tags/ShortTag.cs @@ -64,5 +64,12 @@ namespace SharpNBT /// The tag represented as a . [CLSCompliant(false)] public static implicit operator ushort(ShortTag tag) => unchecked((ushort)tag.Value); + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}{Value}S"; } } \ No newline at end of file diff --git a/SharpNBT/Tags/StringTag.cs b/SharpNBT/Tags/StringTag.cs index 0d58677..4c53992 100644 --- a/SharpNBT/Tags/StringTag.cs +++ b/SharpNBT/Tags/StringTag.cs @@ -37,5 +37,12 @@ namespace SharpNBT /// The tag to convert. /// The tag represented as a . public static implicit operator string(StringTag tag) => tag.Value; + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public override string Stringify() => $"{StringifyName}\"{Value}\""; } } \ No newline at end of file diff --git a/SharpNBT/Tags/Tag.cs b/SharpNBT/Tags/Tag.cs index 9d58b59..f518943 100644 --- a/SharpNBT/Tags/Tag.cs +++ b/SharpNBT/Tags/Tag.cs @@ -4,6 +4,7 @@ using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; +using System.Text.RegularExpressions; using JetBrains.Annotations; [assembly: CLSCompliant(true)] @@ -208,6 +209,27 @@ namespace SharpNBT /// Second value to compare. /// Result of comparison. public static bool operator !=(Tag left, Tag right) => !Equals(left, right); + + /// + /// Gets the string representation of this NBT tag (SNBT). + /// + /// This NBT tag in SNBT format. + /// + public abstract string Stringify(); + + /// + /// Gets the name in a formatted properly for SNBT. + /// + [NotNull] + protected internal string StringifyName + { + get + { + if (string.IsNullOrEmpty(Name)) + return string.Empty; + return Regex.IsMatch(Name, @"^[A-Ba-z0-9_-]+$") ? $"{Name}: " : $"\"{Name}\": "; + } + } } ///