Rewrote nbt and text translation

This commit is contained in:
RaphiMC 2023-04-11 20:52:00 +02:00
parent 1a706fd04f
commit b4d755cf07
6 changed files with 85 additions and 883 deletions

View File

@ -155,7 +155,7 @@ public class Protocol1_7_2_5to1_6_4 extends AbstractProtocol<ClientboundPackets1
this.registerClientbound(State.LOGIN, ClientboundPackets1_6_4.DISCONNECT.getId(), ClientboundLoginPackets.LOGIN_DISCONNECT.getId(), new PacketHandlers() {
@Override
public void register() {
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClient); // reason
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClientDisconnect); // reason
}
});
this.cancelClientbound(State.LOGIN, ClientboundPackets1_6_4.PLUGIN_MESSAGE.getId());
@ -884,7 +884,7 @@ public class Protocol1_7_2_5to1_6_4 extends AbstractProtocol<ClientboundPackets1
this.registerClientbound(ClientboundPackets1_6_4.DISCONNECT, new PacketHandlers() {
@Override
public void register() {
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClient);
map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClientDisconnect); // reason
}
});
this.cancelClientbound(ClientboundPackets1_6_4.CREATIVE_INVENTORY_ACTION);

View File

@ -17,24 +17,24 @@
*/
package net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import net.lenni0451.mcstructs.text.ATextComponent;
import net.lenni0451.mcstructs.text.components.StringComponent;
import net.lenni0451.mcstructs.text.serializer.LegacyStringDeserializer;
import net.lenni0451.mcstructs.text.serializer.TextComponentSerializer;
import net.lenni0451.mcstructs.text.utils.TextUtils;
public class ChatComponentRewriter {
private static final ComponentRewriter FIX_COMPONENT = new ComponentRewriter() {
@Override
protected void handleTranslate(JsonObject object, String translate) {
final JsonElement args = object.remove("using");
if (args != null) {
object.add("with", args);
}
}
};
public static String toClient(final String text) {
return FIX_COMPONENT.processText(text).toString();
final ATextComponent component = TextComponentSerializer.V1_6.deserialize(text);
// Convert all section sign formatted strings to json formatted ones with styles so the formatting isn't reset on chat line split
final ATextComponent newComponent = TextUtils.replace(component, ".*", c -> 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));
}
}

View File

@ -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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<String, INbtTag> 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());
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Character> 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<Character> 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<ViaStringTagReader1_11_2.Any> 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<ViaStringTagReader1_11_2.Any> 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());
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Byte> 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<Long> 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<Integer> 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 <T extends Number> List<T> readArray(byte b, byte c) throws NBTException {
List<T> 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() : "<EOF>") + "'", 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();
}
}
}