package ru.dbotthepony.kstarbound.io import com.google.gson.* import java.io.DataInputStream import java.io.InputStream import java.io.RandomAccessFile /** * Represents interface to read binary json format by Chucklefish */ object BinaryJson { const val TYPE_NULL = 0x01 const val TYPE_FLOAT = 0x02 const val TYPE_DOUBLE = TYPE_FLOAT const val TYPE_BOOLEAN = 0x03 /** * На самом деле, variable int */ const val TYPE_INT = 0x04 const val TYPE_STRING = 0x05 const val TYPE_ARRAY = 0x06 const val TYPE_OBJECT = 0x07 fun readElement(reader: RandomAccessFile): 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) 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) else -> throw JsonParseException("Unknown element type $id") } } fun readObject(reader: RandomAccessFile): 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 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 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 } 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 } } class VersionedJSON(var name: String = "Versioned JSON") { var isVersioned = false var version = 0 var data: JsonElement? = null constructor(stream: DataInputStream) : this() { name = stream.readASCIIString(stream.readVarInt()) isVersioned = stream.readBoolean() if (isVersioned) { version = stream.readInt() } data = BinaryJson.readElement(stream) } }