Add JagexGzipOutputStream

Equivalent to the built-in GZIPOutputStream, except it produces output
that is bit-for-bit identical to Jagex's implementation.

Signed-off-by: Graham <gpe@openrs2.org>
Graham 3 years ago
parent a761a26cc8
commit 6c508922fc
  1. 75
      compress/src/main/kotlin/org/openrs2/compress/gzip/JagexGzipOutputStream.kt

@ -0,0 +1,75 @@
package org.openrs2.compress.gzip
import com.google.common.io.LittleEndianDataOutputStream
import java.io.DataOutputStream
import java.io.OutputStream
import java.util.zip.CRC32
import java.util.zip.Deflater
public class JagexGzipOutputStream(
private val output: OutputStream
) : OutputStream() {
private val deflater = Deflater(Deflater.DEFAULT_COMPRESSION, true)
private val buffer = ByteArray(4096)
private val checksum = CRC32()
private var size = 0
private var closed = false
init {
val dataOutput = DataOutputStream(output)
dataOutput.writeShort(HEADER_MAGIC)
dataOutput.writeByte(METHOD_DEFLATE)
dataOutput.writeByte(0) // FLG
dataOutput.writeInt(0) // MTIME
dataOutput.writeByte(0) // XFL
dataOutput.writeByte(0) // OS
}
override fun write(b: Int) {
buffer[0] = b.toByte()
write(buffer, 0, 1)
}
override fun write(b: ByteArray, off: Int, len: Int) {
checksum.update(b, off, len)
size += len
deflater.setInput(b, off, len)
while (!deflater.needsInput()) {
drain()
}
}
private fun drain() {
val n = deflater.deflate(buffer)
if (n != 0) {
output.write(buffer, 0, n)
}
}
override fun close() {
if (!closed) {
deflater.finish()
while (!deflater.finished()) {
drain()
}
val dataOutput = LittleEndianDataOutputStream(output)
dataOutput.writeInt(checksum.value.toInt())
dataOutput.writeInt(size)
deflater.end()
output.close()
closed = true
}
}
private companion object {
private const val HEADER_MAGIC = 0x1F8B
private const val METHOD_DEFLATE = 8
}
}
Loading…
Cancel
Save