From b4d755cf074e49fd63b658844e271fd445d8e79f Mon Sep 17 00:00:00 2001 From: RaphiMC <50594595+RaphiMC@users.noreply.github.com> Date: Tue, 11 Apr 2023 20:52:00 +0200 Subject: [PATCH] Rewrote nbt and text translation --- .../Protocol1_7_2_5to1_6_4.java | 4 +- .../rewriter/ChatComponentRewriter.java | 28 +- .../rewriter/ChatItemRewriter.java | 7 +- .../raphimc/vialegacy/util/NbtConverter.java | 65 +++ .../util/ViaStringTagReader1_11_2.java | 439 ------------------ .../util/ViaStringTagReader1_12_2.java | 425 ----------------- 6 files changed, 85 insertions(+), 883 deletions(-) create mode 100644 src/main/java/net/raphimc/vialegacy/util/NbtConverter.java delete mode 100644 src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_11_2.java delete mode 100644 src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_12_2.java diff --git a/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_7_2_5to1_6_4/Protocol1_7_2_5to1_6_4.java b/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_7_2_5to1_6_4/Protocol1_7_2_5to1_6_4.java index 523a723..6b8fbad 100644 --- a/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_7_2_5to1_6_4/Protocol1_7_2_5to1_6_4.java +++ b/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_7_2_5to1_6_4/Protocol1_7_2_5to1_6_4.java @@ -155,7 +155,7 @@ public class Protocol1_7_2_5to1_6_4 extends AbstractProtocol LegacyStringDeserializer.parse(c.asSingleString(), true).setStyle(c.getStyle())); + // Also convert "using" -> "with" in translatable components + return TextComponentSerializer.V1_7.serialize(newComponent); + } + + public static String toClientDisconnect(final String reason) { + return TextComponentSerializer.V1_7.serialize(new StringComponent(reason)); } } diff --git a/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/rewriter/ChatItemRewriter.java b/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/rewriter/ChatItemRewriter.java index c67cd82..cc85d46 100644 --- a/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/rewriter/ChatItemRewriter.java +++ b/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/rewriter/ChatItemRewriter.java @@ -29,9 +29,10 @@ import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.ShortTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.StringTag; import com.viaversion.viaversion.rewriter.ComponentRewriter; +import net.lenni0451.mcstructs.snbt.SNbtSerializer; import net.raphimc.vialegacy.ViaLegacy; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.Protocol1_8to1_7_6_10; -import net.raphimc.vialegacy.util.ViaStringTagReader1_11_2; +import net.raphimc.vialegacy.util.NbtConverter; import java.io.IOException; @@ -375,8 +376,8 @@ public class ChatItemRewriter { final CompoundTag tag; try { - tag = ViaStringTagReader1_11_2.getTagFromJson(text); - } catch (Exception e) { + tag = (CompoundTag) NbtConverter.mcStructToVia(SNbtSerializer.V1_7.deserialize(text)); + } catch (Throwable e) { ViaLegacy.getPlatform().getLogger().warning("Error reading NBT in show_item:" + text); throw new RuntimeException(e); } diff --git a/src/main/java/net/raphimc/vialegacy/util/NbtConverter.java b/src/main/java/net/raphimc/vialegacy/util/NbtConverter.java new file mode 100644 index 0000000..94043da --- /dev/null +++ b/src/main/java/net/raphimc/vialegacy/util/NbtConverter.java @@ -0,0 +1,65 @@ +/* + * This file is part of ViaLegacy - https://github.com/RaphiMC/ViaLegacy + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.vialegacy.util; + +import com.viaversion.viaversion.libs.opennbt.tag.builtin.*; +import net.lenni0451.mcstructs.nbt.INbtTag; + +import java.util.Map; + +public class NbtConverter { + + public static Tag mcStructToVia(final INbtTag tag) { + if (tag == null) return null; + if (tag instanceof net.lenni0451.mcstructs.nbt.tags.ByteTag) { + return new ByteTag(tag.asByteTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.ShortTag) { + return new ShortTag(tag.asShortTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.IntTag) { + return new IntTag(tag.asIntTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.LongTag) { + return new LongTag(tag.asLongTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.FloatTag) { + return new FloatTag(tag.asFloatTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.DoubleTag) { + return new DoubleTag(tag.asDoubleTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.ByteArrayTag) { + return new ByteArrayTag(tag.asByteArrayTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.StringTag) { + return new StringTag(tag.asStringTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.ListTag) { + final ListTag list = new ListTag(); + for (INbtTag e : tag.asListTag()) { + list.add(mcStructToVia(e)); + } + return list; + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.CompoundTag) { + final CompoundTag compound = new CompoundTag(); + for (Map.Entry e : tag.asCompoundTag().getValue().entrySet()) { + compound.put(e.getKey(), mcStructToVia(e.getValue())); + } + return compound; + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.IntArrayTag) { + return new IntArrayTag(tag.asIntArrayTag().getValue()); + } else if (tag instanceof net.lenni0451.mcstructs.nbt.tags.LongArrayTag) { + return new LongArrayTag(tag.asLongArrayTag().getValue()); + } + throw new IllegalArgumentException("Unsupported tag type: " + tag.getClass().getName()); + } + +} diff --git a/src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_11_2.java b/src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_11_2.java deleted file mode 100644 index fc8cf99..0000000 --- a/src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_11_2.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * This file is part of ViaLegacy - https://github.com/RaphiMC/ViaLegacy - * Copyright (C) 2023 RK_01/RaphiMC and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package net.raphimc.vialegacy.util; - -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.viaversion.viaversion.libs.opennbt.tag.builtin.*; - -import java.util.Stack; -import java.util.regex.Pattern; - -@Deprecated // Replace with mcstructs -public class ViaStringTagReader1_11_2 { - - private static final Pattern INT_ARRAY_MATCHER = Pattern.compile("\\[[-+\\d|,\\s]+\\]"); - - public static CompoundTag getTagFromJson(String jsonString) throws RuntimeException { - jsonString = jsonString.trim(); - - if (!jsonString.startsWith("{")) { - throw new RuntimeException("Invalid tag encountered, expected '{' as first char."); - } else if (topTagsCount(jsonString) != 1) { - throw new RuntimeException("Encountered multiple top tags, only one expected"); - } else { - return (CompoundTag) nameValueToNBT("tag", jsonString).parse(); - } - } - - static int topTagsCount(String str) throws RuntimeException { - int i = 0; - boolean flag = false; - Stack stack = new Stack<>(); - - for (int j = 0; j < str.length(); ++j) { - char c0 = str.charAt(j); - - if (c0 == 34) { - if (isCharEscaped(str, j)) { - if (!flag) { - throw new RuntimeException("Illegal use of \\\": " + str); - } - } else { - flag = !flag; - } - } else if (!flag) { - if (c0 != 123 && c0 != 91) { - if (c0 == 125 && (stack.isEmpty() || stack.pop() != 123)) { - throw new RuntimeException("Unbalanced curly brackets {}: " + str); - } - - if (c0 == 93 && (stack.isEmpty() || stack.pop() != 91)) { - throw new RuntimeException("Unbalanced square brackets []: " + str); - } - } else { - if (stack.isEmpty()) { - ++i; - } - - stack.push(c0); - } - } - } - - if (flag) { - throw new RuntimeException("Unbalanced quotation: " + str); - } else if (!stack.isEmpty()) { - throw new RuntimeException("Unbalanced brackets: " + str); - } else { - if (i == 0 && !str.isEmpty()) { - i = 1; - } - - return i; - } - } - - static ViaStringTagReader1_11_2.Any joinStrToNBT(String... args) throws RuntimeException { - return nameValueToNBT(args[0], args[1]); - } - - static ViaStringTagReader1_11_2.Any nameValueToNBT(String key, String value) throws RuntimeException { - value = value.trim(); - - if (value.startsWith("{")) { - value = value.substring(1, value.length() - 1); - ViaStringTagReader1_11_2.Compound jsontonbt$compound; - String s1; - - for (jsontonbt$compound = new ViaStringTagReader1_11_2.Compound(key); value.length() > 0; value = value.substring(s1.length() + 1)) { - s1 = nextNameValuePair(value, true); - - if (s1.length() > 0) { - jsontonbt$compound.tagList.add(getTagFromNameValue(s1, false)); - } - - if (value.length() < s1.length() + 1) { - break; - } - - char c1 = value.charAt(s1.length()); - - if (c1 != 44 && c1 != 123 && c1 != 125 && c1 != 91 && c1 != 93) { - throw new RuntimeException("Unexpected token '" + c1 + "' at: " + value.substring(s1.length())); - } - } - - return jsontonbt$compound; - } else if (value.startsWith("[") && !INT_ARRAY_MATCHER.matcher(value).matches()) { - value = value.substring(1, value.length() - 1); - ViaStringTagReader1_11_2.List jsontonbt$list; - String s; - - for (jsontonbt$list = new ViaStringTagReader1_11_2.List(key); value.length() > 0; value = value.substring(s.length() + 1)) { - s = nextNameValuePair(value, false); - - if (s.length() > 0) { - jsontonbt$list.tagList.add(getTagFromNameValue(s, true)); - } - - if (value.length() < s.length() + 1) { - break; - } - - char c0 = value.charAt(s.length()); - - if (c0 != 44 && c0 != 123 && c0 != 125 && c0 != 91 && c0 != 93) { - throw new RuntimeException("Unexpected token '" + c0 + "' at: " + value.substring(s.length())); - } - } - - return jsontonbt$list; - } else { - return new ViaStringTagReader1_11_2.Primitive(key, value); - } - } - - private static ViaStringTagReader1_11_2.Any getTagFromNameValue(String str, boolean isArray) throws RuntimeException { - String s = locateName(str, isArray); - String s1 = locateValue(str, isArray); - return joinStrToNBT(s, s1); - } - - private static String nextNameValuePair(String str, boolean isCompound) throws RuntimeException { - int i = getNextCharIndex(str, ':'); - int j = getNextCharIndex(str, ','); - - if (isCompound) { - if (i == -1) { - throw new RuntimeException("Unable to locate name/value separator for string: " + str); - } - - if (j != -1 && j < i) { - throw new RuntimeException("Name error at: " + str); - } - } else if (i == -1 || i > j) { - i = -1; - } - - return locateValueAt(str, i); - } - - private static String locateValueAt(String str, int index) throws RuntimeException { - Stack stack = new Stack<>(); - int i = index + 1; - boolean flag = false; - boolean flag1 = false; - boolean flag2 = false; - - for (int j = 0; i < str.length(); ++i) { - char c0 = str.charAt(i); - - if (c0 == 34) { - if (isCharEscaped(str, i)) { - if (!flag) { - throw new RuntimeException("Illegal use of \\\": " + str); - } - } else { - flag = !flag; - - if (flag && !flag2) { - flag1 = true; - } - - if (!flag) { - j = i; - } - } - } else if (!flag) { - if (c0 != 123 && c0 != 91) { - if (c0 == 125 && (stack.isEmpty() || stack.pop() != 123)) { - throw new RuntimeException("Unbalanced curly brackets {}: " + str); - } - - if (c0 == 93 && (stack.isEmpty() || stack.pop() != 91)) { - throw new RuntimeException("Unbalanced square brackets []: " + str); - } - - if (c0 == 44 && stack.isEmpty()) { - return str.substring(0, i); - } - } else { - stack.push(c0); - } - } - - if (!Character.isWhitespace(c0)) { - if (!flag && flag1 && j != i) { - return str.substring(0, j + 1); - } - - flag2 = true; - } - } - - return str.substring(0, i); - } - - private static String locateName(String str, boolean isArray) throws RuntimeException { - if (isArray) { - str = str.trim(); - - if (str.startsWith("{") || str.startsWith("[")) { - return ""; - } - } - - int i = getNextCharIndex(str, ':'); - - if (i == -1) { - if (isArray) { - return ""; - } else { - throw new RuntimeException("Unable to locate name/value separator for string: " + str); - } - } else { - return str.substring(0, i).trim(); - } - } - - private static String locateValue(String str, boolean isArray) throws RuntimeException { - if (isArray) { - str = str.trim(); - - if (str.startsWith("{") || str.startsWith("[")) { - return str; - } - } - - int i = getNextCharIndex(str, ':'); - - if (i == -1) { - if (isArray) { - return str; - } else { - throw new RuntimeException("Unable to locate name/value separator for string: " + str); - } - } else { - return str.substring(i + 1).trim(); - } - } - - private static int getNextCharIndex(String str, char targetChar) { - int i = 0; - - for (boolean flag = true; i < str.length(); ++i) { - char c0 = str.charAt(i); - - if (c0 == 34) { - if (!isCharEscaped(str, i)) { - flag = !flag; - } - } else if (flag) { - if (c0 == targetChar) { - return i; - } - - if (c0 == 123 || c0 == 91) { - return -1; - } - } - } - - return -1; - } - - private static boolean isCharEscaped(String str, int index) { - return index > 0 && str.charAt(index - 1) == 92 && !isCharEscaped(str, index - 1); - } - - abstract static class Any { - protected String json; - - public abstract Tag parse() throws RuntimeException; - } - - static class Compound extends ViaStringTagReader1_11_2.Any { - protected java.util.List tagList = Lists.newArrayList(); - - public Compound(String jsonIn) { - this.json = jsonIn; - } - - public Tag parse() throws RuntimeException { - CompoundTag nbttagcompound = new CompoundTag(); - - for (ViaStringTagReader1_11_2.Any jsontonbt$any : this.tagList) { - nbttagcompound.put(jsontonbt$any.json, jsontonbt$any.parse()); - } - - return nbttagcompound; - } - } - - static class List extends ViaStringTagReader1_11_2.Any { - protected java.util.List tagList = Lists.newArrayList(); - - public List(String json) { - this.json = json; - } - - public Tag parse() throws RuntimeException { - ListTag nbttaglist = new ListTag(); - - for (ViaStringTagReader1_11_2.Any jsontonbt$any : this.tagList) { - nbttaglist.add(jsontonbt$any.parse()); - } - - return nbttaglist; - } - } - - static class Primitive extends ViaStringTagReader1_11_2.Any { - private static final Pattern DOUBLE = Pattern.compile("[-+]?[0-9]*\\.?[0-9]+[d|D]"); - private static final Pattern FLOAT = Pattern.compile("[-+]?[0-9]*\\.?[0-9]+[f|F]"); - private static final Pattern BYTE = Pattern.compile("[-+]?[0-9]+[b|B]"); - private static final Pattern LONG = Pattern.compile("[-+]?[0-9]+[l|L]"); - private static final Pattern SHORT = Pattern.compile("[-+]?[0-9]+[s|S]"); - private static final Pattern INTEGER = Pattern.compile("[-+]?[0-9]+"); - private static final Pattern DOUBLE_UNTYPED = Pattern.compile("[-+]?[0-9]*\\.?[0-9]+"); - private static final Splitter SPLITTER = Splitter.on(',').omitEmptyStrings(); - protected String jsonValue; - - public Primitive(String jsonIn, String valueIn) { - this.json = jsonIn; - this.jsonValue = valueIn; - } - - public Tag parse() throws RuntimeException { - try { - if (DOUBLE.matcher(this.jsonValue).matches()) { - return new DoubleTag(Double.parseDouble(this.jsonValue.substring(0, this.jsonValue.length() - 1))); - } - - if (FLOAT.matcher(this.jsonValue).matches()) { - return new FloatTag(Float.parseFloat(this.jsonValue.substring(0, this.jsonValue.length() - 1))); - } - - if (BYTE.matcher(this.jsonValue).matches()) { - return new ByteTag(Byte.parseByte(this.jsonValue.substring(0, this.jsonValue.length() - 1))); - } - - if (LONG.matcher(this.jsonValue).matches()) { - return new LongTag(Long.parseLong(this.jsonValue.substring(0, this.jsonValue.length() - 1))); - } - - if (SHORT.matcher(this.jsonValue).matches()) { - return new ShortTag(Short.parseShort(this.jsonValue.substring(0, this.jsonValue.length() - 1))); - } - - if (INTEGER.matcher(this.jsonValue).matches()) { - return new IntTag(Integer.parseInt(this.jsonValue)); - } - - if (DOUBLE_UNTYPED.matcher(this.jsonValue).matches()) { - return new DoubleTag(Double.parseDouble(this.jsonValue)); - } - - if ("true".equalsIgnoreCase(this.jsonValue) || "false".equalsIgnoreCase(this.jsonValue)) { - return new ByteTag((byte) (Boolean.parseBoolean(this.jsonValue) ? 1 : 0)); - } - } catch (NumberFormatException var6) { - this.jsonValue = this.jsonValue.replaceAll("\\\\\"", "\""); - return new StringTag(this.jsonValue); - } - - if (this.jsonValue.startsWith("[") && this.jsonValue.endsWith("]")) { - String s = this.jsonValue.substring(1, this.jsonValue.length() - 1); - String[] astring = Iterables.toArray(SPLITTER.split(s), String.class); - - try { - int[] aint = new int[astring.length]; - - for (int j = 0; j < astring.length; ++j) { - aint[j] = Integer.parseInt(astring[j].trim()); - } - - return new IntArrayTag(aint); - } catch (NumberFormatException var5) { - return new StringTag(this.jsonValue); - } - } else { - if (this.jsonValue.startsWith("\"") && this.jsonValue.endsWith("\"")) { - this.jsonValue = this.jsonValue.substring(1, this.jsonValue.length() - 1); - } - - this.jsonValue = this.jsonValue.replaceAll("\\\\\"", "\""); - StringBuilder stringbuilder = new StringBuilder(); - - for (int i = 0; i < this.jsonValue.length(); ++i) { - if (i < this.jsonValue.length() - 1 && this.jsonValue.charAt(i) == 92 && this.jsonValue.charAt(i + 1) == 92) { - stringbuilder.append('\\'); - ++i; - } else { - stringbuilder.append(this.jsonValue.charAt(i)); - } - } - - return new StringTag(stringbuilder.toString()); - } - } - } - -} diff --git a/src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_12_2.java b/src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_12_2.java deleted file mode 100644 index 44aca3d..0000000 --- a/src/main/java/net/raphimc/vialegacy/util/ViaStringTagReader1_12_2.java +++ /dev/null @@ -1,425 +0,0 @@ -/* - * This file is part of ViaLegacy - https://github.com/RaphiMC/ViaLegacy - * Copyright (C) 2023 RK_01/RaphiMC and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package net.raphimc.vialegacy.util; - - -import com.viaversion.viaversion.libs.opennbt.tag.TagRegistry; -import com.viaversion.viaversion.libs.opennbt.tag.builtin.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -@Deprecated // Replace with mcstructs -public class ViaStringTagReader1_12_2 { - - private static final Pattern DOUBLE_PATTERN_IMPLICIT = Pattern.compile("[-+]?(?:[0-9]+[.]|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?", Pattern.CASE_INSENSITIVE); - private static final Pattern DOUBLE_PATTERN = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?d", Pattern.CASE_INSENSITIVE); - private static final Pattern FLOAT_PATTERN = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?f", Pattern.CASE_INSENSITIVE); - private static final Pattern BYTE_PATTERN = Pattern.compile("[-+]?(?:0|[1-9][0-9]*)b", Pattern.CASE_INSENSITIVE); - private static final Pattern LONG_PATTERN = Pattern.compile("[-+]?(?:0|[1-9][0-9]*)l", Pattern.CASE_INSENSITIVE); - private static final Pattern SHORT_PATTERN = Pattern.compile("[-+]?(?:0|[1-9][0-9]*)s", Pattern.CASE_INSENSITIVE); - private static final Pattern INT_PATTERN = Pattern.compile("[-+]?(?:0|[1-9][0-9]*)"); - private final String JSON_TAG; - private int currentPosition; - - private ViaStringTagReader1_12_2(String jsonTag) { - this.JSON_TAG = jsonTag; - } - - public static CompoundTag getTagFromJson(String jsonString) throws NBTException { - return (new ViaStringTagReader1_12_2(jsonString)).readCompound(); - } - - private CompoundTag readCompound() throws NBTException { - CompoundTag nbttagcompound = this.parseCompound(); - this.skipWhitespace(); - - if (this.canRead()) { - ++this.currentPosition; - throw this.makeError("Trailing data found"); - } else { - return nbttagcompound; - } - } - - private String readString() throws NBTException { - this.skipWhitespace(); - - if (!this.canRead()) { - throw this.makeError("Expected key"); - } else { - return this.peek() == '"' ? this.readQuotedString() : this.readUnquotedString(); - } - } - - private NBTException makeError(String message) { - return new NBTException(message, this.JSON_TAG, this.currentPosition); - } - - private Tag parseTagPrimitive() throws NBTException { - this.skipWhitespace(); - - if (this.peek() == '"') { - return new StringTag(this.readQuotedString()); - } else { - String s = this.readUnquotedString(); - - if (s.isEmpty()) { - throw this.makeError("Expected value"); - } else { - return this.parsePrimitive(s); - } - } - } - - private Tag parsePrimitive(String input) { - try { - if (FLOAT_PATTERN.matcher(input).matches()) { - return new FloatTag(Float.parseFloat(input.substring(0, input.length() - 1))); - } - - if (BYTE_PATTERN.matcher(input).matches()) { - return new ByteTag(Byte.parseByte(input.substring(0, input.length() - 1))); - } - - if (LONG_PATTERN.matcher(input).matches()) { - return new LongTag(Long.parseLong(input.substring(0, input.length() - 1))); - } - - if (SHORT_PATTERN.matcher(input).matches()) { - return new ShortTag(Short.parseShort(input.substring(0, input.length() - 1))); - } - - if (INT_PATTERN.matcher(input).matches()) { - return new IntTag(Integer.parseInt(input)); - } - - if (DOUBLE_PATTERN.matcher(input).matches()) { - return new DoubleTag(Double.parseDouble(input.substring(0, input.length() - 1))); - } - - if (DOUBLE_PATTERN_IMPLICIT.matcher(input).matches()) { - return new DoubleTag(Double.parseDouble(input)); - } - - if ("true".equalsIgnoreCase(input)) { - return new ByteTag((byte) 1); - } - - if ("false".equalsIgnoreCase(input)) { - return new ByteTag((byte) 0); - } - } catch (NumberFormatException ignored) { - } - - return new StringTag(input); - } - - private String readQuotedString() throws NBTException { - int i = ++this.currentPosition; - StringBuilder stringbuilder = null; - boolean flag = false; - - while (this.canRead()) { - char c0 = this.read(); - - if (flag) { - if (c0 != '\\' && c0 != '"') { - throw this.makeError("Invalid escape of '" + c0 + "'"); - } - - flag = false; - } else { - if (c0 == '\\') { - flag = true; - - if (stringbuilder == null) { - stringbuilder = new StringBuilder(this.JSON_TAG.substring(i, this.currentPosition - 1)); - } - - continue; - } - - if (c0 == '"') { - return stringbuilder == null ? this.JSON_TAG.substring(i, this.currentPosition - 1) : stringbuilder.toString(); - } - } - - if (stringbuilder != null) { - stringbuilder.append(c0); - } - } - - throw this.makeError("Missing termination quote"); - } - - private String readUnquotedString() { - int i = this.currentPosition; - - while (this.canRead() && this.isAllowedInUnquotedString(this.peek())) { - this.currentPosition++; - } - - return this.JSON_TAG.substring(i, this.currentPosition); - } - - private Tag parseTag() throws NBTException { - this.skipWhitespace(); - - if (!this.canRead()) { - throw this.makeError("Expected value"); - } else { - char c0 = this.peek(); - - if (c0 == '{') { - return this.parseCompound(); - } else { - return c0 == '[' ? this.parseTagArray() : this.parseTagPrimitive(); - } - } - } - - private Tag parseTagArray() throws NBTException { - return this.canRead(2) && this.peek(1) != '"' && this.peek(2) == ';' ? this.parseTagPrimitiveArray() : this.parseListTag(); - } - - private CompoundTag parseCompound() throws NBTException { - this.expect('{'); - CompoundTag nbttagcompound = new CompoundTag(); - this.skipWhitespace(); - - while (this.canRead() && this.peek() != '}') { - String key = this.readString(); - - if (key.isEmpty()) { - throw this.makeError("Expected non-empty key"); - } - - this.expect(':'); - Tag tag = this.parseTag(); - nbttagcompound.put(key, tag); - - if (!this.readComma()) { - break; - } - - if (!this.canRead()) { - throw this.makeError("Expected key"); - } - } - - this.expect('}'); - return nbttagcompound; - } - - private Tag parseListTag() throws NBTException { - this.expect('['); - this.skipWhitespace(); - - if (!this.canRead()) { - throw this.makeError("Expected value"); - } else { - ListTag nbttaglist = new ListTag(); - int i = -1; - - while (this.peek() != ']') { - Tag nbtbase = this.parseTag(); - int j = TagRegistry.getIdFor(nbtbase.getClass()); - - if (i < 0) { - i = j; - } else if (j != i) { - throw this.makeError("Unable to insert " + TagRegistry.getClassFor(j).getSimpleName() + " into ListTag of type " + TagRegistry.getClassFor(i).getSimpleName()); - } - - nbttaglist.add(nbtbase); - - if (!this.readComma()) { - break; - } - - if (!this.canRead()) { - throw this.makeError("Expected value"); - } - } - - this.expect(']'); - return nbttaglist; - } - } - - private Tag parseTagPrimitiveArray() throws NBTException { - this.expect('['); - char c0 = this.read(); - this.read(); - this.skipWhitespace(); - - if (!this.canRead()) { - throw this.makeError("Expected value"); - } else if (c0 == 'B') { - return new ByteArrayTag(toByteArray(this.readArray((byte) 7, (byte) 1))); - } else if (c0 == 'L') { - return new LongArrayTag(toLongArray(this.readArray((byte) 12, (byte) 4))); - } else if (c0 == 'I') { - return new IntArrayTag(toIntArray(this.readArray((byte) 11, (byte) 3))); - } else { - throw this.makeError("Invalid array type '" + c0 + "' found"); - } - } - - private static byte[] toByteArray(List list) { - byte[] bs = new byte[list.size()]; - - for (int i = 0; i < list.size(); ++i) { - Byte byte_ = list.get(i); - bs[i] = byte_ == null ? 0 : byte_; - } - - return bs; - } - - private static long[] toLongArray(List list) { - long[] ls = new long[list.size()]; - - for (int i = 0; i < list.size(); ++i) { - Long long_ = list.get(i); - ls[i] = long_ == null ? 0L : long_; - } - - return ls; - } - - private static int[] toIntArray(List list) { - int[] is = new int[list.size()]; - - for (int i = 0; i < list.size(); ++i) { - Integer integer = list.get(i); - is[i] = integer == null ? 0 : integer; - } - - return is; - } - - private List readArray(byte b, byte c) throws NBTException { - List list = new ArrayList<>(); - - while (true) { - if (this.peek() != ']') { - Tag nbtbase = this.parseTag(); - int i = TagRegistry.getIdFor(nbtbase.getClass()); - - if (i != c) { - throw this.makeError("Unable to insert " + TagRegistry.getClassFor(i).getSimpleName() + " into " + TagRegistry.getClassFor(b).getSimpleName()); - } - - if (c == 1) { - list.add((T) ((ByteTag) nbtbase).getValue()); - } else if (c == 4) { - list.add((T) ((LongTag) nbtbase).getValue()); - } else { - list.add((T) ((IntTag) nbtbase).getValue()); - } - - if (this.readComma()) { - if (!this.canRead()) { - throw this.makeError("Expected value"); - } - - continue; - } - } - - this.expect(']'); - return list; - } - } - - private void skipWhitespace() { - while (this.canRead() && Character.isWhitespace(this.peek())) { - ++this.currentPosition; - } - } - - private boolean readComma() { - this.skipWhitespace(); - - if (this.canRead() && this.peek() == ',') { - ++this.currentPosition; - this.skipWhitespace(); - return true; - } else { - return false; - } - } - - private void expect(char expectedChar) throws NBTException { - this.skipWhitespace(); - boolean flag = this.canRead(); - - if (flag && this.peek() == expectedChar) { - ++this.currentPosition; - } else { - throw new NBTException("Expected '" + expectedChar + "' but got '" + (flag ? this.peek() : "") + "'", this.JSON_TAG, this.currentPosition + 1); - } - } - - private boolean isAllowedInUnquotedString(char c) { - return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_' || c == '-' || c == '.' || c == '+'; - } - - private boolean canRead(int offset) { - return this.currentPosition + offset < this.JSON_TAG.length(); - } - - private boolean canRead() { - return this.canRead(0); - } - - private char peek(int offset) { - return this.JSON_TAG.charAt(this.currentPosition + offset); - } - - private char peek() { - return this.peek(0); - } - - private char read() { - return this.JSON_TAG.charAt(this.currentPosition++); - } - - public static final class NBTException extends Exception { - public NBTException(String message, String jsonTag, int position) { - super(message + " at: " + generate(jsonTag, position)); - } - - private static String generate(String jsonTag, int position) { - StringBuilder stringbuilder = new StringBuilder(); - int i = Math.min(jsonTag.length(), position); - - if (i > 35) { - stringbuilder.append("..."); - } - - stringbuilder.append(jsonTag, Math.max(0, i - 35), i); - stringbuilder.append("<--[HERE]"); - return stringbuilder.toString(); - } - } - -}