Fix set compresion, added nbt and snbt(wip) lib
This commit is contained in:
56 files changed
+1064
-544
No files matched your search
@@ -11,4 +11,8 @@ public expect fun ByteArray.zlibDecompress(): ByteArray
|
||||
|
||||
public expect fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray
|
||||
|
||||
public expect fun ByteArray.zlibCompress(): ByteArray
|
||||
public expect fun ByteArray.zlibCompress(): ByteArray
|
||||
|
||||
public expect fun ByteArray.gzipCompress(): ByteArray
|
||||
|
||||
public expect fun ByteArray.gzipDecompress(): ByteArray
|
||||
@@ -10,6 +10,7 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import java.util.zip.Inflater
|
||||
|
||||
public actual fun ByteArray.zlibDecompress(): ByteArray {
|
||||
@@ -29,28 +30,18 @@ public actual fun ByteArray.zlibDecompress(): ByteArray {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ByteArray.gzipDecompress(): ByteArray {
|
||||
if (isEmpty()) return byteArrayOf()
|
||||
ByteArrayInputStream(this).use { bais ->
|
||||
GZIPInputStream(bais).use { gzis ->
|
||||
val outputStream = ByteArrayOutputStream(this.size * 2)
|
||||
val buffer = ByteArray(1024)
|
||||
var len: Int
|
||||
while (gzis.read(buffer).also { len = it } != -1) {
|
||||
outputStream.write(buffer, 0, len)
|
||||
}
|
||||
return outputStream.toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public actual fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray {
|
||||
val inflater = Inflater()
|
||||
inflater.setInput(this)
|
||||
val result = ByteArray(expectedSize)
|
||||
var totalRead = 0
|
||||
try {
|
||||
val resultLength = inflater.inflate(result)
|
||||
check(resultLength == expectedSize) { "Decompression failed: expected $expectedSize bytes, but got $resultLength" }
|
||||
while (!inflater.finished() && totalRead < expectedSize) {
|
||||
val read = inflater.inflate(result, totalRead, expectedSize - totalRead)
|
||||
if (read == 0) break
|
||||
totalRead += read
|
||||
}
|
||||
check(totalRead == expectedSize) { "Decompression failed: expected $expectedSize bytes, but got $totalRead" }
|
||||
return result
|
||||
} finally {
|
||||
inflater.end()
|
||||
@@ -61,8 +52,22 @@ public actual fun ByteArray.zlibCompress(): ByteArray {
|
||||
val deflater = Deflater()
|
||||
deflater.setInput(this)
|
||||
deflater.finish()
|
||||
val output = ByteArray(this.size + 64)
|
||||
val compressedSize = deflater.deflate(output)
|
||||
val bos = ByteArrayOutputStream(this.size)
|
||||
val buffer = ByteArray(1024)
|
||||
while (!deflater.finished()) {
|
||||
val count = deflater.deflate(buffer)
|
||||
if (count > 0) bos.write(buffer, 0, count)
|
||||
}
|
||||
deflater.end()
|
||||
return output.copyOf(compressedSize)
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
public actual fun ByteArray.gzipCompress(): ByteArray {
|
||||
val bos = ByteArrayOutputStream()
|
||||
GZIPOutputStream(bos).use { it.write(this) }
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
public actual fun ByteArray.gzipDecompress(): ByteArray {
|
||||
return GZIPInputStream(ByteArrayInputStream(this)).use { it.readBytes() }
|
||||
}
|
||||
@@ -1,104 +1,131 @@
|
||||
/*
|
||||
* Copyright © 2026 RTAkland
|
||||
* Author: RTAkland
|
||||
* Date: 2026/9/4
|
||||
* Copyright © 2025-2026 RTAkland
|
||||
* Open Source Under Apache-2.0 License
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
|
||||
@file:OptIn(ExperimentalForeignApi::class)
|
||||
|
||||
package cn.rtast.libmc.common
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import platform.posix.u_longVar
|
||||
import platform.zlib.*
|
||||
|
||||
private const val ENABLE_ZLIB_GZIP_HEADER = 15 + 32
|
||||
private const val MAX_WBITS = 15
|
||||
private const val DEFAULT_BUFFER_SIZE = 4096
|
||||
|
||||
public actual fun ByteArray.zlibDecompress(): ByteArray {
|
||||
if (isEmpty()) return ByteArray(0)
|
||||
return memScoped {
|
||||
val stream = alloc<z_stream>()
|
||||
stream.zalloc = null
|
||||
stream.zfree = null
|
||||
stream.opaque = null
|
||||
private inline fun ByteArray.processDecompress(initStream: (CPointer<z_stream>) -> Unit): ByteArray = memScoped {
|
||||
val stream = alloc<z_stream>()
|
||||
stream.zalloc = null
|
||||
stream.zfree = null
|
||||
stream.opaque = null
|
||||
initStream(stream.ptr)
|
||||
stream.next_in = this@processDecompress.refTo(0).getPointer(this).reinterpret()
|
||||
stream.avail_in = this@processDecompress.size.toUInt()
|
||||
val output = mutableListOf<Byte>()
|
||||
val tempBuffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
|
||||
val initResult = inflateInit2_(
|
||||
stream.ptr,
|
||||
ENABLE_ZLIB_GZIP_HEADER,
|
||||
ZLIB_VERSION,
|
||||
sizeOf<z_stream>().toInt()
|
||||
)
|
||||
check(initResult == Z_OK) { "inflateInit2_ failed with code: $initResult" }
|
||||
try {
|
||||
do {
|
||||
stream.next_out = tempBuffer.refTo(0).getPointer(this).reinterpret()
|
||||
stream.avail_out = DEFAULT_BUFFER_SIZE.toUInt()
|
||||
val result = inflate(stream.ptr, Z_NO_FLUSH)
|
||||
check(result == Z_OK || result == Z_STREAM_END) { "inflate error: $result" }
|
||||
val bytesDecompressed = DEFAULT_BUFFER_SIZE - stream.avail_out.toInt()
|
||||
output.addAll(tempBuffer.take(bytesDecompressed))
|
||||
|
||||
val inputPinned = this@zlibDecompress.pin()
|
||||
try {
|
||||
stream.next_in = inputPinned.addressOf(0).reinterpret()
|
||||
stream.avail_in = this@zlibDecompress.size.toUInt()
|
||||
|
||||
val bufferSize = 4096
|
||||
val tempBuffer = ByteArray(bufferSize)
|
||||
val tempPinned = tempBuffer.pin()
|
||||
val output = ArrayList<Byte>(this@zlibDecompress.size * 3)
|
||||
|
||||
try {
|
||||
var result: Int
|
||||
do {
|
||||
stream.next_out = tempPinned.addressOf(0).reinterpret()
|
||||
stream.avail_out = bufferSize.toUInt()
|
||||
|
||||
result = inflate(stream.ptr, Z_NO_FLUSH)
|
||||
check(result == Z_OK || result == Z_STREAM_END) { "inflate error: $result" }
|
||||
|
||||
val bytesDecompressed = bufferSize - stream.avail_out.toInt()
|
||||
for (i in 0 until bytesDecompressed) {
|
||||
output.add(tempBuffer[i])
|
||||
}
|
||||
|
||||
if (result == Z_STREAM_END) break
|
||||
} while (stream.avail_in > 0u || stream.avail_out == 0u)
|
||||
} finally {
|
||||
tempPinned.unpin()
|
||||
}
|
||||
|
||||
inflateEnd(stream.ptr)
|
||||
return@memScoped output.toByteArray()
|
||||
} finally {
|
||||
inputPinned.unpin()
|
||||
}
|
||||
if (result == Z_STREAM_END) break
|
||||
} while (stream.avail_out == 0u)
|
||||
} finally {
|
||||
inflateEnd(stream.ptr)
|
||||
}
|
||||
return output.toByteArray()
|
||||
}
|
||||
|
||||
public actual fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray {
|
||||
private inline fun ByteArray.processCompress(initStream: (CPointer<z_stream>) -> Unit): ByteArray = memScoped {
|
||||
val stream = alloc<z_stream>()
|
||||
stream.zalloc = null
|
||||
stream.zfree = null
|
||||
stream.opaque = null
|
||||
initStream(stream.ptr)
|
||||
stream.next_in = this@processCompress.refTo(0).getPointer(this).reinterpret()
|
||||
stream.avail_in = this@processCompress.size.toUInt()
|
||||
val output = mutableListOf<Byte>()
|
||||
val tempBuffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
try {
|
||||
do {
|
||||
stream.next_out = tempBuffer.refTo(0).getPointer(this).reinterpret()
|
||||
stream.avail_out = DEFAULT_BUFFER_SIZE.toUInt()
|
||||
val result = deflate(stream.ptr, Z_FINISH)
|
||||
check(result == Z_OK || result == Z_STREAM_END) { "deflate error: $result" }
|
||||
val have = DEFAULT_BUFFER_SIZE - stream.avail_out.toInt()
|
||||
output.addAll(tempBuffer.take(have))
|
||||
} while (stream.avail_out == 0u)
|
||||
} finally {
|
||||
deflateEnd(stream.ptr)
|
||||
}
|
||||
|
||||
return output.toByteArray()
|
||||
}
|
||||
|
||||
public actual fun ByteArray.zlibDecompress(expectedSize: Int): ByteArray = memScoped {
|
||||
val stream = alloc<z_stream>()
|
||||
stream.zalloc = null
|
||||
stream.zfree = null
|
||||
stream.opaque = null
|
||||
check(inflateInit_(stream.ptr, ZLIB_VERSION, sizeOf<z_stream>().toInt()) == Z_OK) { "inflateInit_ failed" }
|
||||
val result = ByteArray(expectedSize)
|
||||
if (this.isEmpty()) return result
|
||||
memScoped {
|
||||
val destLen = alloc<u_longVar>()
|
||||
destLen.value = expectedSize.toUInt()
|
||||
val res = uncompress(
|
||||
result.refTo(0).getPointer(this).reinterpret(),
|
||||
destLen.ptr,
|
||||
this@zlibDecompress.refTo(0).getPointer(this).reinterpret(),
|
||||
this@zlibDecompress.size.toUInt()
|
||||
)
|
||||
check(res == Z_OK) { "zlib uncompress failed with error code: $res" }
|
||||
try {
|
||||
stream.next_in = this@zlibDecompress.refTo(0).getPointer(this).reinterpret()
|
||||
stream.avail_in = this@zlibDecompress.size.toUInt()
|
||||
stream.next_out = result.refTo(0).getPointer(this).reinterpret()
|
||||
stream.avail_out = expectedSize.toUInt()
|
||||
var totalRead = 0
|
||||
while (stream.avail_in > 0u && totalRead < expectedSize) {
|
||||
val status = inflate(stream.ptr, Z_NO_FLUSH)
|
||||
check(status == Z_STREAM_END || status == Z_OK) { "inflate failed with status: $status" }
|
||||
val decompressedThisTurn = expectedSize - totalRead - stream.avail_out.toInt()
|
||||
totalRead += decompressedThisTurn
|
||||
if (status == Z_STREAM_END) break
|
||||
}
|
||||
check(totalRead == expectedSize) {
|
||||
"Decompression failed: expected $expectedSize bytes, but got $totalRead"
|
||||
}
|
||||
return result
|
||||
} finally {
|
||||
inflateEnd(stream.ptr)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public actual fun ByteArray.zlibCompress(): ByteArray {
|
||||
if (this.isEmpty()) return byteArrayOf()
|
||||
val maxCompressedLen = compressBound(this.size.toUInt()).toInt()
|
||||
val output = ByteArray(maxCompressedLen)
|
||||
memScoped {
|
||||
val destLen = alloc<u_longVar>()
|
||||
destLen.value = maxCompressedLen.toUInt()
|
||||
val res = compress(
|
||||
output.refTo(0).getPointer(this).reinterpret(),
|
||||
destLen.ptr,
|
||||
this@zlibCompress.refTo(0).getPointer(this).reinterpret(),
|
||||
this@zlibCompress.size.toUInt()
|
||||
)
|
||||
check(res == Z_OK) { "zlib compress failed with error code: $res" }
|
||||
return output.copyOf(destLen.value.toInt())
|
||||
}
|
||||
public actual fun ByteArray.gzipCompress(): ByteArray = processCompress { ptr ->
|
||||
check(
|
||||
deflateInit2_(
|
||||
ptr, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY, ZLIB_VERSION, sizeOf<z_stream>().toInt()
|
||||
) == Z_OK
|
||||
) { "deflateInit2_ failed" }
|
||||
}
|
||||
|
||||
public actual fun ByteArray.gzipDecompress(): ByteArray = processDecompress { ptr ->
|
||||
check(
|
||||
inflateInit2_(
|
||||
ptr, 16 + MAX_WBITS, ZLIB_VERSION, sizeOf<z_stream>().toInt()
|
||||
) == Z_OK
|
||||
) { "inflateInit2_ failed" }
|
||||
}
|
||||
|
||||
public actual fun ByteArray.zlibCompress(): ByteArray = processCompress { ptr ->
|
||||
check(
|
||||
deflateInit_(
|
||||
ptr, Z_DEFAULT_COMPRESSION, ZLIB_VERSION, sizeOf<z_stream>().toInt()
|
||||
) == Z_OK
|
||||
) { "deflateInit_ failed" }
|
||||
}
|
||||
|
||||
public actual fun ByteArray.zlibDecompress(): ByteArray = processDecompress { ptr ->
|
||||
check(
|
||||
inflateInit_(
|
||||
ptr, ZLIB_VERSION, sizeOf<z_stream>().toInt()
|
||||
) == Z_OK
|
||||
) { "inflateInit_ failed" }
|
||||
}
|
||||
Reference in New Issue
Block a user