package net.taehui.twilight import net.taehui.twilight.awilight.Component import net.taehui.twilight.awilight.DefaultCompute import net.taehui.twilight.note.* import java.io.BufferedReader import java.io.ByteArrayInputStream import java.io.InputStreamReader import java.util.* import kotlin.math.abs import kotlin.math.floor class BMSCompiler(noteFileContents: ByteArray, format: String) : BaseCompiler(noteFileContents, format) { private open class BMSInputItem(val inputNote: InputNote, val bmsPosition: Double) : Comparable<BMSInputItem> { override fun compareTo(other: BMSInputItem): Int { return inputNote.compareTo(other.inputNote) } } private class BMSLongInputItem(inputNote: InputNote, bmsPosition: Double, val bmsID: String) : BMSInputItem(inputNote, bmsPosition) private open class EarlyBMSInputItem(val bmsPosition: Double) : Comparable<EarlyBMSInputItem> { override fun compareTo(other: EarlyBMSInputItem): Int { return bmsPosition.compareTo(other.bmsPosition) } } private class EarlyBMSLongInputItem(bmsPosition: Double, val bmsID: String) : EarlyBMSInputItem(bmsPosition) private val lines = mutableListOf<String>() private val bmsPositionLogicalYMap = TreeMap<Double, Double>() private val bmsPositionBPMMap = TreeMap<Double, Double>() private val bmsPositionMultiplierMap = TreeMap<Double, Double>() private val bmsIDStopMap = mutableMapOf<String, Double>() private val bmsIDMultiplierMap = mutableMapOf<String, Double>() private var longNoteBMSID = "" private var highestMeter = 0 private var is4K = false private var is6K = false override fun handleCompile(targetComputing: Computing) { val bmsPositionBPMMap = TreeMap<Double, Double>() val meterMeterMultiplierMap = LinkedHashMap<Int, Double>() meterMeterMultiplierMap[-1] = 1.0 val bmsIDBPMMap = mutableMapOf<String, Double>() BufferedReader(InputStreamReader(ByteArrayInputStream(noteFileContents), format)).use { val saltComputer = Random() var lastBin = 0 var isValidStatement = true for (line in it.lines()) { if (line.startsWith("#")) { val mainContents = line.substring(1) val mainData = mainContents.split(":", " ", ".", ";", limit = 2).toTypedArray() val property = mainData[0] if (isValidStatement) { if (mainData.size > 1) { val data = mainData[1] if (property.equals("RANDOM", true)) { val rValue = mainData[1].toInt() lastBin = 1 + saltComputer.nextInt(data.toInt()) if (rValue > 1) { targetComputing.isSalt = true } continue } else if (property.equals("IF", true)) { isValidStatement = lastBin == data.toInt() continue } } lines.add(line) } else { if (property.equals("ELSE", true) || property.equals("ENDIF", true) || property.equals( "IFEND", true ) ) { isValidStatement = true } } } } } for (line in lines) { val mainContents = line.substring(1) val mainData = mainContents.split(":", " ", ".", ";", limit = 2).toTypedArray() if (isMainBMSData(mainContents)) { var property = if (mainData.size > 1) mainData[0] else mainData[0].substring(0, 5) property = property.padStart(5, '0') val data = if (mainData.size > 1) mainData[1] else mainData[0].substring(5) val meter = property.substring(0, 3).toInt() highestMeter = meter.coerceAtLeast(highestMeter) if (property[3] == '0' && property[4] == '2') { meterMeterMultiplierMap[meter] = data.toDouble() } } else { val property = mainData[0] if (mainData.size > 1) { val data = mainData[1] if (property.startsWith("BPM", true)) { if (property.length > 3) { bmsIDBPMMap[property.substring(3, 5)] = data.toDouble() } else { try { targetComputing.levyingBPM = data.toDouble() } catch (e: NumberFormatException) { val delimitedData = data.split(" ") if (delimitedData.size > 1) { bmsIDBPMMap[delimitedData[0]] = delimitedData[1].toDouble() } else { targetComputing.levyingBPM = data.split("-").toTypedArray()[0].toDouble() } } } } else if (property.equals("LNOBJ", true)) { longNoteBMSID = data.trim { it <= ' ' } } else if (property.equals("LNMODE", true)) { when (data.toInt()) { 1 -> targetComputing.isAutoLongNote = true 2, 3 -> targetComputing.isAutoLongNote = false } } } else { if (property.equals("4K", true)) { is4K = true } else if (property.equals("6K", true)) { is6K = true } } } } val earlyBMSInputItemSet = mutableMapOf<String, SortedSet<EarlyBMSInputItem>>() val earlyBMSLongInputItemSet = mutableMapOf<String, SortedSet<EarlyBMSLongInputItem>>() valueComponent = Component(targetComputing.levyingBPM) val bmsPositionSet = TreeSet<Double>() var title = "" var titleAssister = "" var artist = "" var artistAssister = "" val audioTargets = mutableListOf<String>() val audioValues = mutableSetOf<String>() for (line in lines) { val mainContents = line.substring(1) val mainData = mainContents.split(":", " ", ".", ";", limit = 2).toTypedArray() if (isMainBMSData(mainContents)) { var property = if (mainData.size > 1) mainData[0] else mainData[0].substring(0, 5) property = property.padStart(5, '0') val noteVariety0 = property[3] val noteVariety1 = property[4] val data = if (mainData.size > 1) mainData[1] else mainData[0].substring(5) val dataCount = data.length - data.length % 2 val meter = property.substring(0, 3).toInt() var meterCount = 0 while (meterCount < dataCount) { val dataValue = data.substring(meterCount, meterCount + 2) if (dataValue != "00") { val bmsPosition = meterCount.toDouble() / dataCount + meter val rawInput = property.substring(3, 5) when (noteVariety0) { '0' -> { when (noteVariety1) { '1', '4', '6', '7' -> { bmsPositionSet.add(bmsPosition) highestPosition = highestPosition.coerceAtLeast(bmsPosition) } '3', '8', '9' -> bmsPositionSet.add(bmsPosition) } } '1', '2' -> { if (noteVariety1 != '7') { if (longNoteBMSID.isNotEmpty()) { targetComputing.totalNotes += 1 if (rawInput[1] == '6') { targetComputing.autoableNotes += 1 } positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 highestPosition = highestPosition.coerceAtLeast(bmsPosition) } else { earlyBMSLongInputItemSet.getOrPut(rawInput) { TreeSet() }.add(EarlyBMSLongInputItem(bmsPosition, dataValue)) } } bmsPositionSet.add(bmsPosition) } '3', '4', '7', '9' -> bmsPositionSet.add(bmsPosition) '5', '6' -> { if (noteVariety1 != '7') { if (longNoteBMSID.equals( dataValue, ignoreCase = true ) ) { earlyBMSLongInputItemSet.getOrPut(rawInput) { TreeSet() }.add(EarlyBMSLongInputItem(bmsPosition, dataValue)) } else { earlyBMSInputItemSet.getOrPut(rawInput) { TreeSet() }.add(EarlyBMSInputItem(bmsPosition)) } bmsPositionSet.add(bmsPosition) } } 'D', 'E' -> { if (noteVariety1 != '7') { targetComputing.trapNotes += 1 highestPosition = highestPosition.coerceAtLeast(bmsPosition) bmsPositionSet.add(bmsPosition) } } 'S' -> { if (noteVariety1 == 'C') { bmsPositionSet.add(bmsPosition) } } } } meterCount += 2 } } else { val property = mainData[0] if (mainData.size > 1) { val data = mainData[1].trim { it <= ' ' } if (property.startsWith("WAV", true)) { if (data.isNotEmpty()) { audioTargets.add(property.substring(3, 5)) } } else if (property.startsWith("STOP", true)) { bmsIDStopMap[property.substring(4, 6)] = data.toDouble() } else if (property.startsWith("SCROLL", true)) { bmsIDMultiplierMap[property.substring(6, 8)] = data.toDouble() } else if (property.equals("TITLE", true)) { title = data } else if (property.equals("SUBTITLE", true)) { titleAssister = data } else if (property.equals("ARTIST", true)) { artist = data } else if (property.equals("SUBARTIST", true)) { artistAssister = data } else if (property.equals("GENRE", true)) { targetComputing.genre = data } else if (property.equals("PLAYLEVEL", true)) { var levelTextValue = data.toDoubleOrNull() if (levelTextValue != null) { levelTextValue = abs(floor(levelTextValue)) if (levelTextValue < 100) { targetComputing.levelText = "LV. $data" } else { val levelText = levelTextValue.toString() targetComputing.levelText = "LV. ${ levelText.substring( 0.coerceAtLeast(levelText.indexOf('.') - 2), levelText.indexOf('.') ) }" } } else { targetComputing.levelText = data } } else if (property.equals("DIFFICULTY", true)) { if (data.isNotEmpty()) { targetComputing.level = 1.coerceAtLeast(data.toInt()).coerceAtMost(5) } } else if (property.equals("RANK", true)) { try { when (0.coerceAtLeast(data.toInt().coerceAtMost(4))) { 0 -> targetComputing.judgmentStage = 10.0 1 -> targetComputing.judgmentStage = 7.0 2 -> targetComputing.judgmentStage = 5.0 3 -> targetComputing.judgmentStage = 3.0 4 -> targetComputing.judgmentStage = 0.0 } } catch (_: NumberFormatException) { } } } } } for ((rawInput, earlyBMSLongInputItems) in earlyBMSLongInputItemSet) { var lastEarlyBMSLongInputItem: EarlyBMSLongInputItem? = null for (earlyBMSLongInputItem in earlyBMSLongInputItems) { if (earlyBMSLongInputItem.bmsID.equals(longNoteBMSID, ignoreCase = true)) { if (lastEarlyBMSLongInputItem != null) { targetComputing.longNotes += 1 targetComputing.totalNotes += 2 if (rawInput[1] == '6') { targetComputing.autoableNotes += 2 } var bmsPosition = lastEarlyBMSLongInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 bmsPosition = earlyBMSLongInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 lastEarlyBMSLongInputItem = null continue } } else { if (lastEarlyBMSLongInputItem != null) { targetComputing.totalNotes += 1 if (rawInput[1] == '6') { targetComputing.autoableNotes += 1 } val bmsPosition = lastEarlyBMSLongInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 } } lastEarlyBMSLongInputItem = earlyBMSLongInputItem } if (lastEarlyBMSLongInputItem != null) { targetComputing.totalNotes += 1 if (rawInput[1] == '6') { targetComputing.autoableNotes += 1 } val bmsPosition = lastEarlyBMSLongInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 } } for ((rawInput, earlyBMSInputItems) in earlyBMSInputItemSet) { var lastEarlyBMSInputItem: EarlyBMSInputItem? = null for (earlyBMSInputItem in earlyBMSInputItems) { if (lastEarlyBMSInputItem != null) { targetComputing.longNotes += 1 targetComputing.totalNotes += 2 if (rawInput[1] == '6') { targetComputing.autoableNotes += 2 } var bmsPosition = lastEarlyBMSInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 bmsPosition = earlyBMSInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 lastEarlyBMSInputItem = null } else { lastEarlyBMSInputItem = earlyBMSInputItem } } if (lastEarlyBMSInputItem != null) { targetComputing.totalNotes += 1 if (rawInput[1] == '6') { targetComputing.autoableNotes += 1 } val bmsPosition = lastEarlyBMSInputItem.bmsPosition highestPosition = highestPosition.coerceAtLeast(bmsPosition) positionStandNoteCountMap[bmsPosition] = positionStandNoteCountMap.getOrDefault(bmsPosition, 0) + 1 } } targetComputing.title = if (titleAssister.isEmpty() || title.endsWith(titleAssister)) title else "$title $titleAssister" targetComputing.artist = if (artistAssister.isEmpty()) artist else "$artist / $artistAssister" val inputSet = mutableSetOf<Int>() for (line in lines) { val mainContents = line.substring(1) val mainData = mainContents.split(":", " ", ".", ";", limit = 2).toTypedArray() if (isMainBMSData(mainContents)) { var property = if (mainData.size > 1) mainData[0] else mainData[0].substring(0, 5) property = property.padStart(5, '0') val data = if (mainData.size > 1) mainData[1] else mainData[0].substring(5) val dataCount = data.length - data.length % 2 val meter = property.substring(0, 3).toInt() var meterCount = 0 while (meterCount < dataCount) { val mData = data.substring(meterCount, meterCount + 2) if (mData != "00") { val bmsPosition = meterCount.toDouble() / dataCount + meter val noteVariety0 = property[3] val noteVariety1 = property[4] when (noteVariety0) { '0' -> { when (noteVariety1) { '3' -> bpmValues[bmsPosition] = mData.toInt(16).toDouble() '8' -> { bmsIDBPMMap[mData]?.let { bpmValues[bmsPosition] = it } } '9' -> { bmsIDStopMap[mData]?.let { bmsPositionBPMMap[bmsPosition] = it } } } } '1', '2', '5', '6' -> { if (audioTargets.contains(mData)) { audioValues.add(mData) } } 'S' -> { if (noteVariety1 == 'C') { bmsIDMultiplierMap[mData]?.let { this.bmsPositionMultiplierMap[bmsPosition] = it } } } } inputSet.add(getBMSInput(noteVariety1, noteVariety0, Component.InputMode.INPUT_MODE_142)) } meterCount += 2 } } else { if (mainData.size > 1) { val data = mainData[1] if (mainData[0].equals("TOTAL", ignoreCase = true)) { try { targetComputing.hitPointsValue = 0.0.coerceAtLeast(data.toDouble()) / (100 * targetComputing.totalNotes) } catch (_: NumberFormatException) { } } } } } targetComputing.inputMode = getInputMode(inputSet) if (targetComputing.inputMode == Component.InputMode.INPUT_MODE_102 && (targetComputing.title.contains( "9B", true ) || targetComputing.title.contains( "9K", true ) || targetComputing.title.contains( "PMS", true )) ) { targetComputing.inputMode = Component.InputMode.INPUT_MODE_9 } for (i in 0..highestMeter + 1) { bmsPositionSet.add(i.toDouble()) } var lastBMSPosition = 0.0 var lastLogicalY = 0.0 var lastWait = 0.0 var lastMultiplierValue = 1.0 var lastBPM = targetComputing.levyingBPM for (bmsPosition in bmsPositionSet) { val meterMultiplier = meterMeterMultiplierMap.getOrDefault(floor(lastBMSPosition).toInt(), 1.0) lastLogicalY -= (bmsPosition - lastBMSPosition) * lastMultiplierValue * meterMultiplier * valueComponent.logicalYMeter bmsPositionLogicalYMap[bmsPosition] = lastLogicalY this.bmsPositionMultiplierMap[bmsPosition]?.let { lastMultiplierValue = it } lastWait += valueComponent.millisMeter * meterMultiplier * (bmsPosition - lastBMSPosition) bpmValues[bmsPosition]?.let { lastBPM = it valueComponent.setBPM(it) } waitValues[bmsPosition] = lastWait lastBMSPosition = bmsPosition bmsPositionBPMMap[bmsPosition]?.let { lastWait += valueComponent.millisMeter * it / Component.STANDARD_METER this.bmsPositionBPMMap[bmsPosition] = lastBPM } } targetComputing.isBanned = audioValues.size < 2 || targetComputing.totalNotes == 0 targetComputing.noteVariety = Component.NoteVariety.BMS } override fun handleCompile(defaultComputer: DefaultCompute) { handleCompile(defaultComputer as Computing) for (i in highestMeter + 1 downTo 0) { val logicalY = valueComponent.levyingHeight + bmsPositionLogicalYMap[i.toDouble()]!! val wait = waitValues[i.toDouble()]!! notes.add(MeterNote(logicalY, wait)) } val inputCount = Component.INPUT_COUNTS[defaultComputer.inputMode.value] val bmsInputItemSets = mutableListOf<TreeSet<BMSInputItem>>() for (i in inputCount downTo 0) { bmsInputItemSets.add(TreeSet()) } val bmsLongInputItemSets = mutableListOf<TreeSet<BMSLongInputItem>>() for (i in inputCount downTo 0) { bmsLongInputItemSets.add(TreeSet()) } for (line in lines) { val mainContents = line.substring(1) if (isMainBMSData(mainContents)) { val mainData = mainContents.split(":", " ", ".", ";", limit = 2).toTypedArray() var property = if (mainData.size > 1) mainData[0] else mainData[0].substring(0, 5) property = property.padStart(5, '0') val noteVariety0 = property[3] val noteVariety1 = property[4] if (noteVariety0 != '0' || noteVariety1 != '2') { val data = if (mainData.size > 1) mainData[1] else mainData[0].substring(5) val dataCount = data.length - data.length % 2 val meter = property.substring(0, 3).toInt() var meterCount = 0 while (meterCount < dataCount) { val dataValue = data.substring(meterCount, meterCount + 2) if (dataValue != "00") { val input = getBMSInput(noteVariety1, noteVariety0, defaultComputer.inputMode) val bmsPosition = meterCount.toDouble() / dataCount + meter val logicalY = valueComponent.levyingHeight + bmsPositionLogicalYMap[bmsPosition]!! val wait = waitValues[bmsPosition]!! when (noteVariety0) { '0' -> { when (noteVariety1) { '1' -> defaultComputer.waitAudioNoteMap.add(wait) '3', '8' -> { bpmValues[bmsPosition]?.let { waitBPMMap[wait] = it } } '4', '6', '7' -> defaultComputer.waitMediaNoteMap.add(wait) '9' -> { bmsPositionBPMMap[bmsPosition]?.let { valueComponent.setBPM(it) waitStopMap[wait] = 0.0 waitStopMap[wait + valueComponent.millisMeter * bmsIDStopMap[dataValue]!! / Component.STANDARD_METER] = it } } } } '1', '2' -> { if (input != 0) { val inputNote = InputNote(logicalY, wait, input) if (longNoteBMSID.isEmpty()) { notes.add(inputNote) } else { bmsLongInputItemSets[input].add( BMSLongInputItem( inputNote, bmsPosition, dataValue ) ) } } } '3', '4' -> { if (input != 0) { notes.add(VoidNote(logicalY, wait, input)) } } '5', '6' -> { if (input != 0) { val inputNote = InputNote(logicalY, wait, input) if (longNoteBMSID.equals( dataValue, ignoreCase = true ) ) { bmsLongInputItemSets[input].add( BMSLongInputItem( inputNote, bmsPosition, dataValue ) ) } else { bmsInputItemSets[input].add(BMSInputItem(inputNote, bmsPosition)) } } } 'D', 'E' -> { if (input != 0) { notes.add(TrapNote(logicalY, wait, input)) } } 'S' -> { if (noteVariety1 == 'C') { bmsPositionMultiplierMap[bmsPosition]?.let { waitMultiplierMap[wait] = it } } } } } meterCount += 2 } } } } for (i in inputCount downTo 1) { var lastBMSLongInputItem: BMSLongInputItem? = null for (bmsLongInputItem in bmsLongInputItemSets[i]) { if (bmsLongInputItem.bmsID.equals(longNoteBMSID, ignoreCase = true)) { if (lastBMSLongInputItem != null) { val lastInputNote = lastBMSLongInputItem.inputNote val lastWait = lastInputNote.wait val targetWait = bmsLongInputItem.inputNote.wait notes.removeAll(notes.filter { it.wait in lastWait..targetWait && it.input == i }.toSet()) notes.add( LongNote( lastInputNote.logicalY, lastWait, lastInputNote.input, targetWait - lastWait, valueComponent.levyingHeight + bmsPositionLogicalYMap[lastBMSLongInputItem.bmsPosition]!! - bmsLongInputItem.inputNote.logicalY ) ) lastBMSLongInputItem = null continue } } else { if (lastBMSLongInputItem != null) { notes.add(lastBMSLongInputItem.inputNote) } } lastBMSLongInputItem = bmsLongInputItem } if (lastBMSLongInputItem != null) { notes.add(lastBMSLongInputItem.inputNote) } } for (i in inputCount downTo 1) { var lastBMSInputItem: BMSInputItem? = null for (bmsInputItem in bmsInputItemSets[i]) { lastBMSInputItem = if (lastBMSInputItem != null) { val lastInputNote = lastBMSInputItem.inputNote val lastWait = lastInputNote.wait val targetWait = bmsInputItem.inputNote.wait notes.removeAll(notes.filter { it.wait in lastWait..targetWait && it.input == i }.toSet()) notes.add( LongNote( lastInputNote.logicalY, lastWait, lastInputNote.input, targetWait - lastWait, valueComponent.levyingHeight + bmsPositionLogicalYMap[lastBMSInputItem.bmsPosition]!! - bmsInputItem.inputNote.logicalY ) ) null } else { bmsInputItem } } if (lastBMSInputItem != null) { notes.add(lastBMSInputItem.inputNote) } } } private fun getInputMode(inputSet: Collection<Int>): Component.InputMode { if (is4K) { return Component.InputMode.INPUT_MODE_4 } if (is6K) { return Component.InputMode.INPUT_MODE_6 } if (inputSet.all { it == 0 }) { return Component.InputMode.INPUT_MODE_51 } val isMode71 = inputSet.contains(7) || inputSet.contains(8) val isMode102 = inputSet.contains(9) || inputSet.contains(10) || inputSet.contains(11) || inputSet.contains(12) || inputSet.contains( 13 ) val isMode142 = isMode71 && isMode102 || inputSet.contains(14) || inputSet.contains(15) if (isMode142) { return Component.InputMode.INPUT_MODE_142 } if (isMode102) { return Component.InputMode.INPUT_MODE_102 } if (isMode71) { return Component.InputMode.INPUT_MODE_71 } return Component.InputMode.INPUT_MODE_51 } private fun getBMSInput(noteVariety0: Char, noteVariety1: Char, inputMode: Component.InputMode): Int { when (noteVariety1) { '1', '3', '5', 'D' -> { when (inputMode) { Component.InputMode.INPUT_MODE_4 -> { when (noteVariety0) { '1', '2' -> { return noteVariety0.code - '0'.code } '4', '5' -> { return noteVariety0.code - '1'.code } } } Component.InputMode.INPUT_MODE_51, Component.InputMode.INPUT_MODE_102 -> { when (noteVariety0) { '1', '2', '3', '4', '5' -> { return noteVariety0.code - '0'.code + 1 } '6' -> { return 1 } } } Component.InputMode.INPUT_MODE_6 -> { when (noteVariety0) { '1', '2', '3' -> { return noteVariety0.code - '0'.code } '5' -> { return noteVariety0.code - '1'.code } '8', '9' -> { return noteVariety0.code - '3'.code } } } Component.InputMode.INPUT_MODE_71, Component.InputMode.INPUT_MODE_142 -> { when (noteVariety0) { '1', '2', '3', '4', '5' -> { return noteVariety0.code - '0'.code + 1 } '6' -> { return 1 } '8', '9' -> { return noteVariety0.code - '1'.code } } } Component.InputMode.INPUT_MODE_9 -> { when (noteVariety0) { '1', '2', '3', '4', '5' -> { return noteVariety0.code - '0'.code } } } else -> Unit } } '2', '4', '6', 'E' -> { when (inputMode) { Component.InputMode.INPUT_MODE_102 -> { when (noteVariety0) { '1', '2', '3', '4', '5' -> { return noteVariety0.code - '0'.code + 6 } '6' -> { return 12 } } } Component.InputMode.INPUT_MODE_142 -> { when (noteVariety0) { '1', '2', '3', '4', '5' -> { return noteVariety0.code - '0'.code + 8 } '6' -> { return 16 } '8', '9' -> { return noteVariety0.code - '2'.code + 8 } } } Component.InputMode.INPUT_MODE_9 -> { when (noteVariety0) { '2', '3', '4', '5' -> { return noteVariety0.code - '0'.code + 4 } } } else -> Unit } } } return 0 } companion object { fun isMainBMSData(mainContents: String): Boolean { return if (mainContents.length >= 5) { val mainContent3 = mainContents[3] val mainContent4 = mainContents[4] Character.isDigit(mainContents[0]) && Character.isDigit(mainContents[1]) && Character.isDigit( mainContents[2] ) && (Character.isDigit(mainContent3) || (mainContent3 == 'D' || mainContent3 == 'E') && Character.isDigit( mainContent4 ) || mainContent3 == 'S' && mainContent4 == 'C') } else { false } } } }