More Examples
This page contains several more examples of UAssetAPI usage for completing specific tasks.
A simple, complete example
UAsset myAsset = new UAsset("C:\\plwp_6aam_a0.uasset", EngineVersion.VER_UE4_18);
// Find the export with the name "Default__plwp_6aam_a0_C"
NormalExport cdoExport = (NormalExport)myAsset["Default__plwp_6aam_a0_C"];
// Add/replace a property called SpeedMaximum
cdoExport["SpeedMaximum"] = new FloatPropertyData() { Value = 999999 };
// or, modify it directly
FloatPropertyData SpeedMaximum = (FloatPropertyData)cdoExport["SpeedMaximum"];
SpeedMaximum.Value = 999999;
myAsset.Write("C:\\NEW.uasset");
Finding specific exports
UAsset myAsset = new UAsset("C:\\plwp_6aam_a0.uasset", EngineVersion.VER_UE4_18);
// We can find specific exports by index:
Export cdo = myAsset.Exports[1]; // Export 2; here, indexing from 0
// or like this:
cdo = new FPackageIndex(2).ToExport(myAsset); // also Export 2; FPackageIndex uses negative numbers for imports, 0 for null, and positive numbers for exports
// or, by ObjectName:
cdo = myAsset["Default__plwp_6aam_a0_C"]; // you can find this string value e.g. in UAssetGUI
cdo = myAsset[new FName(myAsset, "Default__plwp_6aam_a0_C")];
// or, to locate the ClassDefaultObject:
foreach (Export exp in myAsset.Exports)
{
if (exp.ObjectFlags.HasFlag(EObjectFlags.RF_ClassDefaultObject))
{
cdo = exp;
break;
}
}
// or, based on any property; maybe by SerialSize (length on disk):
long maxSerialSize = -1;
foreach (Export exp in myAsset.Exports)
{
if (exp.SerialSize > maxSerialSize)
{
maxSerialSize = exp.SerialSize;
cdo = exp;
}
}
Accessing basic export types and property types
UAsset myAsset = new UAsset("C:\\plwp_6aam_a0.uasset", EngineVersion.VER_UE4_18);
Export exp = myAsset["Default__plwp_6aam_a0_C"];
// Export contains all fields contained within UAssetGUI's "Export Information"
// to manipulate data under "Export Data," you generally need to cast it to a child type
// NormalExport contains most all "normal" data, i.e. standard tagged properties
if (exp is NormalExport normalExport)
{
for (int i = 0; i < normalExport.Data.Count; i++)
{
PropertyData prop = normalExport.Data[i];
Console.WriteLine(prop.Name.ToString() + ": " + prop.PropertyType.ToString());
// you can access prop.Value for many types, but for other types, you can cast to a child type and access other fields
if (prop is FloatPropertyData floatProp) floatProp.Value = 60; // change all floats to = 60
// ArrayPropertyData.Value is a PropertyData[] array, entries referenced by index
// StructPropertyData.Value is a List<PropertyData>, or you can index StructPropertyData directly
if (prop is ArrayPropertyData arrProp)
{
for (int j = 0; j < arrProp.Value.Length; j++)
{
PropertyData prop2 = arrProp.Value[j];
Console.WriteLine(prop2.Name.ToString() + ": " + prop2.PropertyType.ToString());
// etc.
// note that arrays and structs can contain arrays and structs too...
}
}
if (prop is StructPropertyData structProp)
{
for (int j = 0; j < structProp.Value.Count; j++)
{
PropertyData prop2 = structProp.Value[j];
Console.WriteLine(prop2.Name.ToString() + ": " + prop2.PropertyType.ToString());
// etc.
// note that arrays and structs can contain arrays and structs too...
}
// or:
// PropertyData prop2 = structProp["PropertyNameHere"];
}
}
}
// DataTableExport is a NormalExport, but also contains entries in DataTables
if (exp is DataTableExport dtExport)
{
// dtExport.Data exists, but it typically only contains struct type information
// to access other entries, use:
List<StructPropertyData> entries = dtExport.Table.Data;
// etc.
}
// RawExport is an export that failed to parse for some reason, but you can still access and modify its binary data
if (exp is RawExport rawExport)
{
byte[] rawData = rawExport.Data;
// etc.
}
// see other examples for more advanced export types!
Duplicating properties
UAsset myAsset = new UAsset("C:\\plwp_6aam_a0.uasset", EngineVersion.VER_UE4_18);
NormalExport cdoExport = (NormalExport)myAsset["Default__plwp_6aam_a0_C"];
FloatPropertyData targetProp = (FloatPropertyData)cdoExport["SpeedMaximum"];
// if we try something like:
/*
FloatPropertyData newProp = targetProp;
newProp.Value = 999999;
*/
// we'll accidentally change the value of targetProp too!
// we can duplicate this property using .Clone() instead:
FloatPropertyData newProp = (FloatPropertyData)targetProp.Clone();
newProp.Value = 999999;
cdoExport["SpeedMaximum2"] = newProp;
// .Clone() performs a deep copy, so you can e.g. clone a StructProperty and modify child properties freely
// .Clone() on an Export directly, however, is not implemented properly for child export types (e.g. the .Data list of a NormalExport is not cloned)
Read assets that use unversioned properties
// to read an asset that uses unversioned properties, you must first source a .usmap mappings file for the game the asset is from, e.g. with UE4SS
// you can read a mappings file with the Usmap class, and pass it into the UAsset constructor
Usmap mappings = new Usmap("C:\\MyGame.usmap");
UAsset myAsset = new UAsset("C:\\my_asset.uasset", EngineVersion.VER_UE5_3, mappings);
// then, read and write data as normal
// myAsset.HasUnversionedProperties will return true
// notes for the curious:
// * using the FName constructor adds new entries to the name map, which is often frivolous with unversioned properties; if you care, use FName.DefineDummy instead, but if UAssetAPI tries to write a dummy FName to disk it will throw an exception
// * UAssetAPI only supports reading .usmap files, not writing
// * UAssetAPI supports .usmap versions 0 through 3, uncompressed and zstandard-compressed files, and PPTH/EATR/ENVP extensions
Interface with JSON
UAsset myAsset = new UAsset("C:\\plwp_6aam_a0.uasset", EngineVersion.VER_UE4_18);
// write asset to JSON
string jsonSerializedAsset = tester.SerializeJson();
File.WriteAllText("C:\\plwp_6aam_a0.json", jsonSerializedAsset);
// read asset back from JSON
UAsset myAsset2 = UAsset.DeserializeJson("C:\\plwp_6aam_a0.json");
// myAsset2 should contain the same information as myAsset
// write asset to binary format
myAsset2.Write("C:\\plwp_6aam_a0_NEW.uasset");
Read and modify blueprint bytecode
UAsset myAsset = new UAsset("C:\\my_asset.uasset", EngineVersion.VER_UE4_18);
// all StructExport exports can contain blueprint bytecode, let's pretend Export 1 is a StructExport
StructExport myStructExport = (StructExport)myAsset.Exports[0];
KismetExpression[] bytecode = myStructExport.ScriptBytecode;
if (bytecode != null) // bytecode may fail to parse, in which case it will be null and stored raw in ScriptBytecodeRaw
{
// KismetExpression has many child classes, one child class for each type of instruction
// as with PropertyData, you can access .RawValue for many instruction types, but you'll need to cast for other kinds of instructions to access specific fields
foreach (KismetExpression instruction in bytecode)
{
Console.WriteLine(instruction.Token.ToString() + ": " + instruction.RawValue.ToString());
}
}