325 lines
13 KiB
Kotlin
325 lines
13 KiB
Kotlin
package ru.dbotthepony.kstarbound.io
|
|
|
|
import it.unimi.dsi.fastutil.bytes.ByteArrayList
|
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
|
import ru.dbotthepony.kommons.io.DelegateSyncher
|
|
import ru.dbotthepony.kommons.io.StreamCodec
|
|
import ru.dbotthepony.kommons.io.readBinaryString
|
|
import ru.dbotthepony.kommons.io.readDouble
|
|
import ru.dbotthepony.kommons.io.readFloat
|
|
import ru.dbotthepony.kommons.io.readInt
|
|
import ru.dbotthepony.kommons.io.readSignedVarInt
|
|
import ru.dbotthepony.kommons.io.readSignedVarLong
|
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
|
import ru.dbotthepony.kommons.io.writeByteArray
|
|
import ru.dbotthepony.kommons.io.writeDouble
|
|
import ru.dbotthepony.kommons.io.writeFloat
|
|
import ru.dbotthepony.kommons.io.writeInt
|
|
import ru.dbotthepony.kommons.io.writeSignedVarInt
|
|
import ru.dbotthepony.kommons.io.writeSignedVarLong
|
|
import ru.dbotthepony.kommons.io.writeStruct2d
|
|
import ru.dbotthepony.kommons.io.writeStruct2f
|
|
import ru.dbotthepony.kommons.io.writeStruct2i
|
|
import ru.dbotthepony.kommons.io.writeStruct3d
|
|
import ru.dbotthepony.kommons.io.writeStruct3f
|
|
import ru.dbotthepony.kommons.io.writeStruct3i
|
|
import ru.dbotthepony.kommons.io.writeStruct4d
|
|
import ru.dbotthepony.kommons.io.writeStruct4f
|
|
import ru.dbotthepony.kommons.io.writeStruct4i
|
|
import ru.dbotthepony.kommons.math.RGBAColor
|
|
import ru.dbotthepony.kommons.util.DelegateGetter
|
|
import ru.dbotthepony.kommons.util.DelegateSetter
|
|
import ru.dbotthepony.kommons.util.Either
|
|
import ru.dbotthepony.kommons.util.IStruct2d
|
|
import ru.dbotthepony.kommons.util.KOptional
|
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.math.AABB
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3f
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector4d
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector4f
|
|
import ru.dbotthepony.kstarbound.math.vector.Vector4i
|
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
|
import java.io.DataInputStream
|
|
import java.io.DataOutputStream
|
|
import java.io.EOFException
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
|
|
fun InputStream.readByteChar(): Char {
|
|
return read().toChar()
|
|
}
|
|
|
|
fun InputStream.readHeader(header: String) {
|
|
for ((i, char) in header.withIndex()) {
|
|
val read = readByteChar()
|
|
|
|
if (read != char) {
|
|
throw IllegalArgumentException("Malformed header at byte $i: expected $char (${char.code}) got $read (${char.code})")
|
|
}
|
|
}
|
|
}
|
|
|
|
fun InputStream.readChunkPos(): ChunkPos {
|
|
return ChunkPos(readSignedVarInt(), readSignedVarInt())
|
|
}
|
|
|
|
fun OutputStream.writeColor(color: RGBAColor) {
|
|
writeFloat(color.red)
|
|
writeFloat(color.green)
|
|
writeFloat(color.blue)
|
|
writeFloat(color.alpha)
|
|
}
|
|
|
|
fun InputStream.readColor(): RGBAColor {
|
|
return RGBAColor(readFloat(), readFloat(), readFloat(), readFloat())
|
|
}
|
|
|
|
fun InputStream.readInternedString(): String = Starbound.STRINGS.intern(readBinaryString())
|
|
|
|
fun InputStream.readAABBLegacy(): AABB {
|
|
val mins = readVector2f()
|
|
val maxs = readVector2f()
|
|
return AABB(mins.toDoubleVector(), maxs.toDoubleVector())
|
|
}
|
|
|
|
fun <S : InputStream, L, R> S.readEither(isLegacy: Boolean, left: S.() -> L, right: S.() -> R): Either<L, R> {
|
|
var type = readUnsignedByte()
|
|
|
|
if (isLegacy)
|
|
type--
|
|
|
|
return when (type) {
|
|
0 -> Either.left(left(this))
|
|
1 -> Either.right(right(this))
|
|
else -> throw IllegalArgumentException("Unexpected either type $type")
|
|
}
|
|
}
|
|
|
|
fun <S : OutputStream, L, R> S.writeEither(value: Either<L, R>, isLegacy: Boolean, left: S.(L) -> Unit, right: S.(R) -> Unit) {
|
|
if (value.isLeft) {
|
|
if (isLegacy) write(1) else write(0)
|
|
left(value.left())
|
|
} else {
|
|
if (isLegacy) write(2) else write(1)
|
|
right(value.right())
|
|
}
|
|
}
|
|
|
|
fun InputStream.readAABBLegacyOptional(): KOptional<AABB> {
|
|
val mins = readVector2f()
|
|
val maxs = readVector2f()
|
|
|
|
// what the fuck are you doing?!
|
|
if (
|
|
mins.x == Float.MAX_VALUE && mins.y == Float.MAX_VALUE &&
|
|
maxs.x == -Float.MAX_VALUE && maxs.y == -Float.MAX_VALUE
|
|
) {
|
|
return KOptional()
|
|
}
|
|
|
|
return KOptional(AABB(mins.toDoubleVector(), maxs.toDoubleVector()))
|
|
}
|
|
|
|
fun InputStream.readAABB(): AABB {
|
|
return AABB(readVector2d(), readVector2d())
|
|
}
|
|
|
|
fun InputStream.readAABB(isLegacy: Boolean): AABB {
|
|
if (isLegacy)
|
|
return readAABBLegacy()
|
|
else
|
|
return readAABB()
|
|
}
|
|
|
|
fun OutputStream.writeAABBLegacy(value: AABB) {
|
|
writeStruct2d(value.mins, true)
|
|
writeStruct2d(value.maxs, true)
|
|
}
|
|
|
|
fun OutputStream.writeAABBLegacyOptional(value: KOptional<AABB>) {
|
|
value.ifPresent {
|
|
writeStruct2f(it.mins.toFloatVector())
|
|
writeStruct2f(it.maxs.toFloatVector())
|
|
}.ifNotPresent {
|
|
writeFloat(Float.MAX_VALUE)
|
|
writeFloat(Float.MAX_VALUE)
|
|
writeFloat(-Float.MAX_VALUE)
|
|
writeFloat(-Float.MAX_VALUE)
|
|
}
|
|
}
|
|
|
|
fun OutputStream.writeAABB(value: AABB) {
|
|
writeStruct2d(value.mins)
|
|
writeStruct2d(value.maxs)
|
|
}
|
|
|
|
fun OutputStream.writeAABB(value: AABB, isLegacy: Boolean) {
|
|
writeStruct2d(value.mins, isLegacy)
|
|
writeStruct2d(value.maxs, isLegacy)
|
|
}
|
|
|
|
private fun InputStream.readBoolean(): Boolean {
|
|
val read = read()
|
|
|
|
if (read == -1)
|
|
throw EOFException("End of stream reached")
|
|
|
|
return read != 0
|
|
}
|
|
|
|
private fun InputStream.readUnsignedByte(): Int {
|
|
val read = read()
|
|
|
|
if (read == -1)
|
|
throw EOFException("End of stream reached")
|
|
|
|
return read
|
|
}
|
|
|
|
fun <S : InputStream, T> S.readNullable(reader: S.() -> T): T? {
|
|
if (readBoolean())
|
|
return reader(this)
|
|
else
|
|
return null
|
|
}
|
|
|
|
fun <S : OutputStream, T> S.writeNullable(value: T?, writer: S.(T) -> Unit) {
|
|
if (value == null)
|
|
write(0)
|
|
else {
|
|
write(1)
|
|
writer(value)
|
|
}
|
|
}
|
|
|
|
fun <S : InputStream, L, R> S.readMVariant2(left: S.() -> L, right: S.() -> R): Either<L, R>? {
|
|
return when (val type = readUnsignedByte()) {
|
|
0 -> null
|
|
1 -> Either.left(left())
|
|
2 -> Either.right(right())
|
|
else -> throw IllegalArgumentException("Unknown variant type $type")
|
|
}
|
|
}
|
|
|
|
fun <S : OutputStream, L, R> S.writeMVariant2(value: Either<L, R>?, left: S.(L) -> Unit, right: S.(R) -> Unit) {
|
|
write(if (value == null) 0 else if (value.isLeft) 1 else 2)
|
|
value?.map({ left(it) }, { right(it) })
|
|
}
|
|
|
|
fun InputStream.readNullableString() = readNullable { readInternedString() }
|
|
fun InputStream.readNullableFloat() = readNullable { readFloat() }
|
|
fun InputStream.readNullableDouble() = readNullable { readDouble() }
|
|
fun InputStream.readNullableDouble(isLegacy: Boolean) = readNullable { if (isLegacy) readFloat().toDouble() else readDouble() }
|
|
fun InputStream.readDouble(isLegacy: Boolean) = if (isLegacy) readFloat().toDouble() else readDouble()
|
|
fun InputStream.readVector2d(isLegacy: Boolean) = if (isLegacy) readVector2f().toDoubleVector() else readVector2d()
|
|
|
|
fun OutputStream.writeNullableString(value: String?) = writeNullable(value) { writeBinaryString(it) }
|
|
fun OutputStream.writeNullableFloat(value: Float?) = writeNullable(value) { writeFloat(it) }
|
|
fun OutputStream.writeNullableDouble(value: Double?) = writeNullable(value) { writeDouble(it) }
|
|
fun OutputStream.writeNullableDouble(value: Double?, isLegacy: Boolean) = writeNullable(value) { if (isLegacy) writeFloat(it.toFloat()) else writeDouble(it) }
|
|
fun OutputStream.writeDouble(value: Double, isLegacy: Boolean) = if (isLegacy) writeFloat(value.toFloat()) else writeDouble(value)
|
|
fun OutputStream.writeStruct2d(value: IStruct2d, isLegacy: Boolean) = if (isLegacy) { writeFloat(value.component1().toFloat()); writeFloat(value.component2().toFloat()) } else { writeDouble(value.component1()); writeDouble(value.component2()) }
|
|
|
|
private inline fun <T, I> InputStream.read2(factory: (I, I) -> T, reader: (InputStream) -> I): T {
|
|
return factory(reader(this), reader(this))
|
|
}
|
|
|
|
private inline fun <T, I> InputStream.read3(factory: (I, I, I) -> T, reader: (InputStream) -> I): T {
|
|
return factory(reader(this), reader(this), reader(this))
|
|
}
|
|
|
|
private inline fun <T, I> InputStream.read4(factory: (I, I, I, I) -> T, reader: (InputStream) -> I): T {
|
|
return factory(reader(this), reader(this), reader(this), reader(this))
|
|
}
|
|
|
|
fun InputStream.readVector2i() = read2(::Vector2i) { it.readInt() }
|
|
fun InputStream.readVector2d() = read2(::Vector2d) { it.readDouble() }
|
|
fun InputStream.readVector2f() = read2(::Vector2f) { it.readFloat() }
|
|
|
|
fun InputStream.readVector3i() = read3(::Vector3i) { it.readInt() }
|
|
fun InputStream.readVector3d() = read3(::Vector3d) { it.readDouble() }
|
|
fun InputStream.readVector3f() = read3(::Vector3f) { it.readFloat() }
|
|
|
|
fun InputStream.readVector4i() = read4(::Vector4i) { it.readInt() }
|
|
fun InputStream.readVector4d() = read4(::Vector4d) { it.readDouble() }
|
|
fun InputStream.readVector4f() = read4(::Vector4f) { it.readFloat() }
|
|
|
|
val Vector2iCodec = StreamCodec.Impl(DataInputStream::readVector2i, DataOutputStream::writeStruct2i)
|
|
val Vector2dCodec = StreamCodec.Impl(DataInputStream::readVector2d, DataOutputStream::writeStruct2d)
|
|
val Vector2fCodec = StreamCodec.Impl(DataInputStream::readVector2f, DataOutputStream::writeStruct2f)
|
|
|
|
val Vector3iCodec = StreamCodec.Impl(DataInputStream::readVector3i, DataOutputStream::writeStruct3i)
|
|
val Vector3dCodec = StreamCodec.Impl(DataInputStream::readVector3d, DataOutputStream::writeStruct3d)
|
|
val Vector3fCodec = StreamCodec.Impl(DataInputStream::readVector3f, DataOutputStream::writeStruct3f)
|
|
|
|
val Vector4iCodec = StreamCodec.Impl(DataInputStream::readVector4i, DataOutputStream::writeStruct4i)
|
|
val Vector4dCodec = StreamCodec.Impl(DataInputStream::readVector4d, DataOutputStream::writeStruct4d)
|
|
val Vector4fCodec = StreamCodec.Impl(DataInputStream::readVector4f, DataOutputStream::writeStruct4f)
|
|
|
|
fun DelegateSyncher.vec2i(value: Vector2i, setter: DelegateSetter<Vector2i> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector2i> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector2iCodec)
|
|
fun DelegateSyncher.vec2d(value: Vector2d, setter: DelegateSetter<Vector2d> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector2d> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector2dCodec)
|
|
fun DelegateSyncher.vec2f(value: Vector2f, setter: DelegateSetter<Vector2f> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector2f> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector2fCodec)
|
|
|
|
fun DelegateSyncher.vec3i(value: Vector3i, setter: DelegateSetter<Vector3i> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector3i> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector3iCodec)
|
|
fun DelegateSyncher.vec3d(value: Vector3d, setter: DelegateSetter<Vector3d> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector3d> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector3dCodec)
|
|
fun DelegateSyncher.vec3f(value: Vector3f, setter: DelegateSetter<Vector3f> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector3f> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector3fCodec)
|
|
|
|
fun DelegateSyncher.vec4i(value: Vector4i, setter: DelegateSetter<Vector4i> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector4i> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector4iCodec)
|
|
fun DelegateSyncher.vec4d(value: Vector4d, setter: DelegateSetter<Vector4d> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector4d> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector4dCodec)
|
|
fun DelegateSyncher.vec4f(value: Vector4f, setter: DelegateSetter<Vector4f> = DelegateSetter.passthrough(), getter: DelegateGetter<Vector4f> = DelegateGetter.passthrough()) = Slot(
|
|
ListenableDelegate.maskSmart(value, getter, setter), Vector4fCodec)
|
|
|
|
fun OutputStream.writeEnumStupid(index: Int, isLegacy: Boolean) {
|
|
if (isLegacy) writeInt(index) else write(index)
|
|
}
|
|
|
|
fun InputStream.readEnumStupid(isLegacy: Boolean): Int {
|
|
return if (isLegacy) readInt() else readUnsignedByte()
|
|
}
|
|
|
|
fun OutputStream.writeIntStupid(index: Int, isLegacy: Boolean) {
|
|
if (isLegacy) writeInt(index) else writeSignedVarInt(index)
|
|
}
|
|
|
|
fun InputStream.readIntStupid(isLegacy: Boolean): Int {
|
|
return if (isLegacy) readInt() else readSignedVarInt()
|
|
}
|
|
|
|
fun OutputStream.writeByteArray(array: ByteArrayList) {
|
|
writeByteArray(array.elements(), 0, array.size)
|
|
}
|
|
|
|
fun OutputStream.writeByteArray(array: FastByteArrayOutputStream) {
|
|
writeByteArray(array.array, 0, array.length)
|
|
}
|
|
|
|
fun OutputStream.writeDouble(value: Double, precision: Double, isLegacy: Boolean) {
|
|
if (isLegacy) {
|
|
writeSignedVarLong((value / precision).toLong())
|
|
} else {
|
|
writeDouble(value)
|
|
}
|
|
}
|
|
|
|
fun InputStream.readDouble(precision: Double, isLegacy: Boolean): Double {
|
|
if (isLegacy) {
|
|
return readSignedVarLong() * precision
|
|
} else {
|
|
return readDouble()
|
|
}
|
|
}
|