287 lines
9.4 KiB
Dart
287 lines
9.4 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_treeview/flutter_treeview.dart';
|
|
import 'package:libac_dart/nbt/NbtIo.dart';
|
|
import 'package:libac_dart/nbt/SnbtIo.dart';
|
|
import 'package:libac_dart/nbt/Tag.dart';
|
|
import 'package:libac_dart/nbt/impl/ByteArrayTag.dart';
|
|
import 'package:libac_dart/nbt/impl/CompoundTag.dart';
|
|
import 'package:libac_dart/nbt/impl/IntArrayTag.dart';
|
|
import 'package:libac_dart/nbt/impl/LongArrayTag.dart';
|
|
import 'package:libacflutter/Prompt.dart';
|
|
import 'package:nbteditor/Constants.dart';
|
|
import 'package:nbteditor/Consts2.dart';
|
|
import 'package:nbteditor/SessionData.dart';
|
|
import 'package:nbteditor/main.dart';
|
|
import 'package:nbteditor/pages/EditValue.dart';
|
|
import 'package:nbteditor/tags/ArrayEntry.dart';
|
|
import 'package:nbteditor/tags/CompoundTag.dart';
|
|
import 'package:nbteditor/tags/Tag.dart';
|
|
|
|
class Editor extends StatefulWidget {
|
|
const Editor({super.key});
|
|
|
|
@override
|
|
EditorState createState() => EditorState();
|
|
}
|
|
|
|
class EditorState extends State<Editor> {
|
|
//List<Node> nodes = [CompoundTag().getNode("/")];
|
|
bool compressed = false;
|
|
|
|
static EditorState? _inst;
|
|
EditorState._() {
|
|
_inst = this;
|
|
}
|
|
late TreeViewController controller;
|
|
factory EditorState() {
|
|
if (_inst == null) {
|
|
return EditorState._();
|
|
} else {
|
|
return _inst!;
|
|
}
|
|
}
|
|
|
|
void update() {
|
|
setState(() {});
|
|
}
|
|
|
|
String appendCompressed() {
|
|
if (compressed) {
|
|
return " - Compressed";
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
void didChangeState() {
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
controller =
|
|
TreeViewController(children: [SessionData.ROOT_TAG.getNode("/")]);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
backgroundColor: Constants.TITLEBAR_COLOR,
|
|
title: Text("Named Binary Tag Editor${appendCompressed()}"),
|
|
actions: [
|
|
IconButton(
|
|
onPressed: () async {
|
|
// Show input prompt
|
|
var searchResponse = await showCupertinoDialog(
|
|
context: context,
|
|
builder: (dialogBuilder) {
|
|
return InputPrompt(
|
|
title: "What tag name to search for?",
|
|
prompt:
|
|
"Enter the tag name or value you want to search for",
|
|
type: InputPromptType.Text);
|
|
});
|
|
},
|
|
icon: const Icon(CupertinoIcons.search_circle))
|
|
],
|
|
),
|
|
drawer: Drawer(
|
|
backgroundColor: Constants.DRAWER_COLOR,
|
|
child: Column(children: [
|
|
const DrawerHeader(
|
|
child: Column(
|
|
children: [
|
|
Text("Named Binary Tag Editor"),
|
|
Text("Created by Tara Piccari"),
|
|
Text("Version: $VERSION")
|
|
],
|
|
)),
|
|
ListTile(
|
|
title: const Text("N E W"),
|
|
subtitle: const Text("Create a new NBT Document"),
|
|
leading: const Icon(Icons.add),
|
|
onTap: () {
|
|
setState(() {
|
|
SessionData.ROOT_TAG = CompoundTag();
|
|
});
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text("O P E N"),
|
|
leading: const Icon(Icons.folder),
|
|
subtitle:
|
|
const Text("Open an existing NBT/SNBT Document for editing"),
|
|
onTap: () async {
|
|
if (await needsPermissionsPage()) {
|
|
Navigator.pushNamed(context, "/perms");
|
|
return;
|
|
}
|
|
String? filePath;
|
|
var result = await FilePicker.platform.pickFiles();
|
|
if (result != null) {
|
|
// Do something with the selected file path
|
|
print('Selected file path: $filePath');
|
|
filePath = result.files.first.path;
|
|
} else {
|
|
// User canceled the picker
|
|
print('File selection canceled.');
|
|
}
|
|
if (filePath == null) {
|
|
// cancelled
|
|
return;
|
|
} else {
|
|
// String!!
|
|
CompoundTag ct = CompoundTag();
|
|
if (filePath.endsWith(".txt") || filePath.endsWith(".snbt")) {
|
|
ct = await SnbtIo.readFromFile(filePath) as CompoundTag;
|
|
} else
|
|
ct = await NbtIo.read(filePath);
|
|
|
|
SessionData.ROOT_TAG = ct;
|
|
}
|
|
|
|
setState(() {
|
|
controller = TreeViewController(
|
|
children: [SessionData.ROOT_TAG.getNode("/")]);
|
|
});
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text("R A W"),
|
|
subtitle: const Text("Edit as raw SNBT"),
|
|
leading: const Icon(Icons.edit),
|
|
onTap: () async {
|
|
await Navigator.pushNamed(context, "/snbt");
|
|
setState(() {});
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text("S A V E N B T"),
|
|
subtitle: const Text("Save to NBT"),
|
|
leading: const Image(
|
|
image: AssetImage("Icons/PNG/Compound.png"),
|
|
),
|
|
onTap: () async {
|
|
if (await needsPermissionsPage()) {
|
|
Navigator.pushNamed(context, "/perms");
|
|
return;
|
|
}
|
|
var result = await showDialog(
|
|
context: context,
|
|
builder: (builder) {
|
|
return AlertDialog(
|
|
title: const Text("Compress the data?"),
|
|
actions: [
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pop(context, true);
|
|
},
|
|
child: const Text("YES")),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
},
|
|
child: const Text("No"))
|
|
],
|
|
);
|
|
});
|
|
|
|
// Prompt for where to save
|
|
print("Begin picking file to save to");
|
|
var FSL = await FilePicker.platform.saveFile(bytes: Uint8List(0));
|
|
String? filePath;
|
|
|
|
if (FSL != null) {
|
|
filePath = FSL;
|
|
}
|
|
|
|
if (filePath == null) {
|
|
print("No file selected");
|
|
return;
|
|
}
|
|
print(filePath);
|
|
CompoundTag tag = controller.children[0].data as CompoundTag;
|
|
if (result == null) {
|
|
// Save uncompressed
|
|
NbtIo.write(filePath, tag);
|
|
} else {
|
|
NbtIo.writeCompressed(filePath, tag);
|
|
}
|
|
},
|
|
),
|
|
ListTile(
|
|
title: const Text("S A V E S N B T"),
|
|
subtitle: const Text("Save to SNBT"),
|
|
leading: const Image(
|
|
image: AssetImage("Icons/PNG/String.png"),
|
|
),
|
|
onTap: () async {
|
|
if (await needsPermissionsPage()) {
|
|
Navigator.pushNamed(context, "/perms");
|
|
return;
|
|
}
|
|
// Prompt for where to save
|
|
print("Begin picking file to save to");
|
|
var FSL = await FilePicker.platform.saveFile(bytes: Uint8List(0));
|
|
String? filePath;
|
|
|
|
if (FSL != null) {
|
|
filePath = FSL;
|
|
}
|
|
|
|
if (filePath == null) {
|
|
print("No file selected");
|
|
return;
|
|
}
|
|
print(filePath);
|
|
|
|
SnbtIo.writeToFile(
|
|
filePath, controller.children[0].data as CompoundTag);
|
|
},
|
|
)
|
|
]),
|
|
),
|
|
body: TreeView(
|
|
nodeBuilder: (context, node) {
|
|
if (node.data is Tag) {
|
|
return TagExt.render(node.data as Tag, context, didChangeState);
|
|
} else if (node.data is ArrayEntry) {
|
|
ArrayEntry entry = node.data as ArrayEntry;
|
|
return ListTile(
|
|
title: Text(entry.value),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// Delete entry
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
if (entry.parent is ByteArrayTag) {
|
|
ByteArrayTag bat = entry.parent as ByteArrayTag;
|
|
bat.value.removeAt(entry.index);
|
|
}
|
|
if (entry.parent is IntArrayTag) {
|
|
IntArrayTag iat = entry.parent as IntArrayTag;
|
|
iat.value.removeAt(entry.index);
|
|
}
|
|
if (entry.parent is LongArrayTag) {
|
|
LongArrayTag lat = entry.parent as LongArrayTag;
|
|
lat.value.removeAt(entry.index);
|
|
}
|
|
|
|
didChangeState();
|
|
},
|
|
child: const Icon(Icons.delete_forever))
|
|
],
|
|
),
|
|
);
|
|
} else {
|
|
return ListTile(title: Text(node.label));
|
|
}
|
|
},
|
|
controller: controller,
|
|
),
|
|
);
|
|
}
|
|
}
|