From 695cb1914c2c3f51780a24e002ca2ab06d5c16c5 Mon Sep 17 00:00:00 2001 From: Deficuet Date: Fri, 23 Dec 2022 21:08:28 -0700 Subject: [PATCH] catch up on the newest version of AssetStudio --- TextureDecoderNative/header.bat | 2 +- build.gradle | 8 +- .../github/deficuet/unitykt/ImportContext.kt | 16 +- .../io/github/deficuet/unitykt/PPtrUtils.kt | 11 +- .../{AssetManager.kt => UnityAssetManager.kt} | 301 ++++++----- .../deficuet/unitykt/data/AssetBundle.kt | 3 +- .../github/deficuet/unitykt/data/AudioClip.kt | 4 +- .../io/github/deficuet/unitykt/data/PPtr.kt | 21 +- .../io/github/deficuet/unitykt/data/Shader.kt | 6 +- .../unitykt/dataImpl/AnimationClipImpl.kt | 7 +- .../unitykt/dataImpl/AudioClipImpl.kt | 37 +- .../deficuet/unitykt/dataImpl/MaterialImpl.kt | 7 +- .../deficuet/unitykt/dataImpl/MeshImpl.kt | 302 ++++++----- .../deficuet/unitykt/dataImpl/ObjectImpl.kt | 2 +- .../deficuet/unitykt/dataImpl/ShaderImpl.kt | 509 +++++++++--------- .../deficuet/unitykt/dataImpl/SpriteImpl.kt | 27 +- .../unitykt/dataImpl/Texture2DImpl.kt | 20 +- .../unitykt/dataImpl/VideoClipImpl.kt | 2 +- .../unitykt/export/EndianByteArrayWriter.kt | 6 +- .../unitykt/export/smolv/SmolvDecoder.kt | 4 +- .../export/spirv/FlagsAttributeEnum.kt | 1 - .../deficuet/unitykt/export/spirv/Module.kt | 4 +- .../unitykt/export/spirv/NumericalEnum.kt | 1 - .../deficuet/unitykt/file/AssetBundleFile.kt | 2 +- .../deficuet/unitykt/file/BundleFile.kt | 26 +- .../deficuet/unitykt/file/SerializedFile.kt | 162 +++--- .../deficuet/unitykt/file/SerializedType.kt | 14 +- .../github/deficuet/unitykt/file/WebFile.kt | 8 +- .../github/deficuet/unitykt/math/Matrix4x4.kt | 21 +- .../deficuet/unitykt/math/Quaternion.kt | 17 +- .../io/github/deficuet/unitykt/math/Vector.kt | 2 +- .../github/deficuet/unitykt/math/Vector2.kt | 63 +-- .../github/deficuet/unitykt/math/Vector3.kt | 72 +-- .../github/deficuet/unitykt/math/Vector4.kt | 91 ++-- .../unitykt/util/EndianBinaryReader.kt | 78 ++- .../github/deficuet/unitykt/util/Reference.kt | 2 - .../deficuet/unitykt/util/ResourceReader.kt | 23 +- .../io/github/deficuet/unitykt/util/utils.kt | 16 +- .../io/github/deficuet/unitykt/utils.kt | 8 +- 39 files changed, 991 insertions(+), 915 deletions(-) rename src/main/kotlin/io/github/deficuet/unitykt/{AssetManager.kt => UnityAssetManager.kt} (86%) diff --git a/TextureDecoderNative/header.bat b/TextureDecoderNative/header.bat index cbca2023..fbf1548c 100644 --- a/TextureDecoderNative/header.bat +++ b/TextureDecoderNative/header.bat @@ -1,2 +1,2 @@ -javac -h ./ D:\UnityKt\src\main\java\io\github\deficuet\unitykt\extension\TextureDecoder.java +javac -h ./ ../src/main/java/io/github/deficuet/unitykt/extension/TextureDecoder.java pause \ No newline at end of file diff --git a/build.gradle b/build.gradle index 39f76964..b77e8a72 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.jetbrains.kotlin.jvm' version '1.5.31' + id 'org.jetbrains.kotlin.jvm' version '1.7.20' id 'maven-publish' } @@ -14,11 +14,11 @@ repositories { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib" implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7' - implementation 'org.json:json:20220320' + implementation 'org.json:json:20220924' implementation 'org.lz4:lz4-java:1.8.0' implementation 'com.nixxcode.jvmbrotli:jvmbrotli:0.2.0' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } test { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt b/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt index 61ce8886..cb68731f 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/ImportContext.kt @@ -21,9 +21,9 @@ class ImportContext: AssetBundleFile { override val name: String /** - * The [AssetManager] which loads this file. + * The [UnityAssetManager] which loads this file. */ - val manager: AssetManager + val manager: UnityAssetManager /** * All [Object] loaded from this file. @@ -31,29 +31,29 @@ class ImportContext: AssetBundleFile { val objects = mutableListOf() internal constructor( - filePath: String, manager: AssetManager, + filePath: String, manager: UnityAssetManager, offsetMode: OffsetMode = OffsetMode.MANUAL, - manualIgnoredOffset: Long = 0 + manualOffset: Long = 0 ) { val file = File(filePath) directory = file.parentFile.canonicalPath name = file.name this.manager = manager files = mapOf(name to init(EndianFileStreamReader( - filePath, offsetMode = offsetMode, manualIgnoredOffset = manualIgnoredOffset + filePath, offsetMode = offsetMode, manualOffset = manualOffset ))) } internal constructor( - data: ByteArray, name: String, manager: AssetManager, + data: ByteArray, name: String, manager: UnityAssetManager, offsetMode: OffsetMode = OffsetMode.MANUAL, - manualIgnoredOffset: Long = 0 + manualOffset: Long = 0 ) { directory = "" this.name = name this.manager = manager files = mapOf(this.name to init(EndianByteArrayReader( - data, offsetMode = offsetMode, manualIgnoredOffset = manualIgnoredOffset + data, offsetMode = offsetMode, manualOffset = manualOffset ))) } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt b/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt index 4489e92b..b5ad8428 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/PPtrUtils.kt @@ -1,6 +1,7 @@ package io.github.deficuet.unitykt -import io.github.deficuet.unitykt.data.* +import io.github.deficuet.unitykt.data.Object +import io.github.deficuet.unitykt.data.PPtr inline fun PPtr.getObj(): O? { if (obj != null) return obj @@ -18,11 +19,15 @@ inline fun PPtr.getObj(): O? { return null } -inline fun Array>.getAllInstanceOf(): List { +inline fun PPtr<*>.getObjAs(): T? { + return getObj() as? T +} + +inline fun Array>.allObjectsOf(): List { return map { it.getObj() }.filterIsInstance() } -inline fun List>.getAllInstanceOf(): List { +inline fun List>.allObjectsOf(): List { return map { it.getObj() }.filterIsInstance() } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/AssetManager.kt b/src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt similarity index 86% rename from src/main/kotlin/io/github/deficuet/unitykt/AssetManager.kt rename to src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt index 22de4fe5..c62da218 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/AssetManager.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/UnityAssetManager.kt @@ -1,147 +1,156 @@ -package io.github.deficuet.unitykt - -import io.github.deficuet.unitykt.file.* -import io.github.deficuet.unitykt.util.OffsetMode -import io.github.deficuet.unitykt.util.isDirectory -import io.github.deficuet.unitykt.util.isFile -import java.nio.file.Files -import java.util.stream.Collectors -import kotlin.io.path.Path -import kotlin.io.path.isRegularFile -import kotlin.io.path.pathString - -class AssetManager { - /** - * Usually [BundleFile] and [WebFile] with their file names. - */ - val assetBundles = mutableMapOf() - - val assetFiles = mutableMapOf() - val resourceFiles = mutableMapOf() - - /** - * All file context loaded from disk. - */ - val contexts = mutableListOf() - - /** - * All Objects loaded except [io.github.deficuet.unitykt.data.AssetBundle] - */ - val objects get() = contexts.flatMap { context -> context.objects.filter { it.mPathID != 1L } } - - /** - * Multi-dictionary of objects associated with their mPathID - */ - val objectDict get() = objects.map { it.mPathID to it } - - data class Configuration internal constructor( - /** - * @see [OffsetMode] - */ - var offsetMode: OffsetMode = OffsetMode.AUTO, - - /** - * Skip specific number of bytes before reading under [OffsetMode.MANUAL] mode. - */ - var manualIgnoredOffset: Long = 0 - ) - - private val configuration = Configuration() - - init { managers.add(this) } - - /** - * @param data Bytes of AsserBundle file - * @param name A string as the "file name" of this bytes - * @param config To set [Configuration.offsetMode] and [Configuration.manualIgnoredOffset] **before** reading - */ - fun loadFromByteArray(data: ByteArray, name: String, config: Configuration.() -> Unit = {}): ImportContext { - configuration.config() - return ImportContext( - data, name, this, - configuration.offsetMode, - configuration.manualIgnoredOffset - ).also { - contexts.add(it) - } - } - - /** - * @param path The path string points to a **file** - * @param config To set [Configuration.offsetMode] and [Configuration.manualIgnoredOffset] **before** reading - * @return A [ImportContext] for this file. - * @throws IllegalStateException If [path] does not point to a **file** - */ - fun loadFile(path: String, config: Configuration.() -> Unit = {}): ImportContext { - configuration.config() - if (!path.isFile()) throw IllegalStateException("\"path\" must be a file") - return ImportContext( - path, this, - configuration.offsetMode, - configuration.manualIgnoredOffset - ).also { - contexts.add(it) - } - } - - /** - * @param path The path strings point to **files** - * @param config To set [Configuration.offsetMode] and [Configuration.manualIgnoredOffset] **before** reading - * @return A list of [ImportContext] for each file. - * @throws IllegalStateException If [path] does not point to a **file** - */ - fun loadFiles(vararg path: String, config: Configuration.() -> Unit = {}): List { - configuration.config() - if (path.any { !it.isFile() }) throw IllegalStateException("\"path\" must be a file") - return mutableListOf().apply { - for (dir in path) { - add(loadFile(dir)) - } - } - } - - /** - * @param folder The path strings point to a **folder** - * @param config To set [Configuration.offsetMode] and [Configuration.manualIgnoredOffset] **before** reading - * @return A list of [ImportContext] for each file under this folder. - * Others folders under this directory will be ignored. - * @throws IllegalStateException If [folder] does not point to a **folder**. - */ - fun loadFolder(folder: String, config: Configuration.() -> Unit = {}): List { - configuration.config() - if (!folder.isDirectory()) throw IllegalStateException("\"path\" must be a directory") - val files = Files.newDirectoryStream(Path(folder)).use { stream -> - stream.filter { it.isRegularFile() } - .map { it.pathString } - .toTypedArray() - } - return loadFiles(*files) - } - - /** - * @param folder The path strings point to a **folder** - * @param config To set [Configuration.offsetMode] and [Configuration.manualIgnoredOffset] **before** reading - * @return A list of [ImportContext] for all reachable files under this folder. - * @throws IllegalStateException If [folder] does not point to a **folder**. - */ - fun loadFolderRecursively(folder: String, config: Configuration.() -> Unit = {}): List { - configuration.config() - if (!folder.isDirectory()) throw IllegalStateException("\"path\" must be a directory") - val files = Files.walk(Path(folder)).filter(Files::isRegularFile) - .map { it.pathString }.collect(Collectors.toList()).toTypedArray() - return loadFiles(*files) - } - - fun closeAll() { - assetFiles.values.forEach { it.reader.close() } - resourceFiles.values.forEach { it.reader.close() } - } - - companion object { - private val managers = mutableListOf() - - fun closeAll() { - managers.forEach { it.closeAll() } - } - } +package io.github.deficuet.unitykt + +import io.github.deficuet.unitykt.file.* +import io.github.deficuet.unitykt.util.OffsetMode +import io.github.deficuet.unitykt.util.isDirectory +import io.github.deficuet.unitykt.util.isFile +import java.io.Closeable +import java.nio.file.Files +import java.util.stream.Collectors +import kotlin.io.path.Path +import kotlin.io.path.isRegularFile +import kotlin.io.path.pathString + +class UnityAssetManager: Closeable { + /** + * Usually [BundleFile] and [WebFile] with their file names. + */ + val assetBundles = mutableMapOf() + + val assetFiles = mutableMapOf() + val resourceFiles = mutableMapOf() + + /** + * All file context loaded from disk. + */ + val contexts = mutableListOf() + + /** + * All Objects loaded except [io.github.deficuet.unitykt.data.AssetBundle] + */ + val objects get() = contexts.flatMap { context -> context.objects.filter { it.mPathID != 1L } } + + /** + * Multi-dictionary of objects associated with their mPathID + */ + val objectDict get() = objects.map { it.mPathID to it } + + data class Configuration internal constructor( + /** + * @see [OffsetMode] + */ + var offsetMode: OffsetMode = OffsetMode.AUTO, + + /** + * Skip specific number of bytes before reading under [OffsetMode.MANUAL] mode. + */ + var manualOffset: Long = 0 + ) + + private val configuration = Configuration() + + internal val otherReaderList = mutableListOf() + + init { managers.add(this) } + + /** + * @param data Bytes of AsserBundle file + * @param name A string as the "file name" of this bytes + * @param config To set [Configuration.offsetMode] and [Configuration.manualOffset] **before** reading + */ + fun loadFromByteArray(data: ByteArray, name: String, config: Configuration.() -> Unit = {}): ImportContext { + configuration.config() + return ImportContext( + data, name, this, + configuration.offsetMode, + configuration.manualOffset + ).also { + contexts.add(it) + } + } + + /** + * @param path The path string points to a **file** + * @param config To set [Configuration.offsetMode] and [Configuration.manualOffset] **before** reading + * @return A [ImportContext] for this file. + * @throws IllegalStateException If [path] does not point to a **file** + */ + fun loadFile(path: String, config: Configuration.() -> Unit = {}): ImportContext { + configuration.config() + if (!path.isFile()) throw IllegalStateException("\"path\" must be a file") + return ImportContext( + path, this, + configuration.offsetMode, + configuration.manualOffset + ).also { + contexts.add(it) + } + } + + /** + * @param path The path strings point to **files** + * @param config To set [Configuration.offsetMode] and [Configuration.manualOffset] **before** reading + * @return A list of [ImportContext] for each file. + * @throws IllegalStateException If [path] does not point to a **file** + */ + fun loadFiles(vararg path: String, config: Configuration.() -> Unit = {}): List { + configuration.config() + if (path.any { !it.isFile() }) throw IllegalStateException("\"path\" must be a file") + return mutableListOf().apply { + for (dir in path) { + add(loadFile(dir)) + } + } + } + + /** + * @param folder The path strings point to a **folder** + * @param config To set [Configuration.offsetMode] and [Configuration.manualOffset] **before** reading + * @return A list of [ImportContext] for each file under this folder. + * Others folders under this directory will be ignored. + * @throws IllegalStateException If [folder] does not point to a **folder**. + */ + fun loadFolder(folder: String, config: Configuration.() -> Unit = {}): List { + configuration.config() + if (!folder.isDirectory()) throw IllegalStateException("\"path\" must be a directory") + val files = Files.newDirectoryStream(Path(folder)).use { stream -> + stream.filter { it.isRegularFile() } + .map { it.pathString } + .toTypedArray() + } + return loadFiles(*files) + } + + /** + * @param folder The path strings point to a **folder** + * @param config To set [Configuration.offsetMode] and [Configuration.manualOffset] **before** reading + * @return A list of [ImportContext] for all reachable files under this folder. + * @throws IllegalStateException If [folder] does not point to a **folder**. + */ + fun loadFolderRecursively(folder: String, config: Configuration.() -> Unit = {}): List { + configuration.config() + if (!folder.isDirectory()) throw IllegalStateException("\"path\" must be a directory") + val files = Files.walk(Path(folder)).filter(Files::isRegularFile) + .map { it.pathString }.collect(Collectors.toList()).toTypedArray() + return loadFiles(*files) + } + + override fun close() { + assetFiles.values.forEach { it.reader.close() } + resourceFiles.values.forEach { it.reader.close() } + otherReaderList.forEach { it.close() } + assetBundles.clear() + assetFiles.clear() + resourceFiles.clear() + otherReaderList.clear() + contexts.clear() + } + + companion object { + private val managers = mutableListOf() + + fun closeAll() { + managers.forEach { it.close() } + } + } } \ No newline at end of file diff --git a/src/main/kotlin/io/github/deficuet/unitykt/data/AssetBundle.kt b/src/main/kotlin/io/github/deficuet/unitykt/data/AssetBundle.kt index 594ea879..ae217389 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/data/AssetBundle.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/data/AssetBundle.kt @@ -1,6 +1,7 @@ package io.github.deficuet.unitykt.data -import io.github.deficuet.unitykt.dataImpl.* +import io.github.deficuet.unitykt.dataImpl.AssetBundleImpl +import io.github.deficuet.unitykt.dataImpl.AssetInfo import io.github.deficuet.unitykt.file.ObjectInfo import io.github.deficuet.unitykt.file.SerializedFile import io.github.deficuet.unitykt.util.ObjectReader diff --git a/src/main/kotlin/io/github/deficuet/unitykt/data/AudioClip.kt b/src/main/kotlin/io/github/deficuet/unitykt/data/AudioClip.kt index 5e785136..321adf88 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/data/AudioClip.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/data/AudioClip.kt @@ -2,7 +2,7 @@ package io.github.deficuet.unitykt.data import io.github.deficuet.unitykt.dataImpl.AudioClipImpl import io.github.deficuet.unitykt.dataImpl.AudioCompressionFormat -import io.github.deficuet.unitykt.dataImpl.AudioType +import io.github.deficuet.unitykt.dataImpl.FMODSoundType import io.github.deficuet.unitykt.file.ObjectInfo import io.github.deficuet.unitykt.file.SerializedFile import io.github.deficuet.unitykt.util.ObjectReader @@ -15,7 +15,7 @@ class AudioClip private constructor( this(ImplementationContainer(assetFile, info) { AudioClipImpl(ObjectReader(assetFile, info)) }) val mFormat: Int get() = container.impl.mFormat - val mType: AudioType get() = container.impl.mType + val mType: FMODSoundType get() = container.impl.mType val m3D: Boolean get() = container.impl.m3D val mUseHardware: Boolean get() = container.impl.mUseHardware 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 e1c80d5b..09062c42 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/data/PPtr.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/data/PPtr.kt @@ -4,13 +4,16 @@ import io.github.deficuet.unitykt.ImportContext import io.github.deficuet.unitykt.dataImpl.ObjectImpl import io.github.deficuet.unitykt.file.FormatVersion import io.github.deficuet.unitykt.file.SerializedFile -import io.github.deficuet.unitykt.util.* +import io.github.deficuet.unitykt.util.ObjectReader +import io.github.deficuet.unitykt.util.containsIgnoreCase +import io.github.deficuet.unitykt.util.listFiles +import io.github.deficuet.unitykt.util.tryGet import java.io.File class PPtr internal constructor(reader: ObjectReader) { var mFileID = reader.readInt() internal set - var mPathID = with(reader) { if (formatVersion < FormatVersion.kUnknown_14) readInt().toLong() else readLong() } + var mPathID = with(reader) { if (formatVersion < FormatVersion.Unknown_14) readInt().toLong() else readLong() } internal set val isNull = mPathID == 0L || mFileID < 0 val assetFile = reader.assetFile @@ -28,27 +31,27 @@ class PPtr internal constructor(reader: ObjectReader) { val manager = assetFile.root.manager val name = assetFile.externals[mFileID - 1].name if (parent !is ImportContext) { - (parent.files.tryGetOrUppercase(name) as? SerializedFile).let { + (parent.files.tryGet(name) as? SerializedFile).let { if (it == null) { val path = parent.bundleParent.root.directory if (path.isNotEmpty()) { - val actualName = StringRef() - if (path.listFiles().containsIgnoreCase(name, actualName)) { - val new = ImportContext("$path/${actualName.value}", manager) - new.files.getValue(actualName.value) as SerializedFile + val actualName = path.listFiles().containsIgnoreCase(name) + if (actualName != null) { + val new = ImportContext("$path/${actualName}", manager) + new.files.getValue(actualName) as SerializedFile } else null } else null } else it } } else { - manager.assetFiles.tryGetOrUppercase(name).let { + manager.assetFiles.tryGet(name).let { if (it == null) { val new = if (File("${parent.directory}/$name").exists()) { ImportContext("${parent.directory}/$name", manager) } else if (File("${parent.directory}/${name.uppercase()}").exists()) { ImportContext("${parent.directory}/${name.uppercase()}", manager) } else null - new?.files?.tryGetOrUppercase(name)!!.let { externalFile -> + new?.files?.tryGet(name)!!.let { externalFile -> if (externalFile is SerializedFile) { externalFile } else null diff --git a/src/main/kotlin/io/github/deficuet/unitykt/data/Shader.kt b/src/main/kotlin/io/github/deficuet/unitykt/data/Shader.kt index be87da68..96fb38db 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/data/Shader.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/data/Shader.kt @@ -18,9 +18,9 @@ class Shader private constructor( val mSubProgramBlob: ByteArray get() = container.impl.mSubProgramBlob val mParsedForm: SerializedShader? get() = container.impl.mParsedForm val platforms: Array get() = container.impl.platforms - val offsets: Array get() = container.impl.offsets - val compressedLengths: Array get() = container.impl.compressedLengths - val decompressedLengths: Array get() = container.impl.decompressedLengths + val offsets: Array> get() = container.impl.offsets + val compressedLengths: Array> get() = container.impl.compressedLengths + val decompressedLengths: Array> get() = container.impl.decompressedLengths val compressedBlob: ByteArray get() = container.impl.compressedBlob val exportString: String get() = container.impl.exportString diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AnimationClipImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AnimationClipImpl.kt index 01eca698..56ba51ae 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AnimationClipImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AnimationClipImpl.kt @@ -36,7 +36,7 @@ class AnimationClipImpl internal constructor(reader: ObjectReader): NamedObjectI reader.readBool() } else if (unityVersion[0] >= 4) { mAnimationType = AnimationType.of(reader.readInt()) - mAnimationType == AnimationType.kLegacy + mAnimationType == AnimationType.Legacy } else { mAnimationType = AnimationType.default true @@ -440,6 +440,7 @@ class GenericBinding internal constructor(reader: ObjectReader) { val typeID: ClassIDType val customType: UByte val isPPtrCurve: UByte + val isIntCurve: UByte init { val version = reader.unityVersion @@ -448,6 +449,7 @@ class GenericBinding internal constructor(reader: ObjectReader) { ) customType = reader.readByte() isPPtrCurve = reader.readByte() + isIntCurve = if (version >= intArrayOf(2022, 1)) reader.readByte() else 0u reader.alignStream() } } @@ -577,9 +579,8 @@ class AnimationEvent internal constructor(reader: ObjectReader) { val messageOptions = reader.readInt() } -@Suppress("EnumEntryName") enum class AnimationType(val id: Int) { - default(0), kLegacy(1), kGeneric(2), kHumanoid(3); + default(0), Legacy(1), Generic(2), Humanoid(3); companion object { fun of(value: Int): AnimationType { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AudioClipImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AudioClipImpl.kt index 79b8c72f..7cb7f007 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AudioClipImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/AudioClipImpl.kt @@ -6,7 +6,7 @@ import io.github.deficuet.unitykt.util.compareTo class AudioClipImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reader) { val mFormat: Int - val mType: AudioType + val mType: FMODSoundType val m3D: Boolean val mUseHardware: Boolean @@ -30,7 +30,7 @@ class AudioClipImpl internal constructor(reader: ObjectReader): NamedObjectImpl( init { if (unityVersion[0] < 5) { mFormat = reader.readInt() - mType = AudioType.of(reader.readInt()) + mType = FMODSoundType.of(reader.readInt()) m3D = reader.readBool() mUseHardware = reader.readBool() reader.alignStream() @@ -79,7 +79,7 @@ class AudioClipImpl internal constructor(reader: ObjectReader): NamedObjectImpl( mSize = reader.readLong() mCompressionFormat = AudioCompressionFormat.of(reader.readInt()) mFormat = 0 - mType = AudioType.UNKNOWN + mType = FMODSoundType.UNKNOWN m3D = false mUseHardware = false } @@ -87,38 +87,55 @@ class AudioClipImpl internal constructor(reader: ObjectReader): NamedObjectImpl( ResourceReader(mSource, assetFile, mOffset, mSize) } else { ResourceReader(reader, reader.absolutePosition, mSize) - } + }.registerToManager(assetFile.root.manager) } } - -enum class AudioType(val id: Int, val ext: String) { - UNKNOWN(1, "."), +enum class FMODSoundType(val id: Int, val ext: String) { + UNKNOWN(1, ""), ACC(2, ".m4a"), AIFF(3, ".aif"), + ASF(3, ""), + AT3(4, ""), + CDDA(5, ""), + DLS(6, ""), + FLAC(7, ""), + FSB(8, ""), + GCADPCM(9, ""), IT(10, ".it"), + MIDI(11, ""), MOD(12, ".mod"), MPEG(13, ".mp3"), OGGVORBIS(14, ".ogg"), + PLAYLIST(15, ""), + RAW(16, ""), S3M(17, ".s3m"), + SF2(18, ""), + USER(19, ""), WAV(20, ".wav"), XM(21, ".xm"), XMA(22, ".wav"), VAG(23, ".vag"), - AUDIOQUEUE(24, ".fsb"); + AUDIOQUEUE(24, ".fsb"), + XWMA(25, ""), + BCWAV(26, ""), + AT9(27, ""), + VORBIS(28, ""), + MEDIA_FOUNDATION(29, ""); companion object { - fun of(value: Int): AudioType { + fun of(value: Int): FMODSoundType { return values().firstOrNull { it.id == value } ?: UNKNOWN } } } + enum class AudioCompressionFormat(val id: Int, val ext: String) { PCM(0, ".fsb"), Vorbis(1, ".fsb"), ADPCM(2, ".fsb"), MP3(3, ".fsb"), - VAG(4, ".vag"), + PSVAG(4, ".vag"), HEVAG(5, ".vag"), XMA(6, ".wav"), AAC(7, ".m4a"), diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MaterialImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MaterialImpl.kt index 848520f9..b2debc4f 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MaterialImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MaterialImpl.kt @@ -14,8 +14,13 @@ class MaterialImpl internal constructor(reader: ObjectReader): NamedObjectImpl(r if (unityVersion[0] == 4 && unityVersion[1] >= 1) { reader.readNextStringArray() //m_ShaderKeywords } - if (unityVersion[0] >= 5) { + if (unityVersion >= intArrayOf(2021, 3)) { + reader.readNextStringArray() + reader.readNextStringArray() + } else if (unityVersion[0] >= 5) { reader.readAlignedString() //m_ShaderKeywords + } + if (unityVersion[0] >= 5) { reader += 4 //m_LightmapFlags: UInt } if (unityVersion >= intArrayOf(5, 6)) { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MeshImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MeshImpl.kt index 8f46ed84..2c8abf6f 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MeshImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/MeshImpl.kt @@ -6,6 +6,7 @@ import io.github.deficuet.unitykt.math.Vector2 import io.github.deficuet.unitykt.math.Vector3 import io.github.deficuet.unitykt.util.* import java.nio.ByteBuffer +import java.nio.ByteOrder import java.util.* import kotlin.math.sqrt @@ -144,6 +145,9 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade reader += reader.readInt() * 4 + 4 //m_CollisionVertexCount } reader += 4 //m_MeshUsageFlags: Int + if (unityVersion >= intArrayOf(2022, 1)) { + reader += 4 //m_CookingOptions + } if (unityVersion[0] >= 5) { reader.readNextByteArray() //m_BakedConvexCollisionMesh reader.alignStream() @@ -160,11 +164,11 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade //region processData if (mStreamData?.path?.isNotEmpty() == true) { if (mVertexData.mVertexCount > 0u) { - println(mStreamData.path) - val resourceReader = ResourceReader( + ResourceReader( mStreamData.path, assetFile, mStreamData.offset, mStreamData.size.toLong() - ) - mVertexData.mDataSize = resourceReader.bytes + ).use { + mVertexData.mDataSize = it.read() + } } } if (unityVersion >= intArrayOf(3, 5)) { @@ -191,7 +195,7 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade for (d in 0 until channel.dimension.toInt()) { val componentOffset = vertexOffset + componentByteSize * d val buff = mVertexData.mDataSize[componentOffset, componentByteSize] - if (reader.endian == EndianType.LittleEndian && componentByteSize > 1) { + if (reader.endian == ByteOrder.LITTLE_ENDIAN && componentByteSize > 1) { buff.reverse() } System.arraycopy( @@ -339,8 +343,7 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade z = sqrt(zsqr) } else { z = 0f - with(Vector3(x, y, z)) { - normalize() + with(Vector3(x, y, z).unit) { x = this.x.toFloat() y = this.y.toFloat() z = this.z.toFloat() @@ -366,8 +369,7 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade z = sqrt(zsqr) } else { z = 0f - with(Vector3(x, y, z)) { - normalize() + with(Vector3(x, y, z).unit) { x = this.x.toFloat() y = this.y.toFloat() z = this.z.toFloat() @@ -440,7 +442,7 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade firstIdx /= 2 } val indexCount = subMesh.indexCount.toInt() - if (subMesh.topology == GfxPrimitiveType.kPrimitiveTriangles) { + if (subMesh.topology == GfxPrimitiveType.Triangles) { for (i in 0 until indexCount step 3) { with(indices) { add(mIndexBuffer[firstIdx + i]) @@ -448,7 +450,7 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade add(mIndexBuffer[firstIdx + i + 2]) } } - } else if (unityVersion[0] < 4 && subMesh.topology == GfxPrimitiveType.kPrimitiveTriangleStrip) { + } else if (unityVersion[0] < 4 && subMesh.topology == GfxPrimitiveType.TriangleStrip) { var triIndex = 0u for (i in 0 until indexCount - 2) { val a = mIndexBuffer[firstIdx + i] @@ -466,7 +468,7 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade triIndex += 3u } subMesh.indexCount = triIndex - } else if (subMesh.topology == GfxPrimitiveType.kPrimitiveQuads) { + } else if (subMesh.topology == GfxPrimitiveType.Quads) { for (q in 0 until indexCount step 4) { for (x in intArrayOf(0, 1, 2, 0, 2, 3)) { indices.add(mIndexBuffer[firstIdx + q + x]) @@ -481,117 +483,122 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade mIndices = indices.toTypedArray() } - val exportString by lazy { - if (mVertexCount < 0) return@lazy "" - val builder = StringBuilder() - builder.append("g $mName\r\n") - if (mVertices.isEmpty()) return@lazy "" - var c = if (mVertices.size == mVertexCount * 4) 4 else 3 - for (v in 0 until mVertexCount) { - builder.append("v ${"%.7G"(-mVertices[v * c])} " + - "${"%.7G"(mVertices[v * c + 1])} " + - "${"%.7G"(mVertices[v * c + 2])}\r\n") - } - if (mUV0.isNotEmpty()) { - c = when (mUV0.size) { - mVertexCount * 2 -> 2 - mVertexCount * 3 -> 3 - else -> 4 + val exportString: String + get() { + if (mVertexCount < 0) return "" + val builder = StringBuilder() + builder.append("g $mName\r\n") + if (mVertices.isEmpty()) return "" + var c = if (mVertices.size == mVertexCount * 4) 4 else 3 + for (v in 0 until mVertexCount) { + builder.append("v ${"%.7G"(-mVertices[v * c])} " + + "${"%.7G"(mVertices[v * c + 1])} " + + "${"%.7G"(mVertices[v * c + 2])}\r\n") } - for (vt in 0 until mVertexCount) { - builder.append("vt ${"%.7G"(mUV0[vt * c]).trimEnd('0')} " + - "${"%.7G"(mUV0[vt * c + 1]).trimEnd('0')}\r\n") + if (mUV0.isNotEmpty()) { + c = when (mUV0.size) { + mVertexCount * 2 -> 2 + mVertexCount * 3 -> 3 + else -> 4 + } + for (vt in 0 until mVertexCount) { + builder.append("vt ${"%.7G"(mUV0[vt * c]).trimEnd('0')} " + + "${"%.7G"(mUV0[vt * c + 1]).trimEnd('0')}\r\n") + } } - } - if (mNormals.isNotEmpty()) { - when(mNormals.size) { - mVertexCount * 3 -> c = 3 - mVertexCount * 4 -> c = 4 + if (mNormals.isNotEmpty()) { + when(mNormals.size) { + mVertexCount * 3 -> c = 3 + mVertexCount * 4 -> c = 4 + } + for (vn in 0 until mVertexCount) { + builder.append("vn ${"%.7G"(-mNormals[vn * c])} " + + "${"%.7G"(mNormals[vn * c + 1])} " + + "${"%.7G"(mNormals[vn * c + 2])}\r\n") + } } - for (vn in 0 until mVertexCount) { - builder.append("vn ${"%.7G"(-mNormals[vn * c])} " + - "${"%.7G"(mNormals[vn * c + 1])} " + - "${"%.7G"(mNormals[vn * c + 2])}\r\n") + var sum = 0 + for (i in mSubMeshes.indices) { + builder.append("g ${mName}_$i\r\n") + val end = sum + mSubMeshes[i].indexCount.toInt() / 3 + for (f in sum until end) { + val v0 = mIndices[f * 3 + 2] + 1u + val v1 = mIndices[f * 3 + 1] + 1u + val v2 = mIndices[f * 3] + 1u + builder.append("f $v0/$v0/$v0 $v1/$v1/$v1 $v2/$v2/$v2\r\n") + } + sum = end } + return builder.toString().replace("NaN", "0") } - var sum = 0 - for (i in mSubMeshes.indices) { - builder.append("g ${mName}_$i\r\n") - val end = sum + mSubMeshes[i].indexCount.toInt() / 3 - for (f in sum until end) { - val v0 = mIndices[f * 3 + 2] + 1u - val v1 = mIndices[f * 3 + 1] + 1u - val v2 = mIndices[f * 3] + 1u - builder.append("f $v0/$v0/$v0 $v1/$v1/$v1 $v2/$v2/$v2\r\n") - } - sum = end - } - return@lazy builder.toString().replace("NaN", "0") - } - val exportVertices by lazy { - val c = if (mVertices.size == mVertexCount * 4) 4 else 3 - Array(mVertexCount) { Vector3(mVertices[it * c], mVertices[it * c + 1], mVertices[it * c + 2]) } - } + val exportVertices: Array + get() { + val c = if (mVertices.size == mVertexCount * 4) 4 else 3 + return Array(mVertexCount) { Vector3(mVertices[it * c], mVertices[it * c + 1], mVertices[it * c + 2]) } + } - val exportUV by lazy { - if (mUV0.isEmpty()) arrayOf() - else { - val c = when (mUV0.size) { - mVertexCount * 2 -> 2 - mVertexCount * 3 -> 3 - else -> 4 + val exportUV: Array + get() { + return if (mUV0.isEmpty()) arrayOf() + else { + val c = when (mUV0.size) { + mVertexCount * 2 -> 2 + mVertexCount * 3 -> 3 + else -> 4 + } + Array(mVertexCount) { Vector2(mUV0[it * c], mUV0[it * c + 1]) } } - Array(mVertexCount) { Vector2(mUV0[it * c], mUV0[it * c + 1]) } } - } - val exportNormals by lazy { - if (mNormals.isEmpty()) arrayOf() - else { - var c = when (mUV0.size) { - mVertexCount * 2 -> 2 - mVertexCount * 3 -> 3 - else -> 4 - } - when(mNormals.size) { - mVertexCount * 3 -> c = 3 - mVertexCount * 4 -> c = 4 + val exportNormals: Array + get() { + return if (mNormals.isEmpty()) arrayOf() + else { + var c = when (mUV0.size) { + mVertexCount * 2 -> 2 + mVertexCount * 3 -> 3 + else -> 4 + } + when(mNormals.size) { + mVertexCount * 3 -> c = 3 + mVertexCount * 4 -> c = 4 + } + Array(mVertexCount) { Vector3(mNormals[it * c], mNormals[it * c + 1], mNormals[it * c + 2]) } } - Array(mVertexCount) { Vector3(mNormals[it * c], mNormals[it * c + 1], mNormals[it * c + 2]) } } - } - val exportFaces by lazy { - var sum = 0 - Array(mSubMeshes.size) { - val end = sum + mSubMeshes[it].indexCount.toInt() / 3 - val v = mutableListOf() - for (f in sum until end) { - v.add(Vector3( - (mIndices[f * 3 + 2] + 1u).toDouble(), - (mIndices[f * 3 + 1] + 1u).toDouble(), - (mIndices[f * 3] + 1u).toDouble() - )) + val exportFaces: Array> + get() { + var sum = 0 + return Array(mSubMeshes.size) { + val end = sum + mSubMeshes[it].indexCount.toInt() / 3 + val v = mutableListOf() + for (f in sum until end) { + v.add(Vector3( + (mIndices[f * 3 + 2] + 1u).toDouble(), + (mIndices[f * 3 + 1] + 1u).toDouble(), + (mIndices[f * 3] + 1u).toDouble() + )) + } + sum = end + v.toTypedArray() } - sum = end - v.toTypedArray() } - } private fun ByteArray.toIntArray(vertexFormat: VertexFormat): IntArray { val len = size / vertexFormat.size.toInt() val result = IntArray(len) for (i in 0 until len) { when (vertexFormat) { - VertexFormat.kVertexFormatUInt8, - VertexFormat.kVertexFormatSInt8 -> result[i] = this[i].toIntBits() - VertexFormat.kVertexFormatUInt16, - VertexFormat.kVertexFormatSInt16 -> { + VertexFormat.UInt8, + VertexFormat.SInt8 -> result[i] = this[i].toIntBits() + VertexFormat.UInt16, + VertexFormat.SInt16 -> { result[i] = ByteBuffer.wrap(this[i * 2, 2]).short.toInt() } - VertexFormat.kVertexFormatUInt32, - VertexFormat.kVertexFormatSInt32 -> { + VertexFormat.UInt32, + VertexFormat.SInt32 -> { result[i] = ByteBuffer.wrap(this[i * 4, 4]).int } else -> { } @@ -605,19 +612,19 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade val result = FloatArray(len) for (i in 0 until len) { when (vertexFormat) { - VertexFormat.kVertexFormatFloat -> { + VertexFormat.Float -> { result[i] = ByteBuffer.wrap(this[i * 4, 4]).float } - VertexFormat.kVertexFormatFloat16 -> { + VertexFormat.Float16 -> { result[i] = this[i * 2, 2].toHalf() } - VertexFormat.kVertexFormatUNorm8 -> { + VertexFormat.UNorm8 -> { result[i] = maxOf(get(i) / 127f, -1f) } - VertexFormat.kVertexFormatSNorm8 -> { + VertexFormat.SNorm8 -> { result[i] = ByteBuffer.wrap(this[i * 2, 2]).short / 65536f } - VertexFormat.kVertexFormatSNorm16 -> { + VertexFormat.SNorm16 -> { result[i] = maxOf( ByteBuffer.wrap(this[i * 2, 2]).short / 32767f, -1f @@ -640,16 +647,16 @@ class MeshImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reade internal class MeshHelper private constructor() { companion object { private val vertexFormatMap = mapOf( - VertexChannelFormat.kChannelFormatFloat to VertexFormat.kVertexFormatFloat, - VertexChannelFormat.kChannelFormatFloat16 to VertexFormat.kVertexFormatFloat16, - VertexChannelFormat.kChannelFormatColor to VertexFormat.kVertexFormatUNorm8, - VertexChannelFormat.kChannelFormatByte to VertexFormat.kVertexFormatUInt8, - VertexChannelFormat.kChannelFormatUInt32 to VertexFormat.kVertexFormatUInt32 + VertexChannelFormat.Float to VertexFormat.Float, + VertexChannelFormat.Float16 to VertexFormat.Float16, + VertexChannelFormat.Color to VertexFormat.UNorm8, + VertexChannelFormat.Byte to VertexFormat.UInt8, + VertexChannelFormat.UInt32 to VertexFormat.UInt32 ) private val vertexFormat2017Map = mapOf( *(VertexFormat.values().map { vf -> VertexFormat2017.valueOf(vf.name) to vf - } + listOf(VertexFormat2017.kVertexFormatColor to VertexFormat.kVertexFormatUNorm8)) + } + listOf(VertexFormat2017.Color to VertexFormat.UNorm8)) .toTypedArray() ) internal fun UByte.toVertexFormat(version: IntArray): VertexFormat { @@ -666,13 +673,12 @@ internal class MeshHelper private constructor() { } } -@Suppress("EnumEntryName") internal enum class VertexChannelFormat { - kChannelFormatFloat, - kChannelFormatFloat16, - kChannelFormatColor, - kChannelFormatByte, - kChannelFormatUInt32; + Float, + Float16, + Color, + Byte, + UInt32; companion object { fun of(value: Int): VertexChannelFormat { @@ -681,21 +687,20 @@ internal enum class VertexChannelFormat { } } -@Suppress("EnumEntryName") internal enum class VertexFormat2017 { - kVertexFormatFloat, - kVertexFormatFloat16, - kVertexFormatColor, - kVertexFormatUNorm8, - kVertexFormatSNorm8, - kVertexFormatUNorm16, - kVertexFormatSNorm16, - kVertexFormatUInt8, - kVertexFormatSInt8, - kVertexFormatUInt16, - kVertexFormatSInt16, - kVertexFormatUInt32, - kVertexFormatSInt32; + Float, + Float16, + Color, + UNorm8, + SNorm8, + UNorm16, + SNorm16, + UInt8, + SInt8, + UInt16, + SInt16, + UInt32, + SInt32; companion object { fun of(value: Int): VertexFormat2017 { @@ -704,22 +709,21 @@ internal enum class VertexFormat2017 { } } -@Suppress("EnumEntryName") internal enum class VertexFormat(val size: UInt) { - kVertexFormatFloat(4u), - kVertexFormatFloat16(2u), - kVertexFormatUNorm8(1u), - kVertexFormatSNorm8(1u), - kVertexFormatUNorm16(2u), - kVertexFormatSNorm16(2u), - kVertexFormatUInt8(1u), - kVertexFormatSInt8(1u), - kVertexFormatUInt16(2u), - kVertexFormatSInt16(2u), - kVertexFormatUInt32(4u), - kVertexFormatSInt32(4u); - - val isIntFormat get() = this >= kVertexFormatUInt8 + Float(4u), + Float16(2u), + UNorm8(1u), + SNorm8(1u), + UNorm16(2u), + SNorm16(2u), + UInt8(1u), + SInt8(1u), + UInt16(2u), + SInt16(2u), + UInt32(4u), + SInt32(4u); + + val isIntFormat get() = this >= UInt8 companion object { fun of(value: Int): VertexFormat { @@ -990,9 +994,9 @@ class BlendShapeData internal constructor(reader: ObjectReader) { class GfxPrimitiveType private constructor() { companion object { - const val kPrimitiveTriangles = 0 - const val kPrimitiveTriangleStrip = 1 - const val kPrimitiveQuads = 2 + const val Triangles = 0 + const val TriangleStrip = 1 + const val Quads = 2 } } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ObjectImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ObjectImpl.kt index 36947713..73090813 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ObjectImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ObjectImpl.kt @@ -10,7 +10,7 @@ open class ObjectImpl internal constructor(private val reader: ObjectReader) { protected val buildType = reader.buildType val platform = reader.platform private val serializedType = reader.serializedType - val bytes by lazy { reader.bytes } + val bytes: ByteArray get() { return reader.bytes } init { println("Object(${reader.type}) path id $mPathID initialized") diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ShaderImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ShaderImpl.kt index 1a796e4c..bede9107 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ShaderImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/ShaderImpl.kt @@ -8,6 +8,7 @@ import io.github.deficuet.unitykt.export.smolv.SmolvDecoder import io.github.deficuet.unitykt.export.spirv.Disassembler import io.github.deficuet.unitykt.export.spirv.Module import io.github.deficuet.unitykt.util.* +import java.nio.ByteOrder class ShaderImpl internal constructor(reader: ObjectReader): NamedObjectImpl(reader) { val mScript: ByteArray @@ -15,9 +16,9 @@ class ShaderImpl internal constructor(reader: ObjectReader): NamedObjectImpl(rea val mSubProgramBlob: ByteArray val mParsedForm: SerializedShader? val platforms: Array - val offsets: Array - val compressedLengths: Array - val decompressedLengths: Array + val offsets: Array> + val compressedLengths: Array> + val decompressedLengths: Array> val compressedBlob: ByteArray init { @@ -27,19 +28,13 @@ class ShaderImpl internal constructor(reader: ObjectReader): NamedObjectImpl(rea Array(size) { ShaderCompilerPlatform.of(this[it].toInt()) } } if (unityVersion >= intArrayOf(2019, 3)) { - offsets = reader.readNestedUIntArray().let { - array -> Array(array.size) { array[it][0] } - } - compressedLengths = reader.readNestedUIntArray().let { - array -> Array(array.size) { array[it][0] } - } - decompressedLengths = reader.readNestedUIntArray().let { - array -> Array(array.size) { array[it][0] } - } + offsets = reader.readNestedUIntArray() + compressedLengths = reader.readNestedUIntArray() + decompressedLengths = reader.readNestedUIntArray() } else { - offsets = reader.readNextUIntArray() - compressedLengths = reader.readNextUIntArray() - decompressedLengths = reader.readNextUIntArray() + offsets = arrayOf(reader.readNextUIntArray()) + compressedLengths = arrayOf(reader.readNextUIntArray()) + decompressedLengths = arrayOf(reader.readNextUIntArray()) } compressedBlob = reader.readNextByteArray() reader.alignStream() @@ -75,32 +70,43 @@ class ShaderImpl internal constructor(reader: ObjectReader): NamedObjectImpl(rea } } - val exportString by lazy { - if (mSubProgramBlob.isNotEmpty()) { - val decompressed = CompressUtils.lz4Decompress(mSubProgramBlob, decompressedSize.toInt()) - EndianByteArrayReader(decompressed).use { blobReader -> - val program = ShaderProgram(blobReader, unityVersion) - return@lazy exportHeader + program.export(mScript.decodeToString()) + val exportString: String + get() { + if (mSubProgramBlob.isNotEmpty()) { + val decompressed = CompressUtils.lz4Decompress(mSubProgramBlob, decompressedSize.toInt()) + EndianByteArrayReader(decompressed).use { blobReader -> + val program = ShaderProgram(blobReader, unityVersion) + return exportHeader + program.export(mScript.decodeToString()) + } } + if (compressedBlob.isNotEmpty()) { + return exportHeader + convertSerializedShader() + } + return exportHeader + mScript.decodeToString() } - if (compressedBlob.isNotEmpty()) { - return@lazy exportHeader + convertSerializedShader() - } - return@lazy exportHeader + mScript.decodeToString() - } private fun convertSerializedShader(): String { - val programs = Array(platforms.size) { - val compressedByte = ByteArray(compressedLengths[it].toInt()) - System.arraycopy( - compressedBlob, offsets[it].toInt(), - compressedByte, 0, compressedLengths[it].toInt() - ) - val decompressedByte = CompressUtils.lz4Decompress(compressedByte, decompressedLengths[it].toInt()) - EndianByteArrayReader(decompressedByte, endian = EndianType.LittleEndian).use { blobReader -> - ShaderProgram(blobReader, unityVersion) + val programsList = mutableListOf() + for (i in platforms.indices) { + for (j in offsets[i].indices) { + val length = compressedLengths[i][j].toInt() + val compressedByte = ByteArray(length) + System.arraycopy( + compressedBlob, offsets[i][j].toInt(), + compressedByte, 0, length + ) + val decompressedByte = CompressUtils.lz4Decompress( + compressedByte, decompressedLengths[i][j].toInt() + ) + EndianByteArrayReader(decompressedByte, endian = ByteOrder.LITTLE_ENDIAN).use { blobReader -> + if (j == 0) { + programsList.add(ShaderProgram(blobReader, unityVersion)) + } + programsList[i].read(blobReader, j) + } } } + val programs = programsList.toTypedArray() return StringBuilder().apply { append("Shader \"${mParsedForm!!.mName}\" {\n") //region convertSerializedProperties @@ -143,18 +149,30 @@ class ShaderImpl internal constructor(reader: ObjectReader): NamedObjectImpl(rea } } +internal class ShaderSubProgramEntry(reader: EndianBinaryReader, version: IntArray) { + val offset = reader.readInt() + val length = reader.readInt() + val segment = if (version >= intArrayOf(2019, 3)) reader.readInt() else 0 +} + internal class ShaderProgram(reader: EndianBinaryReader, version: IntArray) { - private val entrySize = if (version >= intArrayOf(2019, 3)) 12 else 8 - val mSubPrograms = reader.readArrayIndexedOf { - reader.position = 4L + it * entrySize - val offset = reader.readInt() - reader.position = offset.toLong() - ShaderSubProgram(reader) + val entries = reader.readArrayOf { + ShaderSubProgramEntry(reader, version) + } + val mSubPrograms = Array(entries.size) { null } + + fun read(reader: EndianBinaryReader, segment: Int) { + for ((i, e) in entries.withIndex()) { + if (e.segment == segment) { + reader.position = e.offset.toLong() + mSubPrograms[i] = ShaderSubProgram(reader) + } + } } fun export(shader: String): String { return exportRegex.replace(shader) { - mSubPrograms[it.groups[1]!!.value.toInt()].export() + mSubPrograms[it.groups[1]!!.value.toInt()]?.export() ?: "" } } @@ -195,36 +213,36 @@ internal class ShaderSubProgram(private val reader: EndianBinaryReader) { builder.append("\"") if (mProgramCode.isNotEmpty()) { when (mProgramType) { - ShaderGpuProgramType.kShaderGpuProgramGLLegacy, - ShaderGpuProgramType.kShaderGpuProgramGLES31AEP, - ShaderGpuProgramType.kShaderGpuProgramGLES31, - ShaderGpuProgramType.kShaderGpuProgramGLES3, - ShaderGpuProgramType.kShaderGpuProgramGLES, - ShaderGpuProgramType.kShaderGpuProgramGLCore32, - ShaderGpuProgramType.kShaderGpuProgramGLCore41, - ShaderGpuProgramType.kShaderGpuProgramGLCore43 -> { + ShaderGpuProgramType.GLLegacy, + ShaderGpuProgramType.GLES31AEP, + ShaderGpuProgramType.GLES31, + ShaderGpuProgramType.GLES3, + ShaderGpuProgramType.GLES, + ShaderGpuProgramType.GLCore32, + ShaderGpuProgramType.GLCore41, + ShaderGpuProgramType.GLCore43 -> { builder.append(mProgramCode.decodeToString(Charsets.UTF_8)) } - ShaderGpuProgramType.kShaderGpuProgramDX9VertexSM20, - ShaderGpuProgramType.kShaderGpuProgramDX9VertexSM30, - ShaderGpuProgramType.kShaderGpuProgramDX9PixelSM20, - ShaderGpuProgramType.kShaderGpuProgramDX9PixelSM30 -> { + ShaderGpuProgramType.DX9VertexSM20, + ShaderGpuProgramType.DX9VertexSM30, + ShaderGpuProgramType.DX9PixelSM20, + ShaderGpuProgramType.DX9PixelSM30 -> { builder.append("// shader disassembly not supported on DXBC") } - ShaderGpuProgramType.kShaderGpuProgramDX10Level9Vertex, - ShaderGpuProgramType.kShaderGpuProgramDX10Level9Pixel, - ShaderGpuProgramType.kShaderGpuProgramDX11VertexSM40, - ShaderGpuProgramType.kShaderGpuProgramDX11VertexSM50, - ShaderGpuProgramType.kShaderGpuProgramDX11PixelSM40, - ShaderGpuProgramType.kShaderGpuProgramDX11PixelSM50, - ShaderGpuProgramType.kShaderGpuProgramDX11GeometrySM40, - ShaderGpuProgramType.kShaderGpuProgramDX11GeometrySM50, - ShaderGpuProgramType.kShaderGpuProgramDX11HullSM50, - ShaderGpuProgramType.kShaderGpuProgramDX11DomainSM50 -> { + ShaderGpuProgramType.DX10Level9Vertex, + ShaderGpuProgramType.DX10Level9Pixel, + ShaderGpuProgramType.DX11VertexSM40, + ShaderGpuProgramType.DX11VertexSM50, + ShaderGpuProgramType.DX11PixelSM40, + ShaderGpuProgramType.DX11PixelSM50, + ShaderGpuProgramType.DX11GeometrySM40, + ShaderGpuProgramType.DX11GeometrySM50, + ShaderGpuProgramType.DX11HullSM50, + ShaderGpuProgramType.DX11DomainSM50 -> { builder.append("// shader disassembly not supported on DXBC") } - ShaderGpuProgramType.kShaderGpuProgramMetalVS, - ShaderGpuProgramType.kShaderGpuProgramMetalFS -> { + ShaderGpuProgramType.MetalVS, + ShaderGpuProgramType.MetalFS -> { val fourCC = reader.readUInt() if (fourCC == 0xF00DCAFEu) { val offset = reader.readInt() @@ -234,7 +252,7 @@ internal class ShaderSubProgram(private val reader: EndianBinaryReader) { val buff = reader.read(with(reader) { length - position }.toInt()) builder.append(buff.decodeToString(Charsets.UTF_8)) } - ShaderGpuProgramType.kShaderGpuProgramSPIRV -> { + ShaderGpuProgramType.SPIRV -> { builder.append( try { mProgramCode.covertToSpirV() @@ -243,11 +261,11 @@ internal class ShaderSubProgram(private val reader: EndianBinaryReader) { } ) } - ShaderGpuProgramType.kShaderGpuProgramConsoleVS, - ShaderGpuProgramType.kShaderGpuProgramConsoleFS, - ShaderGpuProgramType.kShaderGpuProgramConsoleHS, - ShaderGpuProgramType.kShaderGpuProgramConsoleDS, - ShaderGpuProgramType.kShaderGpuProgramConsoleGS -> { + ShaderGpuProgramType.ConsoleVS, + ShaderGpuProgramType.ConsoleFS, + ShaderGpuProgramType.ConsoleHS, + ShaderGpuProgramType.ConsoleDS, + ShaderGpuProgramType.ConsoleGS -> { builder.append(mProgramCode.decodeToString(Charsets.UTF_8)) } else -> { builder.append("//shader disassembly not supported on $mProgramType") } @@ -260,7 +278,7 @@ internal class ShaderSubProgram(private val reader: EndianBinaryReader) { companion object { private fun ByteArray.covertToSpirV(): String { val builder = StringBuilder() - EndianByteArrayReader(this, endian = EndianType.LittleEndian).use { reader -> + EndianByteArrayReader(this, endian = ByteOrder.LITTLE_ENDIAN).use { reader -> reader += 4 var minOffset = reader.length for (i in 0..4) { @@ -273,7 +291,7 @@ internal class ShaderSubProgram(private val reader: EndianBinaryReader) { position = offset.toLong() val decodedSize = SmolvDecoder.getDecodedBufferSize(reader) if (decodedSize == 0) return "// disassembly error: Invalid SMOL-V shader header" - EndianByteArrayWriter(decodedSize, endianType = EndianType.LittleEndian).use { writer -> + EndianByteArrayWriter(decodedSize, endianType = ByteOrder.LITTLE_ENDIAN).use { writer -> if (SmolvDecoder.decode(this, size, writer)) { val module = Module.readFrom(writer.array) builder.append(Disassembler().disassemble(module)) @@ -328,21 +346,19 @@ class SamplerParameter internal constructor(reader: EndianBinaryReader) { val bindPoint = reader.readInt() } -@Suppress("EnumEntryName") enum class TextureDimension(val id: Int) { - kTexDimUnknown(-1), - kTexDimNone(0), - kTexDimAny(1), - kTexDim2D(2), - kTexDim3D(3), - kTexDimCUBE(4), - kTexDim2DArray(5), - kTexDimCubeArray(6), - kTexDimForce32Bit(Int.MAX_VALUE); + Unknown(-1), + None(0), + Any(1), + Tex2D(2), + Tex3D(3), + Cube(4), + Tex2DArray(5), + CubeArray(6); companion object { fun of(value: Int): TextureDimension { - return values().firstOrNull { it.id == value } ?: kTexDimUnknown + return values().firstOrNull { it.id == value } ?: Unknown } } } @@ -352,17 +368,17 @@ class SerializedTextureProperty internal constructor(reader: EndianBinaryReader) val mTexDim = TextureDimension.of(reader.readInt()) } -@Suppress("EnumEntryName") enum class SerializedPropertyType(val id: Int) { - kColor(0), - kVector(1), - kFloat(2), - kRange(3), - kTexture(4); + Color(0), + Vector(1), + Float(2), + Range(3), + Texture(4); +// Integer(5); companion object { fun of(value: Int): SerializedPropertyType { - return values().firstOrNull { it.id == value } ?: kColor + return values().firstOrNull { it.id == value } ?: Color } } } @@ -383,18 +399,18 @@ class SerializedProperty internal constructor(reader: EndianBinaryReader) { builder.append("$mName (\"$mDescription\", ") builder.append( when (mType) { - SerializedPropertyType.kColor -> "Color" - SerializedPropertyType.kVector -> "Vector" - SerializedPropertyType.kFloat -> "Float" - SerializedPropertyType.kRange -> "Range(${mDefValue[1]}, ${mDefValue[2]})" - SerializedPropertyType.kTexture -> { + SerializedPropertyType.Color -> "Color" + SerializedPropertyType.Vector -> "Vector" + SerializedPropertyType.Float -> "Float" + SerializedPropertyType.Range -> "Range(${mDefValue[1]}, ${mDefValue[2]})" + SerializedPropertyType.Texture -> { when (mDefTexture.mTexDim) { - TextureDimension.kTexDimAny -> "any" - TextureDimension.kTexDim2D -> "2D" - TextureDimension.kTexDim3D -> "3D" - TextureDimension.kTexDimCUBE -> "Cube" - TextureDimension.kTexDim2DArray -> "2DArray" - TextureDimension.kTexDimCubeArray -> "CubeArray" + TextureDimension.Any -> "any" + TextureDimension.Tex2D -> "2D" + TextureDimension.Tex3D -> "3D" + TextureDimension.Cube -> "Cube" + TextureDimension.Tex2DArray -> "2DArray" + TextureDimension.CubeArray -> "CubeArray" else -> "" } } @@ -403,15 +419,15 @@ class SerializedProperty internal constructor(reader: EndianBinaryReader) { builder.append(") = ") builder.append( when (mType) { - SerializedPropertyType.kColor, - SerializedPropertyType.kVector -> { + SerializedPropertyType.Color, + SerializedPropertyType.Vector -> { "(${mDefValue[0]},${mDefValue[1]},${mDefValue[2]},${mDefValue[3]})" } - SerializedPropertyType.kFloat, - SerializedPropertyType.kRange -> { + SerializedPropertyType.Float, + SerializedPropertyType.Range -> { mDefValue[0] } - SerializedPropertyType.kTexture -> { + SerializedPropertyType.Texture -> { "\"${mDefTexture.mDefaultName}\" { }" } } @@ -533,17 +549,16 @@ class SerializedShaderVectorValue internal constructor(reader: EndianBinaryReade val name = reader.readAlignedString() } -@Suppress("EnumEntryName") enum class FogMode(val id: Int) { - kFogUnknown(-1), - kFogDisabled(0), - kFogLinear(1), - kFogExp(2), - kFogExp2(3); + Unknown(-1), + Disabled(0), + Linear(1), + Exp(2), + Exp2(3); companion object { fun of(value: Int): FogMode { - return values().firstOrNull { it.id == value } ?: kFogDisabled + return values().firstOrNull { it.id == value } ?: Disabled } } } @@ -704,7 +719,7 @@ class SerializedShaderState internal constructor(reader: ObjectReader) { builder.append(" }\n") } if ( - fogMode != FogMode.kFogUnknown || + fogMode != FogMode.Unknown || fogColor.x.value != 0f || fogColor.y.value != 0f || fogColor.z.value != 0f || @@ -714,13 +729,13 @@ class SerializedShaderState internal constructor(reader: ObjectReader) { fogEnd.value != 0f ) { builder.append(" Fog {\n") - if (fogMode != FogMode.kFogUnknown) { + if (fogMode != FogMode.Unknown) { builder.append(" Mode ") when (fogMode) { - FogMode.kFogDisabled -> builder.append("Off") - FogMode.kFogLinear -> builder.append("Linear") - FogMode.kFogExp -> builder.append("Exp") - FogMode.kFogExp2 -> builder.append("Exp2") + FogMode.Disabled -> builder.append("Off") + FogMode.Linear -> builder.append("Linear") + FogMode.Exp -> builder.append("Exp") + FogMode.Exp2 -> builder.append("Exp2") else -> { } } builder.append("\n") @@ -844,7 +859,7 @@ class ConstantBuffer internal constructor(reader: ObjectReader) { val mSize = reader.readInt() val mIsPartialCB = if (with(reader.unityVersion) { (this[0] == 2020 && this >= intArrayOf(2020, 3, 2)) || - (this[0] == 2021 && this >= intArrayOf(2021, 1, 4)) + (this >= intArrayOf(2021, 1, 4)) }) { reader.readBool() } else false @@ -858,44 +873,44 @@ class UAVParameter internal constructor(reader: EndianBinaryReader) { val mOriginalIndex = reader.readInt() } -@Suppress("EnumEntryName") enum class ShaderGpuProgramType(val id: Int) { - kShaderGpuProgramUnknown(0), - kShaderGpuProgramGLLegacy(1), - kShaderGpuProgramGLES31AEP(2), - kShaderGpuProgramGLES31(3), - kShaderGpuProgramGLES3(4), - kShaderGpuProgramGLES(5), - kShaderGpuProgramGLCore32(6), - kShaderGpuProgramGLCore41(7), - kShaderGpuProgramGLCore43(8), - kShaderGpuProgramDX9VertexSM20(9), - kShaderGpuProgramDX9VertexSM30(10), - kShaderGpuProgramDX9PixelSM20(11), - kShaderGpuProgramDX9PixelSM30(12), - kShaderGpuProgramDX10Level9Vertex(13), - kShaderGpuProgramDX10Level9Pixel(14), - kShaderGpuProgramDX11VertexSM40(15), - kShaderGpuProgramDX11VertexSM50(16), - kShaderGpuProgramDX11PixelSM40(17), - kShaderGpuProgramDX11PixelSM50(18), - kShaderGpuProgramDX11GeometrySM40(19), - kShaderGpuProgramDX11GeometrySM50(20), - kShaderGpuProgramDX11HullSM50(21), - kShaderGpuProgramDX11DomainSM50(22), - kShaderGpuProgramMetalVS(23), - kShaderGpuProgramMetalFS(24), - kShaderGpuProgramSPIRV(25), - kShaderGpuProgramConsoleVS(26), - kShaderGpuProgramConsoleFS(27), - kShaderGpuProgramConsoleHS(28), - kShaderGpuProgramConsoleDS(29), - kShaderGpuProgramConsoleGS(30), - kShaderGpuProgramRayTracing(31); + Unknown(0), + GLLegacy(1), + GLES31AEP(2), + GLES31(3), + GLES3(4), + GLES(5), + GLCore32(6), + GLCore41(7), + GLCore43(8), + DX9VertexSM20(9), + DX9VertexSM30(10), + DX9PixelSM20(11), + DX9PixelSM30(12), + DX10Level9Vertex(13), + DX10Level9Pixel(14), + DX11VertexSM40(15), + DX11VertexSM50(16), + DX11PixelSM40(17), + DX11PixelSM50(18), + DX11GeometrySM40(19), + DX11GeometrySM50(20), + DX11HullSM50(21), + DX11DomainSM50(22), + MetalVS(23), + MetalFS(24), + SPIRV(25), + ConsoleVS(26), + ConsoleFS(27), + ConsoleHS(28), + ConsoleDS(29), + ConsoleGS(30), + RayTracing(31), + PS5NGGC(32); companion object { fun of(value: Int): ShaderGpuProgramType { - return values().firstOrNull { it.id == value } ?: kShaderGpuProgramUnknown + return values().firstOrNull { it.id == value } ?: Unknown } } } @@ -944,7 +959,7 @@ class SerializedSubProgram internal constructor(reader: ObjectReader) { reader.alignStream() if ( (version[0] == 2020 && version >= intArrayOf(2020, 3, 2)) || - (version[0] == 2021 && version >= intArrayOf(2021, 2, 4)) + (version >= intArrayOf(2021, 2, 4)) ) { mParameters = SerializedProgramParameters(reader) mVectorParams = emptyArray() @@ -976,17 +991,28 @@ class SerializedProgram internal constructor(reader: ObjectReader) { val mSubPrograms = reader.readArrayOf { SerializedSubProgram(reader) } val mCommonParameters = if (with(reader.unityVersion) { (this[0] == 2020 && this >= intArrayOf(2020, 3, 2)) || - (this[0] == 2021 && this >= intArrayOf(2021, 1, 4)) + (this >= intArrayOf(2021, 1, 4)) }) SerializedProgramParameters(reader) else null + val mSerializedKeywordStateMask: Array + + init { + if (reader.unityVersion >= intArrayOf(2022, 1)) { + mSerializedKeywordStateMask = reader.readNextUShortArray() + reader.alignStream() + } else { + mSerializedKeywordStateMask = emptyArray() + } + } } -@Suppress("EnumEntryName") enum class PassType(val id: Int) { - kPassTypeNormal(0), kPassTypeUse(1), kPassTypeGrab(2); + Normal(0), + Use(1), + Grab(2); companion object { fun of(value: Int): PassType { - return values().firstOrNull { it.id == value } ?: kPassTypeNormal + return values().firstOrNull { it.id == value } ?: Normal } } } @@ -1052,7 +1078,7 @@ class SerializedPass internal constructor(reader: ObjectReader) { mName = reader.readAlignedString() mTextureName = reader.readAlignedString() mTags = SerializedTagMap(reader) - if (version >= intArrayOf(2021, 2)) { + if (version[0] == 2021 && version[1] >= 2) { mSerializedKeywordStateMask = reader.readNextUShortArray() reader.alignStream() } else { @@ -1067,16 +1093,16 @@ class SerializedPass internal constructor(reader: ObjectReader) { ): StringBuilder { builder.append( when (mType) { - PassType.kPassTypeNormal -> " Pass " - PassType.kPassTypeUse -> " UsePass " - PassType.kPassTypeGrab -> " GrabPass " + PassType.Normal -> " Pass " + PassType.Use -> " UsePass " + PassType.Grab -> " GrabPass " } ) - if (mType == PassType.kPassTypeUse) { + if (mType == PassType.Use) { builder.append("\"$mUseName\"\n") } else { builder.append("{\n") - if (mType == PassType.kPassTypeGrab) { + if (mType == PassType.Grab) { if (mTextureName.isNotEmpty()) { builder.append(" \"${mTextureName}\"\n") } @@ -1146,7 +1172,7 @@ class SerializedPass internal constructor(reader: ObjectReader) { } builder.append( "\" {\n" + - shaderPrograms[i].mSubPrograms[subProgram.mBlobIndex.toInt()].export() + + shaderPrograms[i].mSubPrograms[subProgram.mBlobIndex.toInt()]?.export() + "\n}\n" ) } @@ -1209,111 +1235,110 @@ class SerializedShader internal constructor(reader: ObjectReader) { } } -@Suppress("EnumEntryName") enum class ShaderCompilerPlatform(val id: Int, val str: String = "unknown") { - kShaderCompPlatformNone(-1), - kShaderCompPlatformGL(0, "openGL"), - kShaderCompPlatformD3D9(1, "d3d9"), - kShaderCompPlatformXbox360(2, "xbox360"), - kShaderCompPlatformPS3(3, "ps3"), - kShaderCompPlatformD3D11(4, "d3d11"), - kShaderCompPlatformGLES20(5, "gles"), - kShaderCompPlatformNaCl(6, "glesdesktop"), - kShaderCompPlatformFlash(7, "flash"), - kShaderCompPlatformD3D11_9x(8, "d3d11_9x"), - kShaderCompPlatformGLES3Plus(9, "gles3"), - kShaderCompPlatformPSP2(10, "psp2"), - kShaderCompPlatformPS4(11, "ps4"), - kShaderCompPlatformXboxOne(12, "xboxone"), - kShaderCompPlatformPSM(13, "psm"), - kShaderCompPlatformMetal(14, "metal"), - kShaderCompPlatformOpenGLCore(15, "glcore"), - kShaderCompPlatformN3DS(16, "n3ds"), - kShaderCompPlatformWiiU(17, "wiiu"), - kShaderCompPlatformVulkan(18, "vulkan"), - kShaderCompPlatformSwitch(19, "switch"), - kShaderCompPlatformXboxOneD3D12(20, "xboxone_d3d12"), - kShaderCompPlatformGameCoreXboxOne(21, "xboxone"), - kShaderCompPlatformGameCoreScarlett(22, "xbox_scarlett"), - kShaderCompPlatformPS5(23, "ps5"), - kShaderCompPlatformPS5NGGC(24, "ps5_nggc"); + None(-1), + GL(0, "openGL"), + D3D9(1, "d3d9"), + Xbox360(2, "xbox360"), + PS3(3, "ps3"), + D3D11(4, "d3d11"), + GLES20(5, "gles"), + NaCl(6, "glesdesktop"), + Flash(7, "flash"), + D3D11_9x(8, "d3d11_9x"), + GLES3Plus(9, "gles3"), + PSP2(10, "psp2"), + PS4(11, "ps4"), + XboxOne(12, "xboxone"), + PSM(13, "psm"), + Metal(14, "metal"), + OpenGLCore(15, "glcore"), + N3DS(16, "n3ds"), + WiiU(17, "wiiu"), + Vulkan(18, "vulkan"), + Switch(19, "switch"), + XboxOneD3D12(20, "xboxone_d3d12"), + GameCoreXboxOne(21, "xboxone"), + GameCoreScarlett(22, "xbox_scarlett"), + PS5(23, "ps5"), + PS5NGGC(24, "ps5_nggc"); internal fun checkProgramUsability(programType: ShaderGpuProgramType): Boolean { return when (this) { - kShaderCompPlatformGL -> programType == ShaderGpuProgramType.kShaderGpuProgramGLLegacy - kShaderCompPlatformD3D9 -> { + GL -> programType == ShaderGpuProgramType.GLLegacy + D3D9 -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramDX9VertexSM20, - ShaderGpuProgramType.kShaderGpuProgramDX9VertexSM30, - ShaderGpuProgramType.kShaderGpuProgramDX9PixelSM20, - ShaderGpuProgramType.kShaderGpuProgramDX9PixelSM30 + ShaderGpuProgramType.DX9VertexSM20, + ShaderGpuProgramType.DX9VertexSM30, + ShaderGpuProgramType.DX9PixelSM20, + ShaderGpuProgramType.DX9PixelSM30 ) } - kShaderCompPlatformXbox360, kShaderCompPlatformPS3, - kShaderCompPlatformPSP2, kShaderCompPlatformPS4, - kShaderCompPlatformXboxOne, kShaderCompPlatformN3DS, - kShaderCompPlatformWiiU, kShaderCompPlatformSwitch, - kShaderCompPlatformXboxOneD3D12, kShaderCompPlatformGameCoreXboxOne, - kShaderCompPlatformGameCoreScarlett, kShaderCompPlatformPS5, - kShaderCompPlatformPS5NGGC -> { + Xbox360, PS3, + PSP2, PS4, + XboxOne, N3DS, + WiiU, Switch, + XboxOneD3D12, GameCoreXboxOne, + GameCoreScarlett, PS5 -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramConsoleVS, - ShaderGpuProgramType.kShaderGpuProgramConsoleFS, - ShaderGpuProgramType.kShaderGpuProgramConsoleHS, - ShaderGpuProgramType.kShaderGpuProgramConsoleDS, - ShaderGpuProgramType.kShaderGpuProgramConsoleGS + ShaderGpuProgramType.ConsoleVS, + ShaderGpuProgramType.ConsoleFS, + ShaderGpuProgramType.ConsoleHS, + ShaderGpuProgramType.ConsoleDS, + ShaderGpuProgramType.ConsoleGS ) } - kShaderCompPlatformD3D11 -> { + PS5NGGC -> programType == ShaderGpuProgramType.PS5NGGC + D3D11 -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramDX11VertexSM40, - ShaderGpuProgramType.kShaderGpuProgramDX11VertexSM50, - ShaderGpuProgramType.kShaderGpuProgramDX11PixelSM40, - ShaderGpuProgramType.kShaderGpuProgramDX11PixelSM50, - ShaderGpuProgramType.kShaderGpuProgramDX11GeometrySM40, - ShaderGpuProgramType.kShaderGpuProgramDX11GeometrySM50, - ShaderGpuProgramType.kShaderGpuProgramDX11HullSM50, - ShaderGpuProgramType.kShaderGpuProgramDX11DomainSM50 + ShaderGpuProgramType.DX11VertexSM40, + ShaderGpuProgramType.DX11VertexSM50, + ShaderGpuProgramType.DX11PixelSM40, + ShaderGpuProgramType.DX11PixelSM50, + ShaderGpuProgramType.DX11GeometrySM40, + ShaderGpuProgramType.DX11GeometrySM50, + ShaderGpuProgramType.DX11HullSM50, + ShaderGpuProgramType.DX11DomainSM50 ) } - kShaderCompPlatformGLES20 -> programType == ShaderGpuProgramType.kShaderGpuProgramGLES - kShaderCompPlatformNaCl -> throw UnsupportedFormatException("Unsupported platform") - kShaderCompPlatformFlash -> throw UnsupportedFormatException("Unsupported platform") - kShaderCompPlatformD3D11_9x -> { + GLES20 -> programType == ShaderGpuProgramType.GLES + NaCl -> throw UnsupportedFormatException("Unsupported platform") + Flash -> throw UnsupportedFormatException("Unsupported platform") + D3D11_9x -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramDX10Level9Vertex, - ShaderGpuProgramType.kShaderGpuProgramDX10Level9Pixel + ShaderGpuProgramType.DX10Level9Vertex, + ShaderGpuProgramType.DX10Level9Pixel ) } - kShaderCompPlatformGLES3Plus -> { + GLES3Plus -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramGLES31AEP, - ShaderGpuProgramType.kShaderGpuProgramGLES31, - ShaderGpuProgramType.kShaderGpuProgramGLES3 + ShaderGpuProgramType.GLES31AEP, + ShaderGpuProgramType.GLES31, + ShaderGpuProgramType.GLES3 ) } - kShaderCompPlatformPSM -> throw UnsupportedFormatException("Unknown") - kShaderCompPlatformMetal -> { + PSM -> throw UnsupportedFormatException("Unknown") + Metal -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramMetalVS, - ShaderGpuProgramType.kShaderGpuProgramMetalFS + ShaderGpuProgramType.MetalVS, + ShaderGpuProgramType.MetalFS ) } - kShaderCompPlatformOpenGLCore -> { + OpenGLCore -> { programType.equalsAnyOf( - ShaderGpuProgramType.kShaderGpuProgramGLCore32, - ShaderGpuProgramType.kShaderGpuProgramGLCore41, - ShaderGpuProgramType.kShaderGpuProgramGLCore43 + ShaderGpuProgramType.GLCore32, + ShaderGpuProgramType.GLCore41, + ShaderGpuProgramType.GLCore43 ) } - kShaderCompPlatformVulkan -> programType == ShaderGpuProgramType.kShaderGpuProgramSPIRV + Vulkan -> programType == ShaderGpuProgramType.SPIRV else -> throw UnsupportedFormatException("Unsupported platform") } } companion object { fun of(value: Int): ShaderCompilerPlatform { - return values().firstOrNull { it.id == value } ?: kShaderCompPlatformNone + return values().firstOrNull { it.id == value } ?: None } } } \ No newline at end of file diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/SpriteImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/SpriteImpl.kt index 4bcbd9e6..46cb5284 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/SpriteImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/SpriteImpl.kt @@ -55,41 +55,38 @@ class SecondarySpriteTexture internal constructor(reader: ObjectReader) { val name = reader.readStringUntilNull() } -@Suppress("EnumEntryName") enum class SpritePackingRotation(val id: UInt) { - kSPRNone(0u), - kSPRFlipHorizontal(1u), - kSPRFlipVertical(2u), - kSPRRotate180(3u), - kSPRRotate90(4u); + None(0u), + FlipHorizontal(1u), + FlipVertical(2u), + Rotate180(3u), + Rotate90(4u); companion object { fun of(value: UInt): SpritePackingRotation { - return values().firstOrNull { it.id == value } ?: kSPRNone + return values().firstOrNull { it.id == value } ?: None } } } -@Suppress("EnumEntryName") enum class SpritePackingMode(val id: UInt) { - kSPMTight(0u), - kSPMRectangle(1u); + Tight(0u), + Rectangle(1u); companion object { fun of(value: UInt): SpritePackingMode { - return values().firstOrNull { it.id == value } ?: kSPMTight + return values().firstOrNull { it.id == value } ?: Tight } } } -@Suppress("EnumEntryName") enum class SpriteMeshType(val id: UInt) { - kSpriteMeshTypeFullRect(0u), - kSpriteMeshTypeTight(1u); + FullRect(0u), + Tight(1u); companion object { fun of(value: UInt): SpriteMeshType { - return values().firstOrNull { it.id == value } ?: kSpriteMeshTypeFullRect + return values().firstOrNull { it.id == value } ?: FullRect } } } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/Texture2DImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/Texture2DImpl.kt index 7e749a76..053aba78 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/Texture2DImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/Texture2DImpl.kt @@ -73,12 +73,12 @@ class Texture2DImpl internal constructor(reader: ObjectReader): TextureImpl(read ResourceReader(mStreamData.path, assetFile, mStreamData.offset, mStreamData.size.toLong()) } else { ResourceReader(reader, reader.absolutePosition, imageDataSize.toLong()) - } + }.registerToManager(assetFile.root.manager) } - val decompressedImageData by lazy { imageData.bytes.decompressTexture() } - val image by lazy { - BufferedImage(mWidth, mHeight, BufferedImage.TYPE_4BYTE_ABGR).apply { + val decompressedImageData get() = imageData.read().decompressTexture() + val image: BufferedImage + get() = BufferedImage(mWidth, mHeight, BufferedImage.TYPE_4BYTE_ABGR).apply { data = Raster.createRaster( ComponentSampleModel( DataBuffer.TYPE_BYTE, mWidth, mHeight, 4, @@ -88,7 +88,6 @@ class Texture2DImpl internal constructor(reader: ObjectReader): TextureImpl(read null ) } - } private val areaIndices by lazy { 0 until mWidth * mHeight } private val dataSizeIndices by lazy { 0 until mWidth * mHeight * 4 step 4 } @@ -385,9 +384,9 @@ class Texture2DImpl internal constructor(reader: ObjectReader): TextureImpl(read mTextureFormat == TextureFormat.ETC_RGB4Crunched || mTextureFormat == TextureFormat.ETC2_RGBA8Crunched ) { - TextureDecoder.unpackUnityCrunch(imageData.bytes) + TextureDecoder.unpackUnityCrunch(imageData.read()) } else { - TextureDecoder.unpackCrunch(imageData.bytes) + TextureDecoder.unpackCrunch(imageData.read()) } } } @@ -401,8 +400,10 @@ enum class TextureFormat(val id: Int) { RGBA32(4), ARGB32(5), RGB565(7), + BGR24(8), R16(9), DXT1(10), + DXT3(11), DXT5(12), RGBA4444(13), BGRA32(14), @@ -414,10 +415,11 @@ enum class TextureFormat(val id: Int) { RGBAFloat(20), YUY2(21), RGB9e5Float(22), - BC4(26), - BC5(27), + RGBFloat(23), BC6H(24), BC7(25), + BC4(26), + BC5(27), DXT1Crunched(28), DXT5Crunched(29), PVRTC_RGB2(30), diff --git a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/VideoClipImpl.kt b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/VideoClipImpl.kt index 71860130..6c309413 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/VideoClipImpl.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/dataImpl/VideoClipImpl.kt @@ -33,7 +33,7 @@ class VideoClipImpl internal constructor(reader: ObjectReader): NamedObjectImpl( } } else { ResourceReader(reader, reader.absolutePosition, mExternamResource.mSize) - } + }.registerToManager(assetFile.root.manager) } } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/export/EndianByteArrayWriter.kt b/src/main/kotlin/io/github/deficuet/unitykt/export/EndianByteArrayWriter.kt index 6c405b28..c711b0b1 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/export/EndianByteArrayWriter.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/export/EndianByteArrayWriter.kt @@ -1,18 +1,18 @@ package io.github.deficuet.unitykt.export -import io.github.deficuet.unitykt.util.EndianType import java.nio.ByteBuffer +import java.nio.ByteOrder internal class EndianByteArrayWriter( size: Int, - var endianType: EndianType = EndianType.BigEndian + var endianType: ByteOrder = ByteOrder.BIG_ENDIAN ): AutoCloseable { val array = ByteArray(size) val length = size var position = 0 @Suppress("SameParameterValue") - private fun newByteBuffer(capacity: Int) = ByteBuffer.allocate(capacity).order(endianType.order) + private fun newByteBuffer(capacity: Int) = ByteBuffer.allocate(capacity).order(endianType) private fun write(bytes: ByteArray) { System.arraycopy(bytes, 0, array, position, bytes.size) diff --git a/src/main/kotlin/io/github/deficuet/unitykt/export/smolv/SmolvDecoder.kt b/src/main/kotlin/io/github/deficuet/unitykt/export/smolv/SmolvDecoder.kt index 179e4a7c..71cb34c4 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/export/smolv/SmolvDecoder.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/export/smolv/SmolvDecoder.kt @@ -3,8 +3,8 @@ package io.github.deficuet.unitykt.export.smolv import io.github.deficuet.unitykt.export.EndianByteArrayWriter import io.github.deficuet.unitykt.export.spirv.of import io.github.deficuet.unitykt.util.EndianBinaryReader -import io.github.deficuet.unitykt.util.EndianType import io.github.deficuet.unitykt.util.Reference +import java.nio.ByteOrder internal class SmolvDecoder private constructor() { companion object { @@ -23,7 +23,7 @@ internal class SmolvDecoder private constructor() { return reader.withMark { position += headerSize - 4 val endian = reader.endian - resetEndian(EndianType.LittleEndian) + resetEndian(ByteOrder.LITTLE_ENDIAN) val size = readInt() resetEndian(endian) size diff --git a/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/FlagsAttributeEnum.kt b/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/FlagsAttributeEnum.kt index d61130a2..617c5c35 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/FlagsAttributeEnum.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/FlagsAttributeEnum.kt @@ -14,7 +14,6 @@ internal interface FlagsAttributeEnum> { internal abstract class FlagsAttributeEnumCompanion> -@Suppress("unused") internal inline fun > FlagsAttributeEnumCompanion.of(value: Long): EnumSet { val enums = enumValues() val enumClass = E::class.java diff --git a/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/Module.kt b/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/Module.kt index 196c4edb..226bd2eb 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/Module.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/Module.kt @@ -1,7 +1,7 @@ package io.github.deficuet.unitykt.export.spirv import io.github.deficuet.unitykt.util.EndianByteArrayReader -import io.github.deficuet.unitykt.util.EndianType +import java.nio.ByteOrder internal class Module private constructor( val header: Header, @@ -176,7 +176,7 @@ internal class Module private constructor( companion object { fun readFrom(data: ByteArray): Module { - val reader = EndianByteArrayReader(data, endian = EndianType.LittleEndian) + val reader = EndianByteArrayReader(data, endian = ByteOrder.LITTLE_ENDIAN) reader += 4 //magic val versionNum = reader.readUInt() val majorVersion = versionNum.shr(16).toInt() diff --git a/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/NumericalEnum.kt b/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/NumericalEnum.kt index 627fa223..8471e007 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/NumericalEnum.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/export/spirv/NumericalEnum.kt @@ -6,7 +6,6 @@ internal interface NumericalEnum> { internal abstract class NumericalEnumCompanion, C: Comparable> -@Suppress("unused") internal inline fun , N: Comparable> NumericalEnumCompanion.of(value: N): E { val values = enumValues() return values.firstOrNull { (it as NumericalEnum<*>).id == value } ?: values.first() diff --git a/src/main/kotlin/io/github/deficuet/unitykt/file/AssetBundleFile.kt b/src/main/kotlin/io/github/deficuet/unitykt/file/AssetBundleFile.kt index 8f1a3140..426ac693 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/AssetBundleFile.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/AssetBundleFile.kt @@ -20,7 +20,7 @@ abstract class RawAssetFile { get() = if (bundleParent is ImportContext) bundleParent as ImportContext else bundleParent.root companion object { - val resourceExt = listOf(".resS", ".resource", ".config", ".xml", ".dat") + val resourceExt = arrayOf(".resS", ".resource", ".config", ".xml", ".dat") } } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/file/BundleFile.kt b/src/main/kotlin/io/github/deficuet/unitykt/file/BundleFile.kt index dd3f99d7..cf3e9915 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/BundleFile.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/BundleFile.kt @@ -2,6 +2,16 @@ package io.github.deficuet.unitykt.file import io.github.deficuet.unitykt.util.* +internal class ArchiveFlags { + companion object { + const val CompressionTypeMask = 0x3Fu + const val BlocksAndDirectoryInfoCombined = 0x40u + const val BlocksInfoAtTheEnd = 0x80u + const val OldWebPluginCompatibility = 0x100u + const val BlockInfoNeedPaddingAtStart = 0x200u + } +} + class BundleFile( private val reader: EndianBinaryReader, override val bundleParent: AssetBundleFile, @@ -29,7 +39,7 @@ class BundleFile( "UnityArchive" -> throw UnsupportedFormatException("Unsupported file type UnityArchive") "UnityWeb", "UnityRaw" -> { if (hVersion == 6u) readFS() - readWebRaw() + else readWebRaw() } "UnityFS" -> readFS() else -> throw UnsupportedFormatException("Unknown Bundle Signature") @@ -51,8 +61,7 @@ class BundleFile( reader += 4 * 2 * (levelCount - 1) blocksInfo.add( Block( - reader.readUInt(), reader.readUInt(), - (if (isCompressed) 1 else 0).toUShort() + reader.readUInt(), reader.readUInt(), 0u ) ) if (hVersion >= 2u) { @@ -63,7 +72,7 @@ class BundleFile( } reader.position = hSize val uncompressedBytes = with(reader.read(blocksInfo[0].compressedSize.toInt())) { - if (blocksInfo[0].flags == 1.toUShort()) { + if (isCompressed) { CompressUtils.lzmaDecompress(this) } else this } @@ -95,7 +104,7 @@ class BundleFile( if (hVersion >= 7u) reader.alignStream(16) val blockOffset = reader.position var blocksInfoBytes: ByteArray - if ((hFlags and 0x80u) != 0u) { + if (hFlags.and(ArchiveFlags.BlocksInfoAtTheEnd) != 0u) { blocksInfoBytes = reader.withMark { position = length - hCompressedBlockSize.toLong() read(hCompressedBlockSize.toInt()) @@ -103,7 +112,7 @@ class BundleFile( } else { blocksInfoBytes = reader.read(hCompressedBlockSize.toInt()) } - when (hFlags and 0x3Fu) { + when (hFlags.and(ArchiveFlags.CompressionTypeMask)) { 1u -> blocksInfoBytes = CompressUtils.lzmaDecompress(blocksInfoBytes) 2u, 3u -> blocksInfoBytes = CompressUtils.lz4Decompress(blocksInfoBytes, uncompressedBlockSize.toInt()) } @@ -132,11 +141,14 @@ class BundleFile( ) } } + if (hFlags.and(ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0u) { + reader.alignStream(16) + } return EndianByteArrayReader( manualOffset = blocksInfoReader.realOffset ) { blocksInfo.map { block -> - when (block.flags and 0x3Fu) { + when (block.flags.and(ArchiveFlags.CompressionTypeMask.toUShort())) { 1.toUShort() -> CompressUtils.lzmaDecompress( reader.read(block.compressedSize.toInt()) ) 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 c4448dd4..b9bd7217 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedFile.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedFile.kt @@ -3,32 +3,32 @@ package io.github.deficuet.unitykt.file import io.github.deficuet.unitykt.data.* import io.github.deficuet.unitykt.util.EndianBinaryReader import io.github.deficuet.unitykt.util.EndianByteArrayReader -import io.github.deficuet.unitykt.util.EndianType import java.io.File +import java.nio.ByteOrder class FormatVersion private constructor() { companion object { -// const val kUnsupported = 1u - const val kUnknown_2 = 2u - const val kUnknown_3 = 3u - const val kUnknown_5 = 5u - const val kUnknown_6 = 6u - const val kUnknown_7 = 7u - const val kUnknown_8 = 8u - const val kUnknown_9 = 9u - const val kUnknown_10 = 10u - const val kHasScriptTypeIndex = 11u - const val kUnknown_12 = 12u - const val kHasTypeTreeHashes = 13u - const val kUnknown_14 = 14u - const val kSupportsStrippedObject = 15u - const val kRefactoredClassId = 16u - const val kRefactorTypeData = 17u -// const val kRefactorShareableTypeTreeData = 18u - const val kTypeTreeNodeWithTypeFlags = 19u - const val kSupportsRefObject = 20u - const val kStoresTypeDependencies = 21u - const val kLargeFilesSupport = 22u +// const val Unsupported = 1u + const val Unknown_2 = 2u + const val Unknown_3 = 3u + const val Unknown_5 = 5u + const val Unknown_6 = 6u + const val Unknown_7 = 7u + const val Unknown_8 = 8u + const val Unknown_9 = 9u + const val Unknown_10 = 10u + const val HasScriptTypeIndex = 11u + const val Unknown_12 = 12u + const val HasTypeTreeHashes = 13u + const val Unknown_14 = 14u + const val SupportsStrippedObject = 15u + const val RefactoredClassId = 16u + const val RefactorTypeData = 17u +// const val RefactorShareableTypeTreeData = 18u + const val TypeTreeNodeWithTypeFlags = 19u + const val SupportsRefObject = 20u + const val StoresTypeDependencies = 21u + const val LargeFilesSupport = 22u } } @@ -39,16 +39,54 @@ class BuildType(private val type: String) { @Suppress("EnumEntryName") enum class BuildTarget(val id: Int) { - DashboardWidget(1), StandaloneOSX(2), StandaloneOSXPPC(3), StandaloneOSXIntel(4), - StandaloneWindows(5), WebPlayer(6), WebPlayerStreamed(7), Wii(8), iOS(9), - PS3(10), XBOX360(11), Android(13), StandaloneGLESEmu(14), NaCl(16), - StandaloneLinux(17), FlashPlayer(18), StandaloneWindows64(19), WebGL(20), - WSAPlayer(21), StandaloneLinux64(24), StandaloneLinuxUniversal(25), WP8Player(26), - StandaloneOSXIntel64(27), BlackBerry(28), Tizen(29), PSP2(30), PS4(31), PSM(32), - XboxOne(33), SamsungTV(34), N3DS(35), WiiU(36), tvOS(37), Switch(38), Lumin(39), - Stadia(40), CloudRendering(41), GameCoreXboxSeries(42), GameCoreXboxOne(43), PS5(44), + NoTarget(-2), + AnyPlayer(-1), + ValidPlayer(1), + StandaloneOSX(2), + StandaloneOSXPPC(3), + StandaloneOSXIntel(4), + StandaloneWindows(5), + WebPlayer(6), + WebPlayerStreamed(7), + Wii(8), + iOS(9), + PS3(10), + XBOX360(11), + Broadcom(12), + Android(13), + StandaloneGLESEmu(14), + StandaloneGLES20Emu(15), + NaCl(16), + StandaloneLinux(17), + FlashPlayer(18), + StandaloneWindows64(19), + WebGL(20), + WSAPlayer(21), + StandaloneLinux64(24), + StandaloneLinuxUniversal(25), + WP8Player(26), + StandaloneOSXIntel64(27), + BlackBerry(28), + Tizen(29), + PSP2(30), + PS4(31), + PSM(32), + XboxOne(33), + SamsungTV(34), + N3DS(35), + WiiU(36), + tvOS(37), + Switch(38), + Lumin(39), + Stadia(40), + CloudRendering(41), + GameCoreXboxSeries(42), + GameCoreXboxOne(43), + PS5(44), + EmbeddedLinux(45), + QNX(46), - UnknownPlatform(9999), NoTarget(-2); + UnknownPlatform(9999); companion object { fun isDefined(v: Int) = values().any { it.id == v } @@ -156,21 +194,21 @@ class SerializedFile( get() = objects.associateBy { it.mPathID } init { - if (hVersion >= FormatVersion.kUnknown_9) { + if (hVersion >= FormatVersion.Unknown_9) { hEndian = reader.readByte() reader += 3 //hReserved = reader.read(3) } else { reader.position = hFileSize - hMetadataSize.toLong() hEndian = reader.readByte() } - if (hVersion >= FormatVersion.kLargeFilesSupport) { + if (hVersion >= FormatVersion.LargeFilesSupport) { hMetadataSize = reader.readUInt() hFileSize = reader.readLong() hDataOffset = reader.readLong() reader += 8 //unknown } - if (hEndian == 0u.toUByte()) reader.resetEndian(EndianType.LittleEndian) - if (hVersion >= FormatVersion.kUnknown_7) { + if (hEndian == 0u.toUByte()) reader.resetEndian(ByteOrder.LITTLE_ENDIAN) + if (hVersion >= FormatVersion.Unknown_7) { unityVersion = reader.readStringUntilNull() buildType = buildTypeRegex.findAll(unityVersion).iterator().let { BuildType( @@ -180,20 +218,20 @@ class SerializedFile( } version = versionSplitRegex.split(unityVersion).map { it.toInt() }.toIntArray() } - if (hVersion >= FormatVersion.kUnknown_8) { + if (hVersion >= FormatVersion.Unknown_8) { val targetPlatformID = reader.readInt() if (BuildTarget.isDefined(targetPlatformID)) { targetPlatform = BuildTarget.values().first { it.id == targetPlatformID } } } - enableTypeTree = if (hVersion >= FormatVersion.kHasTypeTreeHashes) reader.readBool() else true + enableTypeTree = if (hVersion >= FormatVersion.HasTypeTreeHashes) reader.readBool() else true val typeCount = reader.readInt() val typesList = mutableListOf() for (i in 0 until typeCount) { typesList.add(readSerializedType(false)) } types = typesList - if (hVersion in with(FormatVersion) { kUnknown_7 until kUnknown_14 }) { + if (hVersion in with(FormatVersion) { Unknown_7 until Unknown_14 }) { bigIDEnabled = reader.readInt() } val objectCount = reader.readInt() @@ -201,20 +239,20 @@ class SerializedFile( for (j in 0 until objectCount) { val mPathID = if (bigIDEnabled != 0) { reader.readLong() - } else if (hVersion < FormatVersion.kUnknown_14) { + } else if (hVersion < FormatVersion.Unknown_14) { reader.readInt().toLong() } else { reader.alignStream() reader.readLong() } - var byteStart = if (hVersion >= FormatVersion.kLargeFilesSupport) { + var byteStart = if (hVersion >= FormatVersion.LargeFilesSupport) { reader.readLong() } else reader.readUInt().toLong() byteStart += hDataOffset val byteSize = reader.readUInt() val typeID = reader.readInt() val classID: Int; val serialisedType: SerializedType? - if (hVersion < FormatVersion.kRefactoredClassId) { + if (hVersion < FormatVersion.RefactoredClassId) { classID = reader.readUShort().toInt() serialisedType = types.find { it.classID == typeID } } else { @@ -224,15 +262,15 @@ class SerializedFile( } } var isDestoryed: UShort = 0u - if (hVersion < FormatVersion.kHasScriptTypeIndex) { + if (hVersion < FormatVersion.HasScriptTypeIndex) { isDestoryed = reader.readUShort() } - if (hVersion in with(FormatVersion) { kHasScriptTypeIndex until kRefactorTypeData } ) { + if (hVersion in with(FormatVersion) { HasScriptTypeIndex until RefactorTypeData } ) { serialisedType?.scriptTypeIndex = reader.readShort() } var stripped: UByte = 0u - if (hVersion == FormatVersion.kSupportsStrippedObject || - hVersion == FormatVersion.kRefactoredClassId) { + if (hVersion == FormatVersion.SupportsStrippedObject || + hVersion == FormatVersion.RefactoredClassId) { stripped = reader.readByte() } objectInfoList.add( @@ -243,13 +281,13 @@ class SerializedFile( ) } this.objectInfoList = objectInfoList - if (hVersion >= FormatVersion.kHasScriptTypeIndex) { + if (hVersion >= FormatVersion.HasScriptTypeIndex) { val scriptCount = reader.readInt() for (k in 0 until scriptCount) { scriptTypes.add( ObjectIdentifier( serializedFileIndex = reader.readInt(), - identifierInFile = if (hVersion < FormatVersion.kUnknown_14) { + identifierInFile = if (hVersion < FormatVersion.Unknown_14) { reader.readInt().toLong() } else { reader.alignStream() @@ -262,9 +300,9 @@ class SerializedFile( val externalsCount = reader.readInt() val externals = mutableListOf() for (l in 0 until externalsCount) { - if (hVersion >= FormatVersion.kUnknown_6) reader.readStringUntilNull() + if (hVersion >= FormatVersion.Unknown_6) reader.readStringUntilNull() var guid = ByteArray(16); var type = 0 - if (hVersion >= FormatVersion.kUnknown_5) { + if (hVersion >= FormatVersion.Unknown_5) { guid = reader.read(16) type = reader.readInt() } @@ -276,13 +314,13 @@ class SerializedFile( ) } this.externals = externals - if (hVersion >= FormatVersion.kSupportsRefObject) { + if (hVersion >= FormatVersion.SupportsRefObject) { val refTypesCount = reader.readInt() for (m in 0 until refTypesCount) { refTypes.add(readSerializedType(true)) } } - userInformation = if (hVersion >= FormatVersion.kUnknown_5) { + userInformation = if (hVersion >= FormatVersion.Unknown_5) { reader.readStringUntilNull() } else "" //region readObjects @@ -338,24 +376,24 @@ class SerializedFile( var nameSpace = "" var asmName = "" var typeDependencies: IntArray = intArrayOf() - if (hVersion >= FormatVersion.kRefactoredClassId) isStrippedType = reader.readBool() - if (hVersion >= FormatVersion.kRefactorTypeData) scriptTypeIndex = reader.readShort() - if (hVersion >= FormatVersion.kHasTypeTreeHashes) { + if (hVersion >= FormatVersion.RefactoredClassId) isStrippedType = reader.readBool() + if (hVersion >= FormatVersion.RefactorTypeData) scriptTypeIndex = reader.readShort() + if (hVersion >= FormatVersion.HasTypeTreeHashes) { if (isRefType && scriptTypeIndex >= 0) scriptID = reader.read(16) - else if ((hVersion < FormatVersion.kRefactoredClassId && classID < 0) || - (hVersion >= FormatVersion.kRefactoredClassId && classID == 114)) { + else if ((hVersion < FormatVersion.RefactoredClassId && classID < 0) || + (hVersion >= FormatVersion.RefactoredClassId && classID == 114)) { scriptID = reader.read(16) } oldTypeHash = reader.read(16) } if (enableTypeTree) { - typeTree = if (hVersion >= FormatVersion.kUnknown_12 || hVersion == FormatVersion.kUnknown_10) { + typeTree = if (hVersion >= FormatVersion.Unknown_12 || hVersion == FormatVersion.Unknown_10) { typeTreeBlobRead() } else { readTypeTree() } } - if (hVersion >= FormatVersion.kStoresTypeDependencies) { + if (hVersion >= FormatVersion.StoresTypeDependencies) { if (isRefType) { className = reader.readStringUntilNull() nameSpace = reader.readStringUntilNull() @@ -396,7 +434,7 @@ class SerializedFile( metaFlag = reader.readInt() ) ) - if (hVersion >= FormatVersion.kTypeTreeNodeWithTypeFlags) { + if (hVersion >= FormatVersion.TypeTreeNodeWithTypeFlags) { reader += 8 //refTypeHash: ULong } } @@ -433,11 +471,11 @@ class SerializedFile( val type = reader.readStringUntilNull() val name = reader.readStringUntilNull() val byteSize = reader.readInt() - if (hVersion == FormatVersion.kUnknown_2) reader += 4 //variableCount - val index = if (hVersion != FormatVersion.kUnknown_3) reader.readInt() else 0 + if (hVersion == FormatVersion.Unknown_2) reader += 4 //variableCount + val index = if (hVersion != FormatVersion.Unknown_3) reader.readInt() else 0 val typeFlags = reader.readInt() val version = reader.readInt() - val metaFlag = if (hVersion != FormatVersion.kUnknown_3) reader.readInt() else 0 + val metaFlag = if (hVersion != FormatVersion.Unknown_3) reader.readInt() else 0 newTree.nodes.add(SerializedType.TreeNode( type, name, byteSize, index, typeFlags, version, metaFlag, level )) 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 61eb3b58..1c63a6cc 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedType.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/SerializedType.kt @@ -3,6 +3,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 kotlin.collections.set data class SerializedType( @@ -55,7 +57,13 @@ data class SerializedType( var value: Any? = null when (node.type) { "SInt8" -> value = reader.readSByte() - "UInt8", "char" -> value = reader.readByte() + "UInt8" -> value = reader.readByte() + "char" -> { + value = Char( + ByteBuffer.wrap(reader.read(2)) + .order(ByteOrder.LITTLE_ENDIAN).short.toUShort() + ) + } "SInt16", "short" -> value = reader.readShort() "UInt16", "unsigned short" -> value = reader.readUShort() "SInt32", "int" -> value = reader.readInt() @@ -69,7 +77,7 @@ data class SerializedType( append = false val str = reader.readAlignedString() builder.append("${"\t".repeat(node.level)}${node.type} ${node.name} = \"$str\"\r\n") - intRef += 3 + intRef += nodes.getNode(intRef.value).size - 1 } "map" -> { if ((nodes[intRef + 1].metaFlag and 0x4000) != 0) align = true @@ -153,7 +161,7 @@ data class SerializedType( "double" -> reader.readDouble() "bool" -> reader.readBool() "string" -> { - iRef += 3 + iRef += nodes.getNode(iRef.value).size - 1 reader.readAlignedString() } "map" -> { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/file/WebFile.kt b/src/main/kotlin/io/github/deficuet/unitykt/file/WebFile.kt index 542b1926..12cfe5f2 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/file/WebFile.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/file/WebFile.kt @@ -3,7 +3,7 @@ package io.github.deficuet.unitykt.file import io.github.deficuet.unitykt.util.CompressUtils import io.github.deficuet.unitykt.util.EndianBinaryReader import io.github.deficuet.unitykt.util.EndianByteArrayReader -import io.github.deficuet.unitykt.util.EndianType +import java.nio.ByteOrder class WebFile( preReader: EndianBinaryReader, @@ -20,7 +20,7 @@ class WebFile( reader = if (magic.contentEquals(CompressUtils.GZIP_MAGIC)) { EndianByteArrayReader( CompressUtils.gzipDecompress(preReader.bytes), - endian = EndianType.LittleEndian + endian = ByteOrder.LITTLE_ENDIAN ) } else { preReader.position = 0x20 @@ -28,10 +28,10 @@ class WebFile( if (magic.contentEquals(CompressUtils.BROTLI_MAGIC)) { EndianByteArrayReader( CompressUtils.brotliDecompress(preReader.bytes), - endian = EndianType.LittleEndian + endian = ByteOrder.LITTLE_ENDIAN ) } else { - preReader.resetEndian(EndianType.LittleEndian) + preReader.resetEndian(ByteOrder.LITTLE_ENDIAN) } } if (reader.readStringUntilNull() == "UnityWebData1.0") { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/math/Matrix4x4.kt b/src/main/kotlin/io/github/deficuet/unitykt/math/Matrix4x4.kt index 366760cf..b6430bf1 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/math/Matrix4x4.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/math/Matrix4x4.kt @@ -1,5 +1,7 @@ package io.github.deficuet.unitykt.math +import io.github.deficuet.unitykt.cast + data class Matrix4x4(private val data: DoubleArray) { private constructor(dataBlock: () -> DoubleArray): this(dataBlock()) @@ -48,8 +50,8 @@ data class Matrix4x4(private val data: DoubleArray) { } operator fun times(m: Matrix4x4) = Matrix4x4 { - mutableList { - val t = this@Matrix4x4 + val t = this + mutableList { for (tc in 0..3) { for (mr in 0..3) { var sum = 0.0 @@ -68,17 +70,16 @@ data class Matrix4x4(private val data: DoubleArray) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - - other as Matrix4x4 - - if (!data.contentEquals(other.data)) return false - - return true + return data.contentEquals(other.cast().data) } override fun toString(): String { - return intArrayOf(0, 1, 2, 3).map { column(it) }.map { doubleArrayOf(it.x, it.y, it.z, it.w) } - .map { it.maxOf { d -> "%.4f".format(d).length } }.joinToString(" ") { "%-${it}.4f" } + return intArrayOf(0, 1, 2, 3).map { + val c = column(it) + doubleArrayOf(c.x, c.y, c.z, c.w).maxOf { + d -> "%.4f".format(d).length + } + }.joinToString(" ") { "%-${it}.4f" } .let { "| $it |\n" }.repeat(4).format(*data.toTypedArray()).trim() } diff --git a/src/main/kotlin/io/github/deficuet/unitykt/math/Quaternion.kt b/src/main/kotlin/io/github/deficuet/unitykt/math/Quaternion.kt index 3dbda469..ba68a2f9 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/math/Quaternion.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/math/Quaternion.kt @@ -1,5 +1,7 @@ package io.github.deficuet.unitykt.math +import io.github.deficuet.unitykt.cast + data class Quaternion(val a: Double, val b: Double, val c: Double, val d: Double) { constructor(a: Float, b: Float, c: Float, d: Float): this(a.toDouble(), b.toDouble(), c.toDouble(), d.toDouble()) @@ -18,8 +20,6 @@ data class Quaternion(val a: Double, val b: Double, val c: Double, val d: Double infix fun dot(other: Quaternion) = a * other.a + b * other.b + c * other.c + d * other.d - fun approxEquals(other: Quaternion): Boolean = dot(other) > 1.0 - kEpsilon - override fun hashCode(): Int { return a.hashCode() .xor(b.hashCode().shl(2)) @@ -27,21 +27,10 @@ data class Quaternion(val a: Double, val b: Double, val c: Double, val d: Double .xor(d.hashCode().shr(1)) } - /** - * @see approxEquals - */ override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - - other as Quaternion - - if (a != other.a) return false - if (b != other.b) return false - if (c != other.c) return false - if (d != other.d) return false - - return true + return dot(other.cast()) > 1.0 - kEpsilon } override fun toString(): String { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector.kt b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector.kt index 12e32fa9..229a14d5 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector.kt @@ -1,7 +1,7 @@ package io.github.deficuet.unitykt.math abstract class Vector { - abstract fun normalize() + abstract val unit: Vector companion object { internal const val kEpsilonSqrt = 0.0031622776601683794 //sqrt(0.00001) diff --git a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector2.kt b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector2.kt index 8b2165f5..1f3e20f4 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector2.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector2.kt @@ -1,78 +1,65 @@ package io.github.deficuet.unitykt.math +import io.github.deficuet.unitykt.cast import kotlin.math.sqrt -class Vector2(private var _x: Double, private var _y: Double): Vector() { +class Vector2(val x: Double, val y: Double): Vector() { constructor(x: Float, y: Float): this(x.toDouble(), y.toDouble()) - val x by ::_x - val y by ::_y - val vector3: Vector3 get() = Vector3(this, 0.0) val vector4: Vector4 get() = Vector4(this, 0.0, 0.0) - val length2 get() = _x * _x + _y * _y + val length2 get() = x * x + y * y - override fun normalize() { - if (length2 > kEpsilonSqrt) { - with(1 / sqrt(length2)) { - _x *= this - _y *= this + override val unit: Vector2 + get() { + return if (length2 > kEpsilonSqrt) { + with(1 / sqrt(length2)) { + Vector2(x * this, y * this) + } + } else { + Zero } - } else { - _x = 0.0; _y = 0.0 } - } operator fun get(index: Int): Double { return when (index) { - 0 -> _x - 1 -> _y + 0 -> x + 1 -> y else -> throw IndexOutOfBoundsException("Vector2 has 2 components only.") } } - operator fun plus(other: Vector2) = Vector2(_x + other.x, _y + other.y) - - operator fun minus(other: Vector2) = Vector2(_x - other.x, _y - other.y) + operator fun plus(other: Vector2) = Vector2(x + other.x, y + other.y) - operator fun times(other: Vector2) = Vector2(_x * other.x, _y * other.y) + operator fun minus(other: Vector2) = Vector2(x - other.x, y - other.y) - operator fun div(other: Vector2) = Vector2(_x / other.x, _y / other.y) + operator fun times(other: Vector2) = Vector2(x * other.x, y * other.y) - operator fun unaryMinus() = Vector2(-_x, -_y) + operator fun div(other: Vector2) = Vector2(x / other.x, y / other.y) - operator fun times(m: N): Vector2 where N: Number, N: Comparable = Vector2(_x * m, _y * m) + operator fun unaryMinus() = Vector2(-x, -y) - operator fun div(d: N): Vector2 where N: Number, N: Comparable = Vector2(_x / d, _y / d) + operator fun times(m: N): Vector2 where N: Number, N: Comparable = Vector2(x * m, y * m) - operator fun component1() = _x - operator fun component2() = _y + operator fun div(d: N): Vector2 where N: Number, N: Comparable = Vector2(x / d, y / d) - infix fun approxEquals(other: Vector2): Boolean = minus(other).length2 < kEpsilon2 + operator fun component1() = x + operator fun component2() = y override fun hashCode(): Int { - return _x.hashCode().xor(_y.hashCode().shl(2)) + return x.hashCode().xor(y.hashCode().shl(2)) } - /** - * @see approxEquals - */ override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - - other as Vector2 - - if (_x != other.x) return false - if (_y != other.y) return false - - return true + return minus(other.cast()).length2 < kEpsilon2 } override fun toString(): String { - return "Vector(x, y) = ($_x, $_y)" + return "Vector(x, y) = ($x, $y)" } companion object { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector3.kt b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector3.kt index fa52714a..bce3c995 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector3.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector3.kt @@ -1,85 +1,69 @@ package io.github.deficuet.unitykt.math +import io.github.deficuet.unitykt.cast import kotlin.math.sqrt -class Vector3(private var _x: Double, private var _y: Double, private var _z: Double): Vector() { +class Vector3(val x: Double, val y: Double, val z: Double): Vector() { constructor(x: Float, y: Float, z: Float): this(x.toDouble(), y.toDouble(), z.toDouble()) constructor(v2: Vector2, z: Double): this(v2.x, v2.y, z) - val x by ::_x - val y by ::_y - val z by ::_z - - val vector2: Vector2 get() = Vector2(_x, _y) + val vector2: Vector2 get() = Vector2(x, y) val vector4: Vector4 get() = Vector4(this, 0.0) - val length2 get() = _x * _x + _y * _y + _z * _z + val length2 get() = x * x + y * y + z * z - override fun normalize() { - if (length2 > kEpsilonSqrt) { - with(1 / sqrt(length2)) { - _x *= this - _y *= this - _z *= this + override val unit: Vector3 + get() { + return if (length2 > kEpsilonSqrt) { + with(1 / sqrt(length2)) { + Vector3(x * this, y * this, z * this) + } + } else { + Zero } - } else { - _x = 0.0; _y = 0.0; _z = 0.0 } - } operator fun get(index: Int): Double { return when (index) { - 0 -> _x - 1 -> _y - 2 -> _z + 0 -> x + 1 -> y + 2 -> z else -> throw IndexOutOfBoundsException("Vector3 has 3 components only.") } } - operator fun plus(other: Vector3) = Vector3(_x + other.x, _y + other.y, _z + other.z) - - operator fun minus(other: Vector3) = Vector3(_x - other.x, _y - other.y, _z - other.z) + operator fun plus(other: Vector3) = Vector3(x + other.x, y + other.y, z + other.z) - operator fun times(other: Vector3) = Vector3(_x * other.x, _y * other.y, _z * other.z) + operator fun minus(other: Vector3) = Vector3(x - other.x, y - other.y, z - other.z) - operator fun div(other: Vector3) = Vector3(_x / other.x, _y / other.y, _z / other.z) + operator fun times(other: Vector3) = Vector3(x * other.x, y * other.y, z * other.z) - operator fun unaryMinus() = Vector3(-_x, -_y, -_z) + operator fun div(other: Vector3) = Vector3(x / other.x, y / other.y, z / other.z) - operator fun times(m: N) where N: Number, N: Comparable = Vector3(_x * m, _y * m, _z * m) + operator fun unaryMinus() = Vector3(-x, -y, -z) - operator fun div(d: N) where N: Number, N: Comparable = Vector3(_x / d, _y / d, _z / d) + operator fun times(m: N) where N: Number, N: Comparable = Vector3(x * m, y * m, z * m) - operator fun component1() = _x - operator fun component2() = _y - operator fun component3() = _z + operator fun div(d: N) where N: Number, N: Comparable = Vector3(x / d, y / d, z / d) - infix fun approxEquals(other: Vector3): Boolean = minus(other).length2 < kEpsilon2 + operator fun component1() = x + operator fun component2() = y + operator fun component3() = z override fun hashCode(): Int { - return _x.hashCode().xor(_y.hashCode().shl(2)).xor(_z.hashCode().shr(2)) + return x.hashCode().xor(y.hashCode().shl(2)).xor(z.hashCode().shr(2)) } - /** - * @see approxEquals - */ override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - - other as Vector3 - - if (_x != other.x) return false - if (_y != other.y) return false - if (_z != other.z) return false - - return true + return minus(other.cast()).length2 < kEpsilon2 } override fun toString(): String { - return "Vector(x, y, z) = ($_x, $_y, $_z)" + return "Vector(x, y, z) = ($x, $y, $z)" } companion object { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector4.kt b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector4.kt index 8e65c6fe..93766731 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/math/Vector4.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/math/Vector4.kt @@ -1,10 +1,11 @@ package io.github.deficuet.unitykt.math +import io.github.deficuet.unitykt.cast import kotlin.math.sqrt class Vector4( - private var _x: Double, private var _y: Double, - private var _z: Double, private var _w: Double + val x: Double, val y: Double, + val z: Double, val w: Double ): Vector() { constructor(x: Float, y: Float, z: Float, w: Float): this(x.toDouble(), y.toDouble(), z.toDouble(), w.toDouble()) @@ -13,91 +14,71 @@ class Vector4( constructor(v3: Vector3, w: Double): this(v3.x, v3.y, v3.z, w) - val x by ::_x - val y by ::_y - val z by ::_z - val w by ::_w + val vector2: Vector2 get() = Vector2(x, y) - val vector2: Vector2 get() = Vector2(_x, _y) + val vector3: Vector3 get() = Vector3(x, y, z) - val vector3: Vector3 get() = Vector3(_x, _y, _z) + val color: Color get() = Color(x, y, z, w) - val color: Color get() = Color(_x, _y, _z, _w) + val length2 get() = x * x + y * y + z * z + w * w - val length2 get() = _x * _x + _y * _y + _z * _z + _w * _w - - override fun normalize() { - if (length2 > kEpsilonSqrt) { - with(1 / sqrt(length2)) { - _x *= this - _y *= this - _z *= this - _w *= this + override val unit: Vector4 + get() { + return if(length2 > kEpsilonSqrt) { + with(1 / sqrt(length2)) { + Vector4(x * this, y * this, z * this, w * this) + } + } else { + Zero } - } else { - _x = 0.0; _y = 0.0; _z = 0.0; _w = 0.0 } - } operator fun get(index: Int): Double { return when (index) { - 0 -> _x - 1 -> _y - 2 -> _z - 3 -> _w + 0 -> x + 1 -> y + 2 -> z + 3 -> w else -> throw IndexOutOfBoundsException("Vector4 has 4 components only.") } } - operator fun plus(other: Vector4) = Vector4(_x + other.x, _y + other.y, _z + other.z, _w + other.w) + operator fun plus(other: Vector4) = Vector4(x + other.x, y + other.y, z + other.z, w + other.w) - operator fun minus(other: Vector4) = Vector4(_x - other.x, _y - other.y, _z - other.z, _w - other.w) + operator fun minus(other: Vector4) = Vector4(x - other.x, y - other.y, z - other.z, w - other.w) - operator fun times(other: Vector4) = Vector4(_x * other.x, _y * other.y, _z * other.z, _w * other.w) + operator fun times(other: Vector4) = Vector4(x * other.x, y * other.y, z * other.z, w * other.w) - operator fun div(other: Vector4) = Vector4(_x / other.x, _y / other.y, _z / other.z, _w / other._w) + operator fun div(other: Vector4) = Vector4(x / other.x, y / other.y, z / other.z, w / other.w) - operator fun unaryMinus() = Vector4(-_x, -_y, -_z, -_w) + operator fun unaryMinus() = Vector4(-x, -y, -z, -w) operator fun times(m: N) where N: Number, N: Comparable = - Vector4(_x * m, _y * m, _z * m, _w * m) + Vector4(x * m, y * m, z * m, w * m) operator fun div(d: N) where N: Number, N: Comparable = - Vector4(_x / d, _y / d, _z / d, _w / d) - - operator fun component1() = _x - operator fun component2() = _y - operator fun component3() = _z - operator fun component4() = _w + Vector4(x / d, y / d, z / d, w / d) - infix fun approxEquals(other: Vector4): Boolean = minus(other).length2 < kEpsilon2 + operator fun component1() = x + operator fun component2() = y + operator fun component3() = z + operator fun component4() = w override fun hashCode(): Int { - return _x.hashCode() - .xor(_y.hashCode().shl(2)) - .xor(_z.hashCode().shr(2)) - .xor(_w.hashCode().shr(1)) + return x.hashCode() + .xor(y.hashCode().shl(2)) + .xor(z.hashCode().shr(2)) + .xor(w.hashCode().shr(1)) } - /** - * @see approxEquals - */ override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - - other as Vector4 - - if (_x != other.x) return false - if (_y != other.y) return false - if (_z != other.z) return false - if (_w != other.w) return false - - return true + return minus(other.cast()).length2 < kEpsilon2 } override fun toString(): String { - return "Vector(x, y, z, w) = ($_x, $_y, $_z, $_w)" + return "Vector(x, y, z, w) = ($x, $y, $z, $w)" } companion object { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/util/EndianBinaryReader.kt b/src/main/kotlin/io/github/deficuet/unitykt/util/EndianBinaryReader.kt index a5f8ffcf..940e793f 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/util/EndianBinaryReader.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/util/EndianBinaryReader.kt @@ -11,11 +11,6 @@ import java.nio.charset.Charset import java.nio.file.Files import kotlin.io.path.Path -enum class EndianType(val order: ByteOrder) { - LittleEndian(ByteOrder.LITTLE_ENDIAN), - BigEndian(ByteOrder.BIG_ENDIAN) -} - enum class FileType { ASSETS, BUNDLE, WEB, RESOURCE //, GZIP, BROTLI } @@ -28,7 +23,7 @@ enum class OffsetMode { AUTO } -sealed class EndianBinaryReader(private val manualIgnoredOffset: Long): Closeable { +sealed class EndianBinaryReader(private val manualOffset: Long): Closeable { abstract val bytes: ByteArray /** * Relative Position @@ -44,7 +39,7 @@ sealed class EndianBinaryReader(private val manualIgnoredOffset: Long): Closeabl * Relative offset to its "parent" endian binary reader. */ abstract val baseOffset: Long - abstract var endian: EndianType + abstract var endian: ByteOrder protected set /** @@ -107,7 +102,7 @@ sealed class EndianBinaryReader(private val manualIgnoredOffset: Long): Closeabl protected fun initOffset(): Long { return if (offsetMode == OffsetMode.MANUAL) { - manualIgnoredOffset.also { position = it } + manualOffset.also { position = it } } else { var first: Byte do { @@ -141,22 +136,22 @@ sealed class EndianBinaryReader(private val manualIgnoredOffset: Long): Closeabl fun readSByte(): Byte = read(1)[0] //-128~127 fun readByte(): UByte = readSByte().toUByte() //0~255 - fun readShort(): Short = ByteBuffer.wrap(read(2)).order(endian.order).short + fun readShort(): Short = ByteBuffer.wrap(read(2)).order(endian).short fun readUShort(): UShort = readShort().toUShort() - fun readInt(): Int = ByteBuffer.wrap(read(4)).order(endian.order).int + fun readInt(): Int = ByteBuffer.wrap(read(4)).order(endian).int fun readUInt(): UInt = readInt().toUInt() - fun readLong(): Long = ByteBuffer.wrap(read(8)).order(endian.order).long + fun readLong(): Long = ByteBuffer.wrap(read(8)).order(endian).long fun readULong(): ULong = readLong().toULong() - fun readFloat(): Float = ByteBuffer.wrap(read(4)).order(endian.order).float - fun readDouble(): Double = ByteBuffer.wrap(read(8)).order(endian.order).double + fun readFloat(): Float = ByteBuffer.wrap(read(4)).order(endian).float + fun readDouble(): Double = ByteBuffer.wrap(read(8)).order(endian).double fun readBool(): Boolean = readSByte() != 0.toByte() - fun readString(size: Int = -1, encode: Charset = Charsets.UTF_8): String { - return if (size == -1) readStringUntilNull(charset = encode) else read(size).decodeToString(encode) + fun readString(size: Int = -1, encoding: Charset = Charsets.UTF_8): String { + return if (size == -1) readStringUntilNull(charset = encoding) else read(size).decodeToString(encoding) } fun readStringUntilNull(maxLength: Int = 32767, charset: Charset = Charsets.UTF_8): String { val ret = mutableListOf() var c: Byte? = null - while (c != 0.toByte() && ret.size < maxLength && position != length) { + while (c != 0.toByte() && ret.size < maxLength && position <= length) { if (c != null) ret.add(c) val nextByte = read(1) if (nextByte.isEmpty()) throw IllegalStateException("Unterminated String: $ret") @@ -196,7 +191,7 @@ sealed class EndianBinaryReader(private val manualIgnoredOffset: Long): Closeabl fun readNextStringArray(): Array = readArray(readInt(), this::readAlignedString) fun readRectangle(): Rectangle = Rectangle(readFloat(), readFloat(), readFloat(), readFloat()) fun readQuaternion(): Quaternion = Quaternion(readFloat(), readFloat(), readFloat(), readFloat()) - private fun readMatrix4x4(): Matrix4x4 = Matrix4x4(*readNextFloatArray(16)) + fun readMatrix4x4(): Matrix4x4 = Matrix4x4(*readNextFloatArray(16)) fun readVector2(): Vector2 = Vector2(readFloat(), readFloat()) fun readVector3(): Vector3 = Vector3(readFloat(), readFloat(), readFloat()) fun readVector4(): Vector4 = Vector4(readFloat(), readFloat(), readFloat(), readFloat()) @@ -213,18 +208,18 @@ sealed class EndianBinaryReader(private val manualIgnoredOffset: Long): Closeabl return readArrayIndexed(num, constructor) } - fun resetEndian(e: EndianType): EndianBinaryReader { endian = e; return this } + fun resetEndian(e: ByteOrder): EndianBinaryReader { endian = e; return this } } class EndianByteArrayReader( private val array: ByteArray, - override var endian: EndianType = EndianType.BigEndian, + override var endian: ByteOrder = ByteOrder.BIG_ENDIAN, override val baseOffset: Long = 0, override val offsetMode: OffsetMode = OffsetMode.MANUAL, - manualIgnoredOffset: Long = 0 -): EndianBinaryReader(manualIgnoredOffset) { + manualOffset: Long = 0 +): EndianBinaryReader(manualOffset) { constructor( - endian: EndianType = EndianType.BigEndian, + endian: ByteOrder = ByteOrder.BIG_ENDIAN, manualOffset: Long = 0, offsetMode: OffsetMode = OffsetMode.MANUAL, manualIgnoredOffset: Long = 0, @@ -239,7 +234,8 @@ class EndianByteArrayReader( override val length get() = array.size.toLong() - ignoredOffset - override val bytes by lazy { with(array) { sliceArray(ignoredOffset.toInt() until size) } } + override val bytes: ByteArray + get() { return with(array) { sliceArray(ignoredOffset.toInt() until size) } } override fun read(size: Int): ByteArray { if (size <= 0 || position >= length) { @@ -257,11 +253,11 @@ class EndianByteArrayReader( class EndianFileStreamReader( filePath: String, - override var endian: EndianType = EndianType.BigEndian, + override var endian: ByteOrder = ByteOrder.BIG_ENDIAN, override val baseOffset: Long = 0, override val offsetMode: OffsetMode = OffsetMode.MANUAL, - manualIgnoredOffset: Long = 0 -): EndianBinaryReader(manualIgnoredOffset) { + manualOffset: Long = 0 +): EndianBinaryReader(manualOffset) { init { if (!Files.isRegularFile(Path(filePath))) throw IllegalStateException("Path $filePath must be a file.") @@ -275,20 +271,21 @@ class EndianFileStreamReader( override val ignoredOffset = initOffset() override val length get() = channel.size() - ignoredOffset - override val bytes: ByteArray by lazy { - withMark { - position = 0 - read((length - position).toInt()) + override val bytes: ByteArray + get() { + return withMark { + position = 0 + read((length - position).toInt()) + } } - } override fun read(size: Int): ByteArray { if (size <= 0 || position >= length) { return byteArrayOf() } - val b = ByteArray(size) - stream.read(b) - return b + return ByteArray(size).apply { + stream.read(this) + } } override fun close() { @@ -327,14 +324,15 @@ class ObjectReader internal constructor( override fun alignStream(alignment: Int) { plusAssign((alignment - absolutePosition % alignment) % alignment) } - override val bytes: ByteArray by lazy { - withMark { - position = 0 - read(info.byteSize.toInt()) + override val bytes: ByteArray + get() { + return withMark { + position = 0 + read(info.byteSize.toInt()) + } } - } override val baseOffset = reader.baseOffset - override var endian = reader.endian + override var endian: ByteOrder = reader.endian override val offsetMode = OffsetMode.MANUAL override fun read(size: Int) = reader.read(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 eb475f58..8b7f645e 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/util/Reference.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/util/Reference.kt @@ -11,5 +11,3 @@ class IntRef(value: Int) { operator fun plus(o: Int) = value + o operator fun plus(u: UInt) = value.toUInt() + u } - -class StringRef(var value: String = "") \ No newline at end of file diff --git a/src/main/kotlin/io/github/deficuet/unitykt/util/ResourceReader.kt b/src/main/kotlin/io/github/deficuet/unitykt/util/ResourceReader.kt index 5cbaba69..760241ca 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/util/ResourceReader.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/util/ResourceReader.kt @@ -1,8 +1,10 @@ package io.github.deficuet.unitykt.util import io.github.deficuet.unitykt.ImportContext +import io.github.deficuet.unitykt.UnityAssetManager import io.github.deficuet.unitykt.file.ResourceFile import io.github.deficuet.unitykt.file.SerializedFile +import java.io.Closeable import java.io.File import java.io.FileNotFoundException @@ -11,7 +13,8 @@ class ResourceReader internal constructor( private val assetFile: SerializedFile?, private val offset: Long, private val size: Long -) { +): Closeable { + private var shouldClose: Boolean = false private var reader: EndianBinaryReader? = null get() { if (field != null) return field @@ -25,6 +28,7 @@ class ResourceReader internal constructor( return if (!File(dir).exists()) { throw FileNotFoundException("Can't find the resource file $dir") } else { + shouldClose = true field = (ImportContext(dir, manager).files.getValue(file.name) as ResourceFile).reader field } @@ -36,12 +40,19 @@ class ResourceReader internal constructor( this.reader = reader } - val bytes: ByteArray by lazy { - reader!!.absolutePosition = offset - reader!!.read(size.toInt()) + fun read(): ByteArray { + return with(reader!!) { + absolutePosition = offset + read(size.toInt()) + } + } + + override fun close() { + if (shouldClose) reader?.close() } - fun read(buffer: ByteArray) { - bytes.let { System.arraycopy(it, 0, buffer, 0, buffer.size) } + fun registerToManager(m: UnityAssetManager): ResourceReader { + m.otherReaderList.add(this) + return this } } \ No newline at end of file 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 88b5ffa7..e1ed7e27 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/util/utils.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/util/utils.kt @@ -8,16 +8,16 @@ import kotlin.io.path.Path import kotlin.io.path.isRegularFile import kotlin.io.path.name -internal operator fun ByteArray.get(i: UInt) = get(i.toInt()).toIntBits() - -internal operator fun ByteArray.get(i: Int, l: Int) = sliceArray(i until i + l) - internal fun Byte.toIntBits() = toUByte().toInt() internal fun Short.toIntBits() = toUShort().toInt() +internal operator fun ByteArray.get(i: UInt) = get(i.toInt()).toIntBits() + +internal operator fun ByteArray.get(i: Int, l: Int) = sliceArray(i until i + l) + internal fun ByteArray.decodeToString(charset: Charset = Charsets.UTF_8) = - java.lang.String(this, charset) as String + java.lang.String(this, charset).toString() internal fun ByteArray.toHalf(): Float { if (size != 2) throw IllegalStateException("There should be 2 bytes only") @@ -76,12 +76,12 @@ internal operator fun String.invoke(v: Any) = format(v) } } -@PublishedApi internal fun Map.tryGetOrUppercase(key: String): V? { +@PublishedApi internal fun Map.tryGet(key: String): V? { return this[key] ?: this[key.uppercase()] } -@PublishedApi internal fun List.containsIgnoreCase(element: String, sRef: StringRef): Boolean { - return find { it.contentEquals(element) }?.also { sRef.value = it } != null +@PublishedApi internal fun List.containsIgnoreCase(element: String): String? { + return find { it.contentEquals(element) } } internal fun List.sum(): ByteArray { diff --git a/src/main/kotlin/io/github/deficuet/unitykt/utils.kt b/src/main/kotlin/io/github/deficuet/unitykt/utils.kt index 6165c17f..7e17eddb 100644 --- a/src/main/kotlin/io/github/deficuet/unitykt/utils.kt +++ b/src/main/kotlin/io/github/deficuet/unitykt/utils.kt @@ -1,6 +1,6 @@ package io.github.deficuet.unitykt -import io.github.deficuet.unitykt.data.* +import io.github.deficuet.unitykt.data.Object operator fun List.get(key: String) = find { it.name.contentEquals(key) } @@ -68,6 +68,8 @@ fun List.allObjectsOf(vararg type: String): List { } } -fun List.objectFromPathID(pathId: Long): Object? { - return firstOrNull { it.mPathID == pathId } +inline fun List.objectFromPathID(pathId: Long): T { + return first { it.mPathID == pathId } as T } + +inline fun Any?.cast(): T = this as T \ No newline at end of file