-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathSerializedFileParser.cs
More file actions
142 lines (123 loc) · 5.98 KB
/
Copy pathSerializedFileParser.cs
File metadata and controls
142 lines (123 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Data.Sqlite;
using UnityDataTools.Analyzer.SQLite.Handlers;
using UnityDataTools.Analyzer.SQLite.Writers;
using UnityDataTools.BinaryFormat;
using UnityDataTools.FileSystem;
namespace UnityDataTools.Analyzer.SQLite.Parsers
{
public class SerializedFileParser : ISQLiteFileParser
{
private SerializedFileSQLiteWriter m_Writer;
public bool Verbose { get; set; }
public bool SkipReferences { get; set; }
public bool SkipCrc { get; set; }
public bool CanParse(string filename)
{
// First check if the file is in the ignore list (by extension or filename)
if (ShouldIgnoreFile(filename))
return false;
// Then validate that it's actually a Unity file by checking its format
// This prevents ugly exceptions when processing non-Unity files
return ArchiveDetector.IsUnityArchive(filename)
|| SerializedFileDetector.TryDetectSerializedFile(filename, out _);
}
public void Dispose()
{
m_Writer.Dispose();
}
public void Init(SqliteConnection db)
{
m_Writer = new SerializedFileSQLiteWriter(db, SkipReferences, SkipCrc);
}
public void Parse(string filename)
{
// only init our writer if we are actually parsing a file
m_Writer.Init();
ProcessFile(filename, Path.GetDirectoryName(filename));
}
bool ShouldIgnoreFile(string file)
{
// Filter out common non-Unity files by extension or filename.
// This is a fast initial filter before we perform format detection.
//
// Note: AssetBundles have no standard extension, and SerializedFiles often have no extension at all.
// Format detection (via ArchiveDetector and SerializedFileDetector) is performed after this filter
// to definitively identify Unity files.
string fileName = Path.GetFileName(file);
string extension = Path.GetExtension(file);
return IgnoredFileNames.Contains(fileName) || IgnoredExtensions.Contains(extension);
}
// These lists are based on expected output files in Player, AssetBundle, Addressables and ECS builds.
// However this is by no means exhaustive.
private static readonly HashSet<string> IgnoredFileNames = new()
{
".DS_Store", "boot.config", "archive_dependencies.bin", "scene_info.bin", "app.info", "link.xml",
"catalog.bin", "catalog.hash"
};
private static readonly HashSet<string> IgnoredExtensions = new()
{
".txt", ".resS", ".resource", ".json", ".dll", ".pdb", ".exe", ".manifest", ".entities", ".entityheader",
".ini", ".config", ".hash", ".md"
};
void ProcessFile(string file, string rootDirectory)
{
if (ArchiveDetector.IsUnityArchive(file))
{
bool archiveHadErrors = false;
using (UnityArchive archive = UnityFileSystem.MountArchive(file, "archive:" + Path.DirectorySeparatorChar))
{
if (archive == null)
throw new FileLoadException($"Failed to mount archive: {file}");
try
{
var assetBundleName = Path.GetRelativePath(rootDirectory, file);
m_Writer.BeginAssetBundle(assetBundleName, new FileInfo(file).Length);
foreach (var node in archive.Nodes)
{
if (node.Flags.HasFlag(ArchiveNodeFlags.SerializedFile))
{
try
{
m_Writer.WriteSerializedFile(node.Path, "archive:/" + node.Path, Path.GetDirectoryName(file));
}
catch (Exception e)
{
// the most likely exception here is Microsoft.Data.Sqlite.SqliteException,
// for example 'UNIQUE constraint failed: serialized_files.id'.
// or 'UNIQUE constraint failed: objects.id' which can happen
// if AssetBundles from different builds are being processed by a single call to Analyze
// or if there is a Unity Data Tool bug.
Console.Error.WriteLine($"Error processing {node.Path} in archive {assetBundleName}");
Console.Error.WriteLine(e.Message);
Console.Error.WriteLine();
// It is possible some files inside an archive will pass and others will fail, to have a partial analyze.
// Overall that is reported as a failure
archiveHadErrors = true;
}
}
}
}
finally
{
m_Writer.EndAssetBundle();
}
}
if (archiveHadErrors)
{
throw new Exception("One or more files in the archive failed to process");
}
}
else
{
// This isn't a Unity Archive file, so process it as a SerializedFile.
// Note: The file has already been validated in CanParse() via SerializedFileDetector,
// so we're confident it's a valid SerializedFile at this point.
var relativePath = Path.GetRelativePath(rootDirectory, file);
m_Writer.WriteSerializedFile(relativePath, file, Path.GetDirectoryName(file));
}
}
}
}