A read-only Unity assets extractor for Kotlin based on AssetStudio and refers to UnityPy.
All the C++ code used to decode texture compression come from AssetStudio/Texture2DDecoderNative.
7zip-LZMA SDK is included to decompress bundle files.
If you encounter an error and want to start a new issue, remember to submit the assets files as well.
Many things are not tested yet.
- Load
- File(s)
- Folder
- Files under the folder only, or
- All reachable files under the folder, recursively.
- ByteArray, bytes of an asset bundle file.
- A
String
is required as the identifier "file name" of thisByteArray
.
- A
- UnityAssetManager
- The entry point of loading object. It contains all objects read from all files that are loaded through it, except
AssetBundle
objects. - Use
UnityAssetManager.new(...)
with three parametersassetRootFolder
,readerConfig
anddebugOutput
to get an instance.assetRootFolder
is optional (nullable), and it can be ajava.io.File
,java.nio.file.Path
or just aString
. When present, it will be used byPPtr
to look for object according to themDependencies
in theAssetBundle
object of the file.readerConfig
is the loading configuration and is also optional.debugOutput
is a lambda function takes aString
. It is used to redirect the debug information. By default, it does nothing.
- The entry point of loading object. It contains all objects read from all files that are loaded through it, except
- Loading Configurations
- When loading file(s)/folder/ByteArray, an optional
ReaderConfig
can also be passed to override the configuration given to the manager, or the configuration given to the manager will be used by default. - The configuration will be applied before loading.
offsetMode
-MANUAL
orAUTO
; Default:MANUAL
- If the
offsetMode
isAUTO
, all the\x00
bytes at the beginning of the file will be ignored. The reading of the file will start at the first non-zero byte. The configmanualOffset
is ignored under this mode. - If the
offsetMode
isMANUAL
, the number of bytes that is ignored depends onmanualOffset
.
- If the
manualOffset
:Int
; Default:0
- Determine the number of bytes that needs to be ignored at the beginning of the file, no matter the byte is
\x00
or not. - This property works under
OffsetMode.MANUAL
mode only, will be ignored underAUTO
mode. - e.g. If an asset bundle file has 1024 bytes of useless non-zero data at the beginning, these bytes can be skipped by setting
offsetMode
toMANUAL
and settingmanualOffset
to 1024.
- Determine the number of bytes that needs to be ignored at the beginning of the file, no matter the byte is
- When loading file(s)/folder/ByteArray, an optional
- ImportContext
- For each file loaded, an
ImportContext
with file name and directory will be returned. AnImportContext
contains all objects read from the file. - When load files/folder, an array of
ImportContext
will be returned. - Don't forget to call the method
close()
to close and release the resources used by the manager, or call the static methodcloseAll()
inUnityAssetManager
to release resources used by all manager instances. It implementsCloseable
interface, so the functionuse
forCloseable
can be used.
- For each file loaded, an
- Object Reading
- The data of a UnityObject will not be read until you access any of its properties.
- However, access to the fields related to the UnityObject's metadata will not cause the initialization of the UnityObject.
- e.g. Its
assetFile
which is aSerializedFile
,mPathID
,unityVersion
, etc. Those are the fields enclosed in the metadata region. See UnityObject class.
- e.g. Its
- However, access to the fields related to the UnityObject's metadata will not cause the initialization of the UnityObject.
- The data of a UnityObject will not be read until you access any of its properties.
- Shortcuts
So far the objects that can export data includes:
- Mesh
exportString
- A string with the same content as exporting mesh to .obj file using AssetStudio.exportVertices
- The data of lines starts with "v" in the .obj file, grouped by Vector3.exportUV
- The data of lines starts with "vt" in the .obj file, grouped by Vector2. Only the datamUV0
is exported.exportNormals
- The data of lines starts with "vn" in the .obj file, grouped by Vector3.exportFaces
- The data of lines starts with "f" in the .obj file, grouped by Vector3.
- Texture2D
getRawData
- Returns compressed texture data inByteArray
.getDecompressedData
- Image data asByteArray
after decoding. Can be used to create image directly. The color channels areBGRA
. The size of the array ismWidth * mHeight * 4
.getImage
- A BufferedImage created from the decompressed data. It is usually up-side-down.- If the format of the texture is unsupported, both functions will return
null
.
- Sprite
getImage
- An BufferedImage cropped from aTexture2D
image. Will returnnull
if theTexture2D
object is not found or the format is unsupported.- The packing mode
SpritePackingMode.Tight
is not supported yet.
- The packing mode
- TextAsset
text(charset)
- This function is used to export content in this object asString
. ACharset
can be passed as a parameter, by default it isCharsets.UTF_8
.
- Shader
exportString
- Export as String. Include the Spir-V Shader data (experimental).
AudioClip
andVideoClip
getRawData
- To get the raw dataByteArray
. The export functions are not implemented yet.
- All objects
typeTreeJson
- A JSONObject contains all the properties they have, including those properties that is not implemented yet. The json could benull
.- May throw exception for some types of object like
Font
because of some unsolved problems.
- May throw exception for some types of object like
Used openJDK 11.0.10 and Kotlin Language 1.8.21.
Note: The decoding for texture compression of Texture2D
that needs to call native library is available on Windows 64-bit JVM only.
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.Deficuet:UnityKt:{version}'
}
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.Deficuet</groupId>
<artifactId>UnityKt</artifactId>
<version>{version}</version>
</dependency>
resolvers += "jitpack" at "https://jitpack.io"
libraryDependencies += "com.github.Deficuet" % "UnityKt" % "{version}"
:repositories [["jitpack" "https://jitpack.io"]]
:dependencies [[com.github.Deficuet/UnityKt "{version}"]]
Example for reading and saving an image from a Texture2D object.
import io.github.deficuet.unitykt.*
import io.github.deficuet.unitykt.classes.*
import java.io.File
import javax.imageio.ImageIO
fun main() {
UnityAssetManager.new().use {
val context: ImportContext = it.loadFile("C:/path/to/AssetBundle.aab")
//If there is no Texture2D object, an IndexOutOfBoundsException will be thrown.
//Use the function firstOfOrNull<>() if the existence of the object is not guaranteed.
//The data of this Texture2D object has not been read yet.
val tex: Texture2D = context.objects.firstObjectOf<Texture2D>()
//The object data will initialize as long as you access its properties.
tex.getImage()?.let {
ImageIO.write(it, "png", File("C:/whatever/you/want/name.png"))
}
//The manager will automatically close under the scope of 'use' function.
}
// Instantiate another manager
UnityAssetManager.new(
"C:/path/to/asset/system/root/folder",
ReaderConfig(OffsetMode.AUTO)
).use {
//Loading configurations
//The new config with mode `MANUAL` and offset `217` will be used, instead of the default config.
//The files and objects loaded by this manager will not show in previous manager.
val context = it.loadFolder("C:/foo/bar", ReaderConfig(OffsetMode.MANUAL, 217))
//The manager holds all the objects loaded through it,
// except those AssetBundle objects, their PathID is usually (always?) 1.
println(it.objects.firstObjectOf<Shader>().exportString)
context.objectMap.getAs<Material>(4054671897103428721) //Map<Long, UnityObject>.getAs<T: UnityObject>(pathId: Long): T
// or .safeGetAs<T: UnityObject>(pathId: Long): T?
.mSavedProperties.mTexEnvs.values.first()[0].mTexture //PPtr<Texture>
.safeGetObj() // if PPtr can not find the object under the same assetFile (SerializedFile)
// nor others loaded assetFiles, it will return null.
// don't forget to load all dependencies first
?.safeCast<Texture2D>()?.getImage()?.let { image ->
ImageIO.write(image, "png", File("C:/whatever/you/want/name.png"))
}
}
}
-
- Refactor
- Renamed
Object
toUnityObject
. - Fixed some errors and incorrect behaviors in
Matrix4x4
andPPtr
. - Removed implementation of
Font
object because of some unsolved errors. - Extended some fields for
GameObject
andVideoClip
. - ...many other things.
-
- Create Canvas object, tested on version 2018.4.34f1, not guaranteed to be stable on other versions.
-
- Catch up on the newest version of AssetStudio. Try to optimize memory use, re-design some structures.
-
- Publish on JitPack
-
- Add the export functions for
MonoBehaviour
,TextAsset
andShader
. - Change
AssetManager
from an object singleton to an instance class.
- Add the export functions for