Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
399 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
AssetRipperCore/Project/Exporters/Script/AltScriptExporter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using AssetRipper.Core.Classes; | ||
using AssetRipper.Core.Configuration; | ||
using AssetRipper.Core.Parser.Asset; | ||
using AssetRipper.Core.Parser.Files.SerializedFiles; | ||
using AssetRipper.Core.Structure.Assembly.Managers; | ||
using AssetRipper.Core.Structure.Collections; | ||
using Mono.Cecil; | ||
using System; | ||
using System.Collections.Generic; | ||
using Object = AssetRipper.Core.Classes.Object.Object; | ||
|
||
namespace AssetRipper.Core.Project.Exporters.Script | ||
{ | ||
public class AltScriptExporter : IAssetExporter | ||
{ | ||
private IAssemblyManager AssemblyManager { get; } | ||
|
||
public AltScriptExporter(IAssemblyManager assemblyManager) | ||
{ | ||
AssemblyManager = assemblyManager; | ||
} | ||
|
||
public bool IsHandle(Object asset, CoreConfiguration options) | ||
{ | ||
return true; | ||
} | ||
|
||
public IExportCollection CreateCollection(VirtualSerializedFile virtualFile, Object asset) | ||
{ | ||
return new ScriptExportCollection(this, (MonoScript)asset); | ||
} | ||
|
||
public bool Export(IExportContainer container, Object asset, string path) | ||
{ | ||
throw new NotSupportedException("Need to export all scripts at once"); | ||
} | ||
|
||
public void Export(IExportContainer container, Object asset, string path, Action<IExportContainer, Object, string> callback) | ||
{ | ||
throw new NotSupportedException("Need to export all scripts at once"); | ||
} | ||
|
||
public bool Export(IExportContainer container, IEnumerable<Object> assets, string dirPath) | ||
{ | ||
Export(container, assets, dirPath, null); | ||
return true; | ||
} | ||
|
||
public void Export(IExportContainer container, IEnumerable<Object> assets, string dirPath, Action<IExportContainer, Object, string> callback) | ||
{ | ||
AltScriptManager scriptManager = new AltScriptManager(AssemblyManager, dirPath); | ||
Dictionary<Object, TypeDefinition> exportTypes = new Dictionary<Object, TypeDefinition>(); | ||
foreach (Object asset in assets) | ||
{ | ||
MonoScript script = (MonoScript)asset; | ||
TypeDefinition exportType = script.GetTypeDefinition(); | ||
exportTypes.Add(asset, exportType); | ||
} | ||
foreach (KeyValuePair<Object, TypeDefinition> exportType in exportTypes) | ||
{ | ||
string path = scriptManager.Export(exportType.Value); | ||
if (path != null) | ||
{ | ||
callback?.Invoke(container, exportType.Key, path); | ||
} | ||
} | ||
scriptManager.ExportRest(); | ||
} | ||
|
||
public AssetType ToExportType(Object asset) | ||
{ | ||
return AssetType.Meta; | ||
} | ||
|
||
public bool ToUnknownExportType(ClassIDType classID, out AssetType assetType) | ||
{ | ||
assetType = AssetType.Meta; | ||
return true; | ||
} | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
AssetRipperCore/Project/Exporters/Script/AltScriptManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
using AssetRipper.Core.IO; | ||
using AssetRipper.Core.Logging; | ||
using AssetRipper.Core.Structure.Assembly.Managers; | ||
using AssetRipper.Core.Structure.Assembly.Mono; | ||
using AssetRipper.Core.Utils; | ||
using Mono.Cecil; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace AssetRipper.Core.Project.Exporters.Script | ||
{ | ||
public sealed class AltScriptManager | ||
{ | ||
ScriptDecompiler Decompiler { get; } | ||
|
||
public IEnumerable<TypeDefinition> Types => m_types.Values; | ||
|
||
private readonly Dictionary<string, TypeDefinition> m_types = new Dictionary<string, TypeDefinition>(); | ||
|
||
private readonly HashSet<string> m_exported = new HashSet<string>(); | ||
|
||
private readonly string m_exportPath; | ||
|
||
public AltScriptManager(IAssemblyManager assemblyManager, string exportPath) | ||
{ | ||
if (string.IsNullOrEmpty(exportPath)) | ||
{ | ||
throw new ArgumentNullException(nameof(exportPath)); | ||
} | ||
Decompiler = new ScriptDecompiler(assemblyManager); | ||
m_exportPath = exportPath; | ||
} | ||
|
||
private static string GetExportSubPath(string assembly, string @namespace, string @class) | ||
{ | ||
string assemblyFolder = BaseManager.ToAssemblyName(assembly); | ||
string namespaceFolder = @namespace.Replace('.', Path.DirectorySeparatorChar); | ||
string folderPath = Path.Combine(assemblyFolder, namespaceFolder); | ||
string filePath = Path.Combine(folderPath, @class); | ||
return $"{DirectoryUtils.FixInvalidPathCharacters(filePath)}.cs"; | ||
} | ||
|
||
private static string GetExportSubPath(TypeDefinition type) | ||
{ | ||
string typeName = type.Name; | ||
int index = typeName.IndexOf('<'); | ||
if (index >= 0) | ||
{ | ||
string normalName = typeName.Substring(0, index); | ||
typeName = normalName + $".{typeName.Count(t => t == ',') + 1}"; | ||
} | ||
return GetExportSubPath(type.Module.Name, type.Namespace, typeName); | ||
} | ||
|
||
public string Export(TypeDefinition exportType) | ||
{ | ||
if (exportType.DeclaringType != null) | ||
{ | ||
throw new NotSupportedException("You can export only topmost types"); | ||
} | ||
|
||
if (IsBuiltInType(exportType)) | ||
{ | ||
return null; | ||
} | ||
|
||
string subPath = GetExportSubPath(exportType); | ||
string filePath = Path.Combine(m_exportPath, subPath); | ||
string uniqueFilePath = ToUniqueFileName(filePath); | ||
string directory = Path.GetDirectoryName(uniqueFilePath); | ||
if (!DirectoryUtils.Exists(directory)) | ||
{ | ||
DirectoryUtils.CreateVirtualDirectory(directory); | ||
} | ||
|
||
using (Stream fileStream = FileUtils.CreateVirtualFile(uniqueFilePath)) | ||
{ | ||
using (StreamWriter writer = new InvariantStreamWriter(fileStream, new UTF8Encoding(false))) | ||
{ | ||
writer.Write(Decompiler.Decompile(exportType)); | ||
} | ||
} | ||
AddExportedType(exportType); | ||
return uniqueFilePath; | ||
} | ||
|
||
public void ExportRest() | ||
{ | ||
foreach (TypeDefinition type in m_types.Values) | ||
{ | ||
if (type.DeclaringType != null) | ||
{ | ||
continue; | ||
} | ||
if (m_exported.Contains(type.FullName)) | ||
{ | ||
continue; | ||
} | ||
|
||
Export(type); | ||
} | ||
} | ||
|
||
private void AddExportedType(TypeDefinition exportType) | ||
{ | ||
m_exported.Add(exportType.FullName); | ||
|
||
foreach (TypeDefinition nestedType in exportType.NestedTypes) | ||
{ | ||
AddExportedType(nestedType); | ||
} | ||
} | ||
|
||
private static string ToUniqueFileName(string filePath) | ||
{ | ||
if (FileUtils.Exists(filePath)) | ||
{ | ||
string directory = Path.GetDirectoryName(filePath); | ||
string fileName = Path.GetFileNameWithoutExtension(filePath); | ||
string fileExtension = Path.GetExtension(filePath); | ||
for (int i = 2; i < int.MaxValue; i++) | ||
{ | ||
string newFilePath = Path.Combine(directory, $"{fileName}.{i}{fileExtension}"); | ||
if (!FileUtils.Exists(newFilePath)) | ||
{ | ||
Logger.Log(LogType.Warning, LogCategory.Export, $"Found duplicate script file at {filePath}. Renamed to {newFilePath}"); | ||
return newFilePath; | ||
} | ||
} | ||
throw new Exception($"Can't create unit file at {filePath}"); | ||
} | ||
else | ||
{ | ||
return filePath; | ||
} | ||
} | ||
|
||
private static bool IsBuiltInType(TypeDefinition type) | ||
{ | ||
return MonoUtils.IsBuiltinLibrary(type.Module.Name); | ||
} | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
AssetRipperCore/Project/Exporters/Script/AssemblyResolver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using AssetRipper.Core.Structure.Assembly.Managers; | ||
using ICSharpCode.Decompiler.Metadata; | ||
using Mono.Cecil; | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
namespace AssetRipper.Core.Project.Exporters.Script | ||
{ | ||
internal class AssemblyResolver : ICSharpCode.Decompiler.Metadata.IAssemblyResolver | ||
{ | ||
private readonly ConcurrentDictionary<string, PEFile> peAssemblies = new ConcurrentDictionary<string, PEFile>(); | ||
public AssemblyResolver(IAssemblyManager manager) : this(manager.GetAssemblies()) { } | ||
public AssemblyResolver(ReadOnlySpan<AssemblyDefinition> assemblies) | ||
{ | ||
foreach (var assembly in assemblies) | ||
{ | ||
PEFile peFile = CreatePEFile(assembly); | ||
if (!peAssemblies.TryAdd(assembly.FullName, peFile)) | ||
{ | ||
throw new Exception("Could not add pe assembly to name dictionary!"); | ||
} | ||
} | ||
} | ||
|
||
private static PEFile CreatePEFile(AssemblyDefinition assembly) | ||
{ | ||
if (assembly == null) | ||
throw new ArgumentNullException(nameof(assembly)); | ||
|
||
MemoryStream memoryStream = new MemoryStream(); | ||
assembly.Write(memoryStream); | ||
memoryStream.Position = 0; | ||
|
||
return new PEFile(assembly.Name.Name, memoryStream); | ||
} | ||
|
||
public PEFile Resolve(IAssemblyReference reference) => Resolve(reference.FullName); | ||
|
||
public PEFile Resolve(string fullName) | ||
{ | ||
if (peAssemblies.TryGetValue(fullName, out PEFile result)) | ||
return result; | ||
else | ||
return null; | ||
} | ||
|
||
public Task<PEFile> ResolveAsync(IAssemblyReference reference) | ||
{ | ||
return Task.Run(() => Resolve(reference)); | ||
} | ||
|
||
/// <summary> | ||
/// Finds a module in the same directory as another | ||
/// </summary> | ||
/// <param name="mainModule"></param> | ||
/// <param name="moduleName"></param> | ||
/// <returns></returns> | ||
public PEFile ResolveModule(PEFile mainModule, string moduleName) | ||
{ | ||
return peAssemblies.Values.Where(x => x.Name == moduleName).Single(); | ||
} | ||
|
||
public Task<PEFile> ResolveModuleAsync(PEFile mainModule, string moduleName) | ||
{ | ||
return Task.Run(() => ResolveModule(mainModule, moduleName)); | ||
} | ||
} | ||
} |
Oops, something went wrong.