-
Notifications
You must be signed in to change notification settings - Fork 102
Getting Started: Assets file reading
This is a simple program to read every GameObject in a serialized file (assets file) and print its name and a list of components.
using AssetsTools.NET;
using AssetsTools.NET.Extra;
void LoadAssetsFile(string filePath)
{
var manager = new AssetsManager();
manager.LoadClassPackage("classdata.tpk");
var afileInst = manager.LoadAssetsFile(filePath, true);
var afile = afileInst.file;
manager.LoadClassDatabaseFromPackage(afile.Metadata.UnityVersion);
foreach (var goInfo in afile.GetAssetsOfType(AssetClassID.GameObject))
{
var goBase = manager.GetBaseField(afileInst, goInfo);
var name = goBase["m_Name"].AsString;
Console.WriteLine(name);
var components = goBase["m_Component.Array"];
foreach (var data in components)
{
var componentPointer = data["component"];
var componentExtInfo = manager.GetExtAsset(afileInst, componentPointer);
var componentType = (AssetClassID)componentExtInfo.info.TypeId;
Console.WriteLine($" {componentType}");
}
}
}
if (args.Length < 1) {
Console.WriteLine("need a file argument");
return;
}
LoadAssetsFile(args[0]);
Let's go over each part of the program.
var manager = new AssetsManager();
manager.LoadClassPackage("classdata.tpk");
This creates an AssetsManager and loads the classdata.tpk file. The AssetsManager handles loading and managing multiple assets files and bundles at the same time. The tpk file contains information about how to deserialize assets for various engine versions, so without this you can't deserialize any assets. Bundle files (.unity3d) are an exception since they embed this information most of the time. (More on this in the bundle loading section).
You can grab a tpk here: https://github.com/AssetRipper/Tpk/actions/workflows/type_tree_tpk.yml (do not use the brotli version.)
var afileInst = manager.LoadAssetsFile(filePath, true);
var afile = afileInst.file;
This loads a serialized file and its dependencies. The AssetsFileInstance is a wrapper around AssetsFile and is primarily used to help the AssetsManager load dependencies.
manager.LoadClassDatabaseFromPackage(afile.Metadata.UnityVersion);
Load the correct class database (cldb) version from the class package (tpk).
foreach (var goInfo in afile.GetAssetsOfType(AssetClassID.GameObject))
{
var goBase = manager.GetBaseField(afileInst, goInfo);
...
}
Loop over all assets that are of type GameObject and get the base field. The base field is the root field or node of the asset. The structure of a GameObject asset looks like this:
GameObject Base <-- this is what we have so far
vector m_Component
Array Array
ComponentPair data
PPtr<Component> component
int m_FileID
long m_PathID
unsigned int m_Layer
string m_Name
UInt16 m_Tag
bool m_IsActive
You can find this structure by opening this type of asset in UABE(A) or AssetsView.
var name = goBase["m_Name"].AsString;
Console.WriteLine(name);
This gets the m_Name field and gets the value as a string. You should always use the .As version that corresponds to the type. For example, to get m_Tag you should use goBase["m_Tag"].AsUShort
.
var components = goBase["m_Component.Array"];
foreach (var data in components)
{
var componentPointer = data["component"];
...
}
Here we get m_Component.Array
and loop through the elements. Each element returns ComponentPair data
so we need to get the pointer field called component
. Note that this could've also been written as goBase["m_Component"]["Array"]
. The A.B
syntax is shorthand for reading into multiple fields at once.
var componentExtInfo = manager.GetExtAsset(afileInst, componentPointer);
var componentType = (AssetClassID)componentExtInfo.info.TypeId;
Console.WriteLine($" {componentType}");
The component is another asset, so we can use GetExtAsset
to load that asset from the PPtr
(asset pointer). AssetExternal
returns three things, the AssetsFileInstance which the asset comes from (in this case of component assets, it's always the same file that the GameObject asset is in), the AssetFileInfo which has details on an asset's class/type id, file size, and file address, and the base field of that object (can be disabled by setting the onlyGetInfo argument to true).
After we have the info of the asset, we get the type with TypeId and cast it to an AssetClassID so we have the name. Then we print it.
That's all there is to it! If you want to load bundles, check out the bundle file reading page as well.