diff --git a/README.md b/README.md index 7e34946..fdcedf4 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,12 @@ java -jar /opt/unbted/unbted-1.2.jar "$@" * ANSI-colorized output for more distinctive and easier to skim output * Can convert NBT files to well-formed JSON for processing by anything that can parse JSON - * As of 1.1, a special NBT JSON format is supported that can be roundtripped. Want to edit an NBT file with jq? Now you can. + * A special NBT JSON format is supported that can be roundtripped. Want to edit an NBT file with jq? Now you can. * Support for "inferred" types when printing NBT trees in its default format. This includes: * **JSON** - JSON objects such as `generatorOptions` in level.dat will be colorized, indented, and split into multiple lines for easier reading - * **UUID** - Pairs of long NBT tags with names ending in `Most` and `Least` will be printed as a UUID - * The `set` command also supports UUIDs, which makes working with them easy + * **Old-style UUID** - Pairs of long NBT tags with names ending in `Most` and `Least` will be printed as a UUID + * **New-style UUID** - Int arrays of length 4 will be decoded into UUIDs + * The `set` command supports both forms of UUIDs, which makes working with them easy * **Forge registries** - Lists containing compounds with only two children, `K` and `V`, will be printed in a condensed format for easier skimming, and sorted by their value * **Booleans** - unbted will try to guess whether or not an NBT byte is a boolean, and if it thinks it is, will print 0 as false and 1 as true. * Support for all NBT tags, including int and long arrays @@ -36,11 +37,11 @@ java -jar /opt/unbted/unbted-1.2.jar "$@" * Compression autodetection * Written in Java and compiled to a native statically linked executable * Support for little-endian legacy Pocket Edition NBT files +* SNBT (i.e. command block format) support for `set --compound` ## Planned features * Anvil file support -* Mojangson (i.e. command block format) support for `set --compound` * Scripting ## Building diff --git a/src/main/java/com/unascribed/nbted/CommandProcessor.java b/src/main/java/com/unascribed/nbted/CommandProcessor.java index 407f7b8..fdcf2ff 100644 --- a/src/main/java/com/unascribed/nbted/CommandProcessor.java +++ b/src/main/java/com/unascribed/nbted/CommandProcessor.java @@ -18,8 +18,10 @@ package com.unascribed.nbted; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.text.DecimalFormat; @@ -66,6 +68,7 @@ import io.github.steveice10.opennbt.NBTIO; import io.github.steveice10.opennbt.NBTRegistry; +import io.github.steveice10.opennbt.SNBTIO.StringifiedNBTReader; import io.github.steveice10.opennbt.tag.NBTCompound; import io.github.steveice10.opennbt.tag.NBTIndexed; import io.github.steveice10.opennbt.tag.NBTList; @@ -73,6 +76,8 @@ import io.github.steveice10.opennbt.tag.NBTString; import io.github.steveice10.opennbt.tag.NBTTag; import io.github.steveice10.opennbt.tag.array.NBTByteArray; +import io.github.steveice10.opennbt.tag.array.NBTIntArray; +import io.github.steveice10.opennbt.tag.array.NBTLongArray; import io.github.steveice10.opennbt.tag.array.support.NBTArrayFake; import io.github.steveice10.opennbt.tag.number.NBTByte; import io.github.steveice10.opennbt.tag.number.NBTDouble; @@ -297,17 +302,8 @@ public CommandProcessor(NBTTag _root, TagPrinter _printer, FileInfo _fileInfo) { // no action can possibly be more destructive than deleting the root, so break break; } else { - if (parent instanceof NBTCompound) { - NBTCompound ct = (NBTCompound)parent; - if (ct.contains(t.getName())) { - ct.remove(t.getName()); - dirty = true; - } else { - throw new ConsistencyError("Tried to delete tag from parent, but it's not in its parent!?"); - } - } else { - throw new CommandException(VALUE_NYI, "Tried to delete tag with non-compound parent (NYI)"); - } + t.removeFromParent(); + dirty = true; } int idx = contextParents.indexOf(t); if (idx != -1) { @@ -566,7 +562,8 @@ public CommandProcessor(NBTTag _root, TagPrinter _printer, FileInfo _fileInfo) { for (String s : NBTRegistry.allByTypeName().keySet()) { exclusive.add(parser.accepts(s, "equivalent to --type="+s).availableUnless("type")); } - exclusive.add(parser.accepts("uuid", "equivalent to --type=uuid").availableUnless("type")); + exclusive.add(parser.accepts("olduuid", "equivalent to --type=olduuid").availableUnless("type")); + exclusive.add(parser.accepts("newuuid", "equivalent to --type=newuuid").availableUnless("type")); parser.mutuallyExclusive(exclusive.toArray(new OptionSpecBuilder[exclusive.size()])); }) .action((alias, set, args) -> { @@ -577,18 +574,25 @@ public CommandProcessor(NBTTag _root, TagPrinter _printer, FileInfo _fileInfo) { if ("add".equals(alias)) shift = true; String path = args.get(0); boolean uuid = false; + boolean newuuid = false; Class explicitType = null; if (set.has("type")) { String typeStr = set.valueOf("type").toString(); - if ("uuid".equals(typeStr)) { + if ("olduuid".equals(typeStr)) { + uuid = true; + } else if ("newuuid".equals(typeStr)) { uuid = true; + newuuid = true; } else { explicitType = NBTRegistry.classByTypeName(typeStr); if (explicitType == null) throw new CommandException(VALUE_BAD_USAGE, "Unrecognized type "+set.valueOf("type")); } } else { - if (set.has("uuid")) { + if (set.has("olduuid")) { uuid = true; + } else if (set.has("newuuid")) { + uuid = true; + newuuid = true; } else { for (Map.Entry> en : NBTRegistry.allByTypeName().entrySet()) { if (set.has(en.getKey())) { @@ -600,13 +604,13 @@ public CommandProcessor(NBTTag _root, TagPrinter _printer, FileInfo _fileInfo) { } String str = SPACE_JOINER.join(args.subList(1, args.size())); if (root == null) { - if (uuid) { - throw new CommandException(VALUE_CMDSPECIFIC_4, "UUIDs are actually two tags, and cannot be the root of a file"); + if (uuid && !newuuid) { + throw new CommandException(VALUE_CMDSPECIFIC_4, "Old-style UUIDs are two tags, and cannot be the root of a file"); } - if (explicitType == null) { + if (explicitType == null && !uuid) { throw new CommandException(VALUE_CMDSPECIFIC_3, "An explicit type must be specified to create new tags"); } - NBTTag tag = NBTRegistry.createInstance(explicitType, path); + NBTTag tag = uuid ? new NBTIntArray(path, UUIDs.toIntArray(UUID.fromString(str))) : NBTRegistry.createInstance(explicitType, path); try { parseAndSet(tag, str); } catch (NumberFormatException e) { @@ -638,8 +642,15 @@ public CommandProcessor(NBTTag _root, TagPrinter _printer, FileInfo _fileInfo) { } catch (IllegalArgumentException e) { throw new CommandException(VALUE_BAD_USAGE, str+" is not a valid UUID"); } - commands.get("set").execute("set", "--type=long", "--", pathNoTrailingSlashes+"Most", Long.toString(u.getMostSignificantBits())); - commands.get("set").execute("set", "--type=long", "--", pathNoTrailingSlashes+"Least", Long.toString(u.getLeastSignificantBits())); + if (newuuid) { + int[] arr = UUIDs.toIntArray(u); + commands.get("set").execute("set", "--type=int-array", "--", pathNoTrailingSlashes, + Integer.toString(arr[0]), Integer.toString(arr[1]), + Integer.toString(arr[2]), Integer.toString(arr[3])); + } else { + commands.get("set").execute("set", "--type=long", "--", pathNoTrailingSlashes+"Most", Long.toString(u.getMostSignificantBits())); + commands.get("set").execute("set", "--type=long", "--", pathNoTrailingSlashes+"Least", Long.toString(u.getLeastSignificantBits())); + } return; } if (p.leaf != null && !(p.immediateParent instanceof NBTIndexed)) { @@ -771,6 +782,28 @@ private void parseAndSet(NBTTag tag, String str) { } catch (IllegalArgumentException e) { throw new CommandException(VALUE_BAD_USAGE, "Invalid base64"); } + } else if (tag instanceof NBTIntArray) { + try { + ((NBTIntArray)tag).setValue(Arrays.stream(str.split(" ")) + .mapToInt(Integer::parseInt) + .toArray()); + } catch (IllegalArgumentException e) { + throw new CommandException(VALUE_BAD_USAGE, "Invalid number "+e.getMessage()); + } + } else if (tag instanceof NBTLongArray) { + try { + ((NBTLongArray)tag).setValue(Arrays.stream(str.split(" ")) + .mapToLong(Long::parseLong) + .toArray()); + } catch (IllegalArgumentException e) { + throw new CommandException(VALUE_BAD_USAGE, "Invalid number "+e.getMessage()); + } + } else if (tag instanceof NBTCompound && !str.trim().isEmpty()) { + try { + tag.destringify(new StringifiedNBTReader(new ByteArrayInputStream(str.trim().getBytes(Charsets.UTF_8)))); + } catch (IOException e) { + throw new CommandException(VALUE_BAD_USAGE, "SNBT parsing failed: "+e.getMessage()); + } } else if (!str.trim().isEmpty()) { throw new CommandException(VALUE_BAD_USAGE, "Tags of type "+NBTRegistry.typeNameFromClass(tag.getClass())+" cannot be created with a value"); } else if (tag instanceof NBTParent) { diff --git a/src/main/java/com/unascribed/nbted/NBTEd.java b/src/main/java/com/unascribed/nbted/NBTEd.java index 4fbd5a2..6532407 100644 --- a/src/main/java/com/unascribed/nbted/NBTEd.java +++ b/src/main/java/com/unascribed/nbted/NBTEd.java @@ -22,10 +22,12 @@ import java.io.EOFException; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PushbackInputStream; import java.io.StringWriter; import java.io.UncheckedIOException; @@ -38,6 +40,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.logging.Handler; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; @@ -177,7 +180,8 @@ public static void main(String[] args) throws Exception { ); parser.mutuallyExclusive( parser.acceptsAll(Arrays.asList("json", "j")), - parser.acceptsAll(Arrays.asList("roundtrip-json", "J")) + parser.acceptsAll(Arrays.asList("roundtrip-json", "J")), + parser.acceptsAll(Arrays.asList("convert-nbt", "N")) ); parser.acceptsAll(Arrays.asList("raw", "r")); parser.acceptsAll(Arrays.asList("no-pager")); @@ -189,14 +193,14 @@ public static void main(String[] args) throws Exception { try { set = parser.parse(args); } catch (OptionException e) { - System.err.println(e.getMessage()); + System.err.println("unbted: "+e.getMessage()); printUsage(); System.exit(1); return; } if (set.has("version")) { System.err.println("Una's NBT Editor v"+VERSION); - System.err.println("Copyright (C) 2018 - 2022 Una Thompson (unascribed)"); + System.err.println("Copyright (C) 2018 - 2023 Una Thompson (unascribed)"); System.err.println("License GPLv3+: GNU GPL version 3 or later ."); System.err.println("This is free software: you are free to change and redistribute it."); System.err.println("There is NO WARRANTY, to the extent permitted by law."); @@ -215,26 +219,30 @@ public static void main(String[] args) throws Exception { PAGER = false; } - terminal = TerminalBuilder.terminal(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - terminal.close(); - } catch (Exception e) { - throw new RuntimeException(e); - } - })); - if (set.has("help")) { + initializeTerminal(); printHelp(); return; } - String in = set.valueOf(nonoption); + List nonoptions = set.valuesOf(nonoption); + if (set.has("convert-nbt")) { + if (nonoptions.size() > 2) { + System.err.println("unbted: Too many arguments - only two arguments, the input NBT JSON and output NBT files, may be specified"); + System.exit(1); + return; + } + } else if (nonoptions.size() > 1) { + System.err.println("unbted: Too many arguments - only one argument, the input file, may be specified"); + System.exit(1); + return; + } File sourceFile; ExceptableSupplier inSupplier; - if (in == null || in.isEmpty()) { + if (nonoptions.isEmpty()) { sourceFile = null; inSupplier = null; } else { + String in = nonoptions.get(0); if ("-".equals(in)) { byte[] bys = ByteStreams.toByteArray(System.in); inSupplier = () -> new ByteArrayInputStream(bys); @@ -247,6 +255,58 @@ public static void main(String[] args) throws Exception { log("Reading from file {}", f); } } + + if (set.has("convert-nbt")) { + if (nonoptions.size() < 2) { + System.err.println("unbted: Not enough arguments - need input NBT JSON file and output NBT file"); + System.exit(1); + return; + } + Compression compression = set.valueOf(compressionOpt); + if (compression == null) { + System.err.println("unbted: A compression method must be specified for conversion from NBT JSON"); + System.exit(1); + return; + } + Endianness endianness = Endianness.BIG; + if (set.has(endiannessOpt)) { + endianness = set.valueOf(endiannessOpt); + } else if (set.has("little-endian")) { + endianness = Endianness.LITTLE; + } else if (set.has("big-endian")) { + endianness = Endianness.BIG; + } + String out = nonoptions.get(1); + ExceptableSupplier outSupplier; + if ("-".equals(out)) { + outSupplier = () -> System.out; + log("Writing to stdout"); + } else { + File f = new File(out); + outSupplier = () -> new FileOutputStream(f); + log("Writing to file {}", f); + } + try { + NBTTag tag = loadJson(inSupplier.get()); + try (OutputStream os = compression.wrap(outSupplier.get())) { + NBTIO.writeTag(endianness.wrap(os), tag); + } catch (Exception e) { + log("Error occurred while writing", e); + System.err.println("unbted: Failed to save "+(sourceFile == FileInfo.STDIN ? "(stdin)" : sourceFile.getAbsolutePath())); + System.err.println("unbted: Are you sure this is an unbted NBT JSON file?"); + System.exit(2); + return; + } + } catch (Exception e) { + log("Exception while trying to load NBT file", e); + System.err.println("unbted: Failed to load "+(sourceFile == FileInfo.STDIN ? "(stdin)" : sourceFile.getAbsolutePath())); + System.err.println("unbted: Are you sure this is an unbted NBT JSON file?"); + System.exit(2); + return; + } + return; + } + if (set.has("json")) { JSON_MODE = JsonMode.BASIC; } else if (set.has("roundtrip-json")) { @@ -309,25 +369,7 @@ public static void main(String[] args) throws Exception { if (firstByte == '{') { isJson = true; log("Detected JSON file"); - JsonObject json = gson.fromJson(new InputStreamReader(is, Charsets.UTF_8), JsonObject.class); - JsonElement unbtedMarker = json.get("_unbted"); - if (unbtedMarker != null) { - int version = unbtedMarker.getAsInt(); - if (version > 1) { - System.err.println("unbted: This looks like an NBT JSON file, but it's of a version newer than I know how to read. ("+version+")"); - System.err.println("unbted: Aborting."); - System.exit(2); - return; - } else { - log("Looks like NBT JSON"); - tag = fromJson(json.get("rootType").getAsString()+":"+json.get("rootName").getAsString(), json.get("root")); - } - } else { - System.err.println("unbted: This looks like a JSON file, but it's not an NBT JSON file."); - System.err.println("unbted: Aborting."); - System.exit(2); - return; - } + tag = loadJson(is); } else { log("Detected binary file"); if (endianness != null) { @@ -407,8 +449,9 @@ public static void main(String[] args) throws Exception { } } if (!set.has("print")) { + initializeTerminal(); System.err.println("Una's NBT Editor v"+VERSION); - System.err.println("Copyright (C) 2018 - 2020 Una Thompson (unascribed)"); + System.err.println("Copyright (C) 2018 - 2023 Una Thompson (unascribed)"); System.err.println("This program comes with ABSOLUTELY NO WARRANTY; for details type `warranty`."); System.err.println("This is free software, and you are welcome to redistribute it under certain"); System.err.println("conditions; type `copying` for details."); @@ -419,6 +462,39 @@ public static void main(String[] args) throws Exception { } } + private static void initializeTerminal() throws IOException { + terminal = TerminalBuilder.terminal(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + terminal.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + })); + } + + private static NBTTag loadJson(InputStream is) { + JsonObject json = gson.fromJson(new InputStreamReader(is, Charsets.UTF_8), JsonObject.class); + JsonElement unbtedMarker = json.get("_unbted"); + if (unbtedMarker != null) { + int version = unbtedMarker.getAsInt(); + if (version > 1) { + System.err.println("unbted: This looks like an NBT JSON file, but it's of a version newer than I know how to read. ("+version+")"); + System.err.println("unbted: Aborting."); + System.exit(2); + return null; + } else { + log("Looks like NBT JSON"); + return fromJson(json.get("rootType").getAsString()+":"+json.get("rootName").getAsString(), json.get("root")); + } + } else { + System.err.println("unbted: This looks like a JSON file, but it's not an NBT JSON file."); + System.err.println("unbted: Aborting."); + System.exit(2); + return null; + } + } + public static String getTypePrefix(NBTTag tag) { if (tag == null) { return "null"; @@ -515,6 +591,21 @@ public static JsonElement toJson(NBTTag tag, boolean roundTrip) { Collections.sort(keys); JsonObject sorted = new JsonObject(); for (String k : keys) { + if (k.endsWith("Least") && sorted.has(k.replaceFirst("Least$", ""))) { + continue; + } + if (k.endsWith("Most") && out.has(k.replaceFirst("Most$", "Least"))) { + String basek = k.replaceFirst("Most$", ""); + String k2 = basek+"Least"; + if (out.get(k) instanceof JsonPrimitive && out.get(k2) instanceof JsonPrimitive) { + JsonPrimitive p1 = (JsonPrimitive)out.get(k); + JsonPrimitive p2 = (JsonPrimitive)out.get(k2); + if (p1.isNumber() && p2.isNumber()) { + sorted.add(basek, new JsonPrimitive(new UUID(p1.getAsLong(), p2.getAsLong()).toString())); + continue; + } + } + } sorted.add(k, out.get(k)); } out = sorted; @@ -534,8 +625,12 @@ public static JsonElement toJson(NBTTag tag, boolean roundTrip) { } else if (tag instanceof NBTByteArray) { return new JsonPrimitive(BaseEncoding.base64().encode(((NBTByteArray)tag).getValue())); } else if (tag instanceof NBTIntArray) { + NBTIntArray arr = ((NBTIntArray)tag); + if (!roundTrip && arr.size() == 4) { + return new JsonPrimitive(UUIDs.fromIntArray(arr.getValue()).toString()); + } JsonArray out = new JsonArray(); - for (int v : ((NBTIntArray)tag).getValue()) { out.add(v); } + for (int v : arr.getValue()) { out.add(v); } return out; } else if (tag instanceof NBTLongArray) { JsonArray out = new JsonArray(); @@ -558,7 +653,7 @@ private static void printHelp() throws Exception { public static void displayEmbeddedFileInPager(String file) throws Exception { if (PAGER && !"dumb".equals(terminal.getType())) { Less less = new Less(NBTEd.terminal, new File("").toPath()); - less.run(new URLSource(ClassLoader.getSystemResource(file), file)); + less.run(Lists.newArrayList(new URLSource(ClassLoader.getSystemResource(file), file))); } else { Resources.copy(ClassLoader.getSystemResource(file), System.err); } diff --git a/src/main/java/com/unascribed/nbted/TagPrinter.java b/src/main/java/com/unascribed/nbted/TagPrinter.java index d95d2ce..9d7caa2 100644 --- a/src/main/java/com/unascribed/nbted/TagPrinter.java +++ b/src/main/java/com/unascribed/nbted/TagPrinter.java @@ -196,7 +196,7 @@ public void printTag(NBTTag tag, String prefix, boolean infer, RecurseMode recur NBTTag least = ct.get(t.getName().replaceFirst("Most$", "Least")); if (most instanceof NBTLong && least instanceof NBTLong) { UUID u = new UUID(((NBTLong)most).longValue(), ((NBTLong)least).longValue()); - printBasic(tag, u.toString(), t.getName().replaceFirst("Most$", ""), "~uuid", AnsiCode.FG_YELLOW_INTENSE, childPrefix, values); + printBasic(tag, u.toString(), t.getName().replaceFirst("Most$", ""), "~uuid", AnsiCode.FG_YELLOW, childPrefix, values); continue; } } @@ -404,7 +404,12 @@ public int compare(NBTCompound a, NBTCompound b) { printBasic(tag, colorizeArray(tag.stringValue()), tag.getName(), "byte[]", AnsiCode.FG_YELLOW_INTENSE, prefix, values); } } else if (tag instanceof NBTIntArray) { - printBasic(tag, colorizeArray(tag.stringValue()), tag.getName(), "int[]", AnsiCode.FG_YELLOW_INTENSE, prefix, values); + NBTIntArray arr = (NBTIntArray)tag; + if (infer && arr.size() == 4) { + printBasic(tag, UUIDs.fromIntArray(arr.getValue()).toString(), tag.getName(), "~uuid", AnsiCode.FG_YELLOW_INTENSE, prefix, values); + } else { + printBasic(tag, colorizeArray(tag.stringValue()), tag.getName(), "int[]", AnsiCode.FG_YELLOW_INTENSE, prefix, values); + } } else if (tag instanceof NBTLongArray) { printBasic(tag, colorizeArray(tag.stringValue()), tag.getName(), "long[]", AnsiCode.FG_YELLOW_INTENSE, prefix, values); } diff --git a/src/main/java/com/unascribed/nbted/UUIDs.java b/src/main/java/com/unascribed/nbted/UUIDs.java new file mode 100644 index 0000000..c34cac1 --- /dev/null +++ b/src/main/java/com/unascribed/nbted/UUIDs.java @@ -0,0 +1,43 @@ +/* + * unbted - Una's NBT Editor + * Copyright (C) 2018 - 2023 Una Thompson (unascribed) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.unascribed.nbted; + +import java.util.UUID; + +public class UUIDs { + + public static UUID fromIntArray(int[] arr) { + return new UUID( + (long)arr[0] << 32 | arr[1] & 0xFFFFFFFFL, + (long)arr[2] << 32 | arr[3] & 0xFFFFFFFFL + ); + } + + public static int[] toIntArray(UUID id) { + long msb = id.getMostSignificantBits(); + long lsb = id.getLeastSignificantBits(); + return new int[] { + (int)((msb >> 32)&0xFFFFFFFFL), + (int)(msb&0xFFFFFFFFL), + (int)((lsb >> 32)&0xFFFFFFFFL), + (int)(lsb&0xFFFFFFFFL) + }; + } + +} diff --git a/src/main/java/io/github/steveice10/opennbt/SNBTIO.java b/src/main/java/io/github/steveice10/opennbt/SNBTIO.java index 4992cad..0ed7bf5 100644 --- a/src/main/java/io/github/steveice10/opennbt/SNBTIO.java +++ b/src/main/java/io/github/steveice10/opennbt/SNBTIO.java @@ -1,3 +1,25 @@ +/* + * Copyright (C) 2013-2017 Steveice10, 2018 - 2023 Una Thompson (unascribed) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package io.github.steveice10.opennbt; import java.io.BufferedInputStream; diff --git a/src/main/resources/commands-help.txt b/src/main/resources/commands-help.txt index 067c364..69ca7de 100644 --- a/src/main/resources/commands-help.txt +++ b/src/main/resources/commands-help.txt @@ -81,8 +81,9 @@ set, put, create, new, add Set a value at the given path, replacing existing values. Parent compounds will be created automatically. - Data may be more than one argument. If it is, the arguments will be - joined with spaces. + Data may be more than one argument. For arrays, each argument will + become one value in the resulting array. For compounds and strings, + each argument will be joined with a space. Integer types support hexadecimal (0xN), octal (0N), and of course, decimal (N). Integers may additionally be prefixed with a - or a + @@ -113,13 +114,16 @@ set, put, create, new, add type cannot be inferred, such as when setting an initial value in a list or when creating a new tag. Valid types: byte, short, int, long, float, double, string, - byte-array. - If no data is specified, additional types are: compound, list, + byte-array, compound, int-array, long-array. + You may also create these types without data: compound, list, int-array, long-array. - Additionally, the false type uuid may be specified, which will + Additionally, the false type olduuid may be specified, which will set two long values, one suffixed with Most, the other suffixed with Least, containing the most-significant 64 bits of the UUID - and the least-significant 64 bits, respectively. + and the least-significant 64 bits, respectively. This is how + UUIDs are represented prior to 1.13. + The false type newuuid may also be specified, which will set an + int array. This is how UUIDs are represented in 1.13 and later. --shift When setting values in a list or array, move all later values forward by one, instead of overwriting the existing value diff --git a/src/main/resources/switches-help.txt b/src/main/resources/switches-help.txt index 0937aaa..bc278fd 100644 --- a/src/main/resources/switches-help.txt +++ b/src/main/resources/switches-help.txt @@ -1,4 +1,5 @@ Usage: unbted [options] [file] + unbted -N [options] Una's NBT Editor @@ -50,12 +51,20 @@ Valid options: colored format. The keys in the JSON match those in the NBT 1:1. Cannot be converted back to NBT; type information is lost in the conversion. + (incompatible with -J and -N) -J, --roundtrip-json Print out well-formed JSON where the keys are prefixed with the original NBT type. Can be converted back into NBT by invoking unbted with the JSON file as an argument. Can also be generated by the save command in an unbted session. + (incompatible with -j and -N) + + -N, --convert-nbt + Convert the given roundtrip JSON file to a binary NBT file, given + as a second argument. The --endian and --compression options will + be respected. + (incompatible with -j and -J) -r, --raw Don't infer types.