From b274b358e588d862e461573cf8012cdd3ae4bf40 Mon Sep 17 00:00:00 2001 From: cfig Date: Wed, 7 Jul 2021 21:21:13 +0800 Subject: [PATCH] Issue #55: fully support vendor boot V4 v3 -> v4 changelog: - vendor ramdisk table - bootconfig Fix bugs: - unpack: vrt postion calculation --- bbootimg/build.gradle.kts | 12 +- bbootimg/src/main/kotlin/bootimg/Common.kt | 29 +++- .../main/kotlin/bootimg/cpio/AndroidCpio.kt | 9 +- .../src/main/kotlin/bootimg/v3/VendorBoot.kt | 161 +++++++++++++----- .../src/main/kotlin/packable/IPackable.kt | 12 +- doc/layout.md | 25 +-- helper/build.gradle.kts | 13 +- helper/src/main/kotlin/cfig/io/Struct3.kt | 6 +- 8 files changed, 184 insertions(+), 83 deletions(-) diff --git a/bbootimg/build.gradle.kts b/bbootimg/build.gradle.kts index d2d7d42..6d24209 100644 --- a/bbootimg/build.gradle.kts +++ b/bbootimg/build.gradle.kts @@ -33,17 +33,17 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.slf4j:slf4j-simple:1.7.30") - implementation("org.slf4j:slf4j-api:1.7.30") - implementation("com.fasterxml.jackson.core:jackson-annotations:2.12.1") - implementation("com.fasterxml.jackson.core:jackson-databind:2.12.1") + implementation("org.slf4j:slf4j-simple:1.7.31") + implementation("org.slf4j:slf4j-api:1.7.31") + implementation("com.fasterxml.jackson.core:jackson-annotations:2.12.3") + implementation("com.fasterxml.jackson.core:jackson-databind:2.12.3") implementation("com.google.guava:guava:18.0") implementation("org.apache.commons:commons-exec:1.3") implementation("org.apache.commons:commons-compress:1.20") - implementation("org.tukaani:xz:1.8") + implementation("org.tukaani:xz:1.9") implementation("commons-codec:commons-codec:1.15") implementation("junit:junit:4.12") - implementation("org.bouncycastle:bcprov-jdk15on:1.68") + implementation("org.bouncycastle:bcprov-jdk15on:1.69") implementation("de.vandermeer:asciitable:0.3.2") implementation(project(":helper")) diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt index 03a00fb..fd61b9b 100644 --- a/bbootimg/src/main/kotlin/bootimg/Common.kt +++ b/bbootimg/src/main/kotlin/bootimg/Common.kt @@ -261,27 +261,25 @@ class Common { fun packRootfs(rootDir: String, ramdiskGz: String) { val root = File(rootDir).path log.info("Packing rootfs $root ...") - val fsConfig = File(ramdiskGz).parentFile.path + "/ramdisk_filelist.txt" when { ramdiskGz.endsWith(".gz") -> { val f = ramdiskGz.removeSuffix(".gz") - AndroidCpio().pack(root, f, fsConfig) + AndroidCpio().pack(root, f, "${f}_filelist.txt") FileInputStream(f).use { ZipHelper.minigzip(ramdiskGz, it) } - } ramdiskGz.endsWith(".lz4") -> { val f = ramdiskGz.removeSuffix(".lz4") - AndroidCpio().pack(root, f, fsConfig) + AndroidCpio().pack(root, f, "${f}_filelist.txt") FileInputStream(f).use { ZipHelper.lz4(ramdiskGz, it) } } ramdiskGz.endsWith(".lzma") -> { val f = ramdiskGz.removeSuffix(".lzma") - AndroidCpio().pack(root, f, fsConfig) + AndroidCpio().pack(root, f, "${f}_filelist.txt") FileInputStream(f).use { ZipHelper.lzma(ramdiskGz, it) } } ramdiskGz.endsWith(".xz") -> { val f = ramdiskGz.removeSuffix(".xz") - AndroidCpio().pack(root, f, fsConfig) + AndroidCpio().pack(root, f, "${f}_filelist.txt") FileInputStream(f).use { ZipHelper.xz(ramdiskGz, it) } } else -> { @@ -327,6 +325,23 @@ class Common { } } + fun writePaddedFiles(inBF: ByteBuffer, srcFiles: List, padding: Int) { + srcFiles.forEach { srcFile -> + FileInputStream(srcFile).use { iS -> + var byteRead: Int + val dataRead = ByteArray(128) + while (true) { + byteRead = iS.read(dataRead) + if (-1 == byteRead) { + break + } + inBF.put(dataRead, 0, byteRead) + } + } + } + padFile(inBF, padding) + } + private fun unpackRamdisk(ramdisk: String, root: String) { val rootFile = File(root).apply { if (exists()) { @@ -339,7 +354,7 @@ class Common { AndroidCpio.decompressCPIO( File(ramdisk).canonicalPath, rootFile.canonicalPath, - File(ramdisk).canonicalPath.removeSuffix(".img") + "_filelist.txt" + File(ramdisk).canonicalPath + "_filelist.txt" ) log.info(" ramdisk extracted : $ramdisk -> $rootFile") } diff --git a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt index df00f53..dea1b31 100644 --- a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt +++ b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt @@ -136,10 +136,10 @@ class AndroidCpio { } } entry.statMode = ftBits.toLong() or java.lang.Long.valueOf(matches[0].mode, 8) - log.debug("${entry.name} ~ " + matches.map { it.prefix }.reduce { acc, s -> "$acc, $s" } + log.warn("${entry.name} ~ " + matches.map { it.prefix }.reduce { acc, s -> "$acc, $s" } + ", stMode=" + java.lang.Long.toOctalString(entry.statMode)) } else { - log.debug("${entry.name} has NO fsconfig/prefix match") + log.warn("${entry.name} has NO fsconfig/prefix match") } } 1 -> { @@ -147,7 +147,7 @@ class AndroidCpio { entry.statMode = itemConfig[0].statMode } else -> { - throw IllegalArgumentException("${entry.name} as multiple exact-match fsConfig") + throw IllegalArgumentException("${entry.name} has multiple exact-match fsConfig") } } } @@ -157,11 +157,12 @@ class AndroidCpio { fsConfig.clear() propFile?.let { if (File(propFile).exists()) { + log.info("loading $propFile") File(propFile).readLines().forEach { line -> fsConfig.add(ObjectMapper().readValue(line, AndroidCpioEntry::class.java)) } } else { - log.warn("fsConfig file has been deleted, using fsConfig prefix matcher") + log.warn("fsConfig file $propFile has been deleted, using fsConfig prefix matcher") } } FileOutputStream(outFile).use { fos -> diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt index 9bafb8d..95597bb 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt @@ -15,24 +15,22 @@ package cfig.bootimg.v3 import cfig.Avb +import cfig.EnvironmentVerifier import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootimg.Signer import cfig.helper.Helper +import cfig.io.Struct3 +import cfig.io.Struct3.ByteArrayExt.Companion.toCString import cfig.packable.VBMetaParser import com.fasterxml.jackson.databind.ObjectMapper import de.vandermeer.asciitable.AsciiTable import org.apache.commons.exec.CommandLine import org.apache.commons.exec.DefaultExecutor import org.slf4j.LoggerFactory -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream +import java.io.* import java.nio.ByteBuffer import java.nio.ByteOrder import cfig.bootimg.Common as C -import cfig.EnvironmentVerifier -import cfig.io.Struct3 -import java.io.InputStream data class VendorBoot( var info: MiscInfo = MiscInfo(), @@ -82,26 +80,52 @@ data class VendorBoot( class Vrt( var size: Int = 0, + var eachEntrySize: Int = 0, var position: Long = 0, var ramdidks: MutableList = mutableListOf() - ) + ) { + fun update(): Vrt { + var totalSz = 0 + this.ramdidks.forEachIndexed { _, vrtEntry -> + vrtEntry.offset = totalSz + vrtEntry.size = File(vrtEntry.file).length().toInt() + totalSz += vrtEntry.size + } + return this + } + + fun encode(inPageSize: Int): ByteArray { + val bf = ByteBuffer.allocate(8192) + this.ramdidks.forEach { + bf.put(it.encode()) + } + val realSize = bf.position() + val ret = ByteArray(Helper.round_to_multiple(realSize, inPageSize)) + bf.array().copyInto(ret, 0, 0, realSize) + return ret + } + } class VrtEntry( var size: Int = 0, var offset: Int = 0, var type: VrtType = VrtType.NONE, var name: String = "", //32s - var boardId: Array = arrayOf(), //16I + var boardId: ByteArray = byteArrayOf(), //16I (aka. 64 bytes) + var boardIdStr: String = "", var file: String = "" ) { companion object { private val log = LoggerFactory.getLogger(VrtEntry::class.java) const val VENDOR_RAMDISK_NAME_SIZE = 32 const val VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16 - const val FORMAT_STRING = "3I${VENDOR_RAMDISK_NAME_SIZE}s${VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I" + + //const val FORMAT_STRING = "3I${VENDOR_RAMDISK_NAME_SIZE}s${VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I" + const val FORMAT_STRING = "3I${VENDOR_RAMDISK_NAME_SIZE}s${VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE * 4}b" + const val SIZE = 108 init { - log.info(Struct3(FORMAT_STRING).calcSize().toString()) + assert(Struct3(FORMAT_STRING).calcSize() == SIZE) } } @@ -110,25 +134,31 @@ data class VendorBoot( return } val info = Struct3(FORMAT_STRING).unpack(iS) - assert((3 + 1 + VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE) == info.size) + assert((3 + 1 + 1) == info.size) this.size = (info[0] as UInt).toInt() this.offset = (info[1] as UInt).toInt() this.type = VrtType.fromInt((info[2] as UInt).toInt()) this.name = info[3] as String + this.boardId = info[4] as ByteArray + this.boardIdStr = boardId.toCString() this.file = dumpFile } - override fun toString(): String { - return "VrtEntry(ramdiskSize=$size, ramdiskOffset=$offset, ramdiskType=$type, ramdiskName='$name', boardId=${boardId.contentToString()})" + fun encode(): ByteArray { + return Struct3(FORMAT_STRING).pack(this.size, this.offset, this.type.ordinal, this.name, this.boardId) } + override fun toString(): String { + return "VrtEntry(size=$size, offset=$offset, type=$type, name='$name', boardIdStr='$boardIdStr', file='$file')" + } } companion object { private val log = LoggerFactory.getLogger(VendorBoot::class.java) + private val workDir = Helper.prop("workDir") + private val VENDOR_RAMDISK_TABLE_ENTRY_V4_SIZE = 108 fun parse(fileName: String): VendorBoot { val ret = VendorBoot() - val workDir = Helper.prop("workDir") FileInputStream(fileName).use { fis -> val header = VendorBootHeader(fis) ret.info.output = File(fileName).name @@ -155,8 +185,9 @@ data class VendorBoot( //vrt if (header.vrtSize > 0) { ret.ramdisk_table.size = header.vrtSize + ret.ramdisk_table.eachEntrySize = header.vrtEntrySize ret.ramdisk_table.position = - ret.dtb.position + Helper.round_to_multiple(ret.ramdisk_table.size, header.pageSize) + ret.dtb.position + Helper.round_to_multiple(ret.dtb.size, header.pageSize) FileInputStream(ret.info.output).use { it.skip(ret.ramdisk_table.position) for (item in 0 until header.vrtEntryNum) { @@ -181,19 +212,30 @@ data class VendorBoot( } fun pack(): VendorBoot { - val workDir = Helper.prop("workDir") - if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) { - //do nothing if we have ramdisk.img.gz but no /root - log.warn("Use prebuilt ramdisk file: ${this.ramdisk.file}") - } else { - File(this.ramdisk.file).deleleIfExists() - File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists() - //Fixed: remove cpio in C/C++ - //C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) - //enable advance JAVA cpio - C.packRootfs("$workDir/root", this.ramdisk.file) + when (this.info.headerVersion) { + 3 -> { + if (File(workDir + this.ramdisk.file).exists() && !File(workDir + "root").exists()) { + //do nothing if we have ramdisk.img.gz but no /root + log.warn("Use prebuilt ramdisk file: ${this.ramdisk.file}") + } else { + File(this.ramdisk.file).deleleIfExists() + File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists() + //Fixed: remove cpio in C/C++ + //C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) + //enable advance JAVA cpio + C.packRootfs("$workDir/root", this.ramdisk.file) + } + this.ramdisk.size = File(this.ramdisk.file).length().toInt() + } + else -> { + this.ramdisk_table.ramdidks.forEachIndexed { index, it -> + File(it.file).deleleIfExists() + log.info(workDir + "root.${index + 1} -> " + it.file) + C.packRootfs(workDir + "root.${index + 1}", it.file) + } + this.ramdisk.size = this.ramdisk_table.ramdidks.sumOf { File(it.file).length() }.toInt() + } } - this.ramdisk.size = File(this.ramdisk.file).length().toInt() this.dtb.size = File(this.dtb.file).length().toInt() //header FileOutputStream(this.info.output + ".clear", false).use { fos -> @@ -203,12 +245,30 @@ data class VendorBoot( } //data log.info("Writing data ...") - //assume total SIZE is smaller than 64MB - val bf = ByteBuffer.allocate(1024 * 1024 * 128).let { - it.order(ByteOrder.LITTLE_ENDIAN) - C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize) - C.writePaddedFile(it, this.dtb.file, this.info.pageSize) - it + val bf = when (this.info.headerVersion) { + //assume total SIZE is smaller than 128MB + 3 -> { + ByteBuffer.allocate(1024 * 1024 * 128).let { + it.order(ByteOrder.LITTLE_ENDIAN) + //1. vendor ramdisks and dtb + C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize) + C.writePaddedFile(it, this.dtb.file, this.info.pageSize) + it + } + } + else -> { + ByteBuffer.allocate(1024 * 1024 * 128).let { + it.order(ByteOrder.LITTLE_ENDIAN) + //1. vendor ramdisks and dtb + C.writePaddedFiles(it, this.ramdisk_table.ramdidks.map { rd -> rd.file }, this.info.pageSize) + C.writePaddedFile(it, this.dtb.file, this.info.pageSize) + //2. vrt + it.put(this.ramdisk_table.update().encode(this.info.pageSize)) + //3. bootconfig + C.writePaddedFile(it, this.bootconfig.file, this.info.pageSize) + it + } + } } //write FileOutputStream("${this.info.output}.clear", true).use { fos -> @@ -243,12 +303,15 @@ data class VendorBoot( product = info.product, headerSize = info.headerSize, dtbSize = dtb.size, - dtbLoadAddr = dtb.loadAddr + dtbLoadAddr = dtb.loadAddr, + vrtSize = ramdisk_table.eachEntrySize * ramdisk_table.ramdidks.size, + vrtEntryNum = ramdisk_table.ramdidks.size, + vrtEntrySize = ramdisk_table.eachEntrySize, + bootconfigSize = File(bootconfig.file).length().toInt() ) } fun extractImages(): VendorBoot { - val workDir = Helper.prop("workDir") //header ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) //ramdisk @@ -257,8 +320,6 @@ data class VendorBoot( Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file), "${workDir}root") //@formatter:on this.ramdisk.file = this.ramdisk.file + ".$fmt" - //dump info again - ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) //dtb C.dumpDtb(Helper.Slice(info.output, dtb.position.toInt(), dtb.size, dtb.file)) //vrt @@ -266,6 +327,7 @@ data class VendorBoot( log.info("dumping vendor ramdisk ${index + 1}/${this.ramdisk_table.ramdidks.size} ...") val s = Helper.Slice(ramdisk.file, it.offset, it.size, it.file) C.dumpRamdisk(s, workDir + "root.${index + 1}") + it.file = it.file + ".$fmt" } //bootconfig if (bootconfig.size > 0) { @@ -273,6 +335,8 @@ data class VendorBoot( Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) } } + //dump info again + ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) return this } @@ -290,7 +354,6 @@ data class VendorBoot( } fun printSummary(): VendorBoot { - val workDir = Helper.prop("workDir") val tableHeader = AsciiTable().apply { addRule() addRow("What", "Where") @@ -303,7 +366,7 @@ data class VendorBoot( it.addRow("ramdisk", this.ramdisk.file) if (this.ramdisk_table.size > 0) { this.ramdisk_table.ramdidks.forEachIndexed { index, entry -> - it.addRow("-- ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file) + it.addRow("-- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file) it.addRow("------- extracted rootfs", "${workDir}root.${index + 1}") } } else { @@ -340,7 +403,25 @@ data class VendorBoot( private fun toCommandLine(): CommandLine { val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else "" return CommandLine.parse(cmdPrefix + Helper.prop("mkbootimg")).apply { - addArgument("--vendor_ramdisk").addArgument(ramdisk.file) + when (info.headerVersion) { + 3 -> { + addArgument("--vendor_ramdisk").addArgument(ramdisk.file) + } + else -> { + ramdisk_table.ramdidks.forEachIndexed { index, it -> + log.info("dumping vendor ramdisk ${index + 1}/${ramdisk_table.ramdidks.size} ...") + addArgument("--ramdisk_type").addArgument(it.type.toString()) + Struct3("${VrtEntry.VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}i").unpack(ByteArrayInputStream(it.boardId)) + .forEachIndexed { boardIdIndex, boardIdValue -> + addArgument("--board_id$boardIdIndex") + addArgument("0x" + Integer.toHexString((boardIdValue as Int))) + } + addArgument("--ramdisk_name").addArgument(it.name, true) + addArgument("--vendor_ramdisk_fragment").addArgument(it.file) + } + addArgument("--vendor_bootconfig").addArgument(bootconfig.file) + } + } addArgument("--dtb").addArgument(dtb.file) addArgument("--vendor_cmdline").addArgument(info.cmdline, false) addArgument("--header_version").addArgument(info.headerVersion.toString()) diff --git a/bbootimg/src/main/kotlin/packable/IPackable.kt b/bbootimg/src/main/kotlin/packable/IPackable.kt index 912e269..a9148dd 100644 --- a/bbootimg/src/main/kotlin/packable/IPackable.kt +++ b/bbootimg/src/main/kotlin/packable/IPackable.kt @@ -40,9 +40,9 @@ interface IPackable { "" } log.info("slot suffix = $slotSuffix") - "adb push $fileName /cache/file.to.burn".check_call() - "adb shell dd if=/cache/file.to.burn of=/dev/block/by-name/$deviceName$slotSuffix".check_call() - "adb shell rm /cache/file.to.burn".check_call() + "adb push $fileName /mnt/file.to.burn".check_call() + "adb shell dd if=/mnt/file.to.burn of=/dev/block/by-name/$deviceName$slotSuffix".check_call() + "adb shell rm /mnt/file.to.burn".check_call() } fun pull(fileName: String = "dtbo.img", deviceName: String = "dtbo") { @@ -55,9 +55,9 @@ interface IPackable { "" } log.info("slot suffix = $slotSuffix") - "adb shell dd if=/dev/block/by-name/$deviceName$slotSuffix of=/cache/file.to.pull".check_call() - "adb pull /cache/file.to.pull $fileName".check_call() - "adb shell rm /cache/file.to.pull".check_call() + "adb shell dd if=/dev/block/by-name/$deviceName$slotSuffix of=/mnt/file.to.pull".check_call() + "adb pull /mnt/file.to.pull $fileName".check_call() + "adb shell rm /mnt/file.to.pull".check_call() } // invoked solely by reflection diff --git a/doc/layout.md b/doc/layout.md index bd2e035..f72e506 100644 --- a/doc/layout.md +++ b/doc/layout.md @@ -159,7 +159,8 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi |--------------------------------+--------------------------| --> 2080 | | 16 | |--------------------------------+--------------------------| --> 2096 - |
| 4 (value=2112) | + |
| 4 (v3: value=2112) | + | | 4 (v4: value=2128) | |--------------------------------+--------------------------| --> 2100 | | 4 | |--------------------------------+--------------------------| --> 2104 @@ -182,23 +183,25 @@ Value at 0x28 is one of {0x00,0x01,0x02,0x03,0x04}, this filed should be read fi +------------------+-------------+--------------------------+ --> pagesize | | ramdisk 1 | | - | +-------------+ | + | |-------------+ | | | ramdisk 2 | | - | +-------------+ padded len | + | |-------------+ padded len | | | ramdisk n | | - | +-------------+ | --> pagesize + vendor_ramdisk_total_size + | |-------------+ | --> pagesize + vendor_ramdisk_total_size | | padding | | - +--------------------------------+--------------------------+ --> pagesize + vendor_ramdisk_total_size + padding - | | padded len | - +--------------------------------+--------------------------+ --> dtb offset + dtb size + padding + +------------------+-------------+--------------------------+ --> pagesize + vendor_ramdisk_total_size + padding + | | dtb | | + | |-------------+ padded len | + | | padding | | + +------------------+-------------+--------------------------+ --> dtb offset + dtb size + padding | | entry 1 | | - | table> +-------------+ | + | table> |-------------+ | | | entry 2 | padded len | - | +-------------+ | + | |-------------+ | | | entry n | | - | (v4) +-------------+ | + | (v4) |-------------+ | | | padding | | - +-----------------------------------------------------------+ --> vrt offset + vrt size + padding + +------------------+----------------------------------------+ --> vrt offset + vrt size + padding | (v4) | padded len | +--------------------------------+--------------------------+ diff --git a/helper/build.gradle.kts b/helper/build.gradle.kts index bf793cc..3d70724 100644 --- a/helper/build.gradle.kts +++ b/helper/build.gradle.kts @@ -33,17 +33,18 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("com.google.guava:guava:18.0") - implementation("org.slf4j:slf4j-api:1.7.30") - implementation("org.slf4j:slf4j-simple:1.7.30") + implementation("org.slf4j:slf4j-api:1.7.31") + implementation("org.slf4j:slf4j-simple:1.7.31") implementation("org.apache.commons:commons-exec:1.3") - implementation("org.bouncycastle:bcprov-jdk15on:1.68") + implementation("org.bouncycastle:bcprov-jdk15on:1.69") implementation("org.apache.commons:commons-compress:1.20") - implementation("org.tukaani:xz:1.8") + implementation("org.tukaani:xz:1.9") + implementation("com.github.freva:ascii-table:1.2.0") testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit") - testImplementation("com.fasterxml.jackson.core:jackson-annotations:2.12.1") - testImplementation("com.fasterxml.jackson.core:jackson-databind:2.12.1") + testImplementation("com.fasterxml.jackson.core:jackson-annotations:2.12.3") + testImplementation("com.fasterxml.jackson.core:jackson-databind:2.12.3") } tasks.withType().all { diff --git a/helper/src/main/kotlin/cfig/io/Struct3.kt b/helper/src/main/kotlin/cfig/io/Struct3.kt index a8f4add..24a1ca0 100644 --- a/helper/src/main/kotlin/cfig/io/Struct3.kt +++ b/helper/src/main/kotlin/cfig/io/Struct3.kt @@ -167,9 +167,9 @@ class Struct3 { if (Char == typeName) { assert(arg is Char) { "[$arg](${arg!!::class.java}) is NOT Char" } if ((arg as Char) !in '\u0000'..'\u00ff') { - throw IllegalArgumentException("arg[${arg.toInt()}] exceeds 8-bit bound") + throw IllegalArgumentException("arg[${arg.code}] exceeds 8-bit bound") } - bf.put(arg.toByte()) + bf.put(arg.code.toByte()) continue } @@ -377,7 +377,7 @@ class Struct3 { fun InputStream.getChar(): Char { val data = ByteArray(Byte.SIZE_BYTES) assert(Byte.SIZE_BYTES == this.read(data)) - return data[0].toChar() + return data[0].toInt().toChar() } fun InputStream.getShort(inByteOrder: ByteOrder): Short {