diff --git a/AGENTS.md b/AGENTS.md
index 82f5783..93fd1f1 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -47,6 +47,23 @@ dotnet test --filter "FullyQualifiedName~SerializedFile"
Test projects: UnityFileSystem.Tests, Analyzer.Tests, UnityDataTool.Tests, TestCommon (helper library)
+### Code Style
+
+#### Comments
+
+* Write comments that explain "why". A few high level comments explaining the purpose of classes or methods is very helpful. Comments explaining tricky code are also helpful.
+* Avoid comments that are redundant with the code. Do not comment before each line of code explaining what it does unless there is something that is not obvious going on.
+* Do not use formal C# XML format when commenting methods, unless it is in an important interface class like UnityFileSystem.
+
+#### Formatting
+
+To repair white space or style issues, run:
+
+```
+dotnet format whitespace . --folder
+dotnet format style
+```
+
### Running the Tool
```bash
# Show all commands
diff --git a/Analyzer/Properties/Resources.Designer.cs b/Analyzer/Properties/Resources.Designer.cs
index 8fb39a8..6eab80f 100644
--- a/Analyzer/Properties/Resources.Designer.cs
+++ b/Analyzer/Properties/Resources.Designer.cs
@@ -628,7 +628,67 @@ internal static string AudioClip {
return ResourceManager.GetString("AudioClip", resourceCulture);
}
}
-
+
+ ///
+ /// Looks up a localized string similar to CREATE TABLE IF NOT EXISTS build_reports(
+ /// id INTEGER,
+ /// build_type TEXT,
+ /// build_result TEXT,
+ /// platform_name TEXT,
+ /// subtarget INTEGER,
+ /// start_time TEXT,
+ /// end_time TEXT,
+ /// total_time_seconds INTEGER,
+ /// total_size INTEGER,
+ /// build_guid TEXT,
+ /// total_errors INTEGER,
+ /// total_warnings INTEGER,
+ /// options INTEGER,
+ /// asset_bundle_options INTEGER,
+ /// output_path TEXT,
+ /// crc INTEGER,
+ /// PRIMARY KEY (id)
+ ///);
+ ///
+ ///CREATE TABLE IF NOT EXISTS build_report_files(
+ /// build_report_id INTEGER NOT NULL,
+ /// file_index INTEGER NOT NULL,
+ /// path TEXT NOT NULL,
+ /// role TEXT NOT NULL,
+ /// size INTEGER NOT NULL,
+ /// PRIMARY KEY (build_report_id, file_index),
+ /// FOREIGN KEY (build_report_id) REFERENCES build_reports(id)
+ ///);
+ ///
+ ///CREATE TABLE IF NOT EXISTS build_report_archive_contents(
+ /// build_report_id INTEGER NOT NULL,
+ /// assetbundle TEXT NOT NULL,
+ /// assetbundle_content TEXT NOT NULL,
+ /// PRIMARY KEY (build_report_id, assetbundle_content),
+ /// FOREIGN KEY (build_report_id) REFERENCES build_reports(id)
+ ///);
+ ///
+ ///CREATE VIEW build_report_files_view AS
+ ///SELECT
+ /// o.serialized_file,
+ /// br.id AS build_report_id,
+ /// br.build_type,
+ /// br.platform_name,
+ /// brf.file_index,
+ /// brf.path,
+ /// brf.role,
+ /// brf.size
+ ///FROM build_report_files brf
+ ///INNER JOIN build_reports br ON brf.build_report_id = br.id
+ ///INNER JOIN object_view o ON br.id = o.id;
+ ///.
+ ///
+ internal static string BuildReport {
+ get {
+ return ResourceManager.GetString("BuildReport", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to CREATE INDEX refs_object_index ON refs(object);
///CREATE INDEX refs_referenced_object_index ON refs(referenced_object);
@@ -835,5 +895,68 @@ internal static string MonoScript {
return ResourceManager.GetString("MonoScript", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to CREATE TABLE IF NOT EXISTS build_report_packed_assets(
+ /// id INTEGER,
+ /// path TEXT,
+ /// file_header_size INTEGER,
+ /// PRIMARY KEY (id)
+ ///);
+ ///
+ ///CREATE TABLE IF NOT EXISTS build_report_source_assets(
+ /// id INTEGER PRIMARY KEY AUTOINCREMENT,
+ /// source_asset_guid TEXT NOT NULL,
+ /// build_time_asset_path TEXT NOT NULL,
+ /// UNIQUE(source_asset_guid, build_time_asset_path)
+ ///);
+ ///
+ ///CREATE TABLE IF NOT EXISTS build_report_packed_asset_info(
+ /// packed_assets_id INTEGER,
+ /// object_id INTEGER,
+ /// type INTEGER,
+ /// size INTEGER,
+ /// offset INTEGER,
+ /// source_asset_id INTEGER NOT NULL,
+ /// FOREIGN KEY (packed_assets_id) REFERENCES build_report_packed_assets(id),
+ /// FOREIGN KEY (source_asset_id) REFERENCES build_report_source_assets(id)
+ ///);
+ ///
+ ///CREATE VIEW build_report_packed_assets_view AS
+ ///SELECT
+ /// pa.id,
+ /// o.object_id,
+ /// brac.assetbundle,
+ /// sf.name as build_report,
+ /// pa.path,
+ /// pa.file_header_size
+ ///FROM build_report_packed_assets pa
+ ///INNER JOIN objects o ON pa.id = o.id
+ ///INNER JOIN serialized_files sf ON o.serialized_file = sf.id
+ ///LEFT JOIN objects br_obj ON o.serialized_file = br_obj.serialized_file AND br_obj.type = 1125
+ ///LEFT JOIN build_report_archive_contents brac ON br_obj.id = brac.build_report_id AND pa.path = brac.assetbundle_content;
+ ///
+ ///CREATE VIEW build_report_packed_asset_contents_view AS
+ ///SELECT
+ /// o.serialized_file,
+ /// pa.path,
+ /// pac.packed_assets_id,
+ /// pac.object_id,
+ /// pac.type,
+ /// pac.size,
+ /// pac.offset,
+ /// sa.source_asset_guid,
+ /// sa.build_time_asset_path
+ ///FROM build_report_packed_asset_info pac
+ ///LEFT JOIN build_report_packed_assets pa ON pac.packed_assets_id = pa.id
+ ///LEFT JOIN object_view o ON o.id = pa.id
+ ///LEFT JOIN build_report_source_assets sa ON pac.source_asset_id = sa.id;
+ ///.
+ ///
+ internal static string PackedAssets {
+ get {
+ return ResourceManager.GetString("PackedAssets", resourceCulture);
+ }
+ }
}
}
diff --git a/Analyzer/Properties/Resources.resx b/Analyzer/Properties/Resources.resx
index da823c0..1722f57 100644
--- a/Analyzer/Properties/Resources.resx
+++ b/Analyzer/Properties/Resources.resx
@@ -127,6 +127,9 @@
..\Resources\AudioClip.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+ ..\Resources\BuildReport.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
..\Resources\Finalize.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
@@ -226,4 +229,7 @@
..\Resources\MonoScript.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+ ..\Resources\PackedAssets.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
diff --git a/Analyzer/Resources/BuildReport.sql b/Analyzer/Resources/BuildReport.sql
new file mode 100644
index 0000000..eab8bb9
--- /dev/null
+++ b/Analyzer/Resources/BuildReport.sql
@@ -0,0 +1,52 @@
+CREATE TABLE IF NOT EXISTS build_reports(
+ id INTEGER,
+ build_type TEXT,
+ build_result TEXT,
+ platform_name TEXT,
+ subtarget INTEGER,
+ start_time TEXT,
+ end_time TEXT,
+ total_time_seconds INTEGER,
+ total_size INTEGER,
+ build_guid TEXT,
+ total_errors INTEGER,
+ total_warnings INTEGER,
+ options INTEGER,
+ asset_bundle_options INTEGER,
+ output_path TEXT,
+ crc INTEGER,
+ PRIMARY KEY (id)
+);
+
+CREATE TABLE IF NOT EXISTS build_report_files(
+ build_report_id INTEGER NOT NULL,
+ file_index INTEGER NOT NULL,
+ path TEXT NOT NULL,
+ role TEXT NOT NULL,
+ size INTEGER NOT NULL,
+ PRIMARY KEY (build_report_id, file_index),
+ FOREIGN KEY (build_report_id) REFERENCES build_reports(id)
+);
+
+CREATE TABLE IF NOT EXISTS build_report_archive_contents(
+ build_report_id INTEGER NOT NULL,
+ assetbundle TEXT NOT NULL,
+ assetbundle_content TEXT NOT NULL,
+ PRIMARY KEY (build_report_id, assetbundle_content),
+ FOREIGN KEY (build_report_id) REFERENCES build_reports(id)
+);
+
+CREATE VIEW build_report_files_view AS
+SELECT
+ o.serialized_file,
+ br.id AS build_report_id,
+ br.build_type,
+ br.platform_name,
+ brf.file_index,
+ brf.path,
+ brf.role,
+ brf.size
+FROM build_report_files brf
+INNER JOIN build_reports br ON brf.build_report_id = br.id
+INNER JOIN object_view o ON br.id = o.id;
+
diff --git a/Analyzer/Resources/PackedAssets.sql b/Analyzer/Resources/PackedAssets.sql
new file mode 100644
index 0000000..dd55bb0
--- /dev/null
+++ b/Analyzer/Resources/PackedAssets.sql
@@ -0,0 +1,61 @@
+CREATE TABLE IF NOT EXISTS build_report_packed_assets(
+ id INTEGER,
+ path TEXT,
+ file_header_size INTEGER,
+ PRIMARY KEY (id)
+);
+
+CREATE TABLE IF NOT EXISTS build_report_source_assets(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ source_asset_guid TEXT NOT NULL,
+ build_time_asset_path TEXT NOT NULL,
+ UNIQUE(source_asset_guid, build_time_asset_path)
+);
+
+CREATE TABLE IF NOT EXISTS build_report_packed_asset_info(
+ packed_assets_id INTEGER,
+ object_id INTEGER,
+ type INTEGER,
+ size INTEGER,
+ offset INTEGER,
+ source_asset_id INTEGER NOT NULL,
+ FOREIGN KEY (packed_assets_id) REFERENCES build_report_packed_assets(id),
+ FOREIGN KEY (source_asset_id) REFERENCES build_report_source_assets(id)
+);
+
+CREATE VIEW build_report_packed_assets_view AS
+SELECT
+ pa.id,
+ o.object_id,
+ brac.assetbundle,
+ pa.path,
+ pa.file_header_size,
+ br_obj.id as build_report_id,
+ sf.name as build_report_filename
+FROM build_report_packed_assets pa
+INNER JOIN objects o ON pa.id = o.id
+INNER JOIN serialized_files sf ON o.serialized_file = sf.id
+LEFT JOIN objects br_obj ON o.serialized_file = br_obj.serialized_file AND br_obj.type = 1125
+LEFT JOIN build_report_archive_contents brac ON br_obj.id = brac.build_report_id AND pa.path = brac.assetbundle_content;
+
+CREATE VIEW build_report_packed_asset_contents_view AS
+SELECT
+ sf.name as serialized_file,
+ brac.assetbundle,
+ pa.path,
+ pac.packed_assets_id,
+ pac.object_id,
+ pac.type,
+ pac.size,
+ pac.offset,
+ sa.source_asset_guid,
+ sa.build_time_asset_path,
+ br_obj.id as build_report_id
+FROM build_report_packed_asset_info pac
+LEFT JOIN build_report_packed_assets pa ON pac.packed_assets_id = pa.id
+LEFT JOIN objects o ON o.id = pa.id
+INNER JOIN serialized_files sf ON o.serialized_file = sf.id
+LEFT JOIN build_report_source_assets sa ON pac.source_asset_id = sa.id
+LEFT JOIN objects br_obj ON o.serialized_file = br_obj.serialized_file AND br_obj.type = 1125
+LEFT JOIN build_report_archive_contents brac ON br_obj.id = brac.build_report_id AND pa.path = brac.assetbundle_content;
+
diff --git a/Analyzer/SQLite/Handlers/BuildReportHandler.cs b/Analyzer/SQLite/Handlers/BuildReportHandler.cs
new file mode 100644
index 0000000..8535ab1
--- /dev/null
+++ b/Analyzer/SQLite/Handlers/BuildReportHandler.cs
@@ -0,0 +1,132 @@
+using System;
+using Microsoft.Data.Sqlite;
+using UnityDataTools.Analyzer.SerializedObjects;
+using UnityDataTools.FileSystem.TypeTreeReaders;
+
+namespace UnityDataTools.Analyzer.SQLite.Handlers;
+
+public class BuildReportHandler : ISQLiteHandler
+{
+ private SqliteCommand m_InsertCommand;
+ private SqliteCommand m_InsertFileCommand;
+ private SqliteCommand m_InsertArchiveContentCommand;
+
+ public void Init(SqliteConnection db)
+ {
+ using var command = db.CreateCommand();
+ command.CommandText = Properties.Resources.BuildReport ?? throw new InvalidOperationException("BuildReport resource not found");
+ command.ExecuteNonQuery();
+
+ m_InsertCommand = db.CreateCommand();
+ m_InsertCommand.CommandText = @"INSERT INTO build_reports(
+ id, build_type, build_result, platform_name, subtarget, start_time, end_time, total_time_seconds,
+ total_size, build_guid, total_errors, total_warnings, options, asset_bundle_options,
+ output_path, crc
+ ) VALUES(
+ @id, @build_type, @build_result, @platform_name, @subtarget, @start_time, @end_time, @total_time_seconds,
+ @total_size, @build_guid, @total_errors, @total_warnings, @options, @asset_bundle_options,
+ @output_path, @crc
+ )";
+
+ m_InsertCommand.Parameters.Add("@id", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@build_type", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@build_result", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@platform_name", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@subtarget", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@start_time", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@end_time", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@total_time_seconds", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@total_size", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@build_guid", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@total_errors", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@total_warnings", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@options", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@asset_bundle_options", SqliteType.Integer);
+ m_InsertCommand.Parameters.Add("@output_path", SqliteType.Text);
+ m_InsertCommand.Parameters.Add("@crc", SqliteType.Integer);
+
+ m_InsertFileCommand = db.CreateCommand();
+ m_InsertFileCommand.CommandText = @"INSERT INTO build_report_files(
+ build_report_id, file_index, path, role, size
+ ) VALUES(
+ @build_report_id, @file_index, @path, @role, @size
+ )";
+
+ m_InsertFileCommand.Parameters.Add("@build_report_id", SqliteType.Integer);
+ m_InsertFileCommand.Parameters.Add("@file_index", SqliteType.Integer);
+ m_InsertFileCommand.Parameters.Add("@path", SqliteType.Text);
+ m_InsertFileCommand.Parameters.Add("@role", SqliteType.Text);
+ m_InsertFileCommand.Parameters.Add("@size", SqliteType.Integer);
+
+ m_InsertArchiveContentCommand = db.CreateCommand();
+ m_InsertArchiveContentCommand.CommandText = @"INSERT INTO build_report_archive_contents(
+ build_report_id, assetbundle, assetbundle_content
+ ) VALUES(
+ @build_report_id, @assetbundle, @assetbundle_content
+ )";
+
+ m_InsertArchiveContentCommand.Parameters.Add("@build_report_id", SqliteType.Integer);
+ m_InsertArchiveContentCommand.Parameters.Add("@assetbundle", SqliteType.Text);
+ m_InsertArchiveContentCommand.Parameters.Add("@assetbundle_content", SqliteType.Text);
+ }
+
+ public void Process(Context ctx, long objectId, RandomAccessReader reader, out string name, out long streamDataSize)
+ {
+ var buildReport = BuildReport.Read(reader);
+ m_InsertCommand.Transaction = ctx.Transaction;
+ m_InsertCommand.Parameters["@id"].Value = objectId;
+ m_InsertCommand.Parameters["@build_type"].Value = BuildReport.GetBuildTypeString(buildReport.BuildType);
+ m_InsertCommand.Parameters["@build_result"].Value = buildReport.BuildResult;
+ m_InsertCommand.Parameters["@platform_name"].Value = buildReport.PlatformName;
+ m_InsertCommand.Parameters["@subtarget"].Value = buildReport.Subtarget;
+ m_InsertCommand.Parameters["@start_time"].Value = buildReport.StartTime;
+ m_InsertCommand.Parameters["@end_time"].Value = buildReport.EndTime;
+ m_InsertCommand.Parameters["@total_time_seconds"].Value = buildReport.TotalTimeSeconds;
+ m_InsertCommand.Parameters["@total_size"].Value = (long)buildReport.TotalSize;
+ m_InsertCommand.Parameters["@build_guid"].Value = buildReport.BuildGuid;
+ m_InsertCommand.Parameters["@total_errors"].Value = buildReport.TotalErrors;
+ m_InsertCommand.Parameters["@total_warnings"].Value = buildReport.TotalWarnings;
+ m_InsertCommand.Parameters["@options"].Value = buildReport.Options;
+ m_InsertCommand.Parameters["@asset_bundle_options"].Value = buildReport.AssetBundleOptions;
+ m_InsertCommand.Parameters["@output_path"].Value = buildReport.OutputPath;
+ m_InsertCommand.Parameters["@crc"].Value = buildReport.Crc;
+
+ m_InsertCommand.ExecuteNonQuery();
+
+ // Insert files
+ foreach (var file in buildReport.Files)
+ {
+ m_InsertFileCommand.Transaction = ctx.Transaction;
+ m_InsertFileCommand.Parameters["@build_report_id"].Value = objectId;
+ m_InsertFileCommand.Parameters["@file_index"].Value = file.Id;
+ m_InsertFileCommand.Parameters["@path"].Value = file.Path;
+ m_InsertFileCommand.Parameters["@role"].Value = file.Role;
+ m_InsertFileCommand.Parameters["@size"].Value = (long)file.Size;
+ m_InsertFileCommand.ExecuteNonQuery();
+ }
+
+ // Insert archive contents mapping
+ foreach (var mapping in buildReport.fileListAssetBundleHelper.internalNameToArchiveMapping)
+ {
+ m_InsertArchiveContentCommand.Transaction = ctx.Transaction;
+ m_InsertArchiveContentCommand.Parameters["@build_report_id"].Value = objectId;
+ m_InsertArchiveContentCommand.Parameters["@assetbundle"].Value = mapping.Value;
+ m_InsertArchiveContentCommand.Parameters["@assetbundle_content"].Value = mapping.Key;
+ m_InsertArchiveContentCommand.ExecuteNonQuery();
+ }
+
+ streamDataSize = 0;
+ name = buildReport.Name;
+ }
+
+ public void Finalize(SqliteConnection db)
+ {
+ }
+
+ void IDisposable.Dispose()
+ {
+ m_InsertCommand?.Dispose();
+ m_InsertFileCommand?.Dispose();
+ m_InsertArchiveContentCommand?.Dispose();
+ }
+}
diff --git a/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs b/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs
new file mode 100644
index 0000000..c2f62db
--- /dev/null
+++ b/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Data.Sqlite;
+using UnityDataTools.Analyzer.SerializedObjects;
+using UnityDataTools.FileSystem.TypeTreeReaders;
+
+namespace UnityDataTools.Analyzer.SQLite.Handlers;
+
+public class PackedAssetsHandler : ISQLiteHandler
+{
+ private SqliteCommand m_InsertPackedAssetsCommand;
+ private SqliteCommand m_InsertSourceAssetCommand;
+ private SqliteCommand m_GetSourceAssetIdCommand;
+ private SqliteCommand m_InsertContentsCommand;
+ private Dictionary<(string guid, string path), long> m_SourceAssetCache = new();
+
+ public void Init(SqliteConnection db)
+ {
+ using var command = db.CreateCommand();
+ command.CommandText = Properties.Resources.PackedAssets ?? throw new InvalidOperationException("PackedAssets resource not found");
+ command.ExecuteNonQuery();
+
+ m_InsertPackedAssetsCommand = db.CreateCommand();
+ m_InsertPackedAssetsCommand.CommandText = @"INSERT INTO build_report_packed_assets(
+ id, path, file_header_size
+ ) VALUES(
+ @id, @path, @file_header_size
+ )";
+
+ m_InsertPackedAssetsCommand.Parameters.Add("@id", SqliteType.Integer);
+ m_InsertPackedAssetsCommand.Parameters.Add("@path", SqliteType.Text);
+ m_InsertPackedAssetsCommand.Parameters.Add("@file_header_size", SqliteType.Integer);
+
+ m_InsertSourceAssetCommand = db.CreateCommand();
+ m_InsertSourceAssetCommand.CommandText = @"INSERT OR IGNORE INTO build_report_source_assets(
+ source_asset_guid, build_time_asset_path
+ ) VALUES(
+ @source_asset_guid, @build_time_asset_path
+ )";
+ m_InsertSourceAssetCommand.Parameters.Add("@source_asset_guid", SqliteType.Text);
+ m_InsertSourceAssetCommand.Parameters.Add("@build_time_asset_path", SqliteType.Text);
+
+ m_GetSourceAssetIdCommand = db.CreateCommand();
+ m_GetSourceAssetIdCommand.CommandText = @"SELECT id FROM build_report_source_assets
+ WHERE source_asset_guid = @source_asset_guid AND build_time_asset_path = @build_time_asset_path";
+ m_GetSourceAssetIdCommand.Parameters.Add("@source_asset_guid", SqliteType.Text);
+ m_GetSourceAssetIdCommand.Parameters.Add("@build_time_asset_path", SqliteType.Text);
+
+ m_InsertContentsCommand = db.CreateCommand();
+ m_InsertContentsCommand.CommandText = @"INSERT INTO build_report_packed_asset_info(
+ packed_assets_id, object_id, type, size, offset, source_asset_id
+ ) VALUES(
+ @packed_assets_id, @object_id, @type, @size, @offset, @source_asset_id
+ )";
+
+ m_InsertContentsCommand.Parameters.Add("@packed_assets_id", SqliteType.Integer);
+ m_InsertContentsCommand.Parameters.Add("@object_id", SqliteType.Integer);
+ m_InsertContentsCommand.Parameters.Add("@type", SqliteType.Integer);
+ m_InsertContentsCommand.Parameters.Add("@size", SqliteType.Integer);
+ m_InsertContentsCommand.Parameters.Add("@offset", SqliteType.Integer);
+ m_InsertContentsCommand.Parameters.Add("@source_asset_id", SqliteType.Integer);
+ }
+
+ public void Process(Context ctx, long objectId, RandomAccessReader reader, out string name, out long streamDataSize)
+ {
+ var packedAssets = PackedAssets.Read(reader);
+
+ m_InsertPackedAssetsCommand.Transaction = ctx.Transaction;
+ m_InsertPackedAssetsCommand.Parameters["@id"].Value = objectId;
+ m_InsertPackedAssetsCommand.Parameters["@path"].Value = packedAssets.Path;
+ m_InsertPackedAssetsCommand.Parameters["@file_header_size"].Value = (long)packedAssets.FileHeaderSize;
+ m_InsertPackedAssetsCommand.ExecuteNonQuery();
+
+ // Insert contents
+ foreach (var content in packedAssets.Contents)
+ {
+ // Get or create source asset ID
+ var cacheKey = (content.SourceAssetGUID, content.BuildTimeAssetPath);
+ if (!m_SourceAssetCache.TryGetValue(cacheKey, out long sourceAssetId))
+ {
+ // Insert the source asset (will be ignored if it already exists)
+ m_InsertSourceAssetCommand.Transaction = ctx.Transaction;
+ m_InsertSourceAssetCommand.Parameters["@source_asset_guid"].Value = content.SourceAssetGUID;
+ m_InsertSourceAssetCommand.Parameters["@build_time_asset_path"].Value = content.BuildTimeAssetPath;
+ m_InsertSourceAssetCommand.ExecuteNonQuery();
+
+ // Get the ID (whether just inserted or already existing)
+ m_GetSourceAssetIdCommand.Transaction = ctx.Transaction;
+ m_GetSourceAssetIdCommand.Parameters["@source_asset_guid"].Value = content.SourceAssetGUID;
+ m_GetSourceAssetIdCommand.Parameters["@build_time_asset_path"].Value = content.BuildTimeAssetPath;
+ sourceAssetId = (long)m_GetSourceAssetIdCommand.ExecuteScalar();
+
+ m_SourceAssetCache[cacheKey] = sourceAssetId;
+ }
+
+ m_InsertContentsCommand.Transaction = ctx.Transaction;
+ m_InsertContentsCommand.Parameters["@packed_assets_id"].Value = objectId;
+ m_InsertContentsCommand.Parameters["@object_id"].Value = content.ObjectID;
+ m_InsertContentsCommand.Parameters["@type"].Value = content.Type;
+ m_InsertContentsCommand.Parameters["@size"].Value = (long)content.Size;
+ m_InsertContentsCommand.Parameters["@offset"].Value = (long)content.Offset;
+ m_InsertContentsCommand.Parameters["@source_asset_id"].Value = sourceAssetId;
+ m_InsertContentsCommand.ExecuteNonQuery();
+ }
+
+ streamDataSize = 0;
+ name = packedAssets.Path;
+ }
+
+ public void Finalize(SqliteConnection db)
+ {
+ }
+
+ void IDisposable.Dispose()
+ {
+ m_InsertPackedAssetsCommand?.Dispose();
+ m_InsertSourceAssetCommand?.Dispose();
+ m_GetSourceAssetIdCommand?.Dispose();
+ m_InsertContentsCommand?.Dispose();
+ }
+}
+
diff --git a/Analyzer/SQLite/Writers/SerializedFileSQLiteWriter.cs b/Analyzer/SQLite/Writers/SerializedFileSQLiteWriter.cs
index 63c1c0d..f91bcd4 100644
--- a/Analyzer/SQLite/Writers/SerializedFileSQLiteWriter.cs
+++ b/Analyzer/SQLite/Writers/SerializedFileSQLiteWriter.cs
@@ -38,6 +38,8 @@ public class SerializedFileSQLiteWriter : IDisposable
{ "AssetBundle", new AssetBundleHandler() },
{ "PreloadData", new PreloadDataHandler() },
{ "MonoScript", new MonoScriptHandler() },
+ { "BuildReport", new BuildReportHandler() },
+ { "PackedAssets", new PackedAssetsHandler() },
};
// serialized files
diff --git a/Analyzer/SerializedObjects/BuildReport.cs b/Analyzer/SerializedObjects/BuildReport.cs
new file mode 100644
index 0000000..ae274e9
--- /dev/null
+++ b/Analyzer/SerializedObjects/BuildReport.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using UnityDataTools.Analyzer.Util;
+using UnityDataTools.FileSystem.TypeTreeReaders;
+
+namespace UnityDataTools.Analyzer.SerializedObjects;
+
+public class BuildReport
+{
+ public string Name { get; init; }
+ public string BuildGuid { get; init; }
+ public string PlatformName { get; init; }
+ public int Subtarget { get; init; }
+ public string StartTime { get; init; }
+ public string EndTime { get; init; }
+ public int Options { get; init; }
+ public int AssetBundleOptions { get; init; }
+ public string OutputPath { get; init; }
+ public uint Crc { get; init; }
+ public ulong TotalSize { get; init; }
+ public int TotalTimeSeconds { get; init; }
+ public int TotalErrors { get; init; }
+ public int TotalWarnings { get; init; }
+ public int BuildType { get; init; }
+ public string BuildResult { get; init; }
+ public List Files { get; init; }
+ public FileListAssetBundleHelper fileListAssetBundleHelper { get; init; }
+
+ private BuildReport() { }
+
+ public static BuildReport Read(RandomAccessReader reader)
+ {
+ var summary = reader["m_Summary"];
+
+ // Read the GUID (4 unsigned ints)
+ // Unity's GUID format reverses nibbles within each uint32
+ var guidData = summary["buildGUID"];
+ var guid0 = guidData["data[0]"].GetValue();
+ var guid1 = guidData["data[1]"].GetValue();
+ var guid2 = guidData["data[2]"].GetValue();
+ var guid3 = guidData["data[3]"].GetValue();
+ var guidString = GuidHelper.FormatUnityGuid(guid0, guid1, guid2, guid3);
+
+ // Convert build start time from ticks to ISO 8601 UTC format
+ var startTimeTicks = summary["buildStartTime"]["ticks"].GetValue();
+ var startTime = new DateTime(startTimeTicks, DateTimeKind.Utc).ToString("o");
+
+ // Convert ticks to seconds (TimeSpan.TicksPerSecond = 10,000,000)
+ var totalTimeTicks = summary["totalTimeTicks"].GetValue();
+ var totalTimeSeconds = (int)Math.Round(totalTimeTicks / 10000000.0);
+
+ var endTime = new DateTime(startTimeTicks + (long)totalTimeTicks, DateTimeKind.Utc).ToString("o");
+
+ // Read the files array
+ var filesList = new List(reader["m_Files"].GetArraySize());
+ foreach (var fileElement in reader["m_Files"])
+ {
+ filesList.Add(new BuildFile
+ {
+ Id = fileElement["id"].GetValue(),
+ Path = fileElement["path"].GetValue(),
+ Role = fileElement["role"].GetValue(),
+ Size = fileElement["totalSize"].GetValue()
+ });
+ }
+
+ TrimCommonPathPrefix(filesList);
+
+ return new BuildReport()
+ {
+ Name = reader["m_Name"].GetValue(),
+ BuildGuid = guidString,
+ PlatformName = summary["platformName"].GetValue(),
+ Subtarget = summary["subtarget"].GetValue(),
+ StartTime = startTime,
+ EndTime = endTime,
+ Options = summary["options"].GetValue(),
+ AssetBundleOptions = summary["assetBundleOptions"].GetValue(),
+ OutputPath = summary["outputPath"].GetValue(),
+ Crc = summary["crc"].GetValue(),
+ TotalSize = summary["totalSize"].GetValue(),
+ TotalTimeSeconds = totalTimeSeconds,
+ TotalErrors = summary["totalErrors"].GetValue(),
+ TotalWarnings = summary["totalWarnings"].GetValue(),
+ BuildType = summary["buildType"].GetValue(),
+ BuildResult = GetBuildResultString(summary["buildResult"].GetValue()),
+ Files = filesList,
+ fileListAssetBundleHelper = new FileListAssetBundleHelper(filesList)
+ };
+ }
+
+ // Currently the file list has the absolute paths of the build output, but what we really want is the relative path.
+ // This code reuses the approach taken in the build report inspector of automatically stripping off whatever part of the path
+ // is repeated as the prefix on each file, which effectively finds the relative output path.
+ static void TrimCommonPathPrefix(List files)
+ {
+ if (files.Count == 0)
+ return;
+ string longestCommonRoot = files[0].Path;
+ foreach (var file in files)
+ {
+ for (var i = 0; i < longestCommonRoot.Length && i < file.Path.Length; i++)
+ {
+ if (longestCommonRoot[i] == file.Path[i])
+ continue;
+ longestCommonRoot = longestCommonRoot.Substring(0, i);
+ }
+ }
+
+ foreach (var file in files)
+ {
+ file.Path = file.Path.Substring(longestCommonRoot.Length);
+ }
+ }
+
+ public static string GetBuildTypeString(int buildType)
+ {
+ return buildType switch
+ {
+ 1 => "Player",
+ 2 => "AssetBundle",
+ 3 => "Player, AssetBundle",
+ _ => buildType.ToString()
+ };
+ }
+
+ public static string GetBuildResultString(int buildResult)
+ {
+ return buildResult switch
+ {
+ 0 => "Unknown",
+ 1 => "Succeeded",
+ 2 => "Failed",
+ 3 => "Cancelled",
+ 4 => "Pending",
+ _ => buildResult.ToString()
+ };
+ }
+}
+
+public class BuildFile
+{
+ public uint Id { get; init; }
+ public string Path { get; set; }
+ public string Role { get; init; }
+ public ulong Size { get; init; }
+}
+
+
+/// Helper for matching files that are inside an Unity Archive file to the file containing it.
+// Currently this only applies to AssetBundle builds, which can have many output files and which use hard to understand internal file names
+// like "CAB-76a378bdc9304bd3c3a82de8dd97981a".
+// For compressed Player builds the PackedAssets reports the internal files, but the file list does not report the unity3d content,
+// so this code will not pick up the mapping. However because there is only a single unity3d file on most platforms this is less important
+public class FileListAssetBundleHelper
+{
+ public Dictionary internalNameToArchiveMapping = new Dictionary();
+
+ public FileListAssetBundleHelper(List files)
+ {
+ CalculateAssetBundleMapping(files);
+ }
+
+ ///
+ // Map between the internal file names inside Archive files back to the Archive filename.
+
+ /*
+ Example input:
+
+ - path: C:/Src/TestProject/Build/AssetBundles/audio.bundle/CAB-76a378bdc9304bd3c3a82de8dd97981a.resource
+ role: StreamingResourceFile
+ ...
+ - path: C:/Src/TestProject/Build/AssetBundles/audio.bundle
+ role: AssetBundle
+ ...
+
+ Result:
+ CAB-76a378bdc9304bd3c3a82de8dd97981a.resource -> audio.bundle
+ */
+ ///
+ private void CalculateAssetBundleMapping(List files)
+ {
+ internalNameToArchiveMapping.Clear();
+
+ // Track archive paths and their base filenames for AssetBundle or manifest files
+ var archivePathToFileName = new Dictionary();
+ foreach (var file in files)
+ {
+ if (file.Role == "AssetBundle" || file.Role == "ManifestAssetBundle")
+ {
+ var justFileName = Path.GetFileName(file.Path);
+ archivePathToFileName[file.Path] = justFileName;
+ }
+ }
+
+ if (archivePathToFileName.Count == 0)
+ return;
+
+ // Map internal file names to their corresponding archive filenames
+ foreach (var file in files)
+ {
+ // Assumes internal files are not in subdirectories inside the archive
+ var justPath = Path.GetDirectoryName(file.Path)?.Replace('\\', '/');
+ var justFileName = Path.GetFileName(file.Path);
+
+ if (!string.IsNullOrEmpty(justPath) && archivePathToFileName.ContainsKey(justPath))
+ {
+ internalNameToArchiveMapping[justFileName] = archivePathToFileName[justPath];
+ }
+ }
+ }
+}
diff --git a/Analyzer/SerializedObjects/PackedAssets.cs b/Analyzer/SerializedObjects/PackedAssets.cs
new file mode 100644
index 0000000..be7741a
--- /dev/null
+++ b/Analyzer/SerializedObjects/PackedAssets.cs
@@ -0,0 +1,61 @@
+using System.Collections.Generic;
+using UnityDataTools.Analyzer.Util;
+using UnityDataTools.FileSystem.TypeTreeReaders;
+
+namespace UnityDataTools.Analyzer.SerializedObjects;
+
+public class PackedAssets
+{
+ public string Path { get; init; }
+ public ulong FileHeaderSize { get; init; }
+ public List Contents { get; init; }
+
+ private PackedAssets() { }
+
+ public static PackedAssets Read(RandomAccessReader reader)
+ {
+ var path = reader["m_ShortPath"].GetValue();
+ var fileHeaderSize = reader["m_Overhead"].GetValue();
+
+ var contentsList = new List(reader["m_Contents"].GetArraySize());
+
+ foreach (var element in reader["m_Contents"])
+ {
+ // Read GUID (4 unsigned ints)
+ var guidData = element["sourceAssetGUID"];
+ var guid0 = guidData["data[0]"].GetValue();
+ var guid1 = guidData["data[1]"].GetValue();
+ var guid2 = guidData["data[2]"].GetValue();
+ var guid3 = guidData["data[3]"].GetValue();
+ var guidString = GuidHelper.FormatUnityGuid(guid0, guid1, guid2, guid3);
+
+ contentsList.Add(new PackedAssetInfo
+ {
+ ObjectID = element["fileID"].GetValue(),
+ Type = element["classID"].GetValue(),
+ Size = element["packedSize"].GetValue(),
+ Offset = element["offset"].GetValue(),
+ SourceAssetGUID = guidString,
+ BuildTimeAssetPath = element["buildTimeAssetPath"].GetValue()
+ });
+ }
+
+ return new PackedAssets()
+ {
+ Path = path,
+ FileHeaderSize = fileHeaderSize,
+ Contents = contentsList
+ };
+ }
+}
+
+public class PackedAssetInfo
+{
+ public long ObjectID { get; init; }
+ public int Type { get; init; }
+ public ulong Size { get; init; }
+ public ulong Offset { get; init; }
+ public string SourceAssetGUID { get; init; }
+ public string BuildTimeAssetPath { get; init; }
+}
+
diff --git a/Analyzer/Util/GuidHelper.cs b/Analyzer/Util/GuidHelper.cs
new file mode 100644
index 0000000..f7a6bd0
--- /dev/null
+++ b/Analyzer/Util/GuidHelper.cs
@@ -0,0 +1,45 @@
+namespace UnityDataTools.Analyzer.Util;
+
+///
+/// Helper class for converting Unity GUID data to string format.
+///
+public static class GuidHelper
+{
+ ///
+ /// Converts Unity GUID data array to string format matching Unity's GUIDToString function.
+ /// Unity stores GUIDs as 4 uint32 values and converts them to a 32-character hex string
+ /// with a specific byte ordering that differs from standard GUID/UUID formatting.
+ ///
+ ///
+ /// data[0]=3856716653 (0xe60765cd) becomes "d63d0e5e"
+ ///
+ public static string FormatUnityGuid(uint data0, uint data1, uint data2, uint data3)
+ {
+ char[] result = new char[32];
+ FormatUInt32Reversed(data0, result, 0);
+ FormatUInt32Reversed(data1, result, 8);
+ FormatUInt32Reversed(data2, result, 16);
+ FormatUInt32Reversed(data3, result, 24);
+ return new string(result);
+ }
+
+ ///
+ /// Formats a uint32 as 8 hex digits matching Unity's GUIDToString logic.
+ /// Unity's implementation extracts nibbles from most significant to least significant
+ /// (j=7 down to j=0) and writes them to output positions in the same order (offset+7 to offset+0),
+ /// which reverses the byte order compared to standard hex formatting.
+ ///
+ ///
+ /// For example: 0xe60765cd becomes "d63d0e5e" (bytes reversed: cd,65,07,e6 → e6,07,65,cd)
+ ///
+ private static void FormatUInt32Reversed(uint value, char[] output, int offset)
+ {
+ const string hexChars = "0123456789abcdef";
+ for (int j = 7; j >= 0; j--)
+ {
+ uint nibble = (value >> (j * 4)) & 0xF;
+ output[offset + j] = hexChars[(int)nibble];
+ }
+ }
+}
+
diff --git a/Documentation/Diagnostics-TextBasedBuildReport.png b/Documentation/Diagnostics-TextBasedBuildReport.png
new file mode 100644
index 0000000..a21c76e
Binary files /dev/null and b/Documentation/Diagnostics-TextBasedBuildReport.png differ
diff --git a/Documentation/analyze-examples.md b/Documentation/analyze-examples.md
index a0a87e9..23f5551 100644
--- a/Documentation/analyze-examples.md
+++ b/Documentation/analyze-examples.md
@@ -64,6 +64,9 @@ Universal Render Pipeline/Lit 115.5 KB 1b2fdfe013c58ffd57d7663
Shader Graphs/CustomLightingBuildingsB 113.4 KB 1b2fdfe013c58ffd57d7663eb8db3e60
```
+## BuildReport support
+
+See [buildreport.md](buildreport.md) for information about using analyze to look at BuildReport files.
## Example: Using AI tools to help write queries
diff --git a/Documentation/analyzer.md b/Documentation/analyzer.md
index aa67f4c..2e95ed3 100644
--- a/Documentation/analyzer.md
+++ b/Documentation/analyzer.md
@@ -63,6 +63,20 @@ This view lists the dependencies of all the assets. You can filter by id or asse
the dependencies of an asset. Conversely, filtering by dep_id will return all the assets that
depend on this object. This can be useful to figure out why an asset was included in a build.
+## monoscripts
+
+Show the class information for all the C# types of MonoBehaviour objects in the build output (including ScriptableObjects).
+
+This includes the assembly name, C# namespace and class name.
+
+## monoscripts_view
+
+This view is a convenient view for seeing which AssetBundle / SerializedFile contains each MonoScript object.
+
+## script_object_view
+
+This view lists all the MonoBehaviour and ScriptableObject objects in the build output, with their location, size and precise C# type (using the `monoscripts` and `refs` tables). This view is not populated if analyze is run with the `--skip-references` option.
+
## animation_view (AnimationClipProcessor)
This provides additional information about AnimationClips. The columns are the same as those in
@@ -162,6 +176,10 @@ This view lists all the shaders aggregated by name. The *instances* column indic
the shader was found in the data files. It also provides the total size per shader and the list of
AssetBundles in which they were found.
+## BuildReport
+
+See [BuildReport.md](buildreport.md) for details of the tables and views related to analyzing BuildReport files.
+
# Advanced
## Using the library
diff --git a/Documentation/buildreport.md b/Documentation/buildreport.md
new file mode 100644
index 0000000..1ce1d87
--- /dev/null
+++ b/Documentation/buildreport.md
@@ -0,0 +1,250 @@
+# BuildReport Support
+
+Unity generates a [BuildReport](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport.html) file for Player builds and when building AssetBundles via [BuildPipeline.BuildAssetBundles](https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildAssetBundles.html). Build reports are **not** generated by the [Addressables](addressables-build-reports.md) package or Scriptable Build Pipeline.
+
+Build reports are written to `Library/LastBuild.buildreport` as Unity SerializedFiles (the same binary format used for build output). UnityDataTool can read this format and extract detailed build information using the same mechanisms as other Unity object types.
+
+## UnityDataTool Support
+
+Since build reports are SerializedFiles, you can use [`dump`](command-dump.md) to convert them to text format.
+
+The [`analyze`](command-analyze.md) command extracts build report data into dedicated database tables with custom handlers for:
+
+* **BuildReport** - The primary object containing build inputs and results
+* **PackedAssets** - Describes the contents of each SerializedFile, .resS, or .resource file, including type, size, and source asset for each object or resource blob, enabling object-level analysis
+
+**Note:** PackedAssets information is not currently written for scenes in the build.
+
+## Examples
+
+These are some example queries that can be run after running analyze on a build report file.
+
+1. Show all successful builds recorded in the database.
+
+```
+SELECT * from build_reports WHERE build_result = "Succeeded"
+```
+
+2. Show information about the data in the build that originates from "Assets/Sprites/Snow.jpg".
+
+```
+SELECT *
+FROM build_report_packed_asset_contents_view
+WHERE build_time_asset_path like "Assets/Sprites/Snow.jpg"
+```
+
+3. Show the AssetBundles that contain content from "Assets/Sprites/Snow.jpg".
+
+```
+SELECT DISTINCT assetbundle
+FROM build_report_packed_asset_contents_view
+WHERE build_time_asset_path like "Assets/Sprites/Snow.jpg"
+```
+
+4. Show all source assets included in the build (excluding C# scripts, e.g. MonoScript objects)
+
+```
+SELECT build_time_asset_path from build_report_source_assets WHERE build_time_asset_path NOT LIKE "%.cs"
+```
+
+## Cross-Referencing with Build Output
+
+For comprehensive analysis, run `analyze` on both the build output **and** the matching build report file. Use a clean build to ensure PackedAssets information is fully populated. You may need to copy the build report into the build output directory so both are found by `analyze`.
+
+PackedAssets data provides source asset information for each object that isn't available when analyzing only the build output. Objects are listed in the same order as they appear in the output SerializedFile, .resS, or .resource file.
+
+### Database Relationships
+
+- Match `build_report_packed_assets` rows to analyzed SerializedFiles using `object_view.serialized_file` and `build_report_packed_assets.path`
+- Match `build_report_packed_asset_info` entries to objects in the build output using `object_id` (local file ID)
+
+**Note:** build_report_packed_assets` also record .resS and .resource files. These rows will not match with the serialized_files table.
+
+**Note:** The source object's local file ID is not recorded in PackedAssetInfo. While you can identify the source asset (e.g., which Prefab), you cannot directly pinpoint the specific object within that asset. When needed, objects can often be distinguished by name or other properties.
+
+## Working with Multiple Build Reports
+
+Multiple build reports can be imported into the same database if their filenames differ. This enables:
+- Comprehensive build history tracking
+- Cross-build comparisons
+- Identifying duplicated data between Player and AssetBundle builds
+
+See the schema sections below for guidance on writing queries that handle multiple build reports correctly.
+
+## Alternatives
+
+UnityDataTool provides low-level access to build reports. Consider these alternatives for easier or more convenient workflows:
+
+### BuildReportInspector Package
+
+View build reports in the Unity Editor using the [BuildReportInspector](https://github.com/Unity-Technologies/BuildReportInspector) package.
+
+### BuildReport API
+
+Access build report data programmatically within Unity using the BuildReport API:
+
+**1. Most recent build:**
+Use [BuildPipeline.GetLatestReport()](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport.GetLatestReport.html)
+
+**2. Build report in Assets folder:**
+Load via AssetDatabase API:
+
+```csharp
+using UnityEditor;
+using UnityEditor.Build.Reporting;
+using UnityEngine;
+
+public class BuildReportInProjectUtility
+{
+ static public BuildReport LoadBuildReport(string buildReportPath)
+ {
+ var report = AssetDatabase.LoadAssetAtPath("Assets/MyBuildReport.buildReport");
+
+ if (report == null)
+ Debug.LogWarning($"Failed to load build report from {buildReportPath}");
+
+ return report;
+ }
+}
+```
+
+**3. Build report outside Assets folder:**
+For files in Library or elsewhere, use `InternalEditorUtility`:
+
+
+```csharp
+using System;
+using System.IO;
+using UnityEditor.Build.Reporting;
+using UnityEditorInternal;
+using UnityEngine;
+
+public class BuildReportUtility
+{
+ static public BuildReport LoadBuildReport(string buildReportPath)
+ {
+ if (!File.Exists(buildReportPath))
+ return null;
+
+ try
+ {
+ var objs = InternalEditorUtility.LoadSerializedFileAndForget(buildReportPath);
+ foreach (UnityEngine.Object obj in objs)
+ {
+ if (obj is BuildReport)
+ return obj as BuildReport;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.LogWarning($"Failed to load build report from {buildReportPath}: {ex.Message}");
+ }
+ return null;
+ }
+}
+```
+
+### Text Format Access
+
+Build reports can be output in Unity's pseudo-YAML format using a diagnostic flag:
+
+
+
+Text files are significantly larger than binary. You can also convert to text by moving the binary file into your Unity project (assets default to text format).
+
+UnityDataTool's `dump` command produces a non-YAML text representation of build report contents.
+
+**When to use text formats:**
+- Quick extraction of specific information via text processing tools (regex, YAML parsers, etc.)
+
+**When to use structured access:**
+- Working with full structured data: use UnityDataTool's `analyze` command or Unity's BuildReport API
+
+### Addressables Build Reports
+
+The Addressables package generates `buildlayout.json` files instead of BuildReport files. While the format and schema differ, they contain similar information. See [Addressables Build Reports](addressables-build-reports.md) for details on importing these files with UnityDataTool.
+
+## Database Schema
+
+Build report data is stored in the following tables and views:
+
+| Name | Type | Description |
+|------|------|-------------|
+| `build_reports` | table | Build summary (type, result, platform, duration, etc.) |
+| `build_report_files` | table | Files included in the build (path, role, size). See [BuildReport.GetFiles](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport.GetFiles.html) |
+| `build_report_archive_contents` | table | Files inside each AssetBundle |
+| `build_report_packed_assets` | table | SerializedFile, .resS, or .resource file info. See [PackedAssets](https://docs.unity3d.com/ScriptReference/Build.Reporting.PackedAssets.html) |
+| `build_report_packed_asset_info` | table | Each object inside a SerializedFile (or data in .resS/.resource files). See [PackedAssetInfo](https://docs.unity3d.com/ScriptReference/Build.Reporting.PackedAssetInfo.html) |
+| `build_report_source_assets` | table | Source asset GUID and path for each PackedAssetInfo reference |
+| `build_report_files_view` | view | All files from all build reports |
+| `build_report_packed_assets_view` | view | All PackedAssets with their BuildReport, AssetBundle, and SerializedFile |
+| `build_report_packed_asset_contents_view` | view | All objects and resources tracked in build reports |
+
+The `build_reports` table contains primary build information. Additional tables store detailed content data. Views simplify queries by automatically joining tables, especially when working with multiple build reports.
+
+### Schema Overview
+
+Views automatically identify which build report each row belongs to, simplifying multi-report queries. To create custom queries, understand the table relationships:
+
+**Primary relationships:**
+- `build_reports`: One row per analyzed BuildReport file, corresponding to the BuildReport object in the `objects` table via the `id` column
+- `build_report_packed_assets`: Records the `id` of each PackedAssets object. Find the associated BuildReport via the shared `objects.serialized_file` value (PackedAssets are processed independently of BuildReport objects)
+
+**Auxiliary tables:**
+- `build_report_files` and `build_report_archive_contents`: Stores the BuildReport object `id` for each row (as `build_report_id`).
+- `build_report_packed_asset_info`: Stores the PackedAssets object `id` for each row (as `packed_assets_id`).
+- `build_report_source_assets`: Normalized table of distinct source asset GUIDs and paths, linked via `build_report_packed_asset_info.source_asset_id`
+
+**Note:** BuildReport and PackedAssets objects are also linked in the `refs` table (BuildReport references PackedAssets in its appendices array), but this relationship is not used in built-in views since `refs` table population is optional.
+
+**Example: build_report_packed_assets_view**
+
+This view demonstrates key relationships:
+- Finds the BuildReport object (`br_obj`) by type (1125) and shared `serialized_file` with the PackedAssets (`pa`)
+- Retrieves the serialized file name from `serialized_files` table (`sf.name`)
+- For AssetBundle builds, retrieves the AssetBundle name from `build_report_archive_contents` by matching BuildReport ID and PackedAssets path (`assetbundle` is NULL for Player builds)
+
+```
+CREATE VIEW build_report_packed_assets_view AS
+SELECT
+ pa.id,
+ o.object_id,
+ brac.assetbundle,
+ pa.path,
+ pa.file_header_size,
+ br_obj.id as build_report_id,
+ sf.name as build_report_filename
+FROM build_report_packed_assets pa
+INNER JOIN objects o ON pa.id = o.id
+INNER JOIN serialized_files sf ON o.serialized_file = sf.id
+LEFT JOIN objects br_obj ON o.serialized_file = br_obj.serialized_file AND br_obj.type = 1125
+LEFT JOIN build_report_archive_contents brac ON br_obj.id = brac.build_report_id AND pa.path = brac.assetbundle_content;
+```
+
+### Column Naming
+
+For consistency and clarity, database columns use slightly different names than the BuildReport API:
+
+| Database Column | BuildReport API | Notes |
+|-----------------|-----------------|-------|
+| `build_report_packed_assets.path` | `PackedAssets.ShortPath` | Filename of the SerializedFile, .resS, or .resource file ("short" was redundant since only one path is recorded) |
+| `build_report_packed_assets.file_header_size` | `PackedAssets.Overhead` | Size of the file header (zero for .resS and .resource files) |
+| `build_report_packed_asset_info.object_id` | `PackedAssetInfo.fileID` | Local file ID of the object (renamed for consistency with `objects.object_id`) |
+| `build_report_packed_asset_info.type` | `PackedAssetInfo.classID` | Unity object type (renamed for consistency with `objects.type`) |
+
+## Limitations
+
+**Duplicate filenames:** Multiple build reports cannot be imported into the same database if they share the same filename. This is a general UnityDataTool limitation that assumes unique SerializedFile names.
+
+**Type names:** While `build_report_packed_asset_info.type` records valid [Class IDs](https://docs.unity3d.com/Manual/ClassIDReference.html), the string type name may not exist in the `types` table. The `types` table is only populated when processing object instances (during TypeTree analysis). Analyzing both build output **and** build report together ensures types are fully populated; otherwise only numeric IDs are available.
+
+### Information Not Exported
+
+Currently, only the most useful BuildReport data is extracted to SQL. Additional data may be added as needed:
+
+* [Code stripping](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport-strippingInfo.html) appendix (IL2CPP Player builds)
+* [ScenesUsingAssets](https://docs.unity3d.com/ScriptReference/Build.Reporting.BuildReport-scenesUsingAssets.html) (detailed build reports)
+* `BuildReport.m_BuildSteps` array (SQL may not be ideal for this hierarchical data)
+* `BuildAssetBundleInfoSet` appendix (undocumented object listing files in each AssetBundle; `build_report_archive_contents` currently derives this from the File list)
+* Analytics-only appendices (unlikely to be valuable for analysis)
+
diff --git a/TestCommon/Data/BuildReport1/LastBuild.buildreport b/TestCommon/Data/BuildReports/AssetBundle.buildreport
similarity index 100%
rename from TestCommon/Data/BuildReport1/LastBuild.buildreport
rename to TestCommon/Data/BuildReports/AssetBundle.buildreport
diff --git a/TestCommon/Data/BuildReports/AssetBundle.buildreport.txt b/TestCommon/Data/BuildReports/AssetBundle.buildreport.txt
new file mode 100644
index 0000000..e67e099
--- /dev/null
+++ b/TestCommon/Data/BuildReports/AssetBundle.buildreport.txt
@@ -0,0 +1,633 @@
+External References
+
+ID: -6210328523265720665 (ClassID: 1126) PackedAssets
+ m_ShortPath (string) CAB-76a378bdc9304bd3c3a82de8dd97981a.resource
+ m_Overhead (UInt64) 0
+ m_Contents (vector)
+ Array[1]
+ data[0] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 0
+ classID (Type*) 83
+ packedSize (UInt64) 18496
+ offset (UInt64) 0
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 510047344
+ data[1] (unsigned int) 1200191226
+ data[2] (unsigned int) 2021230213
+ data[3] (unsigned int) 2122331027
+ buildTimeAssetPath (string) Assets/audio/audio.mp3
+
+ID: -5068572036128640151 (ClassID: 382020655) PluginBuildInfo
+ m_RuntimePlugins (vector)
+ Array[5]
+ data[0] (string) Newtonsoft.Json
+ data[1] (string) UnityAdsInitializationListener
+ data[2] (string) UnityAdsShowListener
+ data[3] (string) nunit.framework
+ data[4] (string) UnityAdsLoadListener
+ m_EditorPlugins (vector)
+ Array[15]
+ data[0] (string) Mono.Cecil.Pdb
+ data[1] (string) log4netPlastic
+ data[2] (string) JetBrains.Rider.PathLocator
+ data[3] (string) Mono.Cecil.Rocks
+ data[4] (string) unityplastic
+ data[5] (string) Mono.Cecil.Mdb
+ data[6] (string) Unity.Plastic.Newtonsoft.Json
+ data[7] (string) Unity.Analytics.Editor
+ data[8] (string) Unity.Plastic.Antlr3.Runtime
+ data[9] (string) zlib64Plastic
+ data[10] (string) liblz4Plastic
+ data[11] (string) lz4x64Plastic
+ data[12] (string) Unity.Analytics.Tracker
+ data[13] (string) AppleEventIntegration
+ data[14] (string) Mono.Cecil
+
+ID: -2699881322159949766 (ClassID: 1126) PackedAssets
+ m_ShortPath (string) CAB-6b49068aebcf9d3b05692c8efd933167
+ m_Overhead (UInt64) 10720
+ m_Contents (vector)
+ Array[7]
+ data[0] (BuildReportPackedAssetInfo)
+ fileID (SInt64) -4266742476527514910
+ classID (Type*) 213
+ packedSize (UInt64) 464
+ offset (UInt64) 10704
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 1377324347
+ data[1] (unsigned int) 1291145104
+ data[2] (unsigned int) 2227835800
+ data[3] (unsigned int) 660996637
+ buildTimeAssetPath (string) Assets/Sprites/Snow 1.jpg
+ data[1] (BuildReportPackedAssetInfo)
+ fileID (SInt64) -3600607445234681765
+ classID (Type*) 28
+ packedSize (UInt64) 204
+ offset (UInt64) 11168
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 2731658232
+ data[1] (unsigned int) 1324654785
+ data[2] (unsigned int) 2360135852
+ data[3] (unsigned int) 2253774587
+ buildTimeAssetPath (string) Assets/Sprites/red.png
+ data[2] (BuildReportPackedAssetInfo)
+ fileID (SInt64) -2408881041259534328
+ classID (Type*) 213
+ packedSize (UInt64) 460
+ offset (UInt64) 11376
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 1179607688
+ data[1] (unsigned int) 1278849281
+ data[2] (unsigned int) 1374027963
+ data[3] (unsigned int) 1900018330
+ buildTimeAssetPath (string) Assets/Sprites/Snow.jpg
+ data[3] (BuildReportPackedAssetInfo)
+ fileID (SInt64) -1350043613627603771
+ classID (Type*) 28
+ packedSize (UInt64) 204
+ offset (UInt64) 11840
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 1179607688
+ data[1] (unsigned int) 1278849281
+ data[2] (unsigned int) 1374027963
+ data[3] (unsigned int) 1900018330
+ buildTimeAssetPath (string) Assets/Sprites/Snow.jpg
+ data[4] (BuildReportPackedAssetInfo)
+ fileID (SInt64) -39415655269619539
+ classID (Type*) 28
+ packedSize (UInt64) 208
+ offset (UInt64) 12048
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 1377324347
+ data[1] (unsigned int) 1291145104
+ data[2] (unsigned int) 2227835800
+ data[3] (unsigned int) 660996637
+ buildTimeAssetPath (string) Assets/Sprites/Snow 1.jpg
+ data[5] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 1
+ classID (Type*) 142
+ packedSize (UInt64) 460
+ offset (UInt64) 12256
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 0
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) AssetBundle Object
+ data[6] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 3866367853307903194
+ classID (Type*) 213
+ packedSize (UInt64) 460
+ offset (UInt64) 12720
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 2731658232
+ data[1] (unsigned int) 1324654785
+ data[2] (unsigned int) 2360135852
+ data[3] (unsigned int) 2253774587
+ buildTimeAssetPath (string) Assets/Sprites/red.png
+
+ID: -1478881110413844972 (ClassID: 1126) PackedAssets
+ m_ShortPath (string) BuildPlayer-Scene2.sharedAssets
+ m_Overhead (UInt64) 83443
+ m_Contents (vector)
+ Array[7]
+ data[0] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 1
+ classID (Type*) 150
+ packedSize (UInt64) 121
+ offset (UInt64) 83408
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 0
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string)
+ data[1] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 2
+ classID (Type*) 142
+ packedSize (UInt64) 184
+ offset (UInt64) 83536
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 0
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string)
+ data[2] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 3
+ classID (Type*) 21
+ packedSize (UInt64) 1064
+ offset (UInt64) 83728
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 15
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) Resources/unity_builtin_extra
+ data[3] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 4
+ classID (Type*) 21
+ packedSize (UInt64) 268
+ offset (UInt64) 84800
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 15
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) Resources/unity_builtin_extra
+ data[4] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 5
+ classID (Type*) 21
+ packedSize (UInt64) 304
+ offset (UInt64) 85072
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 15
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) Resources/unity_builtin_extra
+ data[5] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 6
+ classID (Type*) 48
+ packedSize (UInt64) 49400
+ offset (UInt64) 85376
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 15
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) Resources/unity_builtin_extra
+ data[6] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 7
+ classID (Type*) 48
+ packedSize (UInt64) 6964
+ offset (UInt64) 134784
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 15
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) Resources/unity_builtin_extra
+
+ID: -1012789659855765783 (ClassID: 1126) PackedAssets
+ m_ShortPath (string) CAB-a1e31f31856813b7384f999fbbf5e9b0
+ m_Overhead (UInt64) 4040
+ m_Contents (vector)
+ Array[2]
+ data[0] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 1
+ classID (Type*) 142
+ packedSize (UInt64) 104
+ offset (UInt64) 4032
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 0
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) AssetBundle Object
+ data[1] (BuildReportPackedAssetInfo)
+ fileID (SInt64) 2
+ classID (Type*) 290
+ packedSize (UInt64) 184
+ offset (UInt64) 4144
+ sourceAssetGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 0
+ data[3] (unsigned int) 0
+ buildTimeAssetPath (string) Built-in AssetBundleManifest: AssetBundleManifest
+
+ID: 1 (ClassID: 1125) BuildReport
+ m_ObjectHideFlags (unsigned int) 0
+ m_CorrespondingSourceObject (PPtr)
+ m_FileID (int) 0
+ m_PathID (SInt64) 0
+ m_PrefabInstance (PPtr)
+ m_FileID (int) 0
+ m_PathID (SInt64) 0
+ m_PrefabAsset (PPtr)
+ m_FileID (int) 0
+ m_PathID (SInt64) 0
+ m_Name (string) Build AssetBundles
+ m_Summary (BuildSummary)
+ buildStartTime (DateTime)
+ ticks (SInt64) 638858729667744767
+ buildGUID (GUID)
+ data[0] (unsigned int) 0
+ data[1] (unsigned int) 0
+ data[2] (unsigned int) 0
+ data[3] (unsigned int) 0
+ platformName (string) Win64
+ platformGroupName (string) Standalone
+ subtarget (int) 2
+ options (int) 0
+ assetBundleOptions (int) 98817
+ outputPath (string) C:/UnitySrc/BuildReportInspector/TestProject/Build/AssetBundles
+ crc (unsigned int) 4147003805
+ totalSize (UInt64) 1434814
+ totalTimeTicks (UInt64) 8038492
+ totalErrors (int) 0
+ totalWarnings (int) 0
+ buildType (int) 2
+ buildResult (int) 1
+ multiProcessEnabled (bool) False
+ m_Files (vector)
+ Array[17]
+ data[0] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/audio.bundle/CAB-76a378bdc9304bd3c3a82de8dd97981a
+ role (string) SharedAssets
+ id (unsigned int) 0
+ totalSize (UInt64) 3616
+ data[1] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/audio.bundle/CAB-76a378bdc9304bd3c3a82de8dd97981a.resource
+ role (string) StreamingResourceFile
+ id (unsigned int) 1
+ totalSize (UInt64) 18496
+ data[2] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/audio.bundle
+ role (string) AssetBundle
+ id (unsigned int) 2
+ totalSize (UInt64) 22256
+ data[3] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/sprites.bundle/CAB-6b49068aebcf9d3b05692c8efd933167
+ role (string) SharedAssets
+ id (unsigned int) 3
+ totalSize (UInt64) 13180
+ data[4] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/sprites.bundle/CAB-6b49068aebcf9d3b05692c8efd933167.resS
+ role (string) StreamingResourceFile
+ id (unsigned int) 4
+ totalSize (UInt64) 1200464
+ data[5] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/sprites.bundle
+ role (string) AssetBundle
+ id (unsigned int) 5
+ totalSize (UInt64) 1213792
+ data[6] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/audio.bundle.manifest
+ role (string) AssetBundleTextManifest
+ id (unsigned int) 6
+ totalSize (UInt64) 488
+ data[7] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/sprites.bundle.manifest
+ role (string) AssetBundleTextManifest
+ id (unsigned int) 7
+ totalSize (UInt64) 581
+ data[8] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/scenes.bundle/BuildPlayer-Scene2
+ role (string) Scene
+ id (unsigned int) 8
+ totalSize (UInt64) 29008
+ data[9] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/scenes.bundle/BuildPlayer-SampleScene
+ role (string) Scene
+ id (unsigned int) 9
+ totalSize (UInt64) 19344
+ data[10] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/scenes.bundle/BuildPlayer-SampleScene.sharedAssets
+ role (string) SharedAssets
+ id (unsigned int) 10
+ totalSize (UInt64) 1213
+ data[11] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/scenes.bundle/BuildPlayer-Scene2.sharedAssets
+ role (string) SharedAssets
+ id (unsigned int) 11
+ totalSize (UInt64) 141748
+ data[12] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/scenes.bundle
+ role (string) AssetBundle
+ id (unsigned int) 12
+ totalSize (UInt64) 191504
+ data[13] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/scenes.bundle.manifest
+ role (string) AssetBundleTextManifest
+ id (unsigned int) 13
+ totalSize (UInt64) 1372
+ data[14] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/AssetBundles/CAB-a1e31f31856813b7384f999fbbf5e9b0
+ role (string) ResourcesFile
+ id (unsigned int) 14
+ totalSize (UInt64) 4328
+ data[15] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/AssetBundles
+ role (string) ManifestAssetBundle
+ id (unsigned int) 15
+ totalSize (UInt64) 4448
+ data[16] (BuildReportFile)
+ path (string) C:/UnitySrc/BuildReportInspector/TestProject/AssetBundles.manifest
+ role (string) AssetBundleTextManifest
+ id (unsigned int) 16
+ totalSize (UInt64) 373
+ m_BuildSteps (vector)
+ Array[19]
+ data[0] (BuildStepInfo)
+ stepName (string) Build Asset Bundles
+ durationTicks (UInt64) 8038492
+ depth (int) 0
+ messages (vector)
+ Array[0]
+ data[1] (BuildStepInfo)
+ stepName (string) Calculate asset bundles to be built
+ durationTicks (UInt64) 13044
+ depth (int) 1
+ messages (vector)
+ Array[0]
+ data[2] (BuildStepInfo)
+ stepName (string) Prebuild Cleanup and Recompile
+ durationTicks (UInt64) 2608713
+ depth (int) 1
+ messages (vector)
+ Array[0]
+ data[3] (BuildStepInfo)
+ stepName (string) Compile scripts
+ durationTicks (UInt64) 2298122
+ depth (int) 2
+ messages (vector)
+ Array[0]
+ data[4] (BuildStepInfo)
+ stepName (string) Generate and validate platform script types
+ durationTicks (UInt64) 268349
+ depth (int) 2
+ messages (vector)
+ Array[0]
+ data[5] (BuildStepInfo)
+ stepName (string) Build bundle: audio.bundle
+ durationTicks (UInt64) 48102
+ depth (int) 1
+ messages (vector)
+ Array[0]
+ data[6] (BuildStepInfo)
+ stepName (string) Build bundle: sprites.bundle
+ durationTicks (UInt64) 99557
+ depth (int) 1
+ messages (vector)
+ Array[0]
+ data[7] (BuildStepInfo)
+ stepName (string) Build Scene AssetBundle(s)
+ durationTicks (UInt64) 2742249
+ depth (int) 1
+ messages (vector)
+ Array[0]
+ data[8] (BuildStepInfo)
+ stepName (string) Build bundle: scenes.bundle
+ durationTicks (UInt64) 2416551
+ depth (int) 2
+ messages (vector)
+ Array[0]
+ data[9] (BuildStepInfo)
+ stepName (string) Verify Build setup
+ durationTicks (UInt64) 217540
+ depth (int) 3
+ messages (vector)
+ Array[0]
+ data[10] (BuildStepInfo)
+ stepName (string) Prepare assets for target platform
+ durationTicks (UInt64) 56191
+ depth (int) 3
+ messages (vector)
+ Array[0]
+ data[11] (BuildStepInfo)
+ stepName (string) Building scenes
+ durationTicks (UInt64) 1157898
+ depth (int) 3
+ messages (vector)
+ Array[0]
+ data[12] (BuildStepInfo)
+ stepName (string) Building scene Assets/Scenes/Scene2.unity
+ durationTicks (UInt64) 652340
+ depth (int) 4
+ messages (vector)
+ Array[0]
+ data[13] (BuildStepInfo)
+ stepName (string) Building scene Assets/Scenes/SampleScene.unity
+ durationTicks (UInt64) 505218
+ depth (int) 4
+ messages (vector)
+ Array[0]
+ data[14] (BuildStepInfo)
+ stepName (string) Writing asset files
+ durationTicks (UInt64) 305260
+ depth (int) 3
+ messages (vector)
+ Array[0]
+ data[15] (BuildStepInfo)
+ stepName (string) Packaging assets - archive:/BuildPlayer-Scene2/BuildPlayer-SampleScene.sharedAssets
+ durationTicks (UInt64) 52472
+ depth (int) 4
+ messages (vector)
+ Array[0]
+ data[16] (BuildStepInfo)
+ stepName (string) Packaging assets - archive:/BuildPlayer-Scene2/BuildPlayer-Scene2.sharedAssets
+ durationTicks (UInt64) 243071
+ depth (int) 4
+ messages (vector)
+ Array[0]
+ data[17] (BuildStepInfo)
+ stepName (string) Creating compressed player package
+ durationTicks (UInt64) 22510
+ depth (int) 3
+ messages (vector)
+ Array[0]
+ data[18] (BuildStepInfo)
+ stepName (string) Postprocess built player
+ durationTicks (UInt64) 70340
+ depth (int) 3
+ messages (vector)
+ Array[0]
+ m_Appendices (vector)
+ Array>[11]
+ data[0] (PPtr