mirror of
https://github.com/Ryujinx/Ryujinx.CustomTasks.git
synced 2024-06-01 00:48:50 +02:00
254 lines
8.0 KiB
C#
254 lines
8.0 KiB
C#
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Ryujinx.CustomTasks.Helper;
|
|
using Ryujinx.CustomTasks.SyntaxWalker;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace Ryujinx.CustomTasks
|
|
{
|
|
public class GenerateArrays : Task
|
|
{
|
|
private const string InterfaceFileName = "IArray.g.cs";
|
|
private const string ArraysFileName = "Arrays.g.cs";
|
|
|
|
private readonly List<string> _outputFiles = new List<string>();
|
|
|
|
[Required]
|
|
public string ArrayNamespace { get; set; }
|
|
|
|
[Required]
|
|
public string OutputPath { get; set; }
|
|
|
|
[Required]
|
|
public ITaskItem[] InputFiles { get; set; }
|
|
|
|
[Output]
|
|
public string[] OutputFiles { get; set; }
|
|
|
|
private static int GetAvailableMaxSize(IReadOnlyList<int> availableSizes, ref int index, int missingSize)
|
|
{
|
|
if (availableSizes.Count == 0 || missingSize == 1)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (availableSizes.Count < index + 1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int size = 0;
|
|
|
|
while (size == 0 || size > missingSize && availableSizes.Count - index > 0)
|
|
{
|
|
index++;
|
|
size = availableSizes[availableSizes.Count - index];
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
private void AddGeneratedSource(string filePath, string content)
|
|
{
|
|
if (File.Exists(filePath))
|
|
{
|
|
File.Delete(filePath);
|
|
}
|
|
else
|
|
{
|
|
_outputFiles.Add(filePath);
|
|
}
|
|
|
|
File.WriteAllText(filePath, content);
|
|
}
|
|
|
|
private ICollection<int> GetArraySizes(string itemPath)
|
|
{
|
|
Log.LogMessage(MessageImportance.Low, $"Searching for StructArray types in: {itemPath}");
|
|
|
|
SyntaxTree tree = CSharpSyntaxTree.ParseText(File.ReadAllText(itemPath), path: itemPath);
|
|
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
|
|
|
|
ArraySizeCollector collector = new ArraySizeCollector();
|
|
|
|
collector.Visit(root);
|
|
|
|
foreach (int size in collector.ArraySizes)
|
|
{
|
|
Log.LogMessage(MessageImportance.Low, $"\tFound array size: {size}");
|
|
}
|
|
|
|
return collector.ArraySizes;
|
|
}
|
|
|
|
private string GenerateInterface()
|
|
{
|
|
CodeGenerator generator = new CodeGenerator();
|
|
|
|
generator.EnterScope($"namespace {ArrayNamespace}");
|
|
|
|
generator.AppendLine("/// <summary>");
|
|
generator.AppendLine("/// Array interface.");
|
|
generator.AppendLine("/// </summary>");
|
|
generator.AppendLine("/// <typeparam name=\"T\">Element type</typeparam>);");
|
|
generator.EnterScope("public interface IArray<T> where T : unmanaged");
|
|
|
|
generator.AppendLine("/// <summary>");
|
|
generator.AppendLine("/// Used to index the array.");
|
|
generator.AppendLine("/// </summary>");
|
|
generator.AppendLine("/// <param name=\"index\">Element index</param>");
|
|
generator.AppendLine("/// <returns>Element at the specified index</returns>");
|
|
generator.AppendLine("ref T this[int index] { get; }");
|
|
|
|
generator.AppendLine();
|
|
|
|
generator.AppendLine("/// <summary>");
|
|
generator.AppendLine("/// Number of elements on the array.");
|
|
generator.AppendLine("/// </summary>");
|
|
generator.AppendLine("int Length { get; }");
|
|
|
|
generator.LeaveScope();
|
|
generator.LeaveScope();
|
|
|
|
return generator.ToString();
|
|
}
|
|
|
|
private void GenerateArray(CodeGenerator generator, int size, IReadOnlyList<int> availableSizes)
|
|
{
|
|
generator.EnterScope($"public struct Array{size}<T> : IArray<T> where T : unmanaged");
|
|
|
|
generator.AppendLine("T _e0;");
|
|
|
|
if (size > 1)
|
|
{
|
|
generator.AppendLine("#pragma warning disable CS0169");
|
|
}
|
|
|
|
if (availableSizes.Count == 0)
|
|
{
|
|
for (int i = 1; i < size; i++)
|
|
{
|
|
generator.AppendLine($"T _e{i};");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int counter = 1;
|
|
int currentSize = 1;
|
|
int maxSizeIndex = 0;
|
|
int maxSize = 0;
|
|
|
|
while (currentSize < size)
|
|
{
|
|
if (maxSize == 0 || currentSize + maxSize > size)
|
|
{
|
|
maxSize = GetAvailableMaxSize(availableSizes, ref maxSizeIndex, size - currentSize);
|
|
}
|
|
|
|
generator.AppendLine(maxSize > 1
|
|
? counter == 1
|
|
? $"Array{maxSize}<T> _other;"
|
|
: $"Array{maxSize}<T> _other{counter};"
|
|
: $"T _e{counter};"
|
|
);
|
|
|
|
counter++;
|
|
currentSize += maxSize;
|
|
}
|
|
}
|
|
|
|
if (size > 1)
|
|
{
|
|
generator.AppendLine("#pragma warning restore CS0169");
|
|
}
|
|
|
|
generator.AppendLine();
|
|
generator.AppendLine($"public int Length => {size};");
|
|
generator.AppendLine("public ref T this[int index] => ref AsSpan()[index];");
|
|
generator.AppendLine("public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);");
|
|
|
|
generator.LeaveScope();
|
|
generator.AppendLine();
|
|
}
|
|
|
|
public override bool Execute()
|
|
{
|
|
string interfaceFilePath = Path.Combine(OutputPath, InterfaceFileName);
|
|
string arraysFilePath = Path.Combine(OutputPath, ArraysFileName);
|
|
List<int> arraySizes = new List<int>();
|
|
|
|
if (!Directory.Exists(OutputPath))
|
|
{
|
|
Directory.CreateDirectory(OutputPath);
|
|
}
|
|
|
|
foreach (var item in InputFiles)
|
|
{
|
|
string fullPath = item.GetMetadata("FullPath");
|
|
|
|
if (fullPath.EndsWith(".g.cs") || fullPath.Contains(Path.Combine("obj","Debug")) || fullPath.Contains(Path.Combine("obj", "Release")))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (int size in GetArraySizes(fullPath))
|
|
{
|
|
if (!arraySizes.Contains(size))
|
|
{
|
|
arraySizes.Add(size);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (arraySizes.Count == 0)
|
|
{
|
|
Log.LogWarning("No StructArray types found. Skipping code generation.");
|
|
|
|
return true;
|
|
}
|
|
|
|
arraySizes.Sort();
|
|
|
|
AddGeneratedSource(interfaceFilePath, GenerateInterface());
|
|
|
|
CodeGenerator generator = new CodeGenerator();
|
|
List<int> sizesAvailable = new List<int>();
|
|
|
|
generator.AppendLine("using System;");
|
|
generator.AppendLine("using System.Runtime.InteropServices;");
|
|
generator.AppendLine();
|
|
|
|
generator.EnterScope($"namespace {ArrayNamespace}");
|
|
|
|
// Always generate Arrays for 1, 2 and 3
|
|
GenerateArray(generator, 1, sizesAvailable);
|
|
sizesAvailable.Add(1);
|
|
GenerateArray(generator, 2, sizesAvailable);
|
|
sizesAvailable.Add(2);
|
|
GenerateArray(generator, 3, sizesAvailable);
|
|
sizesAvailable.Add(3);
|
|
|
|
foreach (var size in arraySizes)
|
|
{
|
|
if (!sizesAvailable.Contains(size))
|
|
{
|
|
GenerateArray(generator, size, sizesAvailable);
|
|
sizesAvailable.Add(size);
|
|
}
|
|
}
|
|
|
|
generator.LeaveScope();
|
|
|
|
AddGeneratedSource(arraysFilePath, generator.ToString());
|
|
|
|
OutputFiles = _outputFiles.ToArray();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
} |