nesrak1 / assetstools.net Goto Github PK
View Code? Open in Web Editor NEWRead and write unity assets/bundle files, based on https://github.com/SeriousCache/UABE
License: MIT License
Read and write unity assets/bundle files, based on https://github.com/SeriousCache/UABE
License: MIT License
If I rightclick any item in AssetsView and choose Properties, the application crashes. The cause is an ArgumentOutOfRangeException that gets thrown at the following line in StartScreen.cs:
ushort monoId = currentFile.file.typeTree.unity5Types[info.curFileTypeOrIndex].scriptIndex;
curFileTypeOrIndex
is (for example) 115, but unity5Types
has only 9 items.
The assets file I opened has version number 5.1.4f1.
Hi. The latest release on NuGet is 9 months old, are there any plans to update it? I'd like to use the new texture replacement support in my tool and adding it via NuGet would make dependency management much easier.
I am trying to create a GameObject asset using the following code:
// Import
AssetsManager assetsManager = new AssetsManager();
List<AssetsReplacer> replacers = new List<AssetsReplacer>();
assetsManager.LoadClassPackage("classdata.tpk");
AssetsFileInstance assetsFileInstance = assetsManager.LoadAssetsFile("sharedassets0.assets", false);
assetsManager.LoadClassDatabaseFromPackage(assetsFileInstance.file.typeTree.unityVersion);
AssetsFileTable assetsFileTable = assetsFileInstance.table;
// Template
AssetTypeTemplateField templateField = new AssetTypeTemplateField();
ClassDatabaseType cldbType = AssetHelper.FindAssetClassByName(assetsManager.classFile, "GameObject");
templateField.FromClassDatabase(assetsManager.classFile, cldbType, 0);
// New Asset
AssetTypeValueField newAsset = ValueBuilder.DefaultValueFieldFromTemplate(templateField);
replacers.Add(new AssetsReplacerFromMemory(0, assetsFileTable.assetFileInfo.Max(i => i.index) + 1, cldbType.classId, 0xFFFF, newAsset.WriteToByteArray()));
// Export
AssetsFileWriter writer = new AssetsFileWriter(File.OpenWrite("sharedassets0-new.assets"));
assetsFileInstance.file.Write(writer, 0, replacers, 0);
writer.Close();
The code runs without any error. However, when I try to view the GameObject using AssetsView, I get the following error:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at AssetsTools.NET.AssetTypeValueField.get_Item(Int32 index)
at AssetsView.Winforms.GameObjectViewer.GetRootGameObject(AssetExternal ext)
at AssetsView.Winforms.GameObjectViewer.GameObjectViewer_Load(Object sender, EventArgs e)
at System.Windows.Forms.Form.OnLoad(EventArgs e)
at System.Windows.Forms.Form.OnCreateControl()
at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
at System.Windows.Forms.Control.CreateControl()
at System.Windows.Forms.Control.WmShowWindow(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.Form.WmShowWindow(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
I think this is an issue with my code and not AssetsView. How should I correctly initialize a new GameObject asset?
Note:
I am using the AssetsTools.NET.dll provided in AssetsTools.NET.zip in #22. The rest of the files are from update 14 (2.0.6)
Caused by removed upon loading into AssetView.NET
************** Exception Text **************
System.ArgumentOutOfRangeException: Non-negative number required.
Parameter name: count
at System.IO.BinaryReader.ReadBytes(Int32 count)
at AssetsTools.NET.AssetsFileReader.ReadStringLength(Int32 len)
at AssetsView.AssetHelpers.AssetInfo.GetAssetNameFast(AssetFileInfoEx afi, ClassDatabaseFile cldb, ClassDatabaseType type, AssetsFileInstance inst)
at AssetsView.Winforms.StartScreen.LoadGeneric(AssetsFileInstance mainFile, Boolean isLevel)
at AssetsView.Winforms.StartScreen.LoadMainAssetsFile(AssetsFileInstance inst)
at AssetsView.Winforms.StartScreen.LoadBundleFile(String path)
at AssetsView.Winforms.StartScreen.addFileToolStripMenuItem_Click(Object sender, EventArgs e)
at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ToolStrip.WndProc(Message& m)
at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Both the example code in the README file and the code of AssetsView itself use Marshal.UnsafeAddrOfPinnedArrayElement() when passing texture data to the System.Drawing.Bitmap constructor. However, neither of them pin the data array first, which goes against the Microsoft documentation:
The array must be pinned using a GCHandle before it is passed to this method. For maximum performance, this method does not validate the array passed to it; this can result in unexpected behavior.
Indeed, if the array is not pinned, the garbage collector is free to move it around in memory at any time - and if this happens while GDI+ is busy creating the bitmap, the user will get anything from a corrupted texture to a crash.
As a side remark, neither the example code nor AssetsView dispose the Bitmap object when they're done with it.
Hello,
I tested UABE.NET, and I found bug about MonoDeserializer.
first of all, I'll show you 'TMPro.TMP_FontAsset' structure by UABE.Cpp
In Array fallbackFontAssets(and TMP_FontWeights in Array fontWeights) field, it reference(include) itself(TMPro.TMP_FontAsset).
Originally, these things should be loaded as "PPtr<TMP_FontAsset>", but MonoDeserializer.cs load TMP_FontAsset in Array fallbackFontAssets as it is.
so, it try to load TMP_FontAsset again, and again. After infinity loop, UABE.NET throw 'System.StackOverflowException'.
I'm try to figure out how to resolve. But before that, I want to make you know about it.
If i find solution before you fix, I'll do pull request.
Thank you.
When trying to read PlayerSettings in globalgamemanagers, an EndOfStreamException gets thrown
File: globalgamemanagers.zip
Exception:
See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text **************
System.IO.EndOfStreamException: Unable to read beyond the end of the stream.
at System.IO.BinaryReader.ReadByte()
at AssetsTools.NET.AssetTypeTemplateField.ReadType(AssetsFileReader reader, UInt64 filePos, AssetTypeValueField valueField, Boolean bigEndian)
at AssetsTools.NET.AssetTypeTemplateField.ReadType(AssetsFileReader reader, UInt64 filePos, AssetTypeValueField valueField, Boolean bigEndian)
at AssetsTools.NET.AssetTypeInstance..ctor(UInt32 baseFieldCount, AssetTypeTemplateField[] ppBaseFields, AssetsFileReader reader, Boolean bigEndian, UInt64 filePos)
at AssetsTools.NET.Extra.AssetsManager.GetATI(AssetsFile file, AssetFileInfoEx info, Boolean fromTypeTree)
at AssetsView.Winforms.StartScreen.assetList_CellDoubleClick(Object sender, DataGridViewCellEventArgs e)
at System.Windows.Forms.DataGridView.OnCellDoubleClick(DataGridViewCellEventArgs e)
at System.Windows.Forms.DataGridView.OnDoubleClick(EventArgs e)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.DataGridView.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
************** Loaded Assemblies **************
mscorlib
Assembly Version: 4.0.0.0
Win32 Version: 4.8.4069.0 built by: NET48REL1LAST_B
CodeBase: file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll
----------------------------------------
AssetsView
Assembly Version: 1.0.0.0
Win32 Version: 1.0.0.0
CodeBase: file:///C:/AssetsToolsNET_AssetsView/AssetsView.exe
----------------------------------------
System.Windows.Forms
Assembly Version: 4.0.0.0
Win32 Version: 4.8.4042.0 built by: NET48REL1LAST_C
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
Assembly Version: 4.0.0.0
Win32 Version: 4.8.4001.0 built by: NET48REL1LAST_C
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
Assembly Version: 4.0.0.0
Win32 Version: 4.8.3761.0 built by: NET48REL1
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
AssetsTools.NET
Assembly Version: 1.0.0.0
Win32 Version: 1.0.0.0
CodeBase: file:///C:/AssetsToolsNET_AssetsView/AssetsTools.NET.DLL
----------------------------------------
System.Configuration
Assembly Version: 4.0.0.0
Win32 Version: 4.8.3761.0 built by: NET48REL1
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Configuration/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll
----------------------------------------
System.Core
Assembly Version: 4.0.0.0
Win32 Version: 4.8.4110.0 built by: NET48REL1LAST_B
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Core/v4.0_4.0.0.0__b77a5c561934e089/System.Core.dll
----------------------------------------
System.Xml
Assembly Version: 4.0.0.0
Win32 Version: 4.8.3761.0 built by: NET48REL1
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.dll
----------------------------------------
************** JIT Debugging **************
To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.
For example:
<configuration>
<system.windows.forms jitDebugging="true" />
</configuration>
When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.
So im getting an System.OutOfMemoryException: Array dimensions exceeded range.
Code:
here
This seems to hold true for any 2019.4.5f1 bundle that I throw at it, the 2019.1.8f1 counterparts work fine.
Here's the Stacktrace:
System.OverflowException: Arithmetic operation resulted in an overflow.
at AssetsTools.NET.Type_0D.Read(Boolean hasTypeTree, AssetsFileReader reader, UInt32 version)
at AssetsTools.NET.TypeTree.Read(AssetsFileReader reader, UInt32 version)
at AssetsTools.NET.AssetsFile..ctor(AssetsFileReader reader)
at AssetsTools.NET.Extra.AssetsFileInstance..ctor(Stream stream, String filePath, String root)
at AssetsTools.NET.Extra.AssetsManager.LoadAssetsFile(Stream stream, String path, Boolean loadDeps, String root)
at AssetsView.Winforms.StartScreen.LoadBundleFile(String path)
at AssetsView.Winforms.StartScreen.addFileToolStripMenuItem_Click(Object sender, EventArgs e)
at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ToolStrip.WndProc(Message& m)
at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
This happens both when opening the bundle using AssetsView and my own implementation (which calls the AssetsFileInstance constructor)
I tried loading the bundle using several applications
On the other hand
I did see you mentioning reading the assets manually using the AssetsFileReader
on the main ReadMe, could you elaborate on that?
edit: never mind, but the documentation should be updated, because this is invalid syntax:
var am = new AssetsManager();
var bun = am.LoadBundleFile("bundle.unity3d", true);
var firstAssetsFile = BundleHelper.LoadAssetFromBundle(bun, 0); //or use name instead
hi, refering to the README document, i can get a asset's name easily using this script:
` //Load assets file
var am = new AssetsManager();
var inst = am.LoadAssetsFile("C:\Users\Desktop\UnityDemo_2018_3D_Data\sharedassets0.assets", true);
//Load class database
am.LoadClassPackage("classdata.tpk");
am.LoadClassDatabaseFromPackage(inst.file.typeTree.unityVersion);
//Get asset info
var table = inst.table;
//foreach (var inf in inst.table.GetAssetsOfType((int)AssetClassID.GameObject))
foreach (var inf in inst.table.GetAssetsOfType(150))
{
var baseField = am.GetTypeInstance(inst, inf).GetBaseField();
var name = baseField.Get("m_Name").GetValue().AsString();
Console.WriteLine(name);
}`
but, i wonder how to get my asset's full field completly, just like this:
I made bug fix for Unity 5.2.4f1, but I don't know why it should be.
I test Detention(5.2.4f1) and testing samples(I built cotaining zero, one two and three asset)
Maybe this does not effect other version
8b00664
Hello,
Thank for you to manage nuget package.
Because of uncomfortable rule about deleting packages ( https://docs.microsoft.com/en-US/nuget/nuget-org/policies/deleting-packages ), I cannot do anything about that package.
As mail that you sent, I also want to give you ownership.
But now it's midnight, so after I wake up, I will search how to transfer ownership and do that.
How to export/import material dump text with tools? It seems that function is not working
Hiya! Is it possible to grab the vertice data of a mesh so I can reconstruct the mesh inside of Unity at runtime?
I've got it to where I can grab any of the data of the Mesh object, only problem is that I don't think theres Vector3 support inside of AssetsTools.NET which is needed for Unity's mesh stuff :P
enabledVRDevices
has an array inside it, so I can get that array like so:
var enabledVRDevices = baseField.Get("enabledVRDevices").Get(0);
If I want to change the value of the first element of the array, I do this:
enabledVRDevices .Get(0).SetValue("Something");
This works as expected, and when I write the changes to a file I can confirm that the new value is there.
But what I need is to actually add a new value to the array, since it starts out as empty.
I tried with SetChildrenList
but in the final file the array is always empty:
var something = new AssetTypeValueField();
something .value = new AssetTypeValue(EnumValueTypes.ValueType_String, "Something");
enabledVRDevices .SetChildrenList(new[] { something });
I was trying to find some way to just to something like this:
var newArray = new AssetTypeArray();
enabledVRDevices.GetValue().Set(newArray);
But I have no idea how to add anything inside this newArray
. Seems like that part is commented out in the source code?
So, what is the correct way to add items into an array here?
The currently latest version of AssetsView just closes itself after i start it, not even showing the window and i have no idea what went wrong.
OS: Windows 10 21H2
Hi, i'm trying to read a bundle file from a game but the code crash at:
public string GetFileName(int index)
{
if (bundleHeader3 != null)
return assetsLists3.entries[index].name;
if (bundleHeader6 != null)
return bundleInf6.dirInf[index].name;
return null;
}
with NullExeption for "bundleInf6.dirInf"
var am = new AssetsManager();
var bun = am.LoadBundleFile("shape");
//load first asset from bundle
var assetInst = am.LoadAssetsFileFromBundle(bun, 0, true);
ths file "shape" without extension start with bytes: "UnityFS ๏ฟฝ5.x.x 2021.1.17f1". It contains a list of fbx files that I have already extracted with the AssetStudioGui program but I would like to manage the extraction in my tool.
Opening my bundle with AssetViewer shows 3 files
Icon - Sprite
SpriteIndex - AssetBundle
Icon - Texture2D
It's unity version is 2019.4.23f1
I've copy pasted the code for Writing an Asset Bundle as so
var am = new AssetsManager();
var bunInst = am.LoadBundleFile("art.bundle");
//read the boring file from the bundle
var inst = am.LoadAssetsFileFromBundle(bunInst, 0, false);
var inf = inst.table.GetAssetInfo("Icon");
var baseField = am.GetTypeInstance(inst, inf).GetBaseField();
var thing = baseField.Get("m_Name").GetValue().AsString();
Console.WriteLine(thing);
baseField.Get("m_Name").GetValue().Set("MyCoolAsset");
var newGoBytes = baseField.WriteToByteArray();
var repl = new AssetsReplacerFromMemory(0, inf.index, (int)inf.curFileType, 0xffff, newGoBytes);
//write changes to memory
byte[] newAssetData;
using (var stream = new MemoryStream())
using (var writer = new AssetsFileWriter(stream))
{
inst.file.Write(writer, 0, new List<AssetsReplacer>() { repl }, 0);
newAssetData = stream.ToArray();
}
//rename this asset name from boring to cool when saving
var bunRepl = new BundleReplacerFromMemory("Icon", "Renamed", true, newAssetData, -1);
var bunWriter = new AssetsFileWriter(File.OpenWrite("coolbundle.bundle"));
bunInst.file.Write(bunWriter, new List<BundleReplacer>() { bunRepl });
Opening the created bundle gives the following error
System.ArgumentOutOfRangeException: Stream length must be non-negative and less than 2^31 - 1 - origin. (Parameter 'value')
at System.IO.MemoryStream.set_Position(Int64 value)
at AssetsTools.NET.AssetsFile..ctor(AssetsFileReader reader) in C:\Users\nesquack\Documents\GitReposLocal\AssetsTools.NET\AssetTools.NET\Standard\AssetsFileFormat\AssetsFile.cs:line 49
at AssetsTools.NET.Extra.AssetsFileInstance..ctor(Stream stream, String filePath, String root) in C:\Users\nesquack\Documents\GitReposLocal\AssetsTools.NET\AssetTools.NET\Extra\AssetsManager\AssetsFileInstance.cs:line 26
at AssetsTools.NET.Extra.AssetsManager.LoadAssetsFile(Stream stream, String path, Boolean loadDeps, String root, BundleFileInstance bunInst) in C:\Users\nesquack\Documents\GitReposLocal\AssetsTools.NET\AssetTools.NET\Extra\AssetsManager\AssetsManager.cs:line 29
at AssetsView.Winforms.StartScreen.LoadBundleFile(String path) in C:\Users\nesquack\Documents\GitReposLocal\AssetsTools.NET\AssetsView\Winforms\StartScreen.cs:line 128
at AssetsView.Winforms.StartScreen.addFileToolStripMenuItem_Click(Object sender, EventArgs e) in C:\Users\nesquack\Documents\GitReposLocal\AssetsTools.NET\AssetsView\Winforms\StartScreen.cs:line 103
at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ToolStrip.WndProc(Message& m)
at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
If I DON'T rename the file then it saves and opens properly. That's exactly why I need this though... to rename a file in a bundle and I can't find a different tool that can do this programatically.
The headers are here where the first is changed and the second unchanged:
UnityFS ๏ฟฝ5.x.x 2019.4.23f1 1ยป [ [ @ ๏ฟฝ bH bH @ ๏ฟฝ 1 ๏ฟฝCAB-0207725071a623d62e45b4a33e9c7b85 1 1( ๏ฟฝ509 รฉ 1 ๏ฟฝ ๏ฟฝ 2019.4.23f1
UnityFS ๏ฟฝ5.x.x 2019.4.23f1 1ยป [ [ @ ๏ฟฝ 1 1 @ ๏ฟฝ 1 ๏ฟฝCAB-0207725071a623d62e45b4a33e9c7b85 รฉ 1 ๏ฟฝ ๏ฟฝ 2019.4.23f1
So how do I rename this file then?
I'm trying to change some field values on an assetbundle, unity version 2018.4.16f1
AssetsTools version is latest commit.
I'm using the following code :
Init the asset manager
private static void InitAssetsManager()
{
_assetsManager = new AssetsManager();
_assetsManager.LoadClassPackage("classdata.tpk");
}
Loading the bundle
private static void LoadUserAssetBundleData(string userAssetBundlePath)
{
_userAssetBundleFileInstance = _assetsManager.LoadBundleFile(userAssetBundlePath, true);
for (int i = 0; i < _userAssetBundleFileInstance.file.NumFiles; i++)
{
if (!_userAssetBundleFileInstance.file.IsAssetsFile(i))
{
continue;
}
var assetBundleAssetsFile = _assetsManager.LoadAssetsFileFromBundle(_userAssetBundleFileInstance, i);
_userAssetBundleAssetsFiles.Add(assetBundleAssetsFile);
_assetsManager.LoadClassDatabaseFromPackage(assetBundleAssetsFile.file.typeTree.unityVersion);
}
}
Two helper methods for saving the changes to the field
private static void SetUnityObjRef(AssetTypeValueField fieldChildren, SerializedUnityObjectRef unityObjectRef)
{
if (fieldChildren.childrenCount == 2)
{
AssetTypeValueField fileId = fieldChildren.children[0];
AssetTypeValueField pathId = fieldChildren.children[1];
string fileIdName = fileId.templateField.name;
string fileIdType = fileId.templateField.type;
string pathIdName = pathId.templateField.name;
string pathIdType = pathId.templateField.type;
if (fileIdName == "m_FileID" && fileIdType == "int" &&
pathIdName == "m_PathID" && pathIdType == "SInt64")
{
fileId.GetValue().Set(unityObjectRef.FileId);
pathId.GetValue().Set(unityObjectRef.PathId);
}
}
}
private static void SaveFieldChange(List<AssetsReplacer> assetsReplacers, AssetFileInfoEx assetInfo, AssetTypeValueField field, ushort monoScriptIndex = 0xFFFF)
{
var newFieldBytes = field.WriteToByteArray();
Console.WriteLine("monoScriptIndex:" + monoScriptIndex);
var repl = new AssetsReplacerFromMemory(0, assetInfo.index, (int)assetInfo.curFileType, monoScriptIndex, newFieldBytes);
assetsReplacers.Add(repl);
}
Code that actually retrieve the "test" field on my custom script, which is a reference to a gameobject asset
foreach (var assetsFile in _userAssetBundleAssetsFiles)
{
if (!_userAssetsFilesToReplacers.TryGetValue(assetsFile, out var assetsReplacers))
{
assetsReplacers = new();
_userAssetsFilesToReplacers.Add(assetsFile, assetsReplacers);
}
foreach (var assetInfo in assetsFile.table.assetFileInfo)
{
if (assetInfo.index != 8948727843312140990)
{
continue;
}
var field = _assetsManager.GetTypeInstance(assetsFile, assetInfo).GetBaseField();
AssetTypeValueField components = field.Get("m_Component").Get("Array");
int componentSize = components.GetValue().AsArray().size;
AssetTypeValueField componentPtr = components[componentSize - 1].GetLastChild();
AssetExternal assetExternal = _assetsManager.GetExtAsset(assetsFile, componentPtr);
if (assetExternal.instance != null)
{
string className = AssetHelper.FindAssetClassByID(_assetsManager.classFile, assetExternal.info.curFileType).name.GetString(_assetsManager.classFile);
if (className == "MonoBehaviour")
{
var managedPath = @"C:\Users\Quentin\desktop_files\code\csharp\ror2\UnityProjFixBundleRef\FixBundleRefTest\OutputGame\FixBundleRefTest_Data\Managed";
var targetBaseField = MonoDeserializer.GetMonoBaseField(_assetsManager, assetsFile, assetExternal.info, managedPath);
var testField = targetBaseField.Get("test");
SetUnityObjRef(testField, new SerializedUnityObjectRef(3, 8));
SaveFieldChange(assetsReplacers, assetExternal.info, testField, AssetHelper.GetScriptIndex(assetsFile.file, assetExternal.info));
break;
}
}
}
}
Code that save the changes to a new bundle file :
private static void SaveChangesToAssetBundle(string userAssetBundlePath)
{
var newAssetsFileDatas = new List<(string, byte[])>();
foreach ((var userAssetFile, var assetsReplacer) in _userAssetsFilesToReplacers)
{
byte[] newAssetsFileData;
using (var memoryStream = new MemoryStream())
using (var _writer = new AssetsFileWriter(memoryStream))
{
userAssetFile.file.Write(_writer, 0, assetsReplacer, 0);
newAssetsFileData = memoryStream.ToArray();
newAssetsFileDatas.Add((userAssetFile.name, newAssetsFileData));
}
}
using (var fileStream = new FileStream(userAssetBundlePath + "_ref_fixed", FileMode.Create))
{
var bundleReplacers = new List<BundleReplacer>();
foreach ((var assetsFileName, var newAssetsFileData) in newAssetsFileDatas)
{
var bundleReplacer = new BundleReplacerFromMemory(assetsFileName, null, false, newAssetsFileData, -1);
bundleReplacers.Add(bundleReplacer);
}
var writer = new AssetsFileWriter(fileStream);
_userAssetBundleFileInstance.file.Write(writer, bundleReplacers);
}
}
Unmodified bundle :
https://i.imgur.com/DVFAiOB.png
Modified bundle :
https://i.imgur.com/mJ08Bzp.png
We can see that the changes end up in the wrong location, and i'm not sure why
I've been trying to use this library in one of my projects to modify asset bundles but I keep running into issues when using with large asset bundles. I'm basically just iterating over the MonoScript assets in the bundle and modifying the m_AssemblyName
property on each one to point to a different assembly. It works well on smaller bundles but I run into issues on larger ones.
Notably that the library seems to use a LOT of memory when reading large files, many times more than the size of the decompressed bundle itself, but also that because of this integer cast bundles with decompressed sizes over 2GB will also cause an integer overflow and fail to load entirely throwing an ArgumentOutOfBounds exception on the ReadBytes()
call below.
I was hoping that the BundleReplacerFromStream
and AssetsReplacerFromStream
would allow me to perform my operations while keeping memory usage down but after trying and looking into it, it too just reads the entire stream into another byte array and doesn't really solve much of anything.
I would like to continue using this library and I'll be experimenting seeing if I can find any workarounds, but I thought I'd open this just in case anyone had some useful info.
I've issues with modifying assets of the game based on Unity 5.6.5. Already found that using AssetsReplacer
with no changes at all crashes game.
I've done 2 tests. First, AssetsFileWriter
without any AssetsReplacer
doesn't change anything. Second, adding AssetsReplacerFromMemory
with changedBytes
(baseField.WriteToByteArray()
) without doing any modifications completely changes whole file and makes game crash.
I'm using AssetsTools.NET v2.0.11
.
Throw System.ArgumentOutOfRangeException in GetAssetNameFast when read '0x00000062 (DelayedCallManager)' in globalgamemanager, because 'DelayedCallManager' is unknown in UABE.Cpp also.( UABE.Cpp said 'Unknown asset format!' when 'View Data' DelayedCallManager Type asset in globalgamemanager.)
in classdatabase file, DelayedCallManager has only one field, so type.fields[1] is invaild.
AssetsTools.NET/UABE.NET/Winforms/AssetViewer.cs
Lines 156 to 157 in 7bee484
I changed to
if (type.fields.Count <= 1) return type.name.GetString(cldb);
if (type.fields[1].fieldName.GetString(cldb) == "m_Name")
Greetings. Is there a way to replace file in assets inside assetBundle, and save the modified bundle?
I got the decompression and extraction part with AssetView.NET code, but replacement seems to be not clear...
After using the sample code from readme for modification (even without the part that modifies filename), the exported output file seems to be filled with Unicode replacement character, same thing happened when I tried to extract TextAsset using baseField.Get("m_Script").GetValue().AsString() (and not only as string).
My goal is to modify TextAsset data and save modified asset bundle.
And even if i'll manage to get a modified asset, how can I place it as a replacement in asset bundle and then save it?
This code always creates an empty 2KB bundle, even if original bundle is not empty
var am = new AssetsManager();
var ab = am.LoadBundleFile(FilePath);
var writer = new AssetsFileWriter(File.OpenWrite("output.ab"));
ab.file.Write(writer);
P.S. I would like to notice that the sample code has multiple errors in it (variable is used before declaration, curFileType is not converted to int, Write function uses AssetsReplacer array instead of List, and is missing two other arguments required by the function) which is really confusing...
Game: Hollow Knight
Version 1.5.68.11808
Unity Version used for it: 2020.2
AssetsView version: AssetsTools Update 3
Procedure:
globalgamemanagers
)BuildSettings
If I use the code below to build bundles
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.DisableWriteTypeTree, BuildTarget.WebGL);
and use the code below to read a bundle. am.LoadAssetsFileFromBundle always returns null.
var am = new AssetsManager();
am.LoadClassPackage("classdata.tpk");
var bun = am.LoadBundleFile("file/bg_11zhuan1.bundle");
var inst = am.LoadAssetsFileFromBundle(bun, bun.file.GetFileName(0));
If I use BuildAssetBundleOptions.None instead of BuildAssetBundleOptions.DisableWriteTypeTree to build bundles, am.LoadAssetsFileFromBundle doesn't return null.
What should I do I, if I want to use BuildAssetBundleOptions.DisableWriteTypeTree?
The Assets Bundle built with Unity2021.3.x cannot be read to the media resources in the bundle by AssetsTools. The 2021.2.x version is allowed.
I notice that AssetsTools.NET will throw error's if the am.classFile is null or if a wrong one is loaded, and your code will select a Unity 5.x version because of how the list is ordered, so that is definately going to cause issues if using a new Unity version, like getting the type instance or basefield.. So short of as you said a version selector, here is a modification to your code that will find the closest matching Unity version if the game version doesn't exist in the tpk. (It helps alot, but some Unity versions still may not work correctly if it's not in the tpk) Note: You will also notice a significant performance increase in parsing resource's You can test it with 'Mist Legacy' from steam, there's no TypeDump for the Unity version (2020.3.20f1) yet but x.18f1 exists which should be your result:
if (am.classFile == null)
{
int index = am.classPackage.header.files.FindIndex(f => f.name.StartsWith("U" + inst.file.typeTree.unityVersion));
if (index >= 0) // -1 mean's it isn't in current tpk
{
am.classFile = am.classPackage.files[index]; // If am.classFile is Null you get errors.
}
else
{
List<ClassDatabaseFile> files = am.classPackage.files;
// Filter all classDbFiles for matching versions
var unityMajMinVer = inst.file.typeTree.unityVersion.Substring(0, 6);
var filter1 = files.Where(f => f.header.unityVersions[0].StartsWith(unityMajMinVer)).OrderBy(v => v.header.unityVersions[v.header.unityVersionCount - 1]);
var filter2 = filter1.Where(f => f.header.unityVersions[f.header.unityVersionCount - 1].Trim().Length == inst.file.typeTree.unityVersion.Length).OrderBy(v => v.header.unityVersions[v.header.unityVersionCount - 1]);
// Get the SubMinor version number
string unitySubMinVer = inst.file.typeTree.unityVersion.Split('.')[2];
string tmpNum = "";
if (unitySubMinVer.Length >= 4) { tmpNum = unitySubMinVer.Substring(0, 2); } else { tmpNum = unitySubMinVer.Substring(0, 1); }
// Make Existing Num List
List<int> nums = new List<int>();
foreach (var pos in filter2) { nums.Add(int.Parse(pos.header.unityVersions[pos.header.unityVersionCount - 1].Split('.')[2].Substring(0, tmpNum.Length))); }
int closest = nums.Aggregate((x, y) => Math.Abs(x - int.Parse(tmpNum)) < Math.Abs(y - int.Parse(tmpNum)) ? x : y);
ClassDatabaseFile result = filter2.Where(f => f.header.unityVersions[f.header.unityVersionCount - 1].StartsWith(unityMajMinVer + "." + closest)).Single();
am.classFile = result;
//ORIG:
//List<ClassDatabaseFile> files = tmpam.classPackage.files;
//tmpam.classFile = files[files.Count - 1]; // will be Unity 5.x, due to order of list
}
}
Bundles right now are being compressed directly in memory. This is because it needs to be placed after a compressed table which is dependent on the compressed data after it, and the compressed table can change in size depending on how the compressed data is compressed. Both Unity and DevX compress in a temporary file and copy over later, UnityPy writes directly in memory (what we're doing), and UABE sets the "table at end of file" flag (0x80).
I think the table at end of file flag is probably the best decision, but it produces bundles with a flag that isn't used normally and can be easily identified as being created by AssetsTools(.NET).
I have the following code that is intended to remove a child from a father transform.
// Import
AssetsManager assetsManager = new AssetsManager();
List<AssetsReplacer> replacers = new List<AssetsReplacer>();
assetsManager.LoadClassPackage("classdata.tpk");
AssetsFileInstance assetsFileInstance = assetsManager.LoadAssetsFile("sharedassets0.assets", false);
assetsManager.LoadClassDatabaseFromPackage(assetsFileInstance.file.typeTree.unityVersion);
AssetsFileTable assetsFileTable = assetsFileInstance.table;
// Get Father
long fatherID = 16256;
long childID = 16153;
AssetFileInfoEx fatherFile = assetsFileTable.GetAssetInfo(fatherID);
AssetTypeValueField fatherBaseField = assetsManager.GetTypeInstance(assetsFileInstance.file, fatherFile).GetBaseField();
// Clear Father's Child
AssetTypeValueField[] fatherChildren = fatherBaseField.Get("m_Children").Get("Array").children;
AssetTypeValueField[] newChildren = fatherChildren.Where(child => child.Get("m_PathID").GetValue().AsInt64() != childID).ToArray();
fatherBaseField.Get("m_Children").Get("Array").SetChildrenList(newChildren);
// Save
replacers.Add(new AssetsReplacerFromMemory(0, fatherFile.index, (int)fatherFile.curFileType, 0xFFFF, fatherBaseField.WriteToByteArray()));
// Export
AssetsFileWriter writer = new AssetsFileWriter(File.OpenWrite("sharedassets0-new.assets"));
assetsFileInstance.file.Write(writer, 0, replacers, 0);
writer.Close();
However, instead of editing the existing father transform, the replaces amend a new transform so I end up with 2 transforms of the same ID (according to AssetsView). What am I missing here? In addition, I also update child's m_Father, I can upload that too if needed. Also, when viewing the transform, AssetsView claims the child has not been removed, but I think it is referencing the older transform and not the one I ammended.
I am using AssetsTools Update 15 v2.0.6 in .NET Framework v4.7.2.
RGBADecoders.ReadARGB4444() outputs the color components in the opposite order of the correct one. The format stores each pixel as 0xGB 0xAR, so the code should be:
int a = bytes[i + 1] >> 4;
int r = bytes[i + 1] & 0xf;
int g = bytes[i] >> 4;
int b = bytes[i] & 0xf;
data[dataPos + 0] = (byte)((b << 4) | b);
data[dataPos + 1] = (byte)((g << 4) | g);
data[dataPos + 2] = (byte)((r << 4) | r);
data[dataPos + 3] = (byte)((a << 4) | a);
ReadRGBA4444() has a similarly incorrect order, and on top of that, doesn't perform the necessary nibble duplication ((component << 4) | component).
Is not an issue but do you know how to get the coordinates and size of sprites that are inside atlas ?
I'm trying to build and unpacker for atlas.
When trying to extract files compressed with LZMA (created using UABE 2.2), this error shows up. I compared lzmaDecoder.cs with the one from Perfare's AssetStudio and they are the same, so the issue lies elsewhere. Any help getting it to extract the files would be appreciated.
I uploaded two example files here, one which works and one which doesn't.
https://anonfiles.com/a7W0E61bya/model_LZMA
https://anonfiles.com/c8WaEd1cy8/model_LZ4
I am trying to uncompress the bundle files because I can't read the compressed files.
This code:
AssetsManager helper = new AssetsManager();
helper.LoadClassPackage("classdata.tpk");
var inst = helper.LoadBundleFile(inputFilename, false);
var file = inst.file;
if (file.bundleHeader6.GetCompressionType() == 0) return;
using (FileStream stream = File.Open(outputFilename, FileMode.Create, FileAccess.ReadWrite))
{
file.reader.Position = 0;
file.Unpack(file.reader, new AssetsFileWriter(stream));
stream.Position = 0;
}
With reference from NuGet crash with message for a bundle file of 900MB but not with a bundle of 6MB
System.OutOfMemoryException in AssetsTools.NET.AssetBundleFile.Unpack(AssetsFileReader reader, AssetsFileWriter writer)
same error from version 2.0.7 to 2.0.9
but work correctly if i donwload the source code and add to my tool the AssetsTools.NET.csproj project
I see that this tool lets you explore globalgamemanagers but doesn't let you edit it. UABE has this option to export these files to a text file, and then import it again as a way to create a modified version.
I'm actually looking for a way to edit globalgamemanagers programmatically. Just want to be able to go into it, change a setting in BuildSettings, and save a new file. Do you have any tips on how this could be achieved?
Sorry that this isn't an actual issue, I just didn't know any other way to contact you.
AssetsTools.NET doesn't support the RGB565 texture format even though it's quite easy to implement:
public static byte[] ReadRGB565(byte[] input, int width, int height)
{
int inputLength = width * height * 2;
byte[] output = new byte[width * height * 4];
int outputPos = 0;
for (int inputPos = 0; inputPos < inputLength; inputPos += 2)
{
int rgb = (input[inputPos + 1] << 8) | input[inputPos];
int r = (rgb >> 11) & 0x1F;
r = (r << 3) | (r & 7);
int g = (rgb >> 5) & 0x3F;
g = (g << 2) | (g & 3);
int b = rgb & 0x1F;
b = (b << 3) | (b & 7);
output[outputPos + 0] = (byte)b;
output[outputPos + 1] = (byte)g;
output[outputPos + 2] = (byte)r;
output[outputPos + 3] = 0xFF;
outputPos += 4;
}
return output;
}
Transparent CI builds would be an important improvement, depending on the platform, I can send a PR that adds this feature.
Greetings. Is there a way to ADD asset to .assets file?
I have been looking for a code to ADD an asset for a long time.
I used example code from readme for modification:
var am = new AssetsManager();
am.LoadClassPackage("classdata.tpk");
var inst = am.LoadAssetsFile("resources.assets", true);
am.LoadClassDatabaseFromPackage(inst.file.typeTree.unityVersion);
var inf = inst.table.GetAssetInfo("MyBoringAsset");
var baseField = am.GetTypeInstance(inst.file, inf).GetBaseField();
baseField.Get("m_Name")
.GetValue()
.Set("MyCoolAsset");
var newGoBytes = baseField.WriteToByteArray();
//AssetsReplacerFromMemory's monoScriptIndex should always be 0xFFFF unless it's a MonoBehaviour
var repl = new AssetsReplacerFromMemory(0, inf.index, (int)inf.curFileType, 0xFFFF, newGoBytes);
var writer = new AssetsFileWriter(File.OpenWrite("resources-modified.assets"));
inst.file.Write(writer, 0, new List() { repl }, 0);
My goal is to ADD an asset and change it or ADD a ready asset.
This code changes the name, but I need to ADD an asset.
I have also tried this code:
var templateField = new AssetTypeTemplateField();
var cldbType = AssetHelper.FindAssetClassByName(am.classFile, "TextAsset");
templateField.FromClassDatabase(am.classFile, cldbType, 0);
var baseField = ValueBuilder.DefaultValueFieldFromTemplate(templateField);
baseField.Get("m_Name").GetValue().Set("MyCoolTextAsset");
baseField.Get("m_Script").GetValue().Set("I have some sick text");
//or you can just use table.assetFileInfoCount + 2 but that doesn't always work
var nextAssetId = table.assetFileInfo.Max(i => i.index) + 1;
replacers.Add(new AssetsReplacerFromMemory(0, nextAssetId, cldbType.classId, 0xffff, baseField.WriteToByteArray()));
//... do other replacer stuff
But when I use it my program crashes.
What do I need to do?
is there a chance u could add support for compressing to lz4hc in asset view?
Is it possible that you provide a nuget package for .net standard or .net Framework 4.5+?
Hi,
Bit a of a newbie here. (especially when it comes to Unity assets) I have some asset bundles that are loaded into memory that I'm looking to read. They are of type MonoBehaviour
, as I understand.
I think I can use the following to load the data, but I'm not sure where to go after that.
var bun = new AssetBundleFile();
bun.Read(new AssetsFileReader(memoryStream), true);
if (bun.bundleHeader6.GetCompressionType() != 0)
{
bun = BundleHelper.UnpackBundle(bun);
}
Hi,
I'm trying to decompress some UnityFS archives that neither UABE or AssetsTools.NET can handle, but AssetsTools is at least providing meaningful exceptions. I'm curious whether it is known that this version of the format doesn't work, or these perhaps are using some new or unusual version of the format?
The header looks like this:
UnityFS.....5.x.x.2018.4.23f1
The '5.x.x' makes it unclear to me whether this version of Unity is producing one of the formats your README says are unsupported (5.0-5.4) or what. 2018.4.23f1 appears to be after the officially numbered 5.x releases, but isn't that new... both UABE and AssetTools agree that they are compressed. If it means anything in this context, the FS files are from an Android game. It has MP4 files and CRIWare archives next to it that both decompress as-is with existing tools, there's no weird encryption applied to them, so I would be surprised if they did anything fancy to the UnityFS packages that leaves the headers intact. The 'opening bundle file' window says these archives are compressed with LZ4.
Sample exception messages:
An error occurred:
The output char buffer is too small to contain the decoded characters, encoding 'Unicode (UTF-8)' fallback 'System.Text.DecoderReplacementFallback'.
Parameter name: chars
An error occurred:
Unable to read beyond the end of the stream.
No valid assets files found in the bundle.
An error occurred:
Arithmetic operation resulted in an overflow.
I'm trying to replace some assets in an asset bundle using a tool I made that runs on this library (https://github.com/Skyluker4/UnityAssetReplacer). When I save a new asset bundle to replace assets, something gets messed up. Without even actually replacing any assets and just making a copy of the bundle using AssetTools.NET it ends up being 20 bytes less than the original. The beginning of the file differs from the original (about the first few hundred bytes or so are different).
This is only for one specific asset bundle, other ones work fine. When trying to read from UABE, it says "invalid assets file".
Here's a look at the files -- the original on the left and the one AssetTools.NET generated on the right.
I don't know if this is a problem with my own code or a problem with the library. I'm using AssetReplacerFromMemory
to replace the assets and BundleReplacerFromMemory
and AssetsFileWriter
to create the new bundle.
When writing a file I read with AssetsManager, the offset of the asset data is increased from 0x1000 to 0x1010. I've tested it with Unity versions 2017.4.33f1 and 2018.4.26f1 and it uniformly increases the offset by 16 bytes. When replacing the original file, the mismatch causes Unity to hang with the following errors (the first probably being specific to the file):
Mismatched serialization in the builtin class 'PreloadData'. (Read 12 bytes but expected 144 bytes)
(Filename: Line: 1929)
The file 'sharedassets0_original.assets' is corrupted! Remove it and launch unity again!
[Position out of bounds!]
(Filename: Line: 215)
I isolated the mismatch to if statement where after the method pads the file to 0x1000 bytes, it adds another 16 if the position is a multiple of 16. I've been removing the first case during development, but is there a way to make sure whatever bug got fixed stays fixed while eliminating the mismatch?
I have attached the code I used to test the issue and the bundles used and produced. Thank you for making this project, it is very helpful!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.