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.readEither(isLegacy: Boolean, left: S.() -> L, right: S.() -> R): Either { 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.writeEither(value: Either, 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 { 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) { 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.readNullable(reader: S.() -> T): T? { if (readBoolean()) return reader(this) else return null } fun S.writeNullable(value: T?, writer: S.(T) -> Unit) { if (value == null) write(0) else { write(1) writer(value) } } fun S.readMVariant2(left: S.() -> L, right: S.() -> R): Either? { return when (val type = readUnsignedByte()) { 0 -> null 1 -> Either.left(left()) 2 -> Either.right(right()) else -> throw IllegalArgumentException("Unknown variant type $type") } } fun S.writeMVariant2(value: Either?, 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 InputStream.read2(factory: (I, I) -> T, reader: (InputStream) -> I): T { return factory(reader(this), reader(this)) } private inline fun InputStream.read3(factory: (I, I, I) -> T, reader: (InputStream) -> I): T { return factory(reader(this), reader(this), reader(this)) } private inline fun 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 = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector2iCodec) fun DelegateSyncher.vec2d(value: Vector2d, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector2dCodec) fun DelegateSyncher.vec2f(value: Vector2f, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector2fCodec) fun DelegateSyncher.vec3i(value: Vector3i, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector3iCodec) fun DelegateSyncher.vec3d(value: Vector3d, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector3dCodec) fun DelegateSyncher.vec3f(value: Vector3f, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector3fCodec) fun DelegateSyncher.vec4i(value: Vector4i, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector4iCodec) fun DelegateSyncher.vec4d(value: Vector4d, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = DelegateGetter.passthrough()) = Slot( ListenableDelegate.maskSmart(value, getter, setter), Vector4dCodec) fun DelegateSyncher.vec4f(value: Vector4f, setter: DelegateSetter = DelegateSetter.passthrough(), getter: DelegateGetter = 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() } }