2025-09-18 21:54:11 -07:00

126 lines
4.8 KiB
C#

using AssetRipper.Assets;
using AssetRipper.Assets.Bundles;
using AssetRipper.Import.Configuration;
using AssetRipper.Import.Logging;
using AssetRipper.SourceGenerated;
namespace AssetRipper.Export.UnityProjects;
public sealed partial class ProjectExporter
{
public event Action? EventExportPreparationStarted;
public event Action? EventExportPreparationFinished;
public event Action? EventExportStarted;
public event Action<int, int>? EventExportProgressUpdated;
public event Action? EventExportFinished;
private readonly ObjectHandlerStack<IAssetExporter> assetExporterStack = new();
/// <summary>Adds an exporter to the stack of exporters for this asset type.</summary>
/// <typeparam name="T">The c sharp type of this asset type. Any inherited types also get this exporter.</typeparam>
/// <param name="exporter">The new exporter. If it doesn't work, the next one in the stack is used.</param>
/// <param name="allowInheritance">Should types that inherit from this type also use the exporter?</param>
public void OverrideExporter<T>(IAssetExporter exporter, bool allowInheritance = true)
{
assetExporterStack.OverrideHandler(typeof(T), exporter, allowInheritance);
}
/// <summary>Adds an exporter to the stack of exporters for this asset type.</summary>
/// <param name="type">The c sharp type of this asset type. Any inherited types also get this exporter.</param>
/// <param name="exporter">The new exporter. If it doesn't work, the next one in the stack is used.</param>
/// <param name="allowInheritance">Should types that inherit from this type also use the exporter?</param>
public void OverrideExporter(Type type, IAssetExporter exporter, bool allowInheritance)
{
assetExporterStack.OverrideHandler(type, exporter, allowInheritance);
}
/// <summary>
/// Use the <see cref="DummyExporter"/> for the specified class type.
/// </summary>
/// <typeparam name="T">The base type for assets of that <paramref name="classType"/>.</typeparam>
/// <param name="isEmptyCollection">
/// True: an exception will be thrown if the asset is referenced by another asset.<br/>
/// False: any references to this asset will be replaced with a missing reference.
/// </param>
/// <param name="isMetaType"><see cref="AssetType.Meta"/> or <see cref="AssetType.Serialized"/>?</param>
private void OverrideDummyExporter<T>(bool isEmptyCollection, bool isMetaType)
{
OverrideExporter<T>(DummyAssetExporter.Get(isEmptyCollection, isMetaType), true);
}
public AssetType ToExportType(Type type)
{
foreach (IAssetExporter exporter in assetExporterStack.GetHandlerStack(type))
{
if (exporter.ToUnknownExportType(type, out AssetType assetType))
{
return assetType;
}
}
throw new NotSupportedException($"There is no exporter that know {nameof(AssetType)} for unknown asset '{type}'");
}
private IExportCollection CreateCollection(IUnityObjectBase asset)
{
foreach (IAssetExporter exporter in assetExporterStack.GetHandlerStack(asset.GetType()))
{
if (exporter.TryCreateCollection(asset, out IExportCollection? collection))
{
return collection;
}
}
throw new Exception($"There is no exporter that can handle '{asset}'");
}
public void Export(GameBundle fileCollection, CoreConfiguration options, FileSystem fileSystem)
{
EventExportPreparationStarted?.Invoke();
List<IExportCollection> collections = CreateCollections(fileCollection);
EventExportPreparationFinished?.Invoke();
EventExportStarted?.Invoke();
ProjectAssetContainer container = new ProjectAssetContainer(this, options, fileCollection.FetchAssets(), collections);
int exportableCount = collections.Count(c => c.Exportable);
int currentExportable = 0;
for (int i = 0; i < collections.Count; i++)
{
IExportCollection collection = collections[i];
container.CurrentCollection = collection;
if (collection.Exportable)
{
currentExportable++;
Logger.Info(LogCategory.ExportProgress, $"({currentExportable}/{exportableCount}) Exporting '{collection.Name}'");
bool exportedSuccessfully = collection.Export(container, options.ProjectRootPath, fileSystem);
if (!exportedSuccessfully)
{
Logger.Warning(LogCategory.ExportProgress, $"Failed to export '{collection.Name}' ({collection.GetType().Name})");
}
}
EventExportProgressUpdated?.Invoke(i, collections.Count);
}
EventExportFinished?.Invoke();
}
private List<IExportCollection> CreateCollections(GameBundle fileCollection)
{
List<IExportCollection> collections = new();
HashSet<IUnityObjectBase> queued = new();
foreach (IUnityObjectBase asset in fileCollection.FetchAssets())
{
if (!queued.Contains(asset))
{
IExportCollection collection = CreateCollection(asset);
foreach (IUnityObjectBase element in collection.Assets)
{
queued.Add(element);
}
collections.Add(collection);
}
}
return collections;
}
}