mirror of
https://github.com/skogaby/butterfly.git
synced 2026-03-21 18:04:24 -05:00
Use a new kbinxml implementation that doesn't depend on xom
This commit is contained in:
parent
5e5b02b3a4
commit
cd502a1f6c
|
|
@ -1,11 +1,11 @@
|
|||
package com.buttongames.butterflycore.xml.kbinxml
|
||||
|
||||
import com.buttongames.butterflycore.xml.kbinxml.ByteConv.E.longToBytes
|
||||
|
||||
import java.lang.Double
|
||||
import java.lang.Float
|
||||
import java.math.BigInteger
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import kotlin.experimental.and
|
||||
|
||||
internal class ByteConv {
|
||||
|
|
@ -67,8 +67,8 @@ internal class ByteConv {
|
|||
|
||||
fun stringToIp(string: String) = string.split(".").map { it.toUByte().toByte() }.toByteArray()
|
||||
|
||||
fun floatToString(array: ByteArray) = String.format("%.6f", ByteBuffer.wrap(array).float)
|
||||
fun doubleToString(array: ByteArray) = String.format("%.6f", ByteBuffer.wrap(array).double)
|
||||
fun floatToString(array: ByteArray) = String.format(Locale.US, "%.6f", ByteBuffer.wrap(array).float)
|
||||
fun doubleToString(array: ByteArray) = String.format(Locale.US, "%.6f", ByteBuffer.wrap(array).double)
|
||||
|
||||
fun stringToFloat(string: String) = Float.floatToRawIntBits(Float.parseFloat(string)).toByteArray()
|
||||
fun stringToDouble(string: String) = Double.doubleToRawLongBits(Double.parseDouble(string)).toByteArray()
|
||||
|
|
@ -98,4 +98,6 @@ internal fun String.toLongBytes() = BigInteger(this).toLong().toByteArray()
|
|||
internal inline fun String.toUByteA() = this.toByteA()
|
||||
internal inline fun String.toUShortBytes() = this.toShortBytes()
|
||||
internal inline fun String.toUIntBytes() = this.toIntBytes()
|
||||
internal inline fun String.toULongBytes() = this.toLongBytes()
|
||||
internal inline fun String.toULongBytes() = this.toLongBytes()
|
||||
|
||||
internal inline fun Byte.posInt() = this.toUByte().toInt()
|
||||
|
|
@ -23,19 +23,29 @@ internal enum class ControlTypes {
|
|||
}
|
||||
|
||||
internal val ControlTypeMap = mapOf(
|
||||
1 to ControlTypes.NodeStart,
|
||||
46 to ControlTypes.Attribute,
|
||||
190 to ControlTypes.NodeEnd,
|
||||
191 to ControlTypes.FileEnd
|
||||
//254 to ControlTypes.NodeEnd,
|
||||
//255 to ControlTypes.FileEnd
|
||||
1 to ControlTypes.NodeStart,
|
||||
46 to ControlTypes.Attribute,
|
||||
190 to ControlTypes.NodeEnd,
|
||||
191 to ControlTypes.FileEnd
|
||||
//254 to ControlTypes.NodeEnd,
|
||||
//255 to ControlTypes.FileEnd
|
||||
)
|
||||
|
||||
internal class KbinConverter(val fromString: (String) -> ByteArray, val toString: (ByteArray) -> String)
|
||||
|
||||
internal class KbinType(var names: List<String>, val size: Int, private val handler: KbinConverter, val count: Int = 1) {
|
||||
internal class KbinType(
|
||||
var names: List<String>,
|
||||
val size: Int,
|
||||
private val handler: KbinConverter,
|
||||
val count: Int = 1
|
||||
) {
|
||||
|
||||
constructor(name: String, size: Int, handler: KbinConverter, count: Int = 1) : this(mutableListOf(name), size, handler, count)
|
||||
constructor(name: String, size: Int, handler: KbinConverter, count: Int = 1) : this(
|
||||
mutableListOf(name),
|
||||
size,
|
||||
handler,
|
||||
count
|
||||
)
|
||||
|
||||
val name: String
|
||||
get() = names[0]
|
||||
|
|
@ -60,10 +70,10 @@ internal operator fun Int.times(t: KbinType): KbinType {
|
|||
val newNames = t.names.map { this.toString() + it }
|
||||
val newSize = t.size * this
|
||||
fun newToString(input: ByteArray) =
|
||||
input.asIterable().chunked(t.size).joinToString(" ") { t.toString(it.toByteArray()) }
|
||||
input.asIterable().chunked(t.size).joinToString(" ") { t.toString(it.toByteArray()) }
|
||||
|
||||
fun newFromString(input: String) =
|
||||
input.split(" ").flatMap { t.fromString(it).asIterable() }.toByteArray()
|
||||
input.split(" ").flatMap { t.fromString(it).asIterable() }.toByteArray()
|
||||
|
||||
return KbinType(newNames, newSize, KbinConverter(::newFromString, ::newToString), this * t.count)
|
||||
}
|
||||
|
|
@ -79,7 +89,7 @@ internal class Types {
|
|||
val s64 = KbinType("s64", 8, Converters.s64)
|
||||
val u64 = KbinType("u64", 8, Converters.u64)
|
||||
|
||||
val bin = KbinType(listOf("bin", "binary"), 1, Converters.stub)
|
||||
val binStub = KbinType(listOf("bin", "binary"), 1, Converters.stub)
|
||||
val strStub = KbinType(listOf("str", "string"), 1, Converters.stub)
|
||||
|
||||
val ip4 = KbinType("ip4", 4, Converters.ip4)
|
||||
|
|
@ -92,60 +102,61 @@ internal class Types {
|
|||
|
||||
internal val kbinTypeMap = with(Types) {
|
||||
mapOf(
|
||||
2 to s8,
|
||||
3 to u8,
|
||||
4 to s16,
|
||||
5 to u16,
|
||||
6 to s32,
|
||||
7 to u32,
|
||||
8 to s64,
|
||||
9 to u64,
|
||||
10 to bin,
|
||||
11 to strStub,
|
||||
12 to ip4,
|
||||
13 to time,
|
||||
14 to float,
|
||||
15 to double,
|
||||
16 to 2 * s8,
|
||||
17 to 2 * u8,
|
||||
18 to 2 * s16,
|
||||
19 to 2 * u16,
|
||||
20 to 2 * s32,
|
||||
21 to 2 * u32,
|
||||
22 to (2 * s64).alias("vs64"),
|
||||
23 to (2 * u64).alias("vu64"),
|
||||
24 to (2 * float).rename("2f"),
|
||||
25 to (2 * double).rename("2d").alias("vd"),
|
||||
26 to 3 * s8,
|
||||
27 to 3 * u8,
|
||||
28 to 3 * s16,
|
||||
29 to 3 * u16,
|
||||
30 to 3 * s32,
|
||||
31 to 3 * u32,
|
||||
32 to 3 * s64,
|
||||
33 to 3 * u64,
|
||||
34 to (3 * float).rename("3f"),
|
||||
35 to (3 * double).rename("3d"),
|
||||
36 to 4 * s8,
|
||||
37 to 4 * u8,
|
||||
38 to 4 * s16,
|
||||
39 to 4 * u16,
|
||||
40 to (4 * s32).alias("vs32"),
|
||||
41 to (4 * u32).alias("vu32"),
|
||||
42 to 4 * s64,
|
||||
43 to 4 * u64,
|
||||
44 to (4 * float).rename("4f").alias("vf"),
|
||||
45 to (4 * double).rename("4d"),
|
||||
48 to (16 * s8).rename("vs8"),
|
||||
49 to (16 * u8).rename("vu8"),
|
||||
50 to (8 * s16).rename("vs16"),
|
||||
51 to (8 * u16).rename("vu16"),
|
||||
52 to bool,
|
||||
53 to (2 * bool).rename("2b"),
|
||||
54 to (3 * bool).rename("3b"),
|
||||
55 to (4 * bool).rename("4b"),
|
||||
56 to (16 * bool).rename("vb")
|
||||
2 to s8,
|
||||
3 to u8,
|
||||
4 to s16,
|
||||
5 to u16,
|
||||
6 to s32,
|
||||
7 to u32,
|
||||
8 to s64,
|
||||
9 to u64,
|
||||
10 to binStub,
|
||||
11 to strStub,
|
||||
12 to ip4,
|
||||
13 to time,
|
||||
14 to float,
|
||||
15 to double,
|
||||
16 to 2 * s8,
|
||||
17 to 2 * u8,
|
||||
18 to 2 * s16,
|
||||
19 to 2 * u16,
|
||||
20 to 2 * s32,
|
||||
21 to 2 * u32,
|
||||
22 to (2 * s64).alias("vs64"),
|
||||
23 to (2 * u64).alias("vu64"),
|
||||
24 to (2 * float).rename("2f"),
|
||||
25 to (2 * double).rename("2d").alias("vd"),
|
||||
26 to 3 * s8,
|
||||
27 to 3 * u8,
|
||||
28 to 3 * s16,
|
||||
29 to 3 * u16,
|
||||
30 to 3 * s32,
|
||||
31 to 3 * u32,
|
||||
32 to 3 * s64,
|
||||
33 to 3 * u64,
|
||||
34 to (3 * float).rename("3f"),
|
||||
35 to (3 * double).rename("3d"),
|
||||
36 to 4 * s8,
|
||||
37 to 4 * u8,
|
||||
38 to 4 * s16,
|
||||
39 to 4 * u16,
|
||||
40 to (4 * s32).alias("vs32"),
|
||||
41 to (4 * u32).alias("vu32"),
|
||||
42 to 4 * s64,
|
||||
43 to 4 * u64,
|
||||
44 to (4 * float).rename("4f").alias("vf"),
|
||||
45 to (4 * double).rename("4d"),
|
||||
48 to (16 * s8).rename("vs8"),
|
||||
49 to (16 * u8).rename("vu8"),
|
||||
50 to (8 * s16).rename("vs16"),
|
||||
51 to (8 * u16).rename("vu16"),
|
||||
52 to bool,
|
||||
53 to (2 * bool).rename("2b"),
|
||||
54 to (3 * bool).rename("3b"),
|
||||
55 to (4 * bool).rename("4b"),
|
||||
56 to (16 * bool).rename("vb")
|
||||
)
|
||||
}
|
||||
|
||||
internal var reverseKbinTypeMap: Map<String, Int> = kbinTypeMap.entries.associateBy({ it.value.name }) { it.key }
|
||||
// internal var reverseKbinTypeMap: Map<String, Int> = kbinTypeMap.entries.associateBy({ it.value.name }) { it.key }
|
||||
internal var reverseKbinTypeMap = mutableMapOf<String, Int>()
|
||||
|
|
@ -35,8 +35,8 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
|
|||
}
|
||||
else -> throw KbinException("Invalid read of $num bytes")
|
||||
}
|
||||
realign(num)
|
||||
// println("Read bytes $debug")
|
||||
realign(num)
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -67,15 +67,18 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
|
|||
if (pos16Follows()) {
|
||||
pos16 = pos32
|
||||
}
|
||||
//println("Index 4: $pos32")
|
||||
//println("Index 2: $pos16")
|
||||
//println("Index 1: $pos8")
|
||||
/*println("Index 4: $pos32")
|
||||
println("Index 2: $pos16")
|
||||
println("Index 1: $pos8")
|
||||
println()*/
|
||||
}
|
||||
|
||||
fun realign4Byte(num: Int) {
|
||||
realign(num + (if (num % 4 != 0) {
|
||||
4 - (num % 4)
|
||||
} else 0))
|
||||
realign(
|
||||
num + (if (num % 4 != 0) {
|
||||
4 - (num % 4)
|
||||
} else 0)
|
||||
)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
|
|
@ -100,15 +103,14 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
|
|||
fun readFrom4Byte(num: Int): ByteArray {
|
||||
if (num == 0) return byteArrayOf()
|
||||
val read = data.slice(pos32 until pos32 + num)
|
||||
// println("Read bytes $pos32 - ${pos32 + num}")
|
||||
realign4Byte(num)
|
||||
return read.toByteArray()
|
||||
}
|
||||
|
||||
fun readString(length: Int): String {
|
||||
var readBytes = readFrom4Byte(length)
|
||||
if (readBytes.last() == 0x00.toByte()) { // null bytes are scary
|
||||
readBytes = readBytes.sliceArray(0 until readBytes.lastIndex)
|
||||
}
|
||||
readBytes = readBytes.sliceArray(0 until readBytes.lastIndex)
|
||||
return readBytes.toString(encoding)
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +148,7 @@ internal class KbinDataBuffer(bytes: ByteArray, val encoding: Charset) {
|
|||
}
|
||||
|
||||
fun writeString(string: String) {
|
||||
var bytes = string.toByteArray(encoding) + 0 // null byte
|
||||
val bytes = string.toByteArray(encoding) + 0 // null byte
|
||||
writeU32(bytes.size.toUInt())
|
||||
writeTo4Byte(bytes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
package com.buttongames.butterflycore.xml.kbinxml
|
||||
|
||||
import nu.xom.Document
|
||||
import nu.xom.Element
|
||||
import com.buttongames.butterflycore.xml.kbinxml.ControlTypes.*
|
||||
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.binStub
|
||||
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.strStub
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import kotlin.experimental.inv
|
||||
|
||||
|
||||
internal class KbinReader(data: ByteArray) {
|
||||
|
||||
init {
|
||||
|
|
@ -48,6 +52,11 @@ internal class KbinReader(data: ByteArray) {
|
|||
val nodeStack = ArrayDeque<Element>()
|
||||
var end = false
|
||||
|
||||
val dbf = DocumentBuilderFactory.newInstance()
|
||||
val builder = dbf.newDocumentBuilder()
|
||||
val doc = builder.newDocument()
|
||||
doc.xmlStandalone = true
|
||||
|
||||
while (!end) {
|
||||
val current = nodeBuffer.readU8().toInt()
|
||||
val actual = current and (1 shl 6).inv()
|
||||
|
|
@ -59,7 +68,7 @@ internal class KbinReader(data: ByteArray) {
|
|||
NodeStart -> {
|
||||
val name = nodeBuffer.readString()
|
||||
|
||||
val newNode = Element(name)
|
||||
val newNode = doc.createElement(name)
|
||||
if (currentNode != null) {
|
||||
currentNode.appendChild(newNode)
|
||||
nodeStack.push(currentNode)
|
||||
|
|
@ -81,7 +90,7 @@ internal class KbinReader(data: ByteArray) {
|
|||
|
||||
//println("Got attribute $name with value $value")
|
||||
|
||||
currentNode!!.addAttribute(name, value)
|
||||
currentNode!!.setAttribute(name, value)
|
||||
}
|
||||
FileEnd -> {
|
||||
if (nodeStack.size == 0) {
|
||||
|
|
@ -95,24 +104,25 @@ internal class KbinReader(data: ByteArray) {
|
|||
val nodeName = nodeBuffer.readString()
|
||||
val arraySize = if (isArray) dataBuffer.readU32().toInt() else valueType.size
|
||||
|
||||
nodeStack.push(currentNode)
|
||||
val newNode = Element(nodeName)
|
||||
newNode.addAttribute("__type", valueName)
|
||||
currentNode!!.appendChild(newNode)
|
||||
nodeStack.push(currentNode!!)
|
||||
val newNode = doc.createElement(nodeName)
|
||||
newNode.setAttribute("__type", valueName)
|
||||
currentNode.appendChild(newNode)
|
||||
currentNode = newNode
|
||||
|
||||
val numElements = arraySize / valueType.size
|
||||
when (valueName) {
|
||||
"bin" -> {
|
||||
currentNode.addAttribute("__size", arraySize.toString())
|
||||
|
||||
when (valueType) {
|
||||
binStub -> {
|
||||
currentNode.setAttribute("__size", arraySize.toString())
|
||||
val bytes = dataBuffer.readFrom4Byte(arraySize)
|
||||
currentNode.text = ByteConv.binToString(bytes)
|
||||
}
|
||||
"str" -> {
|
||||
strStub -> {
|
||||
currentNode.text = dataBuffer.readString(arraySize)
|
||||
}
|
||||
else -> {
|
||||
if (isArray) currentNode.addAttribute("__count", numElements.toString())
|
||||
if (isArray) currentNode.setAttribute("__count", numElements.toString())
|
||||
|
||||
val byteList = dataBuffer.readBytes(arraySize)
|
||||
val stringList = mutableListOf<String>()
|
||||
|
|
@ -128,6 +138,7 @@ internal class KbinReader(data: ByteArray) {
|
|||
throw KbinException("Unsupported node type with ID $actual")
|
||||
}
|
||||
}
|
||||
return Document(currentNode)
|
||||
doc.appendChild(currentNode)
|
||||
return doc
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,38 @@
|
|||
package com.buttongames.butterflycore.xml.kbinxml
|
||||
|
||||
import nu.xom.Document
|
||||
import nu.xom.Element
|
||||
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.binStub
|
||||
import com.buttongames.butterflycore.xml.kbinxml.Types.Companion.strStub
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.experimental.inv
|
||||
|
||||
internal class KbinWriter(val xml: Document, val encoding: String = "SHIFT_JIS", val compressed: Boolean = true) {
|
||||
|
||||
internal class KbinWriter(val xml: Document, val encoding: String = "UTF-8", val compressed: Boolean = true) {
|
||||
private val header = ByteArray(4)
|
||||
|
||||
val charset = Charset.forName(encoding)
|
||||
|
||||
fun getKbin(): ByteArray {
|
||||
if (reverseKbinTypeMap.isEmpty()) {
|
||||
for (entry in kbinTypeMap.entries) {
|
||||
for (name in entry.value.names) {
|
||||
reverseKbinTypeMap[name] = entry.key
|
||||
}
|
||||
}
|
||||
}
|
||||
val dataBuffer = KbinDataBuffer(charset)
|
||||
val nodeBuffer = KbinNodeBuffer(compressed, charset)
|
||||
|
||||
header[0] = 0xa0u.toByte()
|
||||
header[0] = 0xA0u.toByte()
|
||||
header[1] = (if (compressed) 0x42u else 0x45u).toByte()
|
||||
header[2] = (Constants.encodingsReverse[encoding]!! shl 5).toByte()
|
||||
header[3] = header[2].inv()
|
||||
|
||||
nodeRecurse(xml.rootElement, dataBuffer, nodeBuffer)
|
||||
nodeRecurse(xml.documentElement, dataBuffer, nodeBuffer)
|
||||
|
||||
nodeBuffer.writeU8(255u)
|
||||
nodeBuffer.pad()
|
||||
|
|
@ -36,13 +48,27 @@ internal class KbinWriter(val xml: Document, val encoding: String = "SHIFT_JIS",
|
|||
}
|
||||
|
||||
private fun nodeRecurse(e: Element, dataBuffer: KbinDataBuffer, nodeBuffer: KbinNodeBuffer) {
|
||||
val typeName = e.getAttribute("__type")?.value
|
||||
var typeName = e.getAttribute("__type")
|
||||
val text = e.text
|
||||
if (typeName == "" && text.trim() != "") {
|
||||
typeName = "str"
|
||||
}
|
||||
val typeId = reverseKbinTypeMap[typeName]
|
||||
if (typeName != null && typeId == null) throw KbinException("Type $typeName is not supported")
|
||||
if (typeName != "" && typeId == null)
|
||||
throw KbinException("Type $typeName is not supported")
|
||||
val type = kbinTypeMap[typeId]
|
||||
val count = (e.getAttribute("__count")?.value ?: e.getAttribute("__size")?.value)?.toInt()
|
||||
|
||||
val isArray = (count != null) && type?.name !in listOf("bin", "str")
|
||||
var count = run {
|
||||
val count_attr = e.getAttribute("__count")
|
||||
val size_attr = e.getAttribute("__size")
|
||||
when {
|
||||
count_attr != "" -> count_attr
|
||||
size_attr != "" -> size_attr
|
||||
else -> null
|
||||
}?.toInt()
|
||||
}
|
||||
|
||||
val isArray = (count != null) && type?.name !in strStub.names + binStub.names
|
||||
|
||||
if (typeId == null) {
|
||||
nodeBuffer.writeU8(1u)
|
||||
|
|
@ -53,45 +79,39 @@ internal class KbinWriter(val xml: Document, val encoding: String = "SHIFT_JIS",
|
|||
}
|
||||
nodeBuffer.writeU8(toWrite)
|
||||
}
|
||||
nodeBuffer.writeString(e.localName)
|
||||
nodeBuffer.writeString(e.nodeName)
|
||||
|
||||
if (type != null) {
|
||||
if (type.name == "bin") {
|
||||
if (count != null) {
|
||||
dataBuffer.writeU32(count.toUInt())
|
||||
}
|
||||
val toWrite = ByteConv.stringToBin(e.text)
|
||||
if (type == binStub) {
|
||||
count = text.length / 2
|
||||
dataBuffer.writeU32(count.toUInt())
|
||||
val toWrite = ByteConv.stringToBin(text)
|
||||
dataBuffer.writeTo4Byte(toWrite)
|
||||
} else if (type.name == "str") {
|
||||
dataBuffer.writeString(e.text)
|
||||
} else if (type == strStub) {
|
||||
dataBuffer.writeString(text)
|
||||
} else {
|
||||
if (count != null) {
|
||||
dataBuffer.writeU32((count * type.size).toUInt())
|
||||
}
|
||||
val split = e.text.splitAndJoin(type.count)
|
||||
val split = text.splitAndJoin(type.count)
|
||||
//val toWrite = split.flatMap { type.fromString(it).asIterable() }.toByteArray()
|
||||
val toWrite = split.flatMap { type.fromString(it).asIterable() }.toByteArray()
|
||||
dataBuffer.writeBytes(toWrite)
|
||||
}
|
||||
}
|
||||
/*for (a in e) {
|
||||
val name = a.localName
|
||||
if (name in arrayOf("__count", "__size", "__type"))
|
||||
continue
|
||||
val value = a.value
|
||||
nodeBuffer.writeU8(46u)
|
||||
nodeBuffer.writeString(name)
|
||||
dataBuffer.writeString(value)
|
||||
}*/
|
||||
val attributes = e.iterator().asSequence()
|
||||
.filter { it.localName !in listOf("__type", "__count", "__size") }
|
||||
.sortedBy { it.localName }
|
||||
|
||||
val attributes = e.attributeList.filter { it.nodeName !in listOf("__type", "__count", "__size") }
|
||||
.sortedBy { it.nodeName }
|
||||
|
||||
for (a in attributes) {
|
||||
a as Attr
|
||||
nodeBuffer.writeU8(46u)
|
||||
nodeBuffer.writeString(a.localName)
|
||||
nodeBuffer.writeString(a.nodeName)
|
||||
dataBuffer.writeString(a.value)
|
||||
}
|
||||
for (c in e.childElements) {
|
||||
nodeRecurse(c, dataBuffer, nodeBuffer)
|
||||
|
||||
for (thing in e.childNodes(Node.ELEMENT_NODE)) {
|
||||
nodeRecurse(thing as Element, dataBuffer, nodeBuffer)
|
||||
}
|
||||
nodeBuffer.writeU8(254u)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package com.buttongames.butterflycore.xml.kbinxml
|
||||
|
||||
import nu.xom.Builder
|
||||
import nu.xom.Document
|
||||
import org.w3c.dom.Document
|
||||
|
||||
fun kbinEncode(d: Document) = KbinWriter(d).getKbin()
|
||||
fun kbinEncode(s: String) = kbinEncode(Builder().build(s, null))
|
||||
fun kbinEncode(d: Document, charset: String = "UTF-8") = KbinWriter(d, charset).getKbin()
|
||||
fun kbinEncode(s: String, charset: String = "UTF-8") = kbinEncode(s.toXml(), charset)
|
||||
|
||||
fun kbinDecode(b: ByteArray) = KbinReader(b).getXml()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.buttongames.butterflycore.xml.kbinxml
|
||||
|
||||
import nu.xom.Attribute
|
||||
import nu.xom.Element
|
||||
import java.nio.charset.Charset
|
||||
|
||||
fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }
|
||||
|
|
@ -17,10 +15,6 @@ internal fun ByteArray.unsigned(): UByteArray {
|
|||
|
||||
class KbinException internal constructor(override var message: String) : Exception(message)
|
||||
|
||||
internal fun Element.addAttribute(key: String, value: String) {
|
||||
this.addAttribute(Attribute(key, value))
|
||||
}
|
||||
|
||||
internal fun MutableList<Byte>.setOrAdd(pos: Int, byte: Byte) {
|
||||
if (pos == this.size) {
|
||||
this.add(byte)
|
||||
|
|
@ -42,14 +36,18 @@ internal fun MutableList<Byte>.setOrAddAll(pos: Int, bytes: ByteArray) {
|
|||
|
||||
internal fun ByteArray.toString(encoding: String) = this.toString(Charset.forName(encoding))
|
||||
|
||||
internal fun String.splitAndJoin(count: Int): Array<String> {
|
||||
/*internal fun String.splitAndJoin(count: Int): Array<String> {
|
||||
val input = this.split(" ")
|
||||
val output = Array(input.size / count) { "" }
|
||||
for (i in 0 until output.size) {
|
||||
output[i] = input.slice(i * count until (i + 1) * count).joinToString(" ")
|
||||
}
|
||||
return output
|
||||
}
|
||||
}*/
|
||||
|
||||
internal fun String.splitAndJoin(count: Int) =
|
||||
this.split(" ").chunked(count).map { it.joinToString(" ") }.toTypedArray()
|
||||
|
||||
|
||||
fun measureMs(times: Int = 1, function: () -> Unit) {
|
||||
for (i in 0 until times) {
|
||||
|
|
@ -60,6 +58,4 @@ fun measureMs(times: Int = 1, function: () -> Unit) {
|
|||
val duration = endTime - startTime //divide by 1000000 to get milliseconds.
|
||||
println("Took ${duration / 1000000.0} ms")
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun Byte.posInt() = this.toUByte().toInt()
|
||||
}
|
||||
|
|
@ -1,77 +1,92 @@
|
|||
package com.buttongames.butterflycore.xml.kbinxml
|
||||
|
||||
import nu.xom.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.File
|
||||
|
||||
fun deepCompare(a: Document, b: Document): Boolean {
|
||||
return deepCompare(a.copy().rootElement, b.copy().rootElement)
|
||||
}
|
||||
|
||||
private fun deepCompare(a: Element, b: Element): Boolean {
|
||||
a.sortAttributes(); b.sortAttributes()
|
||||
val nameA = a.localName
|
||||
val nameB = b.localName
|
||||
check(nameA == nameB) { "Element names are different: $nameA != $nameB" }
|
||||
val attrItA = a.iterator()
|
||||
val attrItB = b.iterator()
|
||||
while (attrItA.hasNext()) {
|
||||
val attrA = attrItA.next()
|
||||
val attrB = attrItB.next()
|
||||
check(attrA.localName == attrB.localName) { "Attribute names are different: ${attrA.localName} != ${attrB.localName}" }
|
||||
if (attrB.value == "bib")
|
||||
println("break")
|
||||
check(attrA.value == attrB.value) { "Attribute values are different for \"${attrA.localName}\": ${attrA.value} != ${attrB.value}" }
|
||||
}
|
||||
if (a.text != "") {
|
||||
check(a.text == b.text) { "Text inside of tags does not match: ${a.value} != ${b.value}" }
|
||||
}
|
||||
val elemItA = a.childElements.iterator()
|
||||
val elemItB = b.childElements.iterator()
|
||||
while (elemItA.hasNext()) {
|
||||
deepCompare(elemItA.next(), elemItB.next())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun Document.prettyString(): String {
|
||||
val o = ByteArrayOutputStream()
|
||||
val serializer = Serializer(o, "UTF-8")
|
||||
serializer.setIndent(4)
|
||||
serializer.write(this)
|
||||
return o.toString("UTF-8")
|
||||
}
|
||||
import java.io.StringReader
|
||||
import java.io.StringWriter
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.OutputKeys
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
|
||||
var Element.text: String
|
||||
get() {
|
||||
for (i in 0 until childCount) {
|
||||
val e = getChild(i)
|
||||
if (e is Text) {
|
||||
return e.value
|
||||
get() = this.childNodes(Node.TEXT_NODE).firstOrNull()?.nodeValue ?: ""
|
||||
set(value) {
|
||||
val children = this.childNodes
|
||||
for (i in 0 until children.length) {
|
||||
val c = children.item(i)
|
||||
if (c.nodeType == Node.TEXT_NODE) {
|
||||
this.removeChild(c)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
set(value: String) {
|
||||
this.appendChild(value)
|
||||
val e = this.ownerDocument.createTextNode(value)
|
||||
this.appendChild(e)
|
||||
}
|
||||
|
||||
|
||||
internal fun Element.sortAttributesRec(): Element {
|
||||
this.sortAttributes()
|
||||
for (e in this.childElements) {
|
||||
sortAttributesRecE(e)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
private fun sortAttributesRecE(e: Element) {
|
||||
e.sortAttributes()
|
||||
for (b in e.childElements) {
|
||||
sortAttributesRecE(b)
|
||||
fun Element.childNodes(type: Short): Sequence<Node> {
|
||||
val children = this.childNodes
|
||||
return sequence {
|
||||
for (i in 0 until children.length) {
|
||||
val c = children.item(i)
|
||||
if (c.nodeType == type) {
|
||||
yield(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toXml() = Builder().build(this, null)!!
|
||||
|
||||
fun File.toXml() = Builder().build(this)
|
||||
fun Element.firstChild(name: String): Element? {
|
||||
var child: Node? = this.firstChild
|
||||
while (child != null) {
|
||||
if (child is Element && name == child.nodeName) return child
|
||||
child = child.nextSibling
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val Element.childElements: Sequence<Element>
|
||||
get() {
|
||||
return this.childNodes(Node.ELEMENT_NODE) as Sequence<Element>
|
||||
}
|
||||
|
||||
val Element.attributeList: List<Attr>
|
||||
get() {
|
||||
val result = mutableListOf<Attr>()
|
||||
val attrs = this.attributes!!
|
||||
for (i in 0 until attrs.length) {
|
||||
result.add(attrs.item(i) as Attr)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun Document.prettyString(indent: Int = 4): String {
|
||||
this.xmlStandalone = true
|
||||
val stringWriter = StringWriter()
|
||||
val xmlOutput = StreamResult(stringWriter)
|
||||
val transformerFactory = TransformerFactory.newInstance()
|
||||
transformerFactory.setAttribute("indent-number", indent)
|
||||
val transformer = transformerFactory.newTransformer()
|
||||
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
|
||||
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "");
|
||||
transformer.transform(DOMSource(this), xmlOutput)
|
||||
return xmlOutput.writer.toString()
|
||||
}
|
||||
|
||||
fun String.toXml(): Document {
|
||||
val db = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val input = InputSource()
|
||||
input.characterStream = (StringReader(this))
|
||||
return db.parse(input)
|
||||
}
|
||||
|
||||
fun File.toXml(): Document {
|
||||
val db = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
return db.parse(this)
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@ public abstract class BaseRequestHandler {
|
|||
|
||||
// convert them to binary XML
|
||||
if (!XmlUtils.isBinaryXML(respBytes)) {
|
||||
respBytes = PublicKt.kbinEncode(new String(respBytes));
|
||||
respBytes = PublicKt.kbinEncode(new String(respBytes), "UTF-8");
|
||||
}
|
||||
|
||||
response.header(COMPRESSION_HEADER, "none");
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,198 +0,0 @@
|
|||
diff --git a/src/nu/xom/Element.java b/src/nu/xom/Element.java
|
||||
index a810cbc..50cee6a 100644
|
||||
--- a/src/nu/xom/Element.java
|
||||
+++ b/src/nu/xom/Element.java
|
||||
@@ -21,13 +21,7 @@
|
||||
|
||||
package nu.xom;
|
||||
|
||||
-import java.util.HashMap;
|
||||
-import java.util.HashSet;
|
||||
-import java.util.Iterator;
|
||||
-import java.util.Map;
|
||||
-import java.util.NoSuchElementException;
|
||||
-import java.util.Set;
|
||||
-import java.util.LinkedHashSet;
|
||||
+import java.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -49,7 +43,7 @@ import java.util.LinkedHashSet;
|
||||
* @version 1.3.0
|
||||
*
|
||||
*/
|
||||
-public class Element extends ParentNode {
|
||||
+public class Element extends ParentNode implements Iterable<Attribute> {
|
||||
|
||||
private String localName;
|
||||
private String prefix;
|
||||
@@ -1839,6 +1833,23 @@ public class Element extends ParentNode {
|
||||
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ public Iterator<Attribute> iterator() {
|
||||
+ return new Iterator<Attribute>() {
|
||||
+ int i = 0;
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasNext() {
|
||||
+ return (i < numAttributes && attributes[i] != null);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Attribute next() {
|
||||
+ return attributes[i++];
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
|
||||
private class AttributeIterator implements Iterator {
|
||||
|
||||
@@ -1863,6 +1874,25 @@ public class Element extends ParentNode {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+
|
||||
+ public void sortAttributes() {
|
||||
+ if (attributes == null) return;
|
||||
+ Arrays.sort(attributes, new Comparator<Attribute>(){
|
||||
+ @Override
|
||||
+ public int compare(Attribute t1, Attribute t2) {
|
||||
+ if (t1 == null && t2 == null) {
|
||||
+ return 0;
|
||||
+ }
|
||||
+ if (t1 == null) {
|
||||
+ return 1;
|
||||
+ }
|
||||
+ if (t2 == null) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ return t1.getLocalName().compareTo(t2.getLocalName());
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
|
||||
Iterator attributeIterator() {
|
||||
|
||||
diff --git a/src/nu/xom/Elements.java b/src/nu/xom/Elements.java
|
||||
index 1fd26ea..524cbb0 100644
|
||||
--- a/src/nu/xom/Elements.java
|
||||
+++ b/src/nu/xom/Elements.java
|
||||
@@ -22,6 +22,7 @@
|
||||
package nu.xom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
+import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -38,7 +39,7 @@ import java.util.List;
|
||||
*
|
||||
*
|
||||
*/
|
||||
-public final class Elements {
|
||||
+public final class Elements implements Iterable<Element> {
|
||||
|
||||
|
||||
private List elements = new ArrayList(1);
|
||||
@@ -82,4 +83,25 @@ public final class Elements {
|
||||
elements.add(element);
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ public Iterator<Element> iterator() {
|
||||
+ return new Iterator<Element>(){
|
||||
+
|
||||
+ int i = 0;
|
||||
+ @Override
|
||||
+ public boolean hasNext() {
|
||||
+ return i < elements.size();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Element next() {
|
||||
+ return (Element)elements.get(i++);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void remove() {
|
||||
+
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/nu/xom/Nodes.java b/src/nu/xom/Nodes.java
|
||||
index a987783..9ca938f 100644
|
||||
--- a/src/nu/xom/Nodes.java
|
||||
+++ b/src/nu/xom/Nodes.java
|
||||
@@ -22,6 +22,7 @@
|
||||
package nu.xom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
+import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -45,9 +46,9 @@ import java.util.List;
|
||||
* @version 1.1b4
|
||||
*
|
||||
*/
|
||||
-public final class Nodes {
|
||||
+public final class Nodes implements Iterable<Node> {
|
||||
|
||||
- private final List nodes;
|
||||
+ private final List<Node> nodes;
|
||||
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ public final class Nodes {
|
||||
* </p>
|
||||
*/
|
||||
public Nodes() {
|
||||
- nodes = new ArrayList();
|
||||
+ nodes = new ArrayList<Node>();
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +75,7 @@ public final class Nodes {
|
||||
if (node == null) {
|
||||
throw new NullPointerException("Nodes content must be non-null");
|
||||
}
|
||||
- nodes = new ArrayList(1);
|
||||
+ nodes = new ArrayList<Node>(1);
|
||||
nodes.add(node);
|
||||
|
||||
}
|
||||
@@ -113,7 +114,7 @@ public final class Nodes {
|
||||
* negative or greater than or equal to the size of the list
|
||||
*/
|
||||
public Node get(int index) {
|
||||
- return (Node) nodes.get(index);
|
||||
+ return nodes.get(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +132,7 @@ public final class Nodes {
|
||||
* negative or greater than or equal to the size of the list
|
||||
*/
|
||||
public Node remove(int index) {
|
||||
- return (Node) nodes.remove(index);
|
||||
+ return nodes.remove(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -187,5 +188,9 @@ public final class Nodes {
|
||||
return nodes.contains(node);
|
||||
}
|
||||
|
||||
-
|
||||
+
|
||||
+ @Override
|
||||
+ public Iterator<Node> iterator() {
|
||||
+ return nodes.iterator();
|
||||
+ }
|
||||
}
|
||||
\ No newline at end of file
|
||||
Loading…
Reference in New Issue
Block a user