diff --git a/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt b/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt index 484fcaad..30f19d2c 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt @@ -28,8 +28,8 @@ class ImportContext: AssetBundleFile { /** * All [Object] loaded from this file. */ - val objects = mutableMapOf() - val objectList: Collection get() = objects.values + val objectMap = mutableMapOf() + val objectList: Collection get() = objectMap.values internal constructor( filePath: String, manager: UnityAssetManager, diff --git a/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt b/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt index 8d5be6aa..9f30fdcc 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt @@ -3,7 +3,7 @@ package io.github.deficuet.unitykt import io.github.deficuet.unitykt.data.Object import io.github.deficuet.unitykt.data.PPtr -inline fun PPtr.getObj(): O? { +inline fun PPtr.safeGetObj(): O? { if (obj != null) return obj getManager()?.let { manager -> if (mPathID in manager.objects) { @@ -19,22 +19,38 @@ inline fun PPtr.getObj(): O? { return null } +inline fun PPtr.getObj(): O { + return safeGetObj()!! +} + +inline fun PPtr<*>.safeGetObjAs(): O? { + return safeGetObj() as? O +} + inline fun PPtr<*>.getObjAs(): T { - return getObj() as T + return safeGetObj() as T } inline fun Array>.allObjectsOf(): List { - return map { it.getObj() }.filterIsInstance() + return map { it.safeGetObj() }.filterIsInstance() } inline fun Collection>.allObjectsOf(): List { - return map { it.getObj() }.filterIsInstance() + return map { it.safeGetObj() }.filterIsInstance() } inline fun Array>.firstObjectOf(): T { - return mapNotNull { it.getObj() }.firstObjectOf() + return mapNotNull { it.safeGetObj() }.firstObjectOf() } inline fun Collection>.firstObjectOf(): T { - return mapNotNull { it.getObj() }.firstObjectOf() -} \ No newline at end of file + return mapNotNull { it.safeGetObj() }.firstObjectOf() +} + +inline fun Array>.firstOfOrNull(): T? { + return mapNotNull { it.safeGetObj() }.firstOfOrNull() +} + +inline fun Collection>.firstOfOrNull(): T? { + return mapNotNull { it.safeGetObj() }.firstOfOrNull() +} diff --git a/src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt b/src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt index f09cb9ae..e07d597b 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt @@ -28,9 +28,9 @@ class UnityAssetManager: Closeable { /** * All Objects loaded except [io.github.deficuet.unitykt.data.AssetBundle] */ - val objects get() = contexts.values.flatMap { context -> + val objectList get() = contexts.values.flatMap { context -> sequence { - for (obj in context.objects) { + for (obj in context.objectMap) { if (obj.key != 1L) { yield(obj.value) } @@ -41,7 +41,7 @@ class UnityAssetManager: Closeable { /** * Multi-dictionary of objects associated with their mPathID */ - val objectDict get() = objects.groupBy { it.mPathID } + val objectMap get() = objectList.groupBy { it.mPathID } data class Configuration internal constructor( /** diff --git a/src/main/kotlin/io/github/deficuet/unitykt/data/PPtr.kt b/src/main/kotlin/io/github/deficuet/unitykt/data/PPtr.kt index 09062c42..a7972605 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/data/PPtr.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/data/PPtr.kt @@ -19,7 +19,7 @@ class PPtr internal constructor(reader: ObjectReader) { val assetFile = reader.assetFile /** - * @see io.github.deficuet.unitykt.getObj + * @see io.github.deficuet.unitykt.safeGetObj */ @PublishedApi internal var obj: @UnsafeVariance T? = null diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/GameObjectImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/GameObjectImpl.kt index 4e7e7ee2..d9bc6617 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/GameObjectImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/GameObjectImpl.kt @@ -1,7 +1,7 @@ package io.github.deficuet.unitykt.dataImpl import io.github.deficuet.unitykt.data.* -import io.github.deficuet.unitykt.getObj +import io.github.deficuet.unitykt.safeGetObj import io.github.deficuet.unitykt.util.ObjectReader import io.github.deficuet.unitykt.util.compareTo @@ -32,7 +32,7 @@ class GameObjectImpl internal constructor(reader: ObjectReader): EditorExtension val animators = mutableListOf() val animations = mutableListOf() for (pptr in mComponents) { - val obj = pptr.getObj() + val obj = pptr.safeGetObj() if (obj != null) { when (obj) { is Transform -> transforms.add(obj) diff --git a/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedFile.kt b/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedFile.kt index 2975a5b0..d09146b4 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedFile.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedFile.kt @@ -360,7 +360,7 @@ class SerializedFile( objectMap[obj.mPathID] = obj } objects = objectMap - root.objects.putAll(objects) + root.objectMap.putAll(objects) objectInfoList.clear() //endregion } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedType.kt b/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedType.kt index 1c63a6cc..09f14523 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedType.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedType.kt @@ -1,10 +1,8 @@ package io.github.deficuet.unitykt.file -import io.github.deficuet.unitykt.util.IntRef import io.github.deficuet.unitykt.util.ObjectReader import io.github.deficuet.unitykt.util.byteArrayOf -import java.nio.ByteBuffer -import java.nio.ByteOrder +import io.github.deficuet.unitykt.util.toChar import kotlin.collections.set data class SerializedType( @@ -22,15 +20,18 @@ data class SerializedType( data class Tree( val nodes: MutableList = mutableListOf() ) { - private operator fun List.get(i: IntRef) = this[i.value] + private data class NodeResult( + val seq: Int, + val value: Any + ) fun readTypeString(reader: ObjectReader): String { reader.position = 0 val builder = StringBuilder() - val iRef = IntRef(0) - while (iRef < nodes.size) { //for (int i = 0; i < nodes.size; i++) - readNodeString(builder, nodes, reader, iRef) // readNodeString(..., ref i) - iRef += 1 + var i = 0 + while (i < nodes.size) { + i = readNodeString(builder, nodes, reader, i) + i++ } return builder.toString() } @@ -38,32 +39,30 @@ data class SerializedType( fun readType(reader: ObjectReader): Map { reader.position = 0 val dict = mutableMapOf() - val iRef = IntRef(1) - while (iRef < nodes.size) { - val node = nodes[iRef] - dict[node.name] = nodes.readNode(reader, iRef) - iRef += 1 + var i = 1 + while (i < nodes.size) { + val node = nodes[i] + val result = readNode(reader, nodes, i) + i = result.seq + dict[node.name] = result.value + i++ } return dict } private fun readNodeString( builder: StringBuilder, nodes: List, - reader: ObjectReader, intRef: IntRef - ) { - val node = nodes[intRef] + reader: ObjectReader, i: Int + ): Int { + var seq = i + val node = nodes[seq] var append = true - var align = (node.metaFlag and 0x4000) != 0 + var align = node.metaFlag.and(0x4000) != 0 var value: Any? = null when (node.type) { "SInt8" -> value = reader.readSByte() "UInt8" -> value = reader.readByte() - "char" -> { - value = Char( - ByteBuffer.wrap(reader.read(2)) - .order(ByteOrder.LITTLE_ENDIAN).short.toUShort() - ) - } + "char" -> value = reader.read(2).toChar() "SInt16", "short" -> value = reader.readShort() "UInt16", "unsigned short" -> value = reader.readUShort() "SInt32", "int" -> value = reader.readInt() @@ -77,10 +76,10 @@ data class SerializedType( append = false val str = reader.readAlignedString() builder.append("${"\t".repeat(node.level)}${node.type} ${node.name} = \"$str\"\r\n") - intRef += nodes.getNode(intRef.value).size - 1 + seq += nodes.getNodes(seq).size - 1 } "map" -> { - if ((nodes[intRef + 1].metaFlag and 0x4000) != 0) align = true + if (nodes[seq + 1].metaFlag.and(0x4000) != 0) align = true append = false val size = reader.readInt() with(builder) { @@ -88,16 +87,16 @@ data class SerializedType( append("${"\t".repeat(node.level + 1)}Array Array\r\n") append("${"\t".repeat(node.level + 1)}int size = $size\r\n") } - val map = nodes.getNode(intRef.value) - intRef += map.size - 1 - val first = map.getNode(4) - val second = map.getNode(first.size + 4) + val map = nodes.getNodes(seq) + seq += map.size - 1 + val first = map.getNodes(4) + val second = map.getNodes(first.size + 4) with(builder) { - for (i in 0 until size) { - append("${"\t".repeat(node.level + 2)}[${i}]\r\n") + for (j in 0 until size) { + append("${"\t".repeat(node.level + 2)}[${j}]\r\n") append("${"\t".repeat(node.level + 2)}pair data\r\n") - readNodeString(builder, first, reader, IntRef(0)) - readNodeString(builder, second, reader, IntRef(0)) + readNodeString(this, first, reader, 0) + readNodeString(this, second, reader, 0) } } } @@ -105,15 +104,15 @@ data class SerializedType( append = false val size = reader.readInt() reader += size - intRef += 2 + seq += 2 with(builder) { append("${"\t".repeat(node.level)}${node.type} ${node.name}\r\n") append("${"\t".repeat(node.level)}int size = $size\r\n") } } else -> { - if (intRef < nodes.size - 1 && nodes[intRef + 1].type == "Array") { - if ((nodes[intRef + 1].metaFlag and 0x4000) != 0) align = true + if (seq < nodes.size - 1 && nodes[seq + 1].type == "Array") { + if (nodes[seq + 1].metaFlag.and(0x4000) != 0) align = true append = false val size = reader.readInt() with(builder) { @@ -121,99 +120,106 @@ data class SerializedType( append("${"\t".repeat(node.level + 1)}Array Array\r\n") append("${"\t".repeat(node.level + 1)}int size = $size\r\n") } - val vector = nodes.getNode(intRef.value) - intRef += vector.size - 1 + val vector = nodes.getNodes(seq) + seq += vector.size - 1 for (j in 0 until size) { builder.append("${"\t".repeat(node.level + 2)}[${j}]\r\n") - readNodeString(builder, vector, reader, IntRef(3)) + readNodeString(builder, vector, reader, 3) } } else { append = false builder.append("${"\t".repeat(node.level)}${node.type} ${node.name}\r\n") - val clazz = nodes.getNode(intRef.value) - intRef += clazz.size - 1 - val kRef = IntRef(1) - while (kRef < clazz.size) { - readNodeString(builder, clazz, reader, kRef) - kRef += 1 + val clazz = nodes.getNodes(seq) + seq += clazz.size - 1 + var j = 1 + while (j < clazz.size) { + j = readNodeString(builder, clazz, reader, j) + j++ } } } } if (append) builder.append("${"\t".repeat(node.level)}${node.type} ${node.name} = $value\n") if (align) reader.alignStream() + return seq } - private fun List.readNode(reader: ObjectReader, iRef: IntRef): Any { - val node = this[iRef] + private fun readNode(reader: ObjectReader, nodes: List, i: Int): NodeResult { + var seq = i + val node = nodes[seq] var align = node.metaFlag.and(0x4000) != 0 - val value: Any = when (node.type) { - "SInt8" -> reader.readSByte() - "UInt8" -> reader.readByte() - "char" -> String(reader.read(2)) - "SInt16", "short" -> reader.readShort() - "UInt16", "unsigned short" -> reader.readUShort() - "SInt32", "int" -> reader.readInt() - "UInt32", "unsigned int", "Type*" -> reader.readUInt() - "SInt64", "long long" -> reader.readLong() - "UInt64", "unsigned long long", "FileSize" -> reader.readULong() - "float" -> reader.readFloat() - "double" -> reader.readDouble() - "bool" -> reader.readBool() + val value: Any + when (node.type) { + "SInt8" -> value = reader.readSByte() + "UInt8" -> value = reader.readByte() + "char" -> value = reader.read(2).toChar() + "SInt16", "short" -> value = reader.readShort() + "UInt16", "unsigned short" -> value = reader.readUShort() + "SInt32", "int" -> value = reader.readInt() + "UInt32", "unsigned int", "Type*" -> value = reader.readUInt() + "SInt64", "long long" -> value = reader.readLong() + "UInt64", "unsigned long long", "FileSize" -> value = reader.readULong() + "float" -> value = reader.readFloat() + "double" -> value = reader.readDouble() + "bool" -> value = reader.readBool() "string" -> { - iRef += nodes.getNode(iRef.value).size - 1 - reader.readAlignedString() + value = reader.readAlignedString() + seq += nodes.getNodes(seq).size - 1 } "map" -> { - if (this[iRef + 1].metaFlag.and(0x4000) != 0) align = true - val map = getNode(iRef.value) - iRef += map.size - 1 - val first = map.getNode(4) - val second = map.getNode(first.size + 4) + if (nodes[seq + 1].metaFlag.and(0x4000) != 0) align = true + val map = nodes.getNodes(seq) + seq += map.size - 1 + val first = map.getNodes(4) + val second = map.getNodes(first.size + 4) val size = reader.readInt() val dict = mutableListOf>() - for (i in 0 until size) { - dict.add(Pair( - first.readNode(reader, IntRef(0)), - second.readNode(reader, IntRef(0)) - )) + for (j in 0 until size) { + dict.add( + Pair( + readNode(reader, first, 0).value, + readNode(reader, second, 0).value + ) + ) } - dict + value = dict } "TypelessData" -> { - iRef += 2 - with(reader) { read(readInt()) } + value = reader.readNextByteArray() + seq += 2 } else -> { - if (iRef < size - 1 && this[iRef + 1].type == "Array") { - if ((this[iRef + 1].metaFlag and 0x4000) != 0) align = true - val vector = getNode(iRef.value) - iRef += vector.size - 1 + if (seq < nodes.size - 1 && nodes[seq + 1].type == "Array") { + if (nodes[seq + 1].metaFlag.and(0x4000) != 0) align = true + val vector = nodes.getNodes(seq) + seq += vector.size - 1 val size = reader.readInt() val list = mutableListOf() for (j in 0 until size) { - list.add(vector.readNode(reader, IntRef(3))) + list.add(readNode(reader, vector, 3).value) } - list + value = list } else { - val clazz = getNode(iRef.value) - iRef += clazz.size - 1 + val clazz = nodes.getNodes(seq) + seq += clazz.size - 1 val dict = mutableMapOf() - val kRef = IntRef(1) - while (kRef < clazz.size) { - val theClass = clazz[kRef] - dict[theClass.name] = clazz.readNode(reader, kRef) - kRef += 1 + var j = 1 + while (j < clazz.size) { + val theClass = clazz[j] + val result = readNode(reader, clazz, j) + j = result.seq + dict[theClass.name] = result.value + j++ } - dict + value = dict } } } if (align) reader.alignStream() - return value + return NodeResult(seq, value) } - private fun List.getNode(index: Int): List { + private fun List.getNodes(index: Int): List { val nodes = mutableListOf(this[index]) val level = this[index].level for (i in index + 1 until size) { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/util/Reference.kt b/src/main/kotlin/io/github/deficuet/unitykt/util/Reference.kt index 8b7f645e..9f7ff6b7 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/util/Reference.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/util/Reference.kt @@ -1,13 +1,3 @@ package io.github.deficuet.unitykt.util class Reference { lateinit var value: T } - -class IntRef(value: Int) { - var value = value - private set - - operator fun plusAssign(o: Int) { value += o } - operator fun compareTo(o: Int) = value.compareTo(o) - operator fun plus(o: Int) = value + o - operator fun plus(u: UInt) = value.toUInt() + u -} diff --git a/src/main/kotlin/io/github/deficuet/unitykt/util/utils.kt b/src/main/kotlin/io/github/deficuet/unitykt/util/utils.kt index b3c2a5c9..1991f39e 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/util/utils.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/util/utils.kt @@ -2,6 +2,8 @@ package io.github.deficuet.unitykt.util import org.json.JSONArray import org.json.JSONObject +import java.nio.ByteBuffer +import java.nio.ByteOrder import java.nio.charset.Charset import java.nio.file.Files import kotlin.io.path.Path @@ -45,6 +47,13 @@ internal fun ByteArray.toHalf(): Float { ) } +internal fun ByteArray.toChar(): Char { + if (size != 2) throw IllegalStateException("There should be 2 bytes only") + return Char( + ByteBuffer.wrap(this).order(ByteOrder.LITTLE_ENDIAN).short.toUShort() + ) +} + internal fun byteArrayOf(vararg bytes: Int) = ByteArray(bytes.size) { bytes[it].toByte() } internal fun byteArrayOf(str: String) = ByteArray(str.length) { str[it].code.toByte() } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/utils.kt b/src/main/kotlin/io/github/deficuet/unitykt/utils.kt index 8812872a..7e145bbb 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/utils.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/utils.kt @@ -52,12 +52,22 @@ fun Collection.allObjectsOf(vararg type: String): List { return filter { it.type.name in type } } -inline fun Collection.objectFromPathID(pathId: Long): T { +inline fun Collection.safeFindWithPathID(pathId: Long): T? { + return with(first { it.mPathID == pathId }) { if (this is T) this else null } +} + +inline fun Collection.findWithPathID(pathId: Long): T { return first { it.mPathID == pathId } as T } +inline fun Map.safeGetAs(pathId: Long): T? { + return this[pathId].safeCast() +} + inline fun Map.getAs(pathId: Long): T { return this[pathId].cast() } +inline fun Any?.safeCast(): T? = this as? T + inline fun Any?.cast(): T = this as T \ No newline at end of file