diff --git a/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt b/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt index 8cc8fa4f..d110b4e2 100644 --- a/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt +++ b/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt @@ -7,6 +7,7 @@ import dev.openrs2.crypto.xteaEncrypt import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBufInputStream import io.netty.buffer.ByteBufOutputStream +import java.io.EOFException public object Js5Compression { public fun compress(input: ByteBuf, type: Js5CompressionType, key: XteaKey = XteaKey.ZERO): ByteBuf { @@ -102,7 +103,14 @@ public object Js5Compression { plaintext.alloc().buffer(uncompressedLen, uncompressedLen).use { output -> type.createInputStream(ByteBufInputStream(plaintext, len), uncompressedLen).use { inputStream -> - output.writeBytes(inputStream, uncompressedLen) + var remaining = uncompressedLen + while (remaining > 0) { + val n = output.writeBytes(inputStream, remaining) + if (n == -1) { + throw EOFException() + } + remaining -= n + } } return output.retain() diff --git a/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt b/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt index 206ee8b3..2151283d 100644 --- a/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt +++ b/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt @@ -55,6 +55,17 @@ object Js5CompressionTest { } } + @Test + fun testUncompressLargeGzip() { + read("gzip-large.dat").use { input -> + Js5Compression.uncompress(input).use { actual -> + read("large.dat").use { expected -> + assertEquals(expected, actual) + } + } + } + } + @Test fun testCompressBzip2() { Unpooled.wrappedBuffer("OpenRS2".toByteArray()).use { expected -> diff --git a/cache/src/test/resources/dev/openrs2/cache/compression/gzip-large.dat b/cache/src/test/resources/dev/openrs2/cache/compression/gzip-large.dat new file mode 100644 index 00000000..c6ec617a Binary files /dev/null and b/cache/src/test/resources/dev/openrs2/cache/compression/gzip-large.dat differ diff --git a/cache/src/test/resources/dev/openrs2/cache/compression/large.dat b/cache/src/test/resources/dev/openrs2/cache/compression/large.dat new file mode 100644 index 00000000..be52615e --- /dev/null +++ b/cache/src/test/resources/dev/openrs2/cache/compression/large.dat @@ -0,0 +1 @@ +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 \ No newline at end of file