Skip to content

Commit 189a4c4

Browse files
committed
updated docs & tests
1 parent 32925ab commit 189a4c4

5 files changed

Lines changed: 188 additions & 37 deletions

File tree

Documentation/command-analyze.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ UnityDataTool analyze <path> [options]
1616
| `-s, --skip-references` | Skip CRC and reference extraction (faster, smaller DB) | `false` |
1717
| `-v, --verbose` | Show more information during analysis | `false` |
1818
| `--no-recurse` | Do not recurse into sub-directories | `false` |
19+
| `-d, --typetree-data <file>` | Load an external TypeTree data file before processing (Unity 6.5+) ||
1920

2021
## Examples
2122

@@ -90,7 +91,13 @@ System.ArgumentException: Invalid object id.
9091

9192
This error occurs when SerializedFiles are built without TypeTrees. The command will skip these files and continue.
9293

93-
**Solution:** Enable **ForceAlwaysWriteTypeTrees** in your Unity build settings. See [Unity Content Format](../../Documentation/unity-content-format.md) for details.
94+
**Solutions:**
95+
- Enable **ForceAlwaysWriteTypeTrees** in your Unity build settings. See [Unity Content Format](../../Documentation/unity-content-format.md) for details.
96+
- If your bundles were built with external TypeTree data (Unity 6.5+), use the `--typetree-data` option to load the TypeTree data file before analysis:
97+
98+
```bash
99+
UnityDataTool analyze /path/to/bundles --typetree-data /path/to/typetree.bin
100+
```
94101

95102
### SQL Constraint Errors
96103

Documentation/command-dump.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ UnityDataTool dump <path> [options]
1515
| `-f, --output-format <format>` | Output format | `text` |
1616
| `-s, --skip-large-arrays` | Skip dumping large arrays | `false` |
1717
| `-i, --objectid <id>` | Only dump object with this ID | All objects |
18+
| `-d, --typetree-data <file>` | Load an external TypeTree data file before processing (Unity 6.5+) ||
1819

1920
## Examples
2021

@@ -87,6 +88,14 @@ UnityDataTool serialized-file metadata /path/to/file
8788

8889
The `TypeTree Definitions` field will show `No` when TypeTrees are absent.
8990

91+
**External TypeTree data (Unity 6.5+):**
92+
93+
If your bundles were built with TypeTree data extracted to a separate file, use the `--typetree-data` option to load it:
94+
95+
```bash
96+
UnityDataTool dump /path/to/file.bundle --typetree-data /path/to/typetree.bin
97+
```
98+
9099
---
91100

92101
## Output Format

Documentation/unitydatatool.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ Use `--help` with any command for details: `UnityDataTool analyze --help`
4141

4242
Use `--version` to print the tool version.
4343

44+
## External TypeTree Data
45+
46+
Starting with Unity 6.5, asset bundles can be built with TypeTree data extracted into a separate file. When bundles are built this way, the TypeTree data file must be loaded before the bundles can be processed.
47+
48+
The `--typetree-data` (`-d`) option is available on the [`analyze`](command-analyze.md) and [`dump`](command-dump.md) commands:
49+
50+
```bash
51+
# Analyze bundles that use an external TypeTree data file
52+
UnityDataTool analyze /path/to/bundles --typetree-data /path/to/typetree.bin
53+
54+
# Dump a bundle with external TypeTree data
55+
UnityDataTool dump /path/to/file.bundle -d /path/to/typetree.bin
56+
```
57+
58+
> **Note:** This option requires a version of UnityFileSystemApi from Unity 6.5 or newer. Using it with an older version of the library will produce an error message.
59+
4460

4561
## Installation
4662

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using Microsoft.Data.Sqlite;
5+
using NUnit.Framework;
6+
7+
namespace UnityDataTools.UnityDataTool.Tests;
8+
9+
#pragma warning disable NUnit2005, NUnit2006
10+
11+
public class ExtractedTypeTreeTests
12+
{
13+
private string m_TestOutputFolder;
14+
private string m_DataFolder;
15+
private string m_SerializedFile;
16+
private string m_TypeTreeDataFile;
17+
18+
[OneTimeSetUp]
19+
public void OneTimeSetup()
20+
{
21+
m_TestOutputFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "test_folder_typetree");
22+
Directory.CreateDirectory(m_TestOutputFolder);
23+
24+
m_DataFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "ExtractedTypeTree");
25+
m_SerializedFile = Path.Combine(m_DataFolder, "sfwithextractedtypetrees1");
26+
m_TypeTreeDataFile = Path.Combine(m_DataFolder, "sfwithextractedtypetrees1.typetreedata");
27+
}
28+
29+
[SetUp]
30+
public void Setup()
31+
{
32+
Directory.SetCurrentDirectory(m_TestOutputFolder);
33+
}
34+
35+
[TearDown]
36+
public void Teardown()
37+
{
38+
SqliteConnection.ClearAllPools();
39+
40+
foreach (var file in new DirectoryInfo(m_TestOutputFolder).EnumerateFiles())
41+
{
42+
file.Delete();
43+
}
44+
45+
foreach (var dir in new DirectoryInfo(m_TestOutputFolder).EnumerateDirectories())
46+
{
47+
dir.Delete(true);
48+
}
49+
}
50+
51+
[Test]
52+
public async Task Analyze_WithTypeTreeData_DatabaseCorrect(
53+
[Values("-d", "--typetree-data")] string option)
54+
{
55+
var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder);
56+
57+
Assert.AreEqual(0, await Program.Main(new string[] { "analyze", m_DataFolder, option, m_TypeTreeDataFile }));
58+
59+
using var db = SQLTestHelper.OpenDatabase(databasePath);
60+
61+
var objectCount = SQLTestHelper.QueryInt(db, "SELECT COUNT(*) FROM objects");
62+
Assert.Greater(objectCount, 0, "Expected objects in database when TypeTree data file is provided");
63+
}
64+
65+
[Test]
66+
public async Task Analyze_WithoutTypeTreeData_ReportsFailure()
67+
{
68+
var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder);
69+
70+
using var swOut = new StringWriter();
71+
using var swErr = new StringWriter();
72+
var currentOut = Console.Out;
73+
var currentErr = Console.Error;
74+
try
75+
{
76+
Console.SetOut(swOut);
77+
Console.SetError(swErr);
78+
79+
await Program.Main(new string[] { "analyze", m_DataFolder });
80+
81+
var output = swOut.ToString() + swErr.ToString();
82+
83+
Assert.That(output, Does.Contain("Failed files: 1"),
84+
"Expected failure when analyzing without TypeTree data file");
85+
}
86+
finally
87+
{
88+
Console.SetOut(currentOut);
89+
Console.SetError(currentErr);
90+
}
91+
}
92+
93+
[Test]
94+
public async Task Dump_WithTypeTreeData_Succeeds(
95+
[Values("-d", "--typetree-data")] string option)
96+
{
97+
Assert.AreEqual(0, await Program.Main(new string[] { "dump", m_SerializedFile, option, m_TypeTreeDataFile }));
98+
99+
var outputFiles = Directory.GetFiles(m_TestOutputFolder, "*.txt");
100+
Assert.IsNotEmpty(outputFiles, "Expected dump output files when TypeTree data file is provided");
101+
}
102+
103+
[Test]
104+
public async Task Dump_WithoutTypeTreeData_Fails()
105+
{
106+
Assert.AreNotEqual(0, await Program.Main(new string[] { "dump", m_SerializedFile }));
107+
}
108+
109+
[Test]
110+
public async Task TypeTreeData_FileNotFound_ReturnsError()
111+
{
112+
var result = await Program.Main(new string[] { "analyze", m_DataFolder, "--typetree-data", "nonexistent_file.bin" });
113+
Assert.AreNotEqual(0, result, "Expected non-zero return code when TypeTree data file does not exist");
114+
}
115+
}

UnityDataTool/Program.cs

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ public static async Task<int> Main(string[] args)
2323

2424
var rootCommand = new RootCommand();
2525

26-
var typeTreeDataOpt = new Option<FileInfo>(
27-
aliases: new[] { "--typetree-data", "-d" },
28-
description: "Path to an external TypeTree data file to load before processing bundles");
29-
typeTreeDataOpt.ExistingOnly();
30-
rootCommand.AddGlobalOption(typeTreeDataOpt);
26+
var typeTreeDataDescription = "Path to an external TypeTree data file to load before processing bundles";
3127

3228
{
3329
var pathArg = new Argument<DirectoryInfo>("path", "The path to the directory containing the files to analyze").ExistingOnly();
@@ -38,6 +34,8 @@ public static async Task<int> Main(string[] args)
3834
var vOpt = new Option<bool>(aliases: new[] { "--verbose", "-v" }, description: "Verbose output");
3935
var recurseOpt = new Option<bool>(aliases: new[] { "--no-recurse" }, description: "Do not analyze contents of subdirectories inside path");
4036

37+
var dOpt = new Option<FileInfo>(aliases: new[] { "--typetree-data", "-d" }, description: typeTreeDataDescription);
38+
4139
var analyzeCommand = new Command("analyze", "Analyze AssetBundles or SerializedFiles.")
4240
{
4341
pathArg,
@@ -46,13 +44,19 @@ public static async Task<int> Main(string[] args)
4644
rOpt,
4745
pOpt,
4846
vOpt,
49-
recurseOpt
47+
recurseOpt,
48+
dOpt
5049
};
5150

5251
analyzeCommand.AddAlias("analyse");
5352
analyzeCommand.SetHandler(
54-
(DirectoryInfo di, string o, bool s, bool r, string p, bool v, bool recurseOpt) => Task.FromResult(HandleAnalyze(di, o, s, r, p, v, recurseOpt)),
55-
pathArg, oOpt, sOpt, rOpt, pOpt, vOpt, recurseOpt);
53+
(DirectoryInfo di, string o, bool s, bool r, string p, bool v, bool noRecurse, FileInfo d) =>
54+
{
55+
var ttResult = LoadTypeTreeDataFile(d);
56+
if (ttResult != 0) return Task.FromResult(ttResult);
57+
return Task.FromResult(HandleAnalyze(di, o, s, r, p, v, noRecurse));
58+
},
59+
pathArg, oOpt, sOpt, rOpt, pOpt, vOpt, recurseOpt, dOpt);
5660

5761
rootCommand.AddCommand(analyzeCommand);
5862
}
@@ -89,17 +93,25 @@ public static async Task<int> Main(string[] args)
8993
var oOpt = new Option<DirectoryInfo>(aliases: new[] { "--output-path", "-o" }, description: "Output folder", getDefaultValue: () => new DirectoryInfo(Environment.CurrentDirectory));
9094
var objectIdOpt = new Option<long>(aliases: new[] { "--objectid", "-i" }, () => 0, "Only dump the object with this signed 64-bit id (default: 0, dump all objects)");
9195

96+
var dOpt = new Option<FileInfo>(aliases: new[] { "--typetree-data", "-d" }, description: typeTreeDataDescription);
97+
9298
var dumpCommand = new Command("dump", "Dump the contents of an AssetBundle or SerializedFile.")
9399
{
94100
pathArg,
95101
fOpt,
96102
sOpt,
97103
oOpt,
98104
objectIdOpt,
105+
dOpt,
99106
};
100107
dumpCommand.SetHandler(
101-
(FileInfo fi, DumpFormat f, bool s, DirectoryInfo o, long objectId) => Task.FromResult(HandleDump(fi, f, s, o, objectId)),
102-
pathArg, fOpt, sOpt, oOpt, objectIdOpt);
108+
(FileInfo fi, DumpFormat f, bool s, DirectoryInfo o, long objectId, FileInfo d) =>
109+
{
110+
var ttResult = LoadTypeTreeDataFile(d);
111+
if (ttResult != 0) return Task.FromResult(ttResult);
112+
return Task.FromResult(HandleDump(fi, f, s, o, objectId));
113+
},
114+
pathArg, fOpt, sOpt, oOpt, objectIdOpt, dOpt);
103115

104116
rootCommand.AddCommand(dumpCommand);
105117
}
@@ -193,32 +205,6 @@ public static async Task<int> Main(string[] args)
193205
rootCommand.AddCommand(serializedFileCommand);
194206
}
195207

196-
// Load external TypeTree data file before any command executes.
197-
for (int i = 0; i < args.Length - 1; i++)
198-
{
199-
if (args[i] == "--typetree-data" || args[i] == "-d")
200-
{
201-
var typeTreeFile = args[i + 1];
202-
if (!File.Exists(typeTreeFile))
203-
{
204-
Console.Error.WriteLine($"TypeTree data file not found: {typeTreeFile}");
205-
UnityFileSystem.Cleanup();
206-
return 1;
207-
}
208-
try
209-
{
210-
UnityFileSystem.AddTypeTreeSourceFromFile(typeTreeFile);
211-
}
212-
catch (EntryPointNotFoundException)
213-
{
214-
Console.Error.WriteLine("Error: The loaded UnityFileSystemApi does not support external TypeTree data files. Please update to Unity 6.5 or newer.");
215-
UnityFileSystem.Cleanup();
216-
return 1;
217-
}
218-
break;
219-
}
220-
}
221-
222208
var r = await rootCommand.InvokeAsync(args);
223209

224210
UnityFileSystem.Cleanup();
@@ -231,6 +217,24 @@ enum DumpFormat
231217
Text,
232218
}
233219

220+
static int LoadTypeTreeDataFile(FileInfo typeTreeDataFile)
221+
{
222+
if (typeTreeDataFile == null)
223+
return 0;
224+
225+
try
226+
{
227+
UnityFileSystem.AddTypeTreeSourceFromFile(typeTreeDataFile.FullName);
228+
}
229+
catch (EntryPointNotFoundException)
230+
{
231+
Console.Error.WriteLine("Error: The loaded UnityFileSystemApi does not support external TypeTree data files. Please update to Unity 6.5 or newer.");
232+
return 1;
233+
}
234+
235+
return 0;
236+
}
237+
234238
static int HandleAnalyze(
235239
DirectoryInfo path,
236240
string outputFile,

0 commit comments

Comments
 (0)