AssetStudio/PathIDExtractor/Program.cs

129 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AssetStudio;
namespace PathIDExtractor
{
struct ParsedObject
{
public readonly long pathId;
public readonly string type;
public string name;
public ParsedObject(long pathId, string type)
{
this.pathId = pathId;
this.type = type;
name = null;
}
public override string ToString() => $"{pathId},{type},{(name is null ? "" : name)}";
}
class Program
{
private static string pathIdsFilePath;
private static string containersFilePath;
static void ParseObjects (List<AssetStudio.Object> objects)
{
/*
* As objects have no particular order and some objects hold other objects,
* and we want to keep a reference to each object's container,
* we will keep all the parsed objects and all the containers we find for those objects,
* then re-link them at the end.
*/
var parsedObjects = new Dictionary<AssetStudio.Object, ParsedObject>(objects.Count);
var containers = new List<(PPtr<AssetStudio.Object>, string)>();
foreach (var asset in objects)
{
var parsedObject = new ParsedObject(asset.m_PathID, asset.type.ToString());
// Find the name of an object depending on its type
switch (asset)
{
case Shader m_Shader:
parsedObject.name = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
break;
case Animator m_Animator:
if (m_Animator.m_GameObject.TryGet(out var gameObject)) parsedObject.name = gameObject.m_Name;
break;
case MonoBehaviour m_MonoBehaviour:
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var m_Script)) parsedObject.name = m_Script.m_ClassName;
else parsedObject.name = m_MonoBehaviour.m_Name;
break;
case AssetBundle m_AssetBundle:
foreach (var m_Container in m_AssetBundle.m_Container)
{
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
{
containers.Add((m_AssetBundle.m_PreloadTable[k], m_Container.Key));
}
}
parsedObject.name = m_AssetBundle.m_Name;
break;
case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container)
{
containers.Add((m_Container.Value, m_Container.Key));
}
break;
case NamedObject namedObject:
parsedObject.name = namedObject.m_Name;
break;
}
parsedObjects.Add(asset, parsedObject);
}
File.AppendAllText(pathIdsFilePath, String.Join("\n", parsedObjects.Values.Select(obj => obj.ToString())) + "\n");
File.AppendAllText(containersFilePath, String.Join("\n", containers.Select(tuple => tuple.Item1.TryGet(out var obj) ? $"{obj.m_PathID},{tuple.Item2}" : null).Where(output => output != null)) + "\n");
Console.WriteLine($"Parsed {parsedObjects.Count} values");
}
static void Main(string[] args)
{
Console.Title = "Path ID Extractor";
Console.BackgroundColor = ConsoleColor.DarkRed;
Console.ForegroundColor = ConsoleColor.White;
Console.Clear();
Console.WriteLine("--- lucidiot's path ID extractor ---");
string basePath;
do
{
Console.WriteLine("Enter the base path of all Unity data files to analyze:");
basePath = Path.GetFullPath(Console.ReadLine());
} while (!Directory.Exists(basePath));
var files = Directory.GetFiles(basePath, "*.*", SearchOption.AllDirectories);
Console.WriteLine($"{files.Length} files to parse");
string baseOutputPath;
do
{
Console.WriteLine("Enter the base output path:");
baseOutputPath = Path.GetFullPath(Console.ReadLine());
} while (!Directory.Exists(baseOutputPath));
pathIdsFilePath = Path.Combine(baseOutputPath, "path_ids.csv");
containersFilePath = Path.Combine(baseOutputPath, "containers.csv");
Console.WriteLine($"Outputting to {pathIdsFilePath} and {containersFilePath}");
File.WriteAllText(pathIdsFilePath, "path_id,type,name\n");
File.WriteAllText(containersFilePath, "path_id,container\n");
var manager = new AssetsManager();
foreach (var filePath in files)
{
Console.WriteLine($"Loading {filePath.Substring(basePath.Length)}");
manager.LoadFiles(filePath);
manager.assetsFileList.ForEach(file => ParseObjects(file.Objects));
manager.Clear();
}
}
}
}