Skip to content

Getting Started: Assets file writing

nesrak1 edited this page Sep 23, 2023 · 12 revisions

Any code shown here is subject to change. Make sure to read Getting Started: Assets file reading first.

There are many different ways to modify an assets file. We'll start with modifying a field of a single asset. Let's say we have a GameObject like this:

GameObject Base
 vector m_Component
  Array Array (2 items)
   int size = 2
   [0]
    ComponentPair data
     PPtr<Component> component
      int m_FileID = 0
      SInt64 m_PathID = 123
   [1]
    ComponentPair data
     PPtr<Component> component
      int m_FileID = 0
      SInt64 m_PathID = 234
 unsigned int m_Layer = 0
 string m_Name = "Example GameObject"
 UInt16 m_Tag = 0
 bool m_IsActive = true

Basic writing

We'll start off simple with a single field change. We can change m_Name like so:

var goBase = manager.GetBaseField(afileInst, goInfo);
goBase["m_Name"].AsString = "New GameObject Name";

That's all it takes to replace a field. Again, make sure you use the correct AsXXX property for the type.

Now it's time to save the asset and then the assets file. To do this, we first add an AssetsReplacer to the info. This tells AssetsTools.NET how to modify the asset. In our case, we can use SetNewData as a quick way to apply an AssetsReplacerFromMemory for base fields. Another replacer you could use is the AssetsRemover which removes the asset from the file.

var goBase = manager.GetBaseField(afileInst, goInfo);
goBase["m_Name"].AsString = "New GameObject Name";

goInfo.SetNewData(goBase);

Alternatively, you can manually use a replacer.

goInfo.Replacer = new ContentReplacerFromBuffer(goBase.WriteToByteArray());

Internally, SetNewData creates a ContentReplacerFromBuffer like the second method. It serializes the base field back to a byte array and tells AssetsTools.NET to replace the entire asset with the byte array on write. You should only pass the base field into replacers, don't pass any children fields into replacers. Passing the base field will handle any children fields for you.

Now we can save the assets file with changes.

using (AssetsFileWriter writer = new AssetsFileWriter("sharedassets0.assets.mod"))
{
    afile.Write(writer);
}

Make sure that you save with a different filename than the file currently open. AssetsTools.NET copies unmodified assets from the original file and writing is not possible when the original file is already open for reading.

Adding to arrays in assets

Adding new array items can be made easy with ValueBuilder. Here's an example of adding new components to the m_Component array.

var m_Component = goBase["m_Component.Array"];
// create the new array item with the type of this array
var newArrayItem = ValueBuilder.DefaultValueFieldFromArrayTemplate(m_Component);
// fill in the fields
newArrayItem["component.m_FileID"].AsInt = 0;
newArrayItem["component.m_PathID"].AsLong = 345;
// add new item into m_Component
m_Component.Children.Add(newArrayItem);

It is not mandatory to set every field of an object created with ValueBuilder. ValueBuilder initializes all fields to 0 or empty string depending on the type. In the above example, we could delete the line setting m_FileID and it would work the same.

Adding new assets to assets files

Adding new assets is roughly the same. ValueBuilder can also create entire new assets instead of just array items. A shortcut with ValueBuilder is manager.CreateValueBaseField which lets you skip making the template field.

var textAssetClassId = (int)AssetClassID.TextAsset;
var newPathId = 12345L; // can be anything as long as it hasn't been used yet
var newBaseField = manager.CreateValueBaseField(afileInst, textAssetClassId);
// set fields
newBaseField["m_Name"].AsString = "interesting textasset";
newBaseField["m_Script"].AsString = "interesting text";
// make new info
var newInfo = AssetFileInfo.Create(afile, newPathId, textAssetClassId);
newInfo.SetNewData(newBaseField);
// add it to the assets file
afile.Metadata.AddAssetInfo(newInfo);

Adding new MonoBehaviour assets

MonoBehaviours are a complicated issue. Because of this, they have their own section here: Adding new MonoBehaviours