450 lines
9.1 KiB
Dart
450 lines
9.1 KiB
Dart
import 'Stream.dart';
|
|
import 'impl/ByteArrayTag.dart';
|
|
import 'impl/ByteTag.dart';
|
|
import 'impl/CompoundTag.dart';
|
|
import 'impl/DoubleTag.dart';
|
|
import 'impl/EndTag.dart';
|
|
import 'impl/FloatTag.dart';
|
|
import 'impl/IntArrayTag.dart';
|
|
import 'impl/IntTag.dart';
|
|
import 'impl/ListTag.dart';
|
|
import 'impl/LongArrayTag.dart';
|
|
import 'impl/LongTag.dart';
|
|
import 'impl/ShortTag.dart';
|
|
import 'impl/StringTag.dart';
|
|
|
|
enum TagType {
|
|
End(0),
|
|
Byte(1),
|
|
Short(2),
|
|
Int(3),
|
|
Long(4),
|
|
Float(5),
|
|
Double(6),
|
|
ByteArray(7),
|
|
String(8),
|
|
List(9),
|
|
Compound(10),
|
|
IntArray(11),
|
|
LongArray(12);
|
|
|
|
final int byte;
|
|
const TagType(this.byte);
|
|
|
|
static TagType get(int byte) {
|
|
for (TagType type in values) {
|
|
if (type.byte == byte) {
|
|
return type;
|
|
}
|
|
}
|
|
|
|
return TagType.End;
|
|
}
|
|
|
|
static TagType getStringifiedTagType(StringReader reader) {
|
|
reader.startSeek();
|
|
TagType ret = TagType.End;
|
|
bool isNumber = true;
|
|
|
|
// Start to determine the next tag type
|
|
while (reader.canRead && ret == TagType.End) {
|
|
var val = reader.next().toUpperCase();
|
|
switch (val) {
|
|
case "{":
|
|
{
|
|
ret = TagType.Compound;
|
|
break;
|
|
}
|
|
case "[":
|
|
{
|
|
// Check for a type Prefix
|
|
var X = reader.readUntil(";");
|
|
switch (X.toUpperCase()) {
|
|
case "B":
|
|
{
|
|
ret = TagType.ByteArray;
|
|
break;
|
|
}
|
|
case "I":
|
|
{
|
|
ret = TagType.IntArray;
|
|
break;
|
|
}
|
|
case "L":
|
|
{
|
|
ret = TagType.LongArray;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ret = TagType.List;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case "B":
|
|
{
|
|
ret = TagType.Byte;
|
|
break;
|
|
}
|
|
case "D":
|
|
{
|
|
ret = TagType.Double;
|
|
break;
|
|
}
|
|
case "F":
|
|
{
|
|
ret = TagType.Float;
|
|
break;
|
|
}
|
|
case "I":
|
|
{
|
|
ret = TagType.Int;
|
|
break;
|
|
}
|
|
case "L":
|
|
{
|
|
ret = TagType.Long;
|
|
break;
|
|
}
|
|
case "S":
|
|
{
|
|
ret = TagType.Short;
|
|
break;
|
|
}
|
|
case "\"":
|
|
{
|
|
ret = TagType.String;
|
|
break;
|
|
}
|
|
case ",":
|
|
case "\n":
|
|
{
|
|
if (reader.getSeeked >= 1) ret = TagType.String;
|
|
if (isNumber) ret = TagType.Int;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (!reader.isDigit(val)) {
|
|
if (isNumber) isNumber = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
reader.endSeek();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
abstract class Tag {
|
|
int getType() {
|
|
return getTagType().byte;
|
|
}
|
|
|
|
TagType getTagType();
|
|
void writeValue(ByteLayer data);
|
|
void readValue(ByteLayer data);
|
|
String? _key;
|
|
|
|
String getKey() {
|
|
return (_key == null ? "" : _key!);
|
|
}
|
|
|
|
void setKey(String key) {
|
|
_key = key;
|
|
}
|
|
|
|
static Tag readNamedTag(ByteLayer data) {
|
|
var type = data.readByte();
|
|
if (type == 0) {
|
|
return EndTag();
|
|
} else {
|
|
Tag tag = makeTagOfType(TagType.get(type));
|
|
tag._key = data.readString();
|
|
tag.readValue(data);
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
static void writeNamedTag(Tag tag, ByteLayer data) {
|
|
data.writeByte(tag.getType());
|
|
if (tag.getType() != 0) {
|
|
data.writeString(tag.getKey());
|
|
tag.writeValue(data);
|
|
}
|
|
}
|
|
|
|
static void writeStringifiedNamedTag(
|
|
Tag tag, StringBuilder builder, int indents) {
|
|
if (tag.getType() != 0) {
|
|
if (!builder.isEmpty) {
|
|
// Write name
|
|
if (tag._key == "") {
|
|
builder.append("${"".padLeft(indents, '\t')}");
|
|
} else {
|
|
if (tag.shouldQuoteName()) {
|
|
builder.append("${"".padLeft(indents, "\t")}\"${tag.getKey()}\": ");
|
|
} else
|
|
builder.append("${"".padLeft(indents, '\t')}${tag.getKey()}: ");
|
|
}
|
|
}
|
|
|
|
tag.writeStringifiedValue(builder, indents + 1, false);
|
|
}
|
|
}
|
|
|
|
static Tag readStringifiedNamedTag(StringReader string) {
|
|
String name = "";
|
|
if (string.peek() == "{" || string.peek() == "[") {
|
|
// No name
|
|
name = "";
|
|
} else {
|
|
name = string.readString();
|
|
string.expect(":");
|
|
}
|
|
TagType type = TagType.getStringifiedTagType(string);
|
|
Tag tag = Tag.makeTagOfType(type);
|
|
tag._key = name;
|
|
tag.readStringifiedValue(string);
|
|
|
|
return tag;
|
|
}
|
|
|
|
bool shouldQuoteName() {
|
|
if (getKey() == "") {
|
|
return false;
|
|
} else {
|
|
String letters = "abcdefghijklmnopqrstuvwxyz";
|
|
for (int i = 0; i < getKey().length; i++) {
|
|
String digit = getKey().substring(i, i + 1);
|
|
if (letters.indexOf(digit) == -1) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void writeStringifiedValue(StringBuilder builder, int indent, bool isList);
|
|
void readStringifiedValue(StringReader reader);
|
|
|
|
bool equals(dynamic object) {
|
|
if (object == null || object is! Tag) return false;
|
|
|
|
Tag tag = object;
|
|
if (tag.getType() != getType()) return false;
|
|
|
|
if (!(getKey() == tag.getKey())) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static Tag makeTagOfType(TagType type) {
|
|
switch (type) {
|
|
case TagType.Byte:
|
|
{
|
|
return ByteTag();
|
|
}
|
|
case TagType.ByteArray:
|
|
{
|
|
return ByteArrayTag();
|
|
}
|
|
|
|
case TagType.Compound:
|
|
{
|
|
return CompoundTag();
|
|
}
|
|
case TagType.Double:
|
|
{
|
|
return DoubleTag();
|
|
}
|
|
case TagType.End:
|
|
{
|
|
return EndTag();
|
|
}
|
|
case TagType.Short:
|
|
{
|
|
return ShortTag();
|
|
}
|
|
case TagType.Int:
|
|
{
|
|
return IntTag();
|
|
}
|
|
case TagType.Long:
|
|
{
|
|
return LongTag();
|
|
}
|
|
case TagType.Float:
|
|
{
|
|
return FloatTag();
|
|
}
|
|
case TagType.IntArray:
|
|
{
|
|
return IntArrayTag();
|
|
}
|
|
case TagType.LongArray:
|
|
{
|
|
return LongArrayTag();
|
|
}
|
|
|
|
case TagType.List:
|
|
{
|
|
return ListTag();
|
|
}
|
|
case TagType.String:
|
|
{
|
|
return StringTag();
|
|
}
|
|
}
|
|
}
|
|
|
|
int asByte() {
|
|
if (this is ByteTag) {
|
|
return (this as ByteTag).value;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
List<int> asByteArray() {
|
|
if (this is ByteArrayTag) {
|
|
return (this as ByteArrayTag).value;
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
double asDouble() {
|
|
if (this is DoubleTag) {
|
|
return (this as DoubleTag).value;
|
|
} else {
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
double asFloat() {
|
|
if (this is FloatTag) {
|
|
return (this as FloatTag).value;
|
|
} else {
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
List<int> asIntArray() {
|
|
if (this is IntArrayTag) {
|
|
return (this as IntArrayTag).value;
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
int asInt() {
|
|
if (this is IntTag) {
|
|
return (this as IntTag).value;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
List<int> asLongArray() {
|
|
if (this is LongArrayTag) {
|
|
return (this as LongArrayTag).value;
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
int asLong() {
|
|
if (this is LongTag) {
|
|
return (this as LongTag).value;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int asShort() {
|
|
if (this is ShortTag) {
|
|
return (this as ShortTag).value;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
String asString() {
|
|
if (this is StringTag) {
|
|
return (this as StringTag).value;
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
CompoundTag asCompoundTag() {
|
|
if (this is CompoundTag) {
|
|
return this as CompoundTag;
|
|
} else
|
|
return CompoundTag();
|
|
}
|
|
|
|
void prettyPrint(int indent, bool recurse);
|
|
|
|
static String getCanonicalName(TagType type) {
|
|
switch (type) {
|
|
case TagType.String:
|
|
return "TAG_String";
|
|
case TagType.List:
|
|
return "TAG_List";
|
|
case TagType.LongArray:
|
|
return "TAG_LongArray";
|
|
case TagType.Long:
|
|
return "TAG_Long";
|
|
case TagType.IntArray:
|
|
return "TAG_IntArray";
|
|
case TagType.Int:
|
|
return "TAG_Int";
|
|
case TagType.Compound:
|
|
return "TAG_Compound";
|
|
case TagType.ByteArray:
|
|
return "TAG_ByteArray";
|
|
case TagType.Byte:
|
|
return "TAG_Byte";
|
|
case TagType.Double:
|
|
return "TAG_Double";
|
|
case TagType.Float:
|
|
return "TAG_Float";
|
|
case TagType.Short:
|
|
return "TAG_Short";
|
|
case TagType.End:
|
|
return "TAG_End";
|
|
}
|
|
}
|
|
}
|
|
|
|
class NBTAccountant {
|
|
static int _prettyIndex = 0;
|
|
static void printRead(Tag tag) {
|
|
tag.prettyPrint(_prettyIndex, false);
|
|
}
|
|
|
|
static void visitTag() {
|
|
_prettyIndex++;
|
|
}
|
|
|
|
static void leaveTag(Tag tag) {
|
|
if (tag is CompoundTag || tag is ListTag) {
|
|
if (tag is CompoundTag) {
|
|
CompoundTag ct = tag;
|
|
ct.endPrettyPrint(_prettyIndex);
|
|
} else if (tag is ListTag) {
|
|
ListTag lt = tag;
|
|
lt.endPrettyPrint(_prettyIndex);
|
|
}
|
|
}
|
|
_prettyIndex--;
|
|
}
|
|
}
|