package org.openrs2.archive.cache.nxt import io.netty.buffer.ByteBuf import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.ByteToMessageDecoder import io.netty.handler.codec.DecoderException import kotlin.math.min public class Js5ResponseDecoder : ByteToMessageDecoder() { private data class Request(val prefetch: Boolean, val archive: Int, val group: Int) private enum class State { READ_HEADER, READ_LEN, READ_DATA } private var state = State.READ_HEADER private val buffers = mutableMapOf() private var request: Request? = null override fun decode(ctx: ChannelHandlerContext, input: ByteBuf, out: MutableList) { if (state == State.READ_HEADER) { if (input.readableBytes() < 5) { return } val prefetch: Boolean val archive = input.readUnsignedByte().toInt() var group = input.readInt() if (group and 0x80000000.toInt() != 0) { prefetch = true group = group and 0x7FFFFFFF } else { prefetch = false } request = Request(prefetch, archive, group) state = if (buffers.containsKey(request)) { State.READ_DATA } else { State.READ_LEN } } if (state == State.READ_LEN) { if (input.readableBytes() < 5) { return } val type = input.readUnsignedByte().toInt() val len = input.readInt() if (len < 0) { throw DecoderException("Length is negative: $len") } val totalLen = if (type == 0) { len + 5 } else { len + 9 } if (totalLen < 0) { throw DecoderException("Total length exceeds maximum ByteBuf size") } val data = ctx.alloc().buffer(totalLen, totalLen) data.writeByte(type) data.writeInt(len) buffers[request!!] = data state = State.READ_DATA } if (state == State.READ_DATA) { val data = buffers[request!!]!! var blockLen = if (data.writerIndex() == 5) { 102400 - 10 } else { 102400 - 5 } blockLen = min(blockLen, data.writableBytes()) if (input.readableBytes() < blockLen) { return } data.writeBytes(input, blockLen) if (!data.isWritable) { out += Js5Response(request!!.prefetch, request!!.archive, request!!.group, data) buffers.remove(request!!) request = null } state = State.READ_HEADER } } override fun channelInactive(ctx: ChannelHandlerContext) { super.channelInactive(ctx) reset() } override fun handlerRemoved0(ctx: ChannelHandlerContext?) { reset() } private fun reset() { buffers.values.forEach(ByteBuf::release) buffers.clear() state = State.READ_HEADER } }