192 lines
4.2 KiB
Kotlin
192 lines
4.2 KiB
Kotlin
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)
|
||
}
|
||
}
|