import com.dowstats.data.rgd.RgdData import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName import com.dowstats.data.rgd.RgdDataUtil.getStringByName import com.dowstats.service.w40k.RgdParserService import com.dowstats.service.w40k.RgdService import org.junit.jupiter.api.Test import java.io.DataInputStream import java.io.File import java.io.PrintWriter import java.nio.ByteBuffer class ModParserServiceTest { val zeroByte: Byte = 0 val rgdDictionary: MutableMap = mutableMapOf() val rgdParseService = RgdParserService() val rgdService = RgdService() val prevVersion = "Dowstats_Balance_Mod_1_0_97" val currentVersion = "Dowstats_Balance_Mod_1_0_98" val spaceMarinesPath = "space_marines" val chaosPath = "chaos" val eldarPath = "eldar" val orksPath = "orks" val guardPath = "guard" val necronPath = "necrons" val tauPath = "tau" val sistersPath = "sisters" val darkPath = "dark_eldar" val racesWikiPaths = setOf(spaceMarinesPath, chaosPath, eldarPath, orksPath, guardPath, necronPath, tauPath, sistersPath) val racesWikiPaths2 = setOf("black_templars") 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 } } } fun getUnitsAndSquadsDiff(out: PrintWriter) { racesWikiPaths.forEach{racePath -> val rgdDataOld = File("src/main/resources/static/mods/$prevVersion/attrib/sbps/races/$racePath").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name to rgdData }.filter { it.second != null } .toMap() val rgdDataNew = File("src/main/resources/static/mods/$currentVersion/attrib/sbps/races/$racePath").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) }else{ null } it.name to rgdData }.filter { it.second != null } .sortedBy { it.first } .toMap() val rgdDataUnitsOld = File("src/main/resources/static/mods/$prevVersion/attrib/ebps/races/$racePath/troops").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) }else{ null } it.name to rgdData }.filter { it.second != null } .toMap() val rgdDataUnitsNew = File("src/main/resources/static/mods/$currentVersion/attrib/ebps/races/$racePath/troops").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) }else{ null } it.name to rgdData }.filter { it.second != null } .toMap() /*classicRgdData.mapValues { findStrategicPointCaptureRate( it.value?: listOf()) }.entries .sortedByDescending { it.value } .filter { it.value != null } .filter { !it.key.contains("_sp") } .forEach{println(it.key + " -> " + it.value )}*/ val changedSquadNames = rgdDataNew.map { } rgdDataNew.forEach { newSquad -> val oldSquad = rgdDataOld[newSquad.key] val baseUnitPath = ((newSquad.value?.find { it.name == "squad_loadout_ext" }?.value as List) .find { it.name == "trooper_base" }?.value as List) .find { it.name == "type" }?.value as String val baseUnitName = baseUnitPath.split("\\").last().replace(".lua", ".rgd") val newUnit = rgdDataUnitsNew[baseUnitName] val oldUnit = rgdDataUnitsOld[baseUnitName] if(oldSquad != newSquad.value || oldUnit != newUnit){ out.println(newSquad.key.replace(".rgd", "").uppercase()) rgdService.printRgdDiff(newSquad.value, oldSquad, out = out) if(oldSquad != newSquad.value && oldUnit != newUnit) out.println("-") rgdService.printRgdDiff(newUnit, oldUnit, out = out) out.println("---") } } } } data class UnitAsSquad(val squadData: List?,val uintData: List?) fun getAllWeaponsDiff(out: PrintWriter) { val wanillaRgdData = File("src/main/resources/static/mods/wanila/attrib/weapon").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name to rgdData }.filter { it.second != null } .toMap() val classicRgdData = File("src/main/resources/static/mods/$prevVersion/attrib/weapon").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name to rgdData }.filter { it.second != null } .toMap() val newRgdData = File("src/main/resources/static/mods/$currentVersion/attrib/weapon").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name to rgdData }.filter { it.second != null } .sortedBy { it.first } .toMap() newRgdData.forEach {newWeapon -> val oldWeapon = classicRgdData[newWeapon.key] if(oldWeapon != newWeapon.value){ out.println(newWeapon.key.replace(".rgd", "").uppercase()) if(oldWeapon != null){ rgdService.printRgdDiff(newWeapon.value, oldWeapon, out = out) }else{ out.println("NOT EXIST IN PREV VERSION") rgdService.printRgdDiff(newWeapon.value, wanillaRgdData[newWeapon.key], out = out) } out.println("---") } } } fun getAllUnits(): Map { return racesWikiPaths.map{racePath -> val rgdData42 = File("src/main/resources/static/mods/$currentVersion/attrib/sbps/races/$racePath").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) }else{ null } it.name to rgdData }.filter { it.second != null } .toMap() val newUnits = File("src/main/resources/static/mods/$currentVersion/attrib/ebps/races/$racePath/troops").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) }else{ null } it.name to rgdData }.filter { it.second != null } .toMap() rgdData42.map {newSquad -> val baseUnitPath = ((newSquad.value?.find { it.name == "squad_loadout_ext" }?.value as List) .find { it.name == "trooper_base" }?.value as List) .find { it.name == "type" }?.value as String val baseUnitName = baseUnitPath.split("\\").last().replace(".lua", ".rgd") baseUnitName to UnitAsSquad(newSquad.value, newUnits[baseUnitName]) }.toMap() }.fold(emptyMap()){sum, map -> map + sum} } fun getWeaponRgdMap(): Map?> { return File("src/main/resources/static/mods/$currentVersion/attrib/weapon").walkTopDown().map { val rgdData = if(it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")){ rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name.replace(".rgd", "") to rgdData }.toMap() } @Test fun getAllWeaponDamage() { val weaponMap = getWeaponRgdMap() getAllUnits().forEach { println(it.key.replace(".rgd","")) it.value.uintData ?.getRgdTableByName("combat_ext") ?.getRgdTableByName("hardpoints") ?.forEach { hardpoint -> if (hardpoint.name.contains("hardpoint")) { val hardpointWeapons = (hardpoint.value as List)?.getRgdTableByName("weapon_table") ?.mapNotNull { weapon -> if (weapon.name.contains("weapon")) { val weapon = (weapon.value as List).getStringByName("weapon") if (weapon != "" && weapon?.contains("dummy_weapon") == false) { weapon } else null } else null } if (hardpointWeapons?.isNotEmpty() == true) { hardpointWeapons.forEach { val weaponId = it.replace("weapon\\", "").replace(".lua", "") println(":" + weaponId + ":") val weaponRgd = weaponMap[weaponId] val reloadTime: Double = weaponRgd?.getDoubleByName("reload_time")!! val accuracy: Double = weaponRgd?.getDoubleByName("accuracy")!! val armourDamage = weaponRgd?.getRgdTableByName("area_effect") ?.getRgdTableByName("weapon_damage") ?.getRgdTableByName("armour_damage") val minDamage = armourDamage?.getDoubleByName("min_damage")!! val maxDamage = armourDamage.getDoubleByName("max_damage")!! val avgDamage = (minDamage + maxDamage) / 2 val weaponDmgMap: Map? = armourDamage ?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing -> if (armour_piercing.name.contains("entry")) { val entry = armour_piercing.value as List val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","") val dmgValue = entry.getDoubleByName("armour_piercing_value") dmgType!! to dmgValue!! } else null }?.toMap()?.toSortedMap() println() val dmgK = accuracy * (1/(reloadTime - reloadTime.mod(0.125))) weaponDmgMap?.forEach { val piercingK = it.value / 100 val totalDmg = piercingK * dmgK * avgDamage print(it.key + "-" + (Math.round(totalDmg * 100.0) / 100.00).toString() + "|") } println() println("-----") } } } } println("============================") } } @Test fun getAllBuildingsDiff(out: PrintWriter) { racesWikiPaths.forEach { racePath -> val classicRgdData = File("src/main/resources/static/mods/$prevVersion/attrib/ebps/races/$racePath/structures").walkTopDown().map { val rgdData = if (it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")) { rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name to rgdData }.filter { it.second != null } .toMap() val newRgdData = File("src/main/resources/static/mods/$currentVersion/attrib/ebps/races/$racePath/structures").walkTopDown().map { val rgdData = if (it.isFile && !it.name.contains("hg_dxp3") && !it.name.contains("npc")) { rgdParseService.parseRgdFileStream(DataInputStream(it.inputStream())) } else { null } it.name to rgdData }.filter { it.second != null } .toMap() newRgdData.forEach { newBuilding -> val oldBuilding = classicRgdData[newBuilding.key] if (oldBuilding != newBuilding.value) { out.println(newBuilding.key.replace(".rgd", "").uppercase()) rgdService.printRgdDiff(newBuilding.value, oldBuilding, out = out) out.println("---") } } } } @Test fun saveAllDataToDb() { // modParserService.saveModDataToDb() } @Test fun getAllDiffAndPrintToFiles(){ val weaponDiffFileName = "$prevVersion-$currentVersion-weapons.txt" File(weaponDiffFileName).printWriter().use { out -> getAllWeaponsDiff(out) } val unitsDiffFileName = "$prevVersion-$currentVersion-units.txt" File(unitsDiffFileName).printWriter().use { out -> getUnitsAndSquadsDiff(out) } val buildingsFileName = "$prevVersion-$currentVersion-building.txt" File(buildingsFileName).printWriter().use { out -> getAllBuildingsDiff(out) } } private fun findStrategicPointCaptureRate(rgdDataList: List): Double? { val capRgdData = rgdDataList.find { it.name == "squad_capture_strategic_point_ext" }?.value as List? val canCapture = capRgdData?.find { it.name == "able_to_capture" }?.value == "[1]" val captureRate = capRgdData?.find { it.name == "capture_rate" } return if(canCapture){ captureRate?.value as Double? } else null } private fun findVisionCaptureRate(rgdDataList: List): Double? { val visionRgdData = rgdDataList.find { it.name == "sight_ext" }?.value as List? return visionRgdData?.find { it.name == "sight_radius" }?.value as Double? } private fun String.decodeHex(): ByteArray { check(length % 2 == 0) { "Must have an even length" } return chunked(2) .map { it.toInt(16).toByte() } .toByteArray() } 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) }