Add quick and dirty path extractor

This commit is contained in:
Lucidiot 2020-12-06 00:06:08 +01:00
parent 3e77c34bd5
commit 682745177b
5 changed files with 243 additions and 0 deletions

View File

@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Texture2DDecoderWrapper", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio.PInvoke", "AssetStudio.PInvoke\AssetStudio.PInvoke.csproj", "{40C796B5-88CE-4ADC-ACD6-2F4862B7F136}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PathIDExtractor", "PathIDExtractor\PathIDExtractor.csproj", "{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -131,6 +133,18 @@ Global
{40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x64.Build.0 = Release|Any CPU
{40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x86.ActiveCfg = Release|Any CPU
{40C796B5-88CE-4ADC-ACD6-2F4862B7F136}.Release|x86.Build.0 = Release|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Debug|x64.ActiveCfg = Debug|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Debug|x64.Build.0 = Debug|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Debug|x86.ActiveCfg = Debug|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Debug|x86.Build.0 = Debug|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Release|Any CPU.Build.0 = Release|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Release|x64.ActiveCfg = Release|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Release|x64.Build.0 = Release|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Release|x86.ActiveCfg = Release|Any CPU
{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D8AE6E83-EC67-47E3-8DAD-8BA96A67B5E8}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>PathIDExtractor</RootNamespace>
<AssemblyName>PathIDExtractor</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj">
<Project>{7662f8c2-7bfd-442e-a948-a43b4f7eb06e}</Project>
<Name>AssetStudio</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

128
PathIDExtractor/Program.cs Normal file
View File

@ -0,0 +1,128 @@
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();
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Les informations générales relatives à un assembly dépendent de
// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations
// associées à un assembly.
[assembly: AssemblyTitle("PathIDExtractor")]
[assembly: AssemblyDescription("PathID extractor from Unity asset bundles")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PathIDExtractor")]
[assembly: AssemblyCopyright("Copyright © Lucidiot 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly
// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de
// COM, affectez la valeur true à l'attribut ComVisible sur ce type.
[assembly: ComVisible(false)]
// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM
[assembly: Guid("d8ae6e83-ec67-47e3-8dad-8ba96a67b5e8")]
// Les informations de version pour un assembly se composent des quatre valeurs suivantes :
//
// Version principale
// Version secondaire
// Numéro de build
// Révision
//
// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut
// en utilisant '*', comme indiqué ci-dessous :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]