diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/BinaryJson.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/BinaryJson.kt index 207fb03f..d833aa59 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/BinaryJson.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/BinaryJson.kt @@ -1,10 +1,12 @@ package ru.dbotthepony.kstarbound.io import com.google.gson.* +import java.io.DataInputStream +import java.io.InputStream import java.io.RandomAccessFile /** - * Интерфейс для чтения и записи двоичного формата Json от Chucklefish + * Represents interface to read binary json format by Chucklefish */ object BinaryJson { const val TYPE_NULL = 0x01 @@ -25,7 +27,40 @@ object BinaryJson { TYPE_NULL -> JsonNull.INSTANCE TYPE_DOUBLE -> JsonPrimitive(reader.readDouble()) TYPE_BOOLEAN -> JsonPrimitive(reader.readBoolean()) - TYPE_INT -> JsonPrimitive(reader.readVarLong()) + TYPE_INT -> { + var read = reader.readVarLong() + val sign = read and 0x1L + read = read ushr 1 + + if (sign == 1L) { + JsonPrimitive(read - 1L) + } else { + JsonPrimitive(read) + } + } + TYPE_STRING -> JsonPrimitive(reader.readASCIIString(reader.readVarInt())) + TYPE_ARRAY -> readArray(reader) + TYPE_OBJECT -> readObject(reader) + else -> throw JsonParseException("Unknown element type $id") + } + } + + fun readElement(reader: DataInputStream): JsonElement { + return when (val id = reader.read()) { + TYPE_NULL -> JsonNull.INSTANCE + TYPE_DOUBLE -> JsonPrimitive(reader.readDouble()) + TYPE_BOOLEAN -> JsonPrimitive(reader.readBoolean()) + TYPE_INT -> { + var read = reader.readVarLong() + val sign = read and 0x1L + read = read ushr 1 + + if (sign == 1L) { + JsonPrimitive(read - 1L) + } else { + JsonPrimitive(read) + } + } TYPE_STRING -> JsonPrimitive(reader.readASCIIString(reader.readVarInt())) TYPE_ARRAY -> readArray(reader) TYPE_OBJECT -> readObject(reader) @@ -65,6 +100,38 @@ object BinaryJson { return build } + fun readObject(reader: DataInputStream): JsonObject { + val values = reader.readVarInt() - 1 + + if (values == -1) { + return JsonObject() + } + + if (values < -1) { + throw JsonParseException("Tried to read json object with $values elements in it") + } + + val build = JsonObject() + + for (i in 0 .. values) { + val key: String + + try { + key = reader.readASCIIString(reader.readVarInt()) + } catch(err: Throwable) { + throw JsonParseException("Reading json object at $i", err) + } + + try { + build.add(key, readElement(reader)) + } catch(err: Throwable) { + throw JsonParseException("Reading json object at $i with name $key", err) + } + } + + return build + } + fun readArray(reader: RandomAccessFile): JsonArray { val values = reader.readVarInt() - 1 @@ -84,4 +151,24 @@ object BinaryJson { return build } + + fun readArray(reader: DataInputStream): JsonArray { + val values = reader.readVarInt() - 1 + + if (values == -1) { + return JsonArray() + } + + if (values < -1) { + throw JsonParseException("Tried to read json array with $values elements in it") + } + + val build = JsonArray() + + for (i in 0 .. values) { + build.add(readElement(reader)) + } + + return build + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/Ext.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/Ext.kt index 8aaabd61..f44e1f83 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/Ext.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.io +import java.io.DataInputStream import java.io.IOException import java.io.InputStream import java.io.RandomAccessFile @@ -44,6 +45,46 @@ fun RandomAccessFile.readVarInt(): Int { return result } +/** + * Читает Variable Length Integer как Long + */ +fun InputStream.readVarLong(): Long { + var result = 0L + var read = read() + + while (true) { + result = (result shl 7) or (read.toLong() and 0x7FL) + + if (read and 0x80 == 0) { + break + } + + read = read() + } + + return result +} + +/** + * Читает Variable Length Integer как Int + */ +fun InputStream.readVarInt(): Int { + var result = 0 + var read = read() + + while (true) { + result = (result shl 7) or (read and 0x7F) + + if (read and 0x80 == 0) { + break + } + + read = read() + } + + return result +} + fun RandomAccessFile.readASCIIString(length: Int): String { require(length >= 0) { "Invalid length $length" }