11package `in`.dragonbra.javasteam.util
22
33import `in`.dragonbra.javasteam.util.compat.readNBytesCompat
4- import `in`.dragonbra.javasteam.util.crypto.CryptoHelper
54import `in`.dragonbra.javasteam.util.log.LogManager
65import `in`.dragonbra.javasteam.util.stream.BinaryReader
7- import `in`.dragonbra.javasteam.util.stream.BinaryWriter
86import `in`.dragonbra.javasteam.util.stream.MemoryStream
97import `in`.dragonbra.javasteam.util.stream.SeekOrigin
10- import org.tukaani.xz.LZMA2Options
118import org.tukaani.xz.LZMAInputStream
12- import org.tukaani.xz.LZMAOutputStream
13- import java.io.ByteArrayOutputStream
14- import java.util.zip.DataFormatException
9+ import java.util.zip.*
1510import kotlin.math.max
1611
1712@Suppress(" SpellCheckingInspection" , " unused" )
@@ -26,6 +21,11 @@ object VZipUtil {
2621
2722 private const val VERSION : Byte = ' a' .code.toByte()
2823
24+ // Thread-local window buffer pool to avoid repeated allocations
25+ private val windowBufferPool = ThreadLocal .withInitial {
26+ ByteArray (1 shl 23 ) // 8MB max size
27+ }
28+
2929 @JvmStatic
3030 fun decompress (ms : MemoryStream , destination : ByteArray , verifyChecksum : Boolean = true): Int {
3131 try {
@@ -67,7 +67,12 @@ object VZipUtil {
6767
6868 // If the value of dictionary size in properties is smaller than (1 << 12),
6969 // the LZMA decoder must set the dictionary size variable to (1 << 12).
70- val windowBuffer = ByteArray (max(1 shl 12 , dictionarySize))
70+ val windowSize = max(1 shl 12 , dictionarySize)
71+ val windowBuffer = if (windowSize <= (1 shl 23 )) {
72+ windowBufferPool.get() // Reuse thread-local buffer
73+ } else {
74+ ByteArray (windowSize) // Fallback for unusually large windows
75+ }
7176 val bytesRead = LZMAInputStream (
7277 ms,
7378 sizeDecompressed.toLong(),
@@ -78,8 +83,11 @@ object VZipUtil {
7883 lzmaInput.readNBytesCompat(destination, 0 , sizeDecompressed)
7984 }
8085
81- if (verifyChecksum && Utils .crc32(destination).toInt() != outputCrc) {
82- throw DataFormatException (" CRC does not match decompressed data. VZip data may be corrupted." )
86+ if (verifyChecksum) {
87+ val actualCrc = Utils .crc32(destination, 0 , bytesRead).toInt()
88+ if (actualCrc != outputCrc) {
89+ throw DataFormatException (" CRC does not match decompressed data. VZip data may be corrupted." )
90+ }
8391 }
8492
8593 return bytesRead
@@ -93,46 +101,16 @@ object VZipUtil {
93101 }
94102 }
95103
96- /* *
97- * Ported from SteamKit2 and is untested, use at your own risk
98- */
99104 @JvmStatic
100105 fun compress (buffer : ByteArray ): ByteArray {
101- try {
102- ByteArrayOutputStream ().use { ms ->
103- BinaryWriter (ms).use { writer ->
104- val crc = CryptoHelper .crcHash(buffer)
105- writer.writeShort(VZIP_HEADER )
106- writer.writeByte(VERSION )
107- writer.write(crc)
108-
109- // Configure LZMA options to match SteamKit2's settings
110- val options = LZMA2Options ().apply {
111- dictSize = 1 shl 23 // 8MB dictionary
112- setPreset(2 ) // Algorithm setting
113- niceLen = 128 // numFastBytes equivalent
114- matchFinder = LZMA2Options .MF_BT4
115- mode = LZMA2Options .MODE_NORMAL
116- }
117-
118- // Write LZMA-compressed data
119- LZMAOutputStream (ms, options, false ).use { lzmaStream ->
120- lzmaStream.write(buffer)
121- }
122-
123- writer.write(crc)
124- writer.writeInt(buffer.size)
125- writer.writeShort(VZIP_FOOTER )
126-
127- return ms.toByteArray()
128- }
129- }
130- } catch (e: NoClassDefFoundError ) {
131- logger.error(" Missing implementation of org.tukaani:xz" )
132- throw e
133- } catch (e: ClassNotFoundException ) {
134- logger.error(" Missing implementation of org.tukaani:xz" )
135- throw e
136- }
106+ throw Exception (" VZipUtil.compress is not implemented." )
107+ // try {
108+ // } catch (e: NoClassDefFoundError) {
109+ // logger.error("Missing implementation of org.tukaani:xz")
110+ // throw e
111+ // } catch (e: ClassNotFoundException) {
112+ // logger.error("Missing implementation of org.tukaani:xz")
113+ // throw e
114+ // }
137115 }
138116}
0 commit comments