import com.dowstats.data.rgd.RgdData import org.junit.jupiter.api.Test import java.io.DataInputStream import java.io.File import java.io.PrintWriter import java.math.RoundingMode import java.nio.ByteBuffer import java.nio.file.Paths class RgdParserServiceTest { val zeroByte: Byte = 0 val rgdDictionary: MutableMap = mutableMapOf() fun readDictionary() { File("src/test/resources/RGD_DIC.TXT").forEachLine { if(it.isNotEmpty() && it[0] != '#'){ val kv = it.split('=') val key = kv.first().drop(2).decodeHex().getUIntAt(0).toInt() val value = kv.last() rgdDictionary[key] = value } } } private fun String.decodeHex(): ByteArray { check(length % 2 == 0) { "Must have an even length" } return chunked(2) .map { it.toInt(16).toByte() } .toByteArray() } @Test fun testSum() { readDictionary() val path = Paths.get("").toAbsolutePath().toString() println(path) val bufferedReader: DataInputStream = DataInputStream(File("src/test/resources/chaos_marine_squad.rgd").inputStream()) val res = bufferedReader.use { it.readAllBytes() } println("${res.size} bytes") val dataInfo = res.copyOfRange(24, 32) println(dataInfo.toString(Charsets.UTF_8)) val chunkBytes = res.drop(32).toByteArray() val version = chunkBytes.copyOfRange(0,4).reversedArray().getUIntAt(0) val chunkLength = chunkBytes.copyOfRange(4,8).reversedArray().getUIntAt(0) val stringLength = chunkBytes.copyOfRange(8,12).reversedArray().getUIntAt(0).toInt() println("version: $version; chunkLength: $chunkLength; stringLength: $stringLength") //val sString = chunkBytes.copyOfRange(12, 12 + stringLength).toString(Charsets.UTF_8) //val iCRC = chunkBytes.copyOfRange(12 + stringLength, 12 + stringLength + 4).reversedArray().getUIntAt(0) val dataLength = chunkBytes.copyOfRange(16 + stringLength, 16 + stringLength + 4).reversedArray().getUIntAt(0).toInt() //println("sString: $sString; iCRC: $iCRC; dataLength: $dataLength") fun printRgdData(data: List, out: PrintWriter){ data.sortedBy { it.name }.forEach{ if(it.value is List<*>){ out.println("
") out.println("${it.name}") out.println("
    ") printRgdData(it.value as List, out) out.println("
") out.println("
") }else{ out.println("
  • ${it.name} → ${it.value}
  • ") } } } } private fun handleRgdData(byteArray: ByteArray): List { var keyCount = byteArray.copyOfRange(0,4).reversedArray().getUIntAt(0).toInt() val totalKeyCount = keyCount var offset = 4 val rgdData = mutableListOf() while (keyCount != 0){ val hash = byteArray.copyOfRange(offset,offset + 4).reversedArray().getUIntAt(0).toInt() offset += 4 val valueName = rgdDictionary[hash] ?: hash.toString() val type = byteArray.copyOfRange(offset,offset + 4).reversedArray().getUIntAt(0).toInt() offset += 4 val iOffset = byteArray.copyOfRange(offset,offset + 4).reversedArray().getUIntAt(0).toInt() offset += 4 //println("$space iOffset: $iOffset") val dataOffset = totalKeyCount * (3 * 4) + iOffset + 4 val data: Any = when(type){ 0 -> {// Float val entityData = byteArray.copyOfRange(dataOffset,dataOffset + 4).reversedArray().getFloat() entityData.toBigDecimal().setScale(2, RoundingMode.HALF_UP).toDouble() } 1 -> {// Int val entityData = byteArray.copyOfRange(dataOffset,dataOffset + 8).reversedArray().getUIntAt(0) entityData } 2 -> {// Bool val entityData = byteArray.copyOfRange(dataOffset,dataOffset + 1).map { it }.toString() entityData } 3 -> {// String var zeroByteOffset = 0 while(byteArray[dataOffset+zeroByteOffset] != zeroByte){ zeroByteOffset++ } byteArray.copyOfRange(dataOffset,dataOffset + zeroByteOffset).toString(Charsets.UTF_8) } 4 -> {// String var zeroByteOffset = 0 while(byteArray[dataOffset+zeroByteOffset] != zeroByte || byteArray[dataOffset+zeroByteOffset+1] != zeroByte){ zeroByteOffset++ } zeroByteOffset++ byteArray.copyOfRange(dataOffset,dataOffset + zeroByteOffset).toString(Charsets.UTF_16LE) } 100 -> { handleRgdData(byteArray.copyOfRange(dataOffset, byteArray.size)) } else -> 0 } rgdData.add(RgdData(hash, valueName, type, data)) keyCount-- } return rgdData } private fun ByteArray.getFloat(): Float { val buffer = ByteBuffer.wrap(this) return buffer.float } private fun ByteArray.getUIntAt(idx: Int): UInt = ((this[idx].toUInt() and 0xFFu) shl 24) or ((this[idx + 1].toUInt() and 0xFFu) shl 16) or ((this[idx + 2].toUInt() and 0xFFu) shl 8) or (this[idx + 3].toUInt() and 0xFFu) }