// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import io.kaitai.struct.ConsistencyError;
import java.util.Objects;
import java.util.ArrayList;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;


/**
 * A structured binary format native to Minecraft for saving game data and transferring
 * it over the network (in multiplayer), such as player data
 * ([`<player>.dat`](https://minecraft.wiki/w/Player.dat_format); contains
 * e.g. player's inventory and location), saved worlds
 * ([`level.dat`](
 *   https://minecraft.wiki/w/Java_Edition_level_format#level.dat_format
 * ) and [Chunk format](https://minecraft.wiki/w/Chunk_format#NBT_structure)),
 * list of saved multiplayer servers
 * ([`servers.dat`](https://minecraft.wiki/w/Servers.dat_format)) and so on -
 * see <https://minecraft.wiki/w/NBT_format#Uses>.
 * 
 * The entire file should be _gzip_-compressed (in accordance with the original
 * specification [NBT.txt](
 *   https://web.archive.org/web/20110723210920/https://www.minecraft.net/docs/NBT.txt
 * ) by Notch), but can also be compressed with _zlib_ or uncompressed.
 * 
 * This spec can only handle uncompressed NBT data, so be sure to first detect
 * what type of data you are dealing with. You can use the Unix `file` command
 * to do this (`file-5.20` or later is required; older versions do not recognize
 * _zlib_-compressed data and return `application/octet-stream` instead):
 * 
 * ```shell
 * file --brief --mime-type input-unknown.nbt
 * ```
 * 
 * If it says:
 * 
 *   * `application/x-gzip` or `application/gzip` (since `file-5.37`), you can decompress it by
 *     * `gunzip -c input-gzip.nbt > output.nbt` or
 *     * `python3 -c "import sys, gzip; sys.stdout.buffer.write(
 *       gzip.decompress(sys.stdin.buffer.read()) )" < input-gzip.nbt > output.nbt`
 *   * `application/zlib`, you can use
 *     * `openssl zlib -d -in input-zlib.nbt -out output.nbt` (does not work on most systems)
 *     * `python3 -c "import sys, zlib; sys.stdout.buffer.write(
 *       zlib.decompress(sys.stdin.buffer.read()) )" < input-zlib.nbt > output.nbt`
 *   * something else (especially `image/x-pcx` and `application/octet-stream`),
 *     it is most likely already uncompressed.
 * 
 * The file `output.nbt` generated by one of the above commands can already be
 * processed with this Kaitai Struct specification.
 * 
 * This spec **only** implements the Java edition format. There is also
 * a [Bedrock edition](https://wiki.vg/NBT#Bedrock_edition) NBT format,
 * which uses little-endian encoding and has a few other differences, but it isn't
 * as popular as the Java edition format.
 * 
 * **Implementation note:** strings in `TAG_String` are incorrectly decoded with
 * standard UTF-8, while they are encoded in [**Modified UTF-8**](
 *   https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8
 * ) (MUTF-8). That's because MUTF-8 is not supported natively by most target
 * languages, and thus one must use external libraries to achieve a fully-compliant
 * decoder. But decoding in standard UTF-8 is still better than nothing, and
 * it usually works fine.
 * 
 * All Unicode code points with incompatible representations in MUTF-8 and UTF-8 are
 * U+0000 (_NUL_), U+D800-U+DFFF (_High_ and _Low Surrogates_) and U+10000-U+10FFFF
 * (all _Supplementary_ Planes; includes e.g. emoticons, pictograms).
 * A _MUTF-8_-encoded string containing these code points cannot be successfully
 * decoded as UTF-8. The behavior in this case depends on the target language -
 * usually an exception is thrown, or the bytes that are not valid UTF-8
 * are replaced or ignored.
 * 
 * **Sample files:**
 * 
 *   * <https://wiki.vg/NBT#Download>
 *   * <https://github.com/twoolie/NBT/blob/f9e892e/tests/world_test/data/scoreboard.dat>
 *   * <https://github.com/chmod222/cNBT/tree/3f74b69/testdata>
 *   * <https://github.com/PistonDevelopers/hematite_nbt/tree/0b85f89/tests>
 * @see <a href="https://wiki.vg/NBT">Source</a>
 * @see <a href="https://web.archive.org/web/20110723210920/https://www.minecraft.net/docs/NBT.txt">Source</a>
 * @see <a href="https://minecraft.wiki/w/NBT_format">Source</a>
 */
public class MinecraftNbt extends KaitaiStruct.ReadWrite {
    public static MinecraftNbt fromFile(String fileName) throws IOException {
        return new MinecraftNbt(new ByteBufferKaitaiStream(fileName));
    }

    public enum Tag {
        END(0),
        BYTE(1),
        SHORT(2),
        INT(3),
        LONG(4),
        FLOAT(5),
        DOUBLE(6),
        BYTE_ARRAY(7),
        STRING(8),
        LIST(9),
        COMPOUND(10),
        INT_ARRAY(11),
        LONG_ARRAY(12);

        private final long id;
        Tag(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, Tag> byId = new HashMap<Long, Tag>(13);
        static {
            for (Tag e : Tag.values())
                byId.put(e.id(), e);
        }
        public static Tag byId(long id) { return byId.get(id); }
    }
    public MinecraftNbt() {
        this(null, null, null);
    }

    public MinecraftNbt(KaitaiStream _io) {
        this(_io, null, null);
    }

    public MinecraftNbt(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
        this(_io, _parent, null);
    }

    public MinecraftNbt(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
    }
    public void _read() {
        if ( ((rootType() == Tag.END) && (false)) ) {
            this.rootCheck = this._io.readBytes(0);
        }
        this.root = new NamedTag(this._io, this, _root);
        this.root._read();
        _dirty = false;
    }

    public void _fetchInstances() {
        if ( ((rootType() == Tag.END) && (false)) ) {
        }
        this.root._fetchInstances();
        rootType();
        if (this.rootType != null) {
        }
    }

    public void _write_Seq() {
        _assertNotDirty();
        _shouldWriteRootType = _enabledRootType;
        if ( ((rootType() == Tag.END) && (false)) ) {
            if (this.rootCheck.length != 0)
                throw new ConsistencyError("root_check", 0, this.rootCheck.length);
            this._io.writeBytes(this.rootCheck);
        }
        this.root._write_Seq(this._io);
    }

    public void _check() {
        if (!Objects.equals(this.root._root(), _root()))
            throw new ConsistencyError("root", _root(), this.root._root());
        if (!Objects.equals(this.root._parent(), this))
            throw new ConsistencyError("root", this, this.root._parent());
        if (_enabledRootType) {
            if (!(this.rootType == Tag.COMPOUND)) {
                throw new KaitaiStream.ValidationNotEqualError(Tag.COMPOUND, this.rootType, null, "/instances/root_type");
            }
        }
        _dirty = false;
    }
    public static class NamedTag extends KaitaiStruct.ReadWrite {
        public static NamedTag fromFile(String fileName) throws IOException {
            return new NamedTag(new ByteBufferKaitaiStream(fileName));
        }
        public NamedTag() {
            this(null, null, null);
        }

        public NamedTag(KaitaiStream _io) {
            this(_io, null, null);
        }

        public NamedTag(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public NamedTag(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.type = MinecraftNbt.Tag.byId(this._io.readU1());
            if (!(isTagEnd())) {
                this.name = new TagString(this._io, this, _root);
                this.name._read();
            }
            if (!(isTagEnd())) {
                {
                    Tag on = type();
                    if (on != null) {
                        switch (type()) {
                        case BYTE: {
                            this.payload = ((Object) (this._io.readS1()));
                            break;
                        }
                        case BYTE_ARRAY: {
                            this.payload = new TagByteArray(this._io, this, _root);
                            ((TagByteArray) (this.payload))._read();
                            break;
                        }
                        case COMPOUND: {
                            this.payload = new TagCompound(this._io, this, _root);
                            ((TagCompound) (this.payload))._read();
                            break;
                        }
                        case DOUBLE: {
                            this.payload = ((Object) (this._io.readF8be()));
                            break;
                        }
                        case FLOAT: {
                            this.payload = ((Object) (this._io.readF4be()));
                            break;
                        }
                        case INT: {
                            this.payload = ((Object) (this._io.readS4be()));
                            break;
                        }
                        case INT_ARRAY: {
                            this.payload = new TagIntArray(this._io, this, _root);
                            ((TagIntArray) (this.payload))._read();
                            break;
                        }
                        case LIST: {
                            this.payload = new TagList(this._io, this, _root);
                            ((TagList) (this.payload))._read();
                            break;
                        }
                        case LONG: {
                            this.payload = ((Object) (this._io.readS8be()));
                            break;
                        }
                        case LONG_ARRAY: {
                            this.payload = new TagLongArray(this._io, this, _root);
                            ((TagLongArray) (this.payload))._read();
                            break;
                        }
                        case SHORT: {
                            this.payload = ((Object) (this._io.readS2be()));
                            break;
                        }
                        case STRING: {
                            this.payload = new TagString(this._io, this, _root);
                            ((TagString) (this.payload))._read();
                            break;
                        }
                        }
                    }
                }
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            if (!(isTagEnd())) {
                this.name._fetchInstances();
            }
            if (!(isTagEnd())) {
                {
                    Tag on = type();
                    if (on != null) {
                        switch (type()) {
                        case BYTE: {
                            break;
                        }
                        case BYTE_ARRAY: {
                            ((TagByteArray) (this.payload))._fetchInstances();
                            break;
                        }
                        case COMPOUND: {
                            ((TagCompound) (this.payload))._fetchInstances();
                            break;
                        }
                        case DOUBLE: {
                            break;
                        }
                        case FLOAT: {
                            break;
                        }
                        case INT: {
                            break;
                        }
                        case INT_ARRAY: {
                            ((TagIntArray) (this.payload))._fetchInstances();
                            break;
                        }
                        case LIST: {
                            ((TagList) (this.payload))._fetchInstances();
                            break;
                        }
                        case LONG: {
                            break;
                        }
                        case LONG_ARRAY: {
                            ((TagLongArray) (this.payload))._fetchInstances();
                            break;
                        }
                        case SHORT: {
                            break;
                        }
                        case STRING: {
                            ((TagString) (this.payload))._fetchInstances();
                            break;
                        }
                        }
                    }
                }
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeU1(((Number) (this.type.id())).intValue());
            if (!(isTagEnd())) {
                this.name._write_Seq(this._io);
            }
            if (!(isTagEnd())) {
                {
                    Tag on = type();
                    if (on != null) {
                        switch (type()) {
                        case BYTE: {
                            this._io.writeS1(((Number) (this.payload)).byteValue());
                            break;
                        }
                        case BYTE_ARRAY: {
                            ((TagByteArray) (this.payload))._write_Seq(this._io);
                            break;
                        }
                        case COMPOUND: {
                            ((TagCompound) (this.payload))._write_Seq(this._io);
                            break;
                        }
                        case DOUBLE: {
                            this._io.writeF8be(((Number) (this.payload)).doubleValue());
                            break;
                        }
                        case FLOAT: {
                            this._io.writeF4be(((Number) (this.payload)).floatValue());
                            break;
                        }
                        case INT: {
                            this._io.writeS4be(((Number) (this.payload)).intValue());
                            break;
                        }
                        case INT_ARRAY: {
                            ((TagIntArray) (this.payload))._write_Seq(this._io);
                            break;
                        }
                        case LIST: {
                            ((TagList) (this.payload))._write_Seq(this._io);
                            break;
                        }
                        case LONG: {
                            this._io.writeS8be(((Number) (this.payload)).longValue());
                            break;
                        }
                        case LONG_ARRAY: {
                            ((TagLongArray) (this.payload))._write_Seq(this._io);
                            break;
                        }
                        case SHORT: {
                            this._io.writeS2be(((Number) (this.payload)).shortValue());
                            break;
                        }
                        case STRING: {
                            ((TagString) (this.payload))._write_Seq(this._io);
                            break;
                        }
                        }
                    }
                }
            }
        }

        public void _check() {
            if (!(isTagEnd())) {
                if (!Objects.equals(this.name._root(), _root()))
                    throw new ConsistencyError("name", _root(), this.name._root());
                if (!Objects.equals(this.name._parent(), this))
                    throw new ConsistencyError("name", this, this.name._parent());
            }
            if (!(isTagEnd())) {
                {
                    Tag on = type();
                    if (on != null) {
                        switch (type()) {
                        case BYTE: {
                            break;
                        }
                        case BYTE_ARRAY: {
                            if (!Objects.equals(((MinecraftNbt.TagByteArray) (this.payload))._root(), _root()))
                                throw new ConsistencyError("payload", _root(), ((MinecraftNbt.TagByteArray) (this.payload))._root());
                            if (!Objects.equals(((MinecraftNbt.TagByteArray) (this.payload))._parent(), this))
                                throw new ConsistencyError("payload", this, ((MinecraftNbt.TagByteArray) (this.payload))._parent());
                            break;
                        }
                        case COMPOUND: {
                            if (!Objects.equals(((MinecraftNbt.TagCompound) (this.payload))._root(), _root()))
                                throw new ConsistencyError("payload", _root(), ((MinecraftNbt.TagCompound) (this.payload))._root());
                            if (!Objects.equals(((MinecraftNbt.TagCompound) (this.payload))._parent(), this))
                                throw new ConsistencyError("payload", this, ((MinecraftNbt.TagCompound) (this.payload))._parent());
                            break;
                        }
                        case DOUBLE: {
                            break;
                        }
                        case FLOAT: {
                            break;
                        }
                        case INT: {
                            break;
                        }
                        case INT_ARRAY: {
                            if (!Objects.equals(((MinecraftNbt.TagIntArray) (this.payload))._root(), _root()))
                                throw new ConsistencyError("payload", _root(), ((MinecraftNbt.TagIntArray) (this.payload))._root());
                            if (!Objects.equals(((MinecraftNbt.TagIntArray) (this.payload))._parent(), this))
                                throw new ConsistencyError("payload", this, ((MinecraftNbt.TagIntArray) (this.payload))._parent());
                            break;
                        }
                        case LIST: {
                            if (!Objects.equals(((MinecraftNbt.TagList) (this.payload))._root(), _root()))
                                throw new ConsistencyError("payload", _root(), ((MinecraftNbt.TagList) (this.payload))._root());
                            if (!Objects.equals(((MinecraftNbt.TagList) (this.payload))._parent(), this))
                                throw new ConsistencyError("payload", this, ((MinecraftNbt.TagList) (this.payload))._parent());
                            break;
                        }
                        case LONG: {
                            break;
                        }
                        case LONG_ARRAY: {
                            if (!Objects.equals(((MinecraftNbt.TagLongArray) (this.payload))._root(), _root()))
                                throw new ConsistencyError("payload", _root(), ((MinecraftNbt.TagLongArray) (this.payload))._root());
                            if (!Objects.equals(((MinecraftNbt.TagLongArray) (this.payload))._parent(), this))
                                throw new ConsistencyError("payload", this, ((MinecraftNbt.TagLongArray) (this.payload))._parent());
                            break;
                        }
                        case SHORT: {
                            break;
                        }
                        case STRING: {
                            if (!Objects.equals(((MinecraftNbt.TagString) (this.payload))._root(), _root()))
                                throw new ConsistencyError("payload", _root(), ((MinecraftNbt.TagString) (this.payload))._root());
                            if (!Objects.equals(((MinecraftNbt.TagString) (this.payload))._parent(), this))
                                throw new ConsistencyError("payload", this, ((MinecraftNbt.TagString) (this.payload))._parent());
                            break;
                        }
                        }
                    }
                }
            }
            _dirty = false;
        }
        private Boolean isTagEnd;
        public Boolean isTagEnd() {
            if (this.isTagEnd != null)
                return this.isTagEnd;
            this.isTagEnd = type() == MinecraftNbt.Tag.END;
            return this.isTagEnd;
        }
        public void _invalidateIsTagEnd() { this.isTagEnd = null; }
        private Tag type;
        private TagString name;
        private Object payload;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;
        public Tag type() { return type; }
        public void setType(Tag _v) { _dirty = true; type = _v; }
        public TagString name() { return name; }
        public void setName(TagString _v) { _dirty = true; name = _v; }
        public Object payload() { return payload; }
        public void setPayload(Object _v) { _dirty = true; payload = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    public static class TagByteArray extends KaitaiStruct.ReadWrite {
        public static TagByteArray fromFile(String fileName) throws IOException {
            return new TagByteArray(new ByteBufferKaitaiStream(fileName));
        }
        public TagByteArray() {
            this(null, null, null);
        }

        public TagByteArray(KaitaiStream _io) {
            this(_io, null, null);
        }

        public TagByteArray(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public TagByteArray(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.lenData = this._io.readS4be();
            this.data = this._io.readBytes(lenData());
            _dirty = false;
        }

        public void _fetchInstances() {
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeS4be(this.lenData);
            this._io.writeBytes(this.data);
        }

        public void _check() {
            if (this.data.length != lenData())
                throw new ConsistencyError("data", lenData(), this.data.length);
            _dirty = false;
        }
        private int lenData;
        private byte[] data;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;
        public int lenData() { return lenData; }
        public void setLenData(int _v) { _dirty = true; lenData = _v; }
        public byte[] data() { return data; }
        public void setData(byte[] _v) { _dirty = true; data = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    public static class TagCompound extends KaitaiStruct.ReadWrite {
        public static TagCompound fromFile(String fileName) throws IOException {
            return new TagCompound(new ByteBufferKaitaiStream(fileName));
        }
        public TagCompound() {
            this(null, null, null);
        }

        public TagCompound(KaitaiStream _io) {
            this(_io, null, null);
        }

        public TagCompound(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public TagCompound(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.tags = new ArrayList<NamedTag>();
            {
                NamedTag _it;
                int i = 0;
                do {
                    NamedTag _t_tags = new NamedTag(this._io, this, _root);
                    try {
                        _t_tags._read();
                    } finally {
                        _it = _t_tags;
                        this.tags.add(_it);
                    }
                    i++;
                } while (!(_it.isTagEnd()));
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            for (int i = 0; i < this.tags.size(); i++) {
                this.tags.get(((Number) (i)).intValue())._fetchInstances();
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            for (int i = 0; i < this.tags.size(); i++) {
                this.tags.get(((Number) (i)).intValue())._write_Seq(this._io);
            }
        }

        public void _check() {
            if (this.tags.size() == 0)
                throw new ConsistencyError("tags", 0, this.tags.size());
            for (int i = 0; i < this.tags.size(); i++) {
                if (!Objects.equals(this.tags.get(((Number) (i)).intValue())._root(), _root()))
                    throw new ConsistencyError("tags", _root(), this.tags.get(((Number) (i)).intValue())._root());
                if (!Objects.equals(this.tags.get(((Number) (i)).intValue())._parent(), this))
                    throw new ConsistencyError("tags", this, this.tags.get(((Number) (i)).intValue())._parent());
                {
                    NamedTag _it = this.tags.get(((Number) (i)).intValue());
                    if (_it.isTagEnd() != (i == this.tags.size() - 1))
                        throw new ConsistencyError("tags", i == this.tags.size() - 1, _it.isTagEnd());
                }
            }
            _dirty = false;
        }
        private Integer dumpNumTags;
        public Integer dumpNumTags() {
            if (this.dumpNumTags != null)
                return this.dumpNumTags;
            this.dumpNumTags = ((Number) (tags().size() - ( ((tags().size() >= 1) && (tags().get(tags().size() - 1).isTagEnd()))  ? 1 : 0))).intValue();
            return this.dumpNumTags;
        }
        public void _invalidateDumpNumTags() { this.dumpNumTags = null; }
        private List<NamedTag> tags;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;
        public List<NamedTag> tags() { return tags; }
        public void setTags(List<NamedTag> _v) { _dirty = true; tags = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    public static class TagIntArray extends KaitaiStruct.ReadWrite {
        public static TagIntArray fromFile(String fileName) throws IOException {
            return new TagIntArray(new ByteBufferKaitaiStream(fileName));
        }
        public TagIntArray() {
            this(null, null, null);
        }

        public TagIntArray(KaitaiStream _io) {
            this(_io, null, null);
        }

        public TagIntArray(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public TagIntArray(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.numTags = this._io.readS4be();
            this.tags = new ArrayList<Integer>();
            for (int i = 0; i < numTags(); i++) {
                this.tags.add(this._io.readS4be());
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            for (int i = 0; i < this.tags.size(); i++) {
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeS4be(this.numTags);
            for (int i = 0; i < this.tags.size(); i++) {
                this._io.writeS4be(this.tags.get(((Number) (i)).intValue()));
            }
        }

        public void _check() {
            if (this.tags.size() != numTags())
                throw new ConsistencyError("tags", numTags(), this.tags.size());
            for (int i = 0; i < this.tags.size(); i++) {
            }
            _dirty = false;
        }
        private Tag tagsType;
        public Tag tagsType() {
            if (this.tagsType != null)
                return this.tagsType;
            this.tagsType = MinecraftNbt.Tag.INT;
            return this.tagsType;
        }
        public void _invalidateTagsType() { this.tagsType = null; }
        private int numTags;
        private List<Integer> tags;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;
        public int numTags() { return numTags; }
        public void setNumTags(int _v) { _dirty = true; numTags = _v; }
        public List<Integer> tags() { return tags; }
        public void setTags(List<Integer> _v) { _dirty = true; tags = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    public static class TagList extends KaitaiStruct.ReadWrite {
        public static TagList fromFile(String fileName) throws IOException {
            return new TagList(new ByteBufferKaitaiStream(fileName));
        }
        public TagList() {
            this(null, null, null);
        }

        public TagList(KaitaiStream _io) {
            this(_io, null, null);
        }

        public TagList(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public TagList(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.tagsType = MinecraftNbt.Tag.byId(this._io.readU1());
            this.numTags = this._io.readS4be();
            this.tags = new ArrayList<Object>();
            for (int i = 0; i < numTags(); i++) {
                {
                    Tag on = tagsType();
                    if (on != null) {
                        switch (tagsType()) {
                        case BYTE: {
                            this.tags.add(((Object) (this._io.readS1())));
                            break;
                        }
                        case BYTE_ARRAY: {
                            TagByteArray _t_tags = new TagByteArray(this._io, this, _root);
                            try {
                                ((TagByteArray) (_t_tags))._read();
                            } finally {
                                this.tags.add(_t_tags);
                            }
                            break;
                        }
                        case COMPOUND: {
                            TagCompound _t_tags = new TagCompound(this._io, this, _root);
                            try {
                                ((TagCompound) (_t_tags))._read();
                            } finally {
                                this.tags.add(_t_tags);
                            }
                            break;
                        }
                        case DOUBLE: {
                            this.tags.add(((Object) (this._io.readF8be())));
                            break;
                        }
                        case FLOAT: {
                            this.tags.add(((Object) (this._io.readF4be())));
                            break;
                        }
                        case INT: {
                            this.tags.add(((Object) (this._io.readS4be())));
                            break;
                        }
                        case INT_ARRAY: {
                            TagIntArray _t_tags = new TagIntArray(this._io, this, _root);
                            try {
                                ((TagIntArray) (_t_tags))._read();
                            } finally {
                                this.tags.add(_t_tags);
                            }
                            break;
                        }
                        case LIST: {
                            TagList _t_tags = new TagList(this._io, this, _root);
                            try {
                                ((TagList) (_t_tags))._read();
                            } finally {
                                this.tags.add(_t_tags);
                            }
                            break;
                        }
                        case LONG: {
                            this.tags.add(((Object) (this._io.readS8be())));
                            break;
                        }
                        case LONG_ARRAY: {
                            TagLongArray _t_tags = new TagLongArray(this._io, this, _root);
                            try {
                                ((TagLongArray) (_t_tags))._read();
                            } finally {
                                this.tags.add(_t_tags);
                            }
                            break;
                        }
                        case SHORT: {
                            this.tags.add(((Object) (this._io.readS2be())));
                            break;
                        }
                        case STRING: {
                            TagString _t_tags = new TagString(this._io, this, _root);
                            try {
                                ((TagString) (_t_tags))._read();
                            } finally {
                                this.tags.add(_t_tags);
                            }
                            break;
                        }
                        }
                    }
                }
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            for (int i = 0; i < this.tags.size(); i++) {
                {
                    Tag on = tagsType();
                    if (on != null) {
                        switch (tagsType()) {
                        case BYTE: {
                            break;
                        }
                        case BYTE_ARRAY: {
                            ((TagByteArray) (this.tags.get(((Number) (i)).intValue())))._fetchInstances();
                            break;
                        }
                        case COMPOUND: {
                            ((TagCompound) (this.tags.get(((Number) (i)).intValue())))._fetchInstances();
                            break;
                        }
                        case DOUBLE: {
                            break;
                        }
                        case FLOAT: {
                            break;
                        }
                        case INT: {
                            break;
                        }
                        case INT_ARRAY: {
                            ((TagIntArray) (this.tags.get(((Number) (i)).intValue())))._fetchInstances();
                            break;
                        }
                        case LIST: {
                            ((TagList) (this.tags.get(((Number) (i)).intValue())))._fetchInstances();
                            break;
                        }
                        case LONG: {
                            break;
                        }
                        case LONG_ARRAY: {
                            ((TagLongArray) (this.tags.get(((Number) (i)).intValue())))._fetchInstances();
                            break;
                        }
                        case SHORT: {
                            break;
                        }
                        case STRING: {
                            ((TagString) (this.tags.get(((Number) (i)).intValue())))._fetchInstances();
                            break;
                        }
                        }
                    }
                }
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeU1(((Number) (this.tagsType.id())).intValue());
            this._io.writeS4be(this.numTags);
            for (int i = 0; i < this.tags.size(); i++) {
                {
                    Tag on = tagsType();
                    if (on != null) {
                        switch (tagsType()) {
                        case BYTE: {
                            this._io.writeS1(((Number) (this.tags.get(((Number) (i)).intValue()))).byteValue());
                            break;
                        }
                        case BYTE_ARRAY: {
                            ((TagByteArray) (this.tags.get(((Number) (i)).intValue())))._write_Seq(this._io);
                            break;
                        }
                        case COMPOUND: {
                            ((TagCompound) (this.tags.get(((Number) (i)).intValue())))._write_Seq(this._io);
                            break;
                        }
                        case DOUBLE: {
                            this._io.writeF8be(((Number) (this.tags.get(((Number) (i)).intValue()))).doubleValue());
                            break;
                        }
                        case FLOAT: {
                            this._io.writeF4be(((Number) (this.tags.get(((Number) (i)).intValue()))).floatValue());
                            break;
                        }
                        case INT: {
                            this._io.writeS4be(((Number) (this.tags.get(((Number) (i)).intValue()))).intValue());
                            break;
                        }
                        case INT_ARRAY: {
                            ((TagIntArray) (this.tags.get(((Number) (i)).intValue())))._write_Seq(this._io);
                            break;
                        }
                        case LIST: {
                            ((TagList) (this.tags.get(((Number) (i)).intValue())))._write_Seq(this._io);
                            break;
                        }
                        case LONG: {
                            this._io.writeS8be(((Number) (this.tags.get(((Number) (i)).intValue()))).longValue());
                            break;
                        }
                        case LONG_ARRAY: {
                            ((TagLongArray) (this.tags.get(((Number) (i)).intValue())))._write_Seq(this._io);
                            break;
                        }
                        case SHORT: {
                            this._io.writeS2be(((Number) (this.tags.get(((Number) (i)).intValue()))).shortValue());
                            break;
                        }
                        case STRING: {
                            ((TagString) (this.tags.get(((Number) (i)).intValue())))._write_Seq(this._io);
                            break;
                        }
                        }
                    }
                }
            }
        }

        public void _check() {
            if (this.tags.size() != numTags())
                throw new ConsistencyError("tags", numTags(), this.tags.size());
            for (int i = 0; i < this.tags.size(); i++) {
                {
                    Tag on = tagsType();
                    if (on != null) {
                        switch (tagsType()) {
                        case BYTE: {
                            break;
                        }
                        case BYTE_ARRAY: {
                            if (!Objects.equals(((MinecraftNbt.TagByteArray) (this.tags.get(((Number) (i)).intValue())))._root(), _root()))
                                throw new ConsistencyError("tags", _root(), ((MinecraftNbt.TagByteArray) (this.tags.get(((Number) (i)).intValue())))._root());
                            if (!Objects.equals(((MinecraftNbt.TagByteArray) (this.tags.get(((Number) (i)).intValue())))._parent(), this))
                                throw new ConsistencyError("tags", this, ((MinecraftNbt.TagByteArray) (this.tags.get(((Number) (i)).intValue())))._parent());
                            break;
                        }
                        case COMPOUND: {
                            if (!Objects.equals(((MinecraftNbt.TagCompound) (this.tags.get(((Number) (i)).intValue())))._root(), _root()))
                                throw new ConsistencyError("tags", _root(), ((MinecraftNbt.TagCompound) (this.tags.get(((Number) (i)).intValue())))._root());
                            if (!Objects.equals(((MinecraftNbt.TagCompound) (this.tags.get(((Number) (i)).intValue())))._parent(), this))
                                throw new ConsistencyError("tags", this, ((MinecraftNbt.TagCompound) (this.tags.get(((Number) (i)).intValue())))._parent());
                            break;
                        }
                        case DOUBLE: {
                            break;
                        }
                        case FLOAT: {
                            break;
                        }
                        case INT: {
                            break;
                        }
                        case INT_ARRAY: {
                            if (!Objects.equals(((MinecraftNbt.TagIntArray) (this.tags.get(((Number) (i)).intValue())))._root(), _root()))
                                throw new ConsistencyError("tags", _root(), ((MinecraftNbt.TagIntArray) (this.tags.get(((Number) (i)).intValue())))._root());
                            if (!Objects.equals(((MinecraftNbt.TagIntArray) (this.tags.get(((Number) (i)).intValue())))._parent(), this))
                                throw new ConsistencyError("tags", this, ((MinecraftNbt.TagIntArray) (this.tags.get(((Number) (i)).intValue())))._parent());
                            break;
                        }
                        case LIST: {
                            if (!Objects.equals(((MinecraftNbt.TagList) (this.tags.get(((Number) (i)).intValue())))._root(), _root()))
                                throw new ConsistencyError("tags", _root(), ((MinecraftNbt.TagList) (this.tags.get(((Number) (i)).intValue())))._root());
                            if (!Objects.equals(((MinecraftNbt.TagList) (this.tags.get(((Number) (i)).intValue())))._parent(), this))
                                throw new ConsistencyError("tags", this, ((MinecraftNbt.TagList) (this.tags.get(((Number) (i)).intValue())))._parent());
                            break;
                        }
                        case LONG: {
                            break;
                        }
                        case LONG_ARRAY: {
                            if (!Objects.equals(((MinecraftNbt.TagLongArray) (this.tags.get(((Number) (i)).intValue())))._root(), _root()))
                                throw new ConsistencyError("tags", _root(), ((MinecraftNbt.TagLongArray) (this.tags.get(((Number) (i)).intValue())))._root());
                            if (!Objects.equals(((MinecraftNbt.TagLongArray) (this.tags.get(((Number) (i)).intValue())))._parent(), this))
                                throw new ConsistencyError("tags", this, ((MinecraftNbt.TagLongArray) (this.tags.get(((Number) (i)).intValue())))._parent());
                            break;
                        }
                        case SHORT: {
                            break;
                        }
                        case STRING: {
                            if (!Objects.equals(((MinecraftNbt.TagString) (this.tags.get(((Number) (i)).intValue())))._root(), _root()))
                                throw new ConsistencyError("tags", _root(), ((MinecraftNbt.TagString) (this.tags.get(((Number) (i)).intValue())))._root());
                            if (!Objects.equals(((MinecraftNbt.TagString) (this.tags.get(((Number) (i)).intValue())))._parent(), this))
                                throw new ConsistencyError("tags", this, ((MinecraftNbt.TagString) (this.tags.get(((Number) (i)).intValue())))._parent());
                            break;
                        }
                        }
                    }
                }
            }
            _dirty = false;
        }
        private Tag tagsType;
        private int numTags;
        private List<Object> tags;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;
        public Tag tagsType() { return tagsType; }
        public void setTagsType(Tag _v) { _dirty = true; tagsType = _v; }
        public int numTags() { return numTags; }
        public void setNumTags(int _v) { _dirty = true; numTags = _v; }
        public List<Object> tags() { return tags; }
        public void setTags(List<Object> _v) { _dirty = true; tags = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    public static class TagLongArray extends KaitaiStruct.ReadWrite {
        public static TagLongArray fromFile(String fileName) throws IOException {
            return new TagLongArray(new ByteBufferKaitaiStream(fileName));
        }
        public TagLongArray() {
            this(null, null, null);
        }

        public TagLongArray(KaitaiStream _io) {
            this(_io, null, null);
        }

        public TagLongArray(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public TagLongArray(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.numTags = this._io.readS4be();
            this.tags = new ArrayList<Long>();
            for (int i = 0; i < numTags(); i++) {
                this.tags.add(this._io.readS8be());
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            for (int i = 0; i < this.tags.size(); i++) {
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeS4be(this.numTags);
            for (int i = 0; i < this.tags.size(); i++) {
                this._io.writeS8be(this.tags.get(((Number) (i)).intValue()));
            }
        }

        public void _check() {
            if (this.tags.size() != numTags())
                throw new ConsistencyError("tags", numTags(), this.tags.size());
            for (int i = 0; i < this.tags.size(); i++) {
            }
            _dirty = false;
        }
        private Tag tagsType;
        public Tag tagsType() {
            if (this.tagsType != null)
                return this.tagsType;
            this.tagsType = MinecraftNbt.Tag.LONG;
            return this.tagsType;
        }
        public void _invalidateTagsType() { this.tagsType = null; }
        private int numTags;
        private List<Long> tags;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;
        public int numTags() { return numTags; }
        public void setNumTags(int _v) { _dirty = true; numTags = _v; }
        public List<Long> tags() { return tags; }
        public void setTags(List<Long> _v) { _dirty = true; tags = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    public static class TagString extends KaitaiStruct.ReadWrite {
        public static TagString fromFile(String fileName) throws IOException {
            return new TagString(new ByteBufferKaitaiStream(fileName));
        }
        public TagString() {
            this(null, null, null);
        }

        public TagString(KaitaiStream _io) {
            this(_io, null, null);
        }

        public TagString(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
            this(_io, _parent, null);
        }

        public TagString(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, MinecraftNbt _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.lenData = this._io.readU2be();
            this.data = new String(this._io.readBytes(lenData()), StandardCharsets.UTF_8);
            _dirty = false;
        }

        public void _fetchInstances() {
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeU2be(this.lenData);
            this._io.writeBytes((this.data).getBytes(Charset.forName("UTF-8")));
        }

        public void _check() {
            if ((this.data).getBytes(Charset.forName("UTF-8")).length != lenData())
                throw new ConsistencyError("data", lenData(), (this.data).getBytes(Charset.forName("UTF-8")).length);
            _dirty = false;
        }
        private int lenData;
        private String data;
        private MinecraftNbt _root;
        private KaitaiStruct.ReadWrite _parent;

        /**
         * unsigned according to <https://wiki.vg/NBT#Specification>
         */
        public int lenData() { return lenData; }
        public void setLenData(int _v) { _dirty = true; lenData = _v; }
        public String data() { return data; }
        public void setData(String _v) { _dirty = true; data = _v; }
        public MinecraftNbt _root() { return _root; }
        public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
        public KaitaiStruct.ReadWrite _parent() { return _parent; }
        public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    }
    private Tag rootType;
    private boolean _shouldWriteRootType = false;
    private boolean _enabledRootType = true;
    public Tag rootType() {
        if (_shouldWriteRootType)
            _writeRootType();
        if (this.rootType != null)
            return this.rootType;
        if (!_enabledRootType)
            return null;
        long _pos = this._io.pos();
        this._io.seek(0);
        this.rootType = Tag.byId(this._io.readU1());
        if (!(this.rootType == Tag.COMPOUND)) {
            throw new KaitaiStream.ValidationNotEqualError(Tag.COMPOUND, this.rootType, this._io, "/instances/root_type");
        }
        this._io.seek(_pos);
        return this.rootType;
    }
    public void setRootType(Tag _v) { _dirty = true; rootType = _v; }
    public void setRootType_Enabled(boolean _v) { _dirty = true; _enabledRootType = _v; }

    private void _writeRootType() {
        _shouldWriteRootType = false;
        long _pos = this._io.pos();
        this._io.seek(0);
        this._io.writeU1(((Number) (this.rootType.id())).intValue());
        this._io.seek(_pos);
    }
    private byte[] rootCheck;
    private NamedTag root;
    private MinecraftNbt _root;
    private KaitaiStruct.ReadWrite _parent;
    public byte[] rootCheck() { return rootCheck; }
    public void setRootCheck(byte[] _v) { _dirty = true; rootCheck = _v; }
    public NamedTag root() { return root; }
    public void setRoot(NamedTag _v) { _dirty = true; root = _v; }
    public MinecraftNbt _root() { return _root; }
    public void set_root(MinecraftNbt _v) { _dirty = true; _root = _v; }
    public KaitaiStruct.ReadWrite _parent() { return _parent; }
    public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
}
