diff --git a/Icons/PNG/Boolean.png b/Icons/PNG/Boolean.png
new file mode 100644
index 0000000..4ba8e38
Binary files /dev/null and b/Icons/PNG/Boolean.png differ
diff --git a/Icons/PNG/Byte.png b/Icons/PNG/Byte.png
new file mode 100644
index 0000000..38f1147
Binary files /dev/null and b/Icons/PNG/Byte.png differ
diff --git a/Icons/PNG/ByteArray.png b/Icons/PNG/ByteArray.png
new file mode 100644
index 0000000..40d5d48
Binary files /dev/null and b/Icons/PNG/ByteArray.png differ
diff --git a/Icons/PNG/Compound.png b/Icons/PNG/Compound.png
new file mode 100644
index 0000000..0ba8911
Binary files /dev/null and b/Icons/PNG/Compound.png differ
diff --git a/Icons/PNG/Double.png b/Icons/PNG/Double.png
new file mode 100644
index 0000000..4138e8d
Binary files /dev/null and b/Icons/PNG/Double.png differ
diff --git a/Icons/PNG/Float.png b/Icons/PNG/Float.png
new file mode 100644
index 0000000..336953e
Binary files /dev/null and b/Icons/PNG/Float.png differ
diff --git a/Icons/PNG/Integer.png b/Icons/PNG/Integer.png
new file mode 100644
index 0000000..001ad5d
Binary files /dev/null and b/Icons/PNG/Integer.png differ
diff --git a/Icons/PNG/IntegerArray.png b/Icons/PNG/IntegerArray.png
new file mode 100644
index 0000000..c89cc2b
Binary files /dev/null and b/Icons/PNG/IntegerArray.png differ
diff --git a/Icons/PNG/List.png b/Icons/PNG/List.png
new file mode 100644
index 0000000..3cb34e1
Binary files /dev/null and b/Icons/PNG/List.png differ
diff --git a/Icons/PNG/Long.png b/Icons/PNG/Long.png
new file mode 100644
index 0000000..af9a2fa
Binary files /dev/null and b/Icons/PNG/Long.png differ
diff --git a/Icons/PNG/LongArray.png b/Icons/PNG/LongArray.png
new file mode 100644
index 0000000..d9e167a
Binary files /dev/null and b/Icons/PNG/LongArray.png differ
diff --git a/Icons/PNG/Short.png b/Icons/PNG/Short.png
new file mode 100644
index 0000000..7813249
Binary files /dev/null and b/Icons/PNG/Short.png differ
diff --git a/Icons/PNG/String.png b/Icons/PNG/String.png
new file mode 100644
index 0000000..15fb453
Binary files /dev/null and b/Icons/PNG/String.png differ
diff --git a/Icons/SVG/Boolean.svg b/Icons/SVG/Boolean.svg
new file mode 100644
index 0000000..8ef3aa6
--- /dev/null
+++ b/Icons/SVG/Boolean.svg
@@ -0,0 +1,140 @@
+
+
diff --git a/Icons/SVG/Byte.svg b/Icons/SVG/Byte.svg
new file mode 100644
index 0000000..8bc43c8
--- /dev/null
+++ b/Icons/SVG/Byte.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/ByteArray.svg b/Icons/SVG/ByteArray.svg
new file mode 100644
index 0000000..d6f04b2
--- /dev/null
+++ b/Icons/SVG/ByteArray.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/Compound.svg b/Icons/SVG/Compound.svg
new file mode 100644
index 0000000..3e749bc
--- /dev/null
+++ b/Icons/SVG/Compound.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/Double.svg b/Icons/SVG/Double.svg
new file mode 100644
index 0000000..af6d335
--- /dev/null
+++ b/Icons/SVG/Double.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/Float.svg b/Icons/SVG/Float.svg
new file mode 100644
index 0000000..db22047
--- /dev/null
+++ b/Icons/SVG/Float.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/IntArray.svg b/Icons/SVG/IntArray.svg
new file mode 100644
index 0000000..5af21b6
--- /dev/null
+++ b/Icons/SVG/IntArray.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/Integer.svg b/Icons/SVG/Integer.svg
new file mode 100644
index 0000000..99f144a
--- /dev/null
+++ b/Icons/SVG/Integer.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/List.svg b/Icons/SVG/List.svg
new file mode 100644
index 0000000..c4ab464
--- /dev/null
+++ b/Icons/SVG/List.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/Long.svg b/Icons/SVG/Long.svg
new file mode 100644
index 0000000..52173b6
--- /dev/null
+++ b/Icons/SVG/Long.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/LongArray.svg b/Icons/SVG/LongArray.svg
new file mode 100644
index 0000000..a3bf0ab
--- /dev/null
+++ b/Icons/SVG/LongArray.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/Short.svg b/Icons/SVG/Short.svg
new file mode 100644
index 0000000..c6e64ab
--- /dev/null
+++ b/Icons/SVG/Short.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/Icons/SVG/String.svg b/Icons/SVG/String.svg
new file mode 100644
index 0000000..9724bb8
--- /dev/null
+++ b/Icons/SVG/String.svg
@@ -0,0 +1,1027 @@
+
+
\ No newline at end of file
diff --git a/lib/Constants.dart b/lib/Constants.dart
index 3c5a616..a811f3a 100644
--- a/lib/Constants.dart
+++ b/lib/Constants.dart
@@ -2,4 +2,5 @@ import 'package:flutter/material.dart';
class Constants {
static const Color TITLEBAR_COLOR = Color.fromARGB(255, 80, 0, 0);
+ static const Color DRAWER_COLOR = Color.fromARGB(255, 0, 75, 75);
}
diff --git a/lib/Editor.dart b/lib/Editor.dart
new file mode 100644
index 0000000..9bc90aa
--- /dev/null
+++ b/lib/Editor.dart
@@ -0,0 +1,129 @@
+import 'package:file_picker/file_picker.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_treeview/flutter_treeview.dart';
+import 'package:nbteditor/Constants.dart';
+import 'package:nbteditor/tags/CompoundTag.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+
+class Editor extends StatefulWidget {
+ Editor({super.key});
+
+ @override
+ EditorState createState() => EditorState();
+}
+
+class EditorState extends State {
+ List nodes = [CompoundTag().getNode("/")];
+ bool compressed = false;
+
+ late TreeViewController controller;
+
+ String appendCompressed() {
+ if (compressed) {
+ return " - Compressed";
+ } else
+ return "";
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ controller = TreeViewController(children: nodes);
+
+ return Scaffold(
+ appBar: AppBar(
+ backgroundColor: Constants.TITLEBAR_COLOR,
+ title: Text("Named Binary Tag Editor${appendCompressed()}"),
+ ),
+ drawer: Drawer(
+ backgroundColor: Constants.DRAWER_COLOR,
+ child: Column(children: [
+ DrawerHeader(
+ child: Column(
+ children: [
+ Text("Named Binary Tag Editor"),
+ Text("Created by Tara Piccari")
+ ],
+ )),
+ ListTile(
+ title: Text("N E W"),
+ subtitle: Text("Create a new NBT Document"),
+ leading: Icon(Icons.add),
+ onTap: () {
+ setState(() {
+ nodes.clear();
+
+ // Add a new compound tag as the root
+ Tag tag = CompoundTag();
+ nodes.add(tag.getNode("/"));
+ });
+ },
+ ),
+ ListTile(
+ title: Text("O P E N"),
+ leading: Icon(Icons.folder),
+ subtitle: Text("Open an existing NBT Document for editing"),
+ onTap: () async {
+ FilePickerResult? result = await FilePicker.platform.pickFiles();
+ String? filePath;
+ if (result != null) {
+ filePath = result.files.single.path;
+ // Do something with the selected file path
+ print('Selected file path: $filePath');
+ } else {
+ // User canceled the picker
+ print('File selection canceled.');
+ }
+ if (filePath == null) {
+ // cancelled
+ return;
+ } else {
+ // String!!
+ compressed = await NbtIo.read(filePath);
+ }
+
+ setState(() {
+ nodes.clear();
+ nodes.add(Tag.read(NbtIo.getStream()).getNode("/"));
+
+ controller = TreeViewController(children: nodes);
+ });
+ },
+ )
+ ]),
+ ),
+ body: TreeView(
+ nodeBuilder: (context, node) {
+ return (node.data as Tag).render();
+ },
+ controller: controller,
+ ),
+ );
+ }
+}
+
+class FileSelectionScreen extends StatelessWidget {
+ Future openFilePicker(BuildContext context) async {
+ try {} catch (e) {
+ // Handle errors
+ print('Error selecting file: $e');
+ }
+
+ Navigator.pop(context);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('File Selection'),
+ ),
+ body: Center(
+ child: ElevatedButton(
+ onPressed: () => openFilePicker(context),
+ child: Text('Select File'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index a725658..17412a5 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:nbteditor/Editor.dart';
void main() {
runApp(const MainApp());
@@ -9,12 +10,12 @@ class MainApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return const MaterialApp(
- home: Scaffold(
- body: Center(
- child: Text('Hello World!'),
- ),
- ),
+ return MaterialApp(
+ theme: ThemeData.dark(),
+ routes: {
+ "/": (context) => Editor(),
+ "/select_file": (context) => FileSelectionScreen()
+ },
);
}
}
diff --git a/lib/tags/ByteArrayTag.dart b/lib/tags/ByteArrayTag.dart
new file mode 100644
index 0000000..65c4616
--- /dev/null
+++ b/lib/tags/ByteArrayTag.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class ByteArrayTag extends Tag {
+ List _value = [];
+
+ @override
+ Node getNode(String path) {
+ List entries = [];
+ int count = 0;
+ for (var element in _value) {
+ entries.add(Node(key: "$path/${count}", label: "${element}"));
+ count++;
+ }
+
+ return Node(key: path, label: Name, data: this, children: entries);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ int count = layer.readInt();
+ for (int i = 0; i < count; i++) {
+ _value.add(layer.readByte());
+ }
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_ByteArray (${Name})"),
+ subtitle: Text("${_value.length} entries"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.ByteArray.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeInt(_value.length);
+
+ for (var element in _value) {
+ layer.writeByte(element);
+ }
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.ByteArray;
+ }
+}
diff --git a/lib/tags/ByteTag.dart b/lib/tags/ByteTag.dart
new file mode 100644
index 0000000..d9685e5
--- /dev/null
+++ b/lib/tags/ByteTag.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class ByteTag extends Tag {
+ int _value = 0;
+
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "TAG_Byte ${Name}", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ this._value = layer.readByte();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Byte (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Byte.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeByte(this._value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Byte;
+ }
+}
diff --git a/lib/tags/CompoundTag.dart b/lib/tags/CompoundTag.dart
new file mode 100644
index 0000000..0790b56
--- /dev/null
+++ b/lib/tags/CompoundTag.dart
@@ -0,0 +1,91 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_treeview/flutter_treeview.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+import 'package:uuid/v4.dart';
+
+class CompoundTag extends Tag {
+ UuidV4 v4 = UuidV4();
+ Map _children = {};
+
+ CompoundTag() {
+ setKey(v4.generate());
+
+ setName("root");
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Compound (${Name})"),
+ subtitle:
+ Text("${_children.length} tag${_children.length > 1 ? "s" : ""}"),
+ );
+ }
+
+ @override
+ Node getNode(String path) {
+ List childTags = [];
+
+ for (var element in _children.entries) {
+ childTags.add(element.value.getNode(path + "/${element.key}"));
+ }
+ Node me = Node(key: path, label: Name, data: this, children: childTags);
+ return me;
+ }
+
+ void put(String name, Tag child) {
+ _children[name] = child.withNick(name);
+ }
+
+ Tag? get(String name) {
+ return _children[name];
+ }
+
+ void remove(String name) {
+ _children.remove(name);
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ TagType type;
+ while (true) {
+ type = Tag.readTagType(layer);
+ if (type == TagType.End) break;
+
+ Tag tag = Tag.readTag(layer, type, false);
+ put(tag.Name, tag);
+ }
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ for (var entry in _children.entries) {
+ layer.writeTagName(entry.key);
+ entry.value.writeValue(layer);
+ }
+
+ layer.writeByte(TagType.End.toByte());
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Compound.toByte());
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Compound;
+ }
+}
diff --git a/lib/tags/DoubleTag.dart b/lib/tags/DoubleTag.dart
new file mode 100644
index 0000000..a4d309c
--- /dev/null
+++ b/lib/tags/DoubleTag.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class DoubleTag extends Tag {
+ double _value = 0.0;
+
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ _value = layer.readDouble();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Double (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Double.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeDouble(_value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Double;
+ }
+}
diff --git a/lib/tags/FloatTag.dart b/lib/tags/FloatTag.dart
new file mode 100644
index 0000000..c020e51
--- /dev/null
+++ b/lib/tags/FloatTag.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class FloatTag extends Tag {
+ double _value = 0.0;
+
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "$_value", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ _value = layer.readFloat();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Float (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Float.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeFloat(_value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Float;
+ }
+}
diff --git a/lib/tags/IntArrayTag.dart b/lib/tags/IntArrayTag.dart
new file mode 100644
index 0000000..0a6c22f
--- /dev/null
+++ b/lib/tags/IntArrayTag.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class IntArrayTag extends Tag {
+ List _value = [];
+
+ @override
+ Node getNode(String path) {
+ List entries = [];
+ int count = 0;
+ for (var element in _value) {
+ entries.add(Node(key: "$path/${count}", label: "${element}"));
+ count++;
+ }
+
+ return Node(key: path, label: Name, data: this, children: entries);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ int count = layer.readInt();
+ for (int i = 0; i < count; i++) {
+ _value.add(layer.readInt());
+ }
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_IntArray (${Name})"),
+ subtitle: Text("${_value.length} entries"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.IntArray.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeInt(_value.length);
+
+ for (var element in _value) {
+ layer.writeInt(element);
+ }
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.IntArray;
+ }
+}
diff --git a/lib/tags/IntTag.dart b/lib/tags/IntTag.dart
new file mode 100644
index 0000000..5a1d5ba
--- /dev/null
+++ b/lib/tags/IntTag.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class IntTag extends Tag {
+ int _value = 0;
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "$_value", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ _value = layer.readInt();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Int (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Int.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeInt(_value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Int;
+ }
+}
diff --git a/lib/tags/ListTag.dart b/lib/tags/ListTag.dart
new file mode 100644
index 0000000..73974c3
--- /dev/null
+++ b/lib/tags/ListTag.dart
@@ -0,0 +1,100 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class ListTag extends Tag {
+ List _value = [];
+
+ @override
+ Node getNode(String path) {
+ List nodes = [];
+
+ int count = 0;
+ for (var element in _value) {
+ nodes.add(element.getNode("$path/$count"));
+ count++;
+ }
+
+ return Node(key: path, label: Name, data: this, children: nodes);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ bool add(Tag tag) {
+ if (_value.length > 0) {
+ if (tag.getTagType() == _value[0].getTagType()) {
+ _value.add(tag);
+
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _value.add(tag);
+ return true;
+ }
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ TagType type = Tag.readTagType(layer);
+ int count = layer.readInt();
+
+ if (count == 0) return;
+
+ for (int i = 0; i < count; i++) {
+ add(Tag.readTag(layer, type, true));
+ }
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_List (${Name}) (${getListTagType()})"),
+ subtitle: Text("${_value.length} entries"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.List.toByte());
+ }
+
+ TagType getListTagType() {
+ if (_value.length > 0) {
+ return TagType.End;
+ } else
+ return _value[0].getTagType();
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ if (_value.length > 0) {
+ _value[0].writeTagType(layer);
+ } else {
+ layer.writeByte(TagType.End.toByte());
+ }
+
+ layer.writeInt(_value.length);
+
+ for (var element in _value) {
+ element.writeValue(layer);
+ }
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.List;
+ }
+}
diff --git a/lib/tags/LongArrayTag.dart b/lib/tags/LongArrayTag.dart
new file mode 100644
index 0000000..6e65346
--- /dev/null
+++ b/lib/tags/LongArrayTag.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class LongArrayTag extends Tag {
+ List _value = [];
+
+ @override
+ Node getNode(String path) {
+ List entries = [];
+ int count = 0;
+ for (var element in _value) {
+ entries.add(Node(key: "$path/${count}", label: "${element}"));
+ count++;
+ }
+
+ return Node(key: path, label: Name, data: this, children: entries);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ int count = layer.readInt();
+ for (int i = 0; i < count; i++) {
+ _value.add(layer.readLong());
+ }
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_LongArray (${Name})"),
+ subtitle: Text("${_value.length} entries"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.LongArray.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeInt(_value.length);
+
+ for (var element in _value) {
+ layer.writeLong(element);
+ }
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.LongArray;
+ }
+}
diff --git a/lib/tags/LongTag.dart b/lib/tags/LongTag.dart
new file mode 100644
index 0000000..b307e8e
--- /dev/null
+++ b/lib/tags/LongTag.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class LongTag extends Tag {
+ int _value = 0;
+
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "$_value", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ _value = layer.readLong();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Long (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Long.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeLong(_value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Long;
+ }
+}
diff --git a/lib/tags/NbtIo.dart b/lib/tags/NbtIo.dart
new file mode 100644
index 0000000..90d9e34
--- /dev/null
+++ b/lib/tags/NbtIo.dart
@@ -0,0 +1,285 @@
+import 'dart:convert';
+import 'dart:ffi';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:nbteditor/tags/TagType.dart';
+
+class ByteLayer {
+ Uint8List _byteBuffer = Uint8List(0);
+ int _position = 0;
+
+ ByteLayer() {
+ _byteBuffer = Uint8List(0); // Initial size, can be adjusted
+ _position = 0;
+ }
+
+ int get length => _byteBuffer.length;
+
+ int get currentPosition => _position;
+
+ Uint8List get bytes => _byteBuffer.sublist(0, _position);
+
+ void _ensureCapacity(int additionalBytes) {
+ final requiredCapacity = _position + additionalBytes;
+ if (requiredCapacity > _byteBuffer.length) {
+ final newCapacity =
+ _position * 2 + additionalBytes; // Adjust capacity as needed
+ final newBuffer = Uint8List(newCapacity);
+ newBuffer.setAll(0, _byteBuffer);
+ _byteBuffer = newBuffer;
+ }
+ }
+
+ void writeInt(int value) {
+ _ensureCapacity(4);
+ _byteBuffer.buffer.asByteData().setInt32(_position, value, Endian.big);
+ _position += 4;
+ }
+
+ void writeDouble(double value) {
+ _ensureCapacity(8);
+ _byteBuffer.buffer.asByteData().setFloat64(_position, value, Endian.big);
+ _position += 8;
+ }
+
+ void writeString(String value) {
+ final encoded = utf8.encode(value);
+ writeShort(encoded.length);
+ _ensureCapacity(encoded.length);
+ _byteBuffer.setAll(_position, encoded);
+ _position += encoded.length;
+ }
+
+ int readInt() {
+ final value =
+ _byteBuffer.buffer.asByteData().getInt32(_position, Endian.big);
+ _position += 4;
+ return value;
+ }
+
+ double readDouble() {
+ final value =
+ _byteBuffer.buffer.asByteData().getFloat64(_position, Endian.big);
+ _position += 8;
+ return value;
+ }
+
+ String readString() {
+ final length = readShort();
+ final encoded = _byteBuffer.sublist(_position, _position + length);
+ _position += length;
+ return utf8.decode(encoded);
+ }
+
+ void writeIntZigZag(int value) {
+ final zigzag = (value << 1) ^ (value >> 31);
+ writeInt(zigzag);
+ }
+
+ int readIntZigZag() {
+ final zigzag = readInt();
+ final value = (zigzag >> 1) ^ -(zigzag & 1);
+ return value;
+ }
+
+ void writeByte(int value) {
+ _ensureCapacity(1);
+ _byteBuffer[_position] = value & 0xFF;
+ _position++;
+ }
+
+ int readByte() {
+ final value = _byteBuffer[_position];
+ _position++;
+ return value;
+ }
+
+ void writeVarInt(int value) {
+ while ((value & ~0x7F) != 0) {
+ writeByte((value & 0x7F) | 0x80);
+ value = (value >> 7) & 0x1FFFFFFF;
+ }
+ writeByte(value & 0x7F);
+ }
+
+ int readVarInt() {
+ int result = 0;
+ int shift = 0;
+ int byte;
+ do {
+ byte = readByte();
+ result |= (byte & 0x7F) << shift;
+ if ((byte & 0x80) == 0) {
+ break;
+ }
+ shift += 7;
+ } while (true);
+
+ return result;
+ }
+
+ void writeVarIntNoZigZag(int value) {
+ while ((value & ~0x7F) != 0) {
+ writeByte((value & 0x7F) | 0x80);
+ value >>= 7;
+ }
+ writeByte(value & 0x7F);
+ }
+
+ int readVarIntNoZigZag() {
+ int result = 0;
+ int shift = 0;
+ int byte;
+ do {
+ byte = readByte();
+ result |= (byte & 0x7F) << shift;
+ if ((byte & 0x80) == 0) {
+ break;
+ }
+ shift += 7;
+ } while (true);
+
+ return result;
+ }
+
+ void writeShort(int value) {
+ _ensureCapacity(2);
+ _byteBuffer.buffer.asByteData().setInt16(_position, value, Endian.big);
+ _position += 2;
+ }
+
+ int readShort() {
+ final value =
+ _byteBuffer.buffer.asByteData().getInt16(_position, Endian.big);
+ _position += 2;
+ return value;
+ }
+
+ void writeFloat(double value) {
+ _ensureCapacity(4);
+ _byteBuffer.buffer.asByteData().setFloat32(_position, value, Endian.big);
+ _position += 2;
+ }
+
+ double readFloat() {
+ final value =
+ _byteBuffer.buffer.asByteData().getFloat32(_position, Endian.big);
+
+ _position += 2;
+ return value;
+ }
+
+ void writeTagName(String name) {
+ final encodedName = utf8.encode(name);
+ writeShort(encodedName.length);
+ _ensureCapacity(encodedName.length);
+ _byteBuffer.setAll(_position, encodedName);
+ _position += encodedName.length;
+ }
+
+ String readTagName() {
+ final length = readShort();
+ final encodedName = _byteBuffer.sublist(_position, _position + length);
+ _position += length;
+ return utf8.decode(encodedName);
+ }
+
+ void resetPosition() {
+ _position = 0;
+ }
+
+ void clear() {
+ resetPosition();
+ _byteBuffer = Uint8List(0);
+ }
+
+ Future writeToFile(String filePath) async {
+ final file = File(filePath);
+ await file.writeAsBytes(bytes);
+ }
+
+ Future readFromFile(String filePath) async {
+ final file = File(filePath);
+ final exists = await file.exists();
+ if (!exists) {
+ print('File does not exist.');
+ return;
+ }
+
+ _byteBuffer = await file.readAsBytes();
+ resetPosition();
+ }
+
+ Future compress() async {
+ final gzip = GZipCodec();
+ final compressedData = gzip.encode(_byteBuffer);
+ _byteBuffer = Uint8List.fromList(compressedData);
+ _position = _byteBuffer.length;
+ }
+
+ Future decompress() async {
+ final gzip = GZipCodec();
+ final decompressedData = gzip.decode(_byteBuffer);
+ _byteBuffer = Uint8List.fromList(decompressedData);
+ _position = _byteBuffer.length;
+ }
+
+ void writeLong(int value) {
+ _ensureCapacity(8);
+ _byteBuffer.buffer.asByteData().setInt64(_position, value, Endian.big);
+ _position += 8;
+ }
+
+ int readLong() {
+ final value =
+ _byteBuffer.buffer.asByteData().getInt64(_position, Endian.big);
+ _position += 8;
+ return value;
+ }
+}
+
+class NbtIo {
+ static ByteLayer _io = ByteLayer();
+
+ // Handle various helper functions here!
+
+ static Future _read(String file) async {
+ _io = ByteLayer();
+
+ await _io.readFromFile(file);
+ }
+
+ // This function will read the file and check if it is infact gzipped
+ static Future read(String file) async {
+ await _read(file);
+ if (_io.readByte() == TagType.Compound.toByte()) {
+ _io.resetPosition();
+ return false;
+ } else {
+ // Is likely gzip compressed
+ await _readCompressed(file);
+ _io.resetPosition();
+ return true;
+ }
+ }
+
+ static Future _readCompressed(String file) async {
+ _io = ByteLayer();
+ await _io.readFromFile(file);
+ await _io.decompress();
+ }
+
+ static Future write(String file) async {
+ await _io.writeToFile(file);
+ }
+
+ static Future writeCompressed(String file) async {
+ await _io.compress();
+ await _io.writeToFile(file);
+ }
+
+ static ByteLayer getStream() {
+ return _io;
+ }
+}
diff --git a/lib/tags/ShortTag.dart b/lib/tags/ShortTag.dart
new file mode 100644
index 0000000..d16fe93
--- /dev/null
+++ b/lib/tags/ShortTag.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class ShortTag extends Tag {
+ int _value = 0;
+
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "$_value", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ _value = layer.readShort();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_Short (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.Short.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeShort(_value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.Short;
+ }
+}
diff --git a/lib/tags/StringTag.dart b/lib/tags/StringTag.dart
new file mode 100644
index 0000000..685431a
--- /dev/null
+++ b/lib/tags/StringTag.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_treeview/src/models/node.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/Tag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+class StringTag extends Tag {
+ String _value = "";
+ @override
+ Node getNode(String path) {
+ return Node(key: path, label: "$_value", data: this);
+ }
+
+ @override
+ void readHeader(ByteLayer layer) {
+ setName(layer.readTagName());
+ }
+
+ @override
+ void readValue(ByteLayer layer) {
+ _value = layer.readString();
+ }
+
+ @override
+ Widget render() {
+ return ListTile(
+ title: Text("TAG_String (${Name})"),
+ subtitle: Text("${_value}"),
+ );
+ }
+
+ @override
+ void writeHeader(ByteLayer layer) {
+ layer.writeTagName(Name);
+ }
+
+ @override
+ void writeTagType(ByteLayer layer) {
+ layer.writeByte(TagType.String.toByte());
+ }
+
+ @override
+ void writeValue(ByteLayer layer) {
+ layer.writeString(_value);
+ }
+
+ @override
+ TagType getTagType() {
+ return TagType.String;
+ }
+}
diff --git a/lib/tags/Tag.dart b/lib/tags/Tag.dart
new file mode 100644
index 0000000..0dc8fd6
--- /dev/null
+++ b/lib/tags/Tag.dart
@@ -0,0 +1,229 @@
+import 'dart:ffi';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_treeview/flutter_treeview.dart';
+import 'package:nbteditor/tags/ByteArrayTag.dart';
+import 'package:nbteditor/tags/ByteTag.dart';
+import 'package:nbteditor/tags/CompoundTag.dart';
+import 'package:nbteditor/tags/DoubleTag.dart';
+import 'package:nbteditor/tags/FloatTag.dart';
+import 'package:nbteditor/tags/IntArrayTag.dart';
+import 'package:nbteditor/tags/IntTag.dart';
+import 'package:nbteditor/tags/ListTag.dart';
+import 'package:nbteditor/tags/LongArrayTag.dart';
+import 'package:nbteditor/tags/LongTag.dart';
+import 'package:nbteditor/tags/NbtIo.dart';
+import 'package:nbteditor/tags/ShortTag.dart';
+import 'package:nbteditor/tags/StringTag.dart';
+import 'package:nbteditor/tags/TagType.dart';
+
+abstract class Tag {
+ String Name = "";
+ Tag();
+
+ late String key;
+
+ void setKey(String key) {
+ this.key = key;
+ }
+
+ Widget render();
+
+ Node getNode(String path);
+
+ ByteTag asByte() {
+ if (this is ByteTag)
+ return this as ByteTag;
+ else
+ return ByteTag();
+ }
+
+ ShortTag asShort() {
+ if (this is ShortTag)
+ return this as ShortTag;
+ else
+ return ShortTag();
+ }
+
+ IntTag asInt() {
+ if (this is IntTag)
+ return this as IntTag;
+ else
+ return IntTag();
+ }
+
+ LongTag asLong() {
+ if (this is LongTag)
+ return this as LongTag;
+ else
+ return LongTag();
+ }
+
+ FloatTag asFloat() {
+ if (this is FloatTag)
+ return this as FloatTag;
+ else
+ return FloatTag();
+ }
+
+ DoubleTag asDouble() {
+ if (this is DoubleTag)
+ return this as DoubleTag;
+ else
+ return DoubleTag();
+ }
+
+ ByteArrayTag asByteArray() {
+ if (this is ByteArrayTag)
+ return this as ByteArrayTag;
+ else
+ return ByteArrayTag();
+ }
+
+ StringTag asString() {
+ if (this is StringTag)
+ return this as StringTag;
+ else
+ return StringTag();
+ }
+
+ ListTag asListTag() {
+ if (this is ListTag)
+ return this as ListTag;
+ else
+ return ListTag();
+ }
+
+ CompoundTag asCompoundTag() {
+ if (this is CompoundTag)
+ return this as CompoundTag;
+ else
+ return CompoundTag();
+ }
+
+ IntArrayTag asIntArrayTag() {
+ if (this is IntArrayTag)
+ return this as IntArrayTag;
+ else
+ return IntArrayTag();
+ }
+
+ LongArrayTag asLongArrayTag() {
+ if (this is LongArrayTag)
+ return this as LongArrayTag;
+ else
+ return LongArrayTag();
+ }
+
+ Tag withNick(String name) {
+ Name = name;
+ return this;
+ }
+
+ void setName(String name) {
+ Name = name;
+ }
+
+ static Tag read(ByteLayer layer) {
+ TagType tagType = readTagType(layer);
+ return readTag(layer, tagType, false);
+ }
+
+ static Tag readTag(ByteLayer layer, TagType tagType, bool isList) {
+ Tag tag;
+ switch (tagType) {
+ case TagType.Byte:
+ {
+ tag = ByteTag();
+ break;
+ }
+ case TagType.Short:
+ {
+ tag = ShortTag();
+ break;
+ }
+ case TagType.Int:
+ {
+ tag = IntTag();
+ break;
+ }
+ case TagType.Long:
+ {
+ tag = LongTag();
+ break;
+ }
+ case TagType.Float:
+ {
+ tag = FloatTag();
+ break;
+ }
+ case TagType.Double:
+ {
+ tag = DoubleTag();
+ break;
+ }
+ case TagType.ByteArray:
+ {
+ tag = ByteArrayTag();
+ break;
+ }
+ case TagType.String:
+ {
+ tag = StringTag();
+ break;
+ }
+ case TagType.List:
+ {
+ tag = ListTag();
+ break;
+ }
+ case TagType.Compound:
+ {
+ tag = CompoundTag();
+ break;
+ }
+ case TagType.IntArray:
+ {
+ tag = IntArrayTag();
+ break;
+ }
+ case TagType.LongArray:
+ {
+ tag = LongArrayTag();
+ break;
+ }
+ default:
+ {
+ print(
+ "Unknown tag: ${tagType}, aborting read at ${layer.currentPosition - 1} bytes");
+
+ throw Exception("Unknown tag, could not deserialize");
+ }
+ }
+
+ print("Read ${tagType}");
+
+ if (!isList) tag.readHeader(layer);
+
+ print("Name: ${tag.Name}");
+
+ tag.readValue(layer);
+
+ return tag;
+ }
+
+ static TagType readTagType(ByteLayer layer) {
+ int type = layer.readByte();
+ TagType tagType = TagTypeExtension.fromByte(type);
+
+ return tagType;
+ }
+
+ void readHeader(ByteLayer layer);
+ void readValue(ByteLayer layer);
+
+ TagType getTagType();
+ void writeTagType(ByteLayer layer);
+ void writeHeader(ByteLayer layer);
+ void writeValue(ByteLayer layer);
+}
diff --git a/lib/tags/TagType.dart b/lib/tags/TagType.dart
new file mode 100644
index 0000000..b397f15
--- /dev/null
+++ b/lib/tags/TagType.dart
@@ -0,0 +1,83 @@
+enum TagType {
+ End,
+ Byte,
+ Short,
+ Int,
+ Long,
+ Float,
+ Double,
+ ByteArray,
+ String,
+ List,
+ Compound,
+ IntArray,
+ LongArray
+}
+
+extension TagTypeExtension on TagType {
+ int toByte() {
+ switch (this) {
+ case TagType.End:
+ return 0;
+ case TagType.Byte:
+ return 1;
+ case TagType.Short:
+ return 2;
+ case TagType.Int:
+ return 3;
+ case TagType.Long:
+ return 4;
+ case TagType.Float:
+ return 5;
+ case TagType.Double:
+ return 6;
+ case TagType.ByteArray:
+ return 7;
+ case TagType.String:
+ return 8;
+ case TagType.List:
+ return 9;
+ case TagType.Compound:
+ return 10;
+ case TagType.IntArray:
+ return 11;
+ case TagType.LongArray:
+ return 12;
+ default:
+ throw Exception('Unknown TagType: $this');
+ }
+ }
+
+ static TagType fromByte(int type) {
+ switch (type) {
+ case 0:
+ return TagType.End;
+ case 1:
+ return TagType.Byte;
+ case 2:
+ return TagType.Short;
+ case 3:
+ return TagType.Int;
+ case 4:
+ return TagType.Long;
+ case 5:
+ return TagType.Float;
+ case 6:
+ return TagType.Double;
+ case 7:
+ return TagType.ByteArray;
+ case 8:
+ return TagType.String;
+ case 9:
+ return TagType.List;
+ case 10:
+ return TagType.Compound;
+ case 11:
+ return TagType.IntArray;
+ case 12:
+ return TagType.LongArray;
+ default:
+ throw Exception("Unknown TagType $type");
+ }
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index dfc05be..e5a2e19 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -7,8 +7,11 @@ environment:
sdk: '>=3.1.5 <4.0.0'
dependencies:
+ file_picker: ^6.1.1
flutter:
sdk: flutter
+ flutter_treeview: ^1.0.7+1
+ uuid: ^4.2.2
dev_dependencies:
flutter_test: