Open-source multiplayer game server compatible with the RuneScape client
https://www.openrs2.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
2.9 KiB
117 lines
2.9 KiB
package org.openrs2.compress.gzip
|
|
|
|
import java.io.DataInputStream
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.util.zip.Inflater
|
|
|
|
public class GzipLaxInputStream(
|
|
private val input: InputStream
|
|
) : InputStream() {
|
|
private val inflater = Inflater(true)
|
|
private val buffer = ByteArray(4096)
|
|
private var checkedTrailer = false
|
|
|
|
init {
|
|
val dataInput = DataInputStream(input)
|
|
if (dataInput.readUnsignedShort() != HEADER_MAGIC) {
|
|
throw IOException("Invalid GZIP header magic")
|
|
} else if (dataInput.readUnsignedByte() != METHOD_DEFLATE) {
|
|
throw IOException("Unsupported compression method")
|
|
}
|
|
|
|
val flags = dataInput.readUnsignedByte()
|
|
dataInput.skip(6)
|
|
|
|
if ((flags and FLAG_EXTRA) != 0) {
|
|
dataInput.skip(dataInput.readUnsignedShort().toLong())
|
|
}
|
|
|
|
if ((flags and FLAG_NAME) != 0) {
|
|
while (dataInput.readUnsignedByte() != 0) {
|
|
// empty
|
|
}
|
|
}
|
|
|
|
if ((flags and FLAG_COMMENT) != 0) {
|
|
while (dataInput.readUnsignedByte() != 0) {
|
|
// empty
|
|
}
|
|
}
|
|
|
|
if ((flags and FLAG_HEADER_CRC) != 0) {
|
|
dataInput.skip(2)
|
|
}
|
|
}
|
|
|
|
override fun read(): Int {
|
|
val n = read(buffer, 0, 1)
|
|
return if (n < 0) {
|
|
-1
|
|
} else {
|
|
buffer[0].toInt() and 0xFF
|
|
}
|
|
}
|
|
|
|
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
|
while (true) {
|
|
val n = inflater.inflate(b, off, len)
|
|
if (n != 0) {
|
|
return n
|
|
}
|
|
|
|
when {
|
|
inflater.finished() -> {
|
|
checkTrailer()
|
|
return -1
|
|
}
|
|
|
|
inflater.needsInput() -> fill()
|
|
inflater.needsDictionary() -> throw IOException("Dictionaries not supported")
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun close() {
|
|
inflater.end()
|
|
input.close()
|
|
}
|
|
|
|
private fun fill() {
|
|
val n = input.read(buffer, 0, buffer.size)
|
|
if (n < 0) {
|
|
throw IOException("Compressed data truncated")
|
|
}
|
|
inflater.setInput(buffer, 0, n)
|
|
}
|
|
|
|
private fun checkTrailer() {
|
|
if (checkedTrailer) {
|
|
return
|
|
}
|
|
|
|
checkedTrailer = true
|
|
|
|
var len = TRAILER_LEN
|
|
len -= inflater.remaining
|
|
|
|
if (len < 0) {
|
|
throw IOException("Compressed data overflow")
|
|
} else if (input.skip(len) != len) {
|
|
throw IOException("GZIP trailer missing")
|
|
}
|
|
}
|
|
|
|
private companion object {
|
|
private const val HEADER_MAGIC = 0x1F8B
|
|
|
|
private const val METHOD_DEFLATE = 8
|
|
|
|
private const val FLAG_HEADER_CRC = 0x2
|
|
private const val FLAG_EXTRA = 0x4
|
|
private const val FLAG_NAME = 0x8
|
|
private const val FLAG_COMMENT = 0x10
|
|
|
|
private const val TRAILER_LEN = 8L
|
|
}
|
|
}
|
|
|