Make the necessary infrastructural changes for parsing protos from lua source files

This commit is contained in:
Xpl0itR 2024-10-06 00:22:25 +01:00
parent bf2feae2e9
commit 1c1e14c58f
7 changed files with 137 additions and 6 deletions

View File

@ -22,6 +22,7 @@ Options:
Commands:
il2cpp Use LibCpp2IL backend to directly load Il2Cpp compiled game assembly. EXPERIMENTAL.
lua Use Lua AST backend to load Lua source files.
```
See per-command help message for more info.

View File

@ -18,6 +18,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Loretta.CodeAnalysis.Lua" Version="0.2.12" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0-preview.6.24327.7" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="Samboy063.LibCpp2IL" Version="2022.1.0-development.1027" />

View File

@ -0,0 +1,41 @@
// Copyright © 2024 Xpl0itR
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Loretta.CodeAnalysis;
using Loretta.CodeAnalysis.Lua;
using Loretta.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
namespace LibProtodec.Loaders;
public sealed class LuaSourceLoader
{
public IReadOnlyList<SyntaxTree> LoadedSyntaxTrees { get; }
public LuaSourceLoader(string sourcePath, ILogger<LuaSourceLoader>? logger = null)
{
LoadedSyntaxTrees = File.Exists(sourcePath)
? [LoadSyntaxTreeFromSourceFile(sourcePath)]
: Directory.EnumerateFiles(sourcePath, searchPattern: "*.lua")
.Select(LoadSyntaxTreeFromSourceFile)
.ToList();
logger?.LogLoadedLuaSyntaxTrees(LoadedSyntaxTrees.Count);
}
private static SyntaxTree LoadSyntaxTreeFromSourceFile(string filePath)
{
using FileStream fileStream = File.OpenRead(filePath);
return LuaSyntaxTree.ParseText(
SourceText.From(fileStream),
null, // TODO: maybe expose the options parameter
Path.GetFileName(filePath));
}
}

View File

@ -18,6 +18,9 @@ internal static partial class LoggerMessages
[LoggerMessage(Level = LogLevel.Information, Message = "Loaded {typeCount} types from {assemblyCount} assemblies for parsing.")]
internal static partial void LogLoadedTypeAndAssemblyCount(this ILogger logger, int typeCount, int assemblyCount);
[LoggerMessage(Level = LogLevel.Information, Message = "Loaded {treeCount} Lua syntax trees for parsing.")]
internal static partial void LogLoadedLuaSyntaxTrees(this ILogger logger, int treeCount);
[LoggerMessage(Level = LogLevel.Debug, Message = "Parsed as enum \"{name}\".")]
internal static partial void LogParsedEnum(this ILogger logger, string name);

View File

@ -0,0 +1,18 @@
// Copyright © 2024 Xpl0itR
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using Loretta.CodeAnalysis;
namespace LibProtodec;
partial class ProtodecContext
{
public void ParseLuaSyntaxTree(SyntaxTree ast)
{
throw new NotImplementedException();
}
}

View File

@ -24,7 +24,7 @@ namespace LibProtodec;
public delegate bool NameLookupFunc(string name, [MaybeNullWhen(false)] out string translatedName);
// ReSharper disable ClassWithVirtualMembersNeverInherited.Global, MemberCanBePrivate.Global, MemberCanBeProtected.Global, PropertyCanBeMadeInitOnly.Global
public class ProtodecContext
public partial class ProtodecContext
{
private readonly Dictionary<string, TopLevel> _parsed = [];

View File

@ -14,6 +14,7 @@ using LibProtodec;
using LibProtodec.Loaders;
using LibProtodec.Models.Cil;
using LibProtodec.Models.Protobuf;
using Loretta.CodeAnalysis;
using Microsoft.Extensions.Logging;
ConsoleApp.ConsoleAppBuilder app = ConsoleApp.Create();
@ -47,10 +48,10 @@ internal sealed class Commands
using ILoggerFactory loggerFactory = CreateLoggerFactory(logLevel);
ILogger logger = CreateProtodecLogger(loggerFactory);
logger.LogInformation("Loading target assemblies...");
logger.LogInformation("Loading CIL assemblies...");
using ClrAssemblyLoader loader = new(targetPath, loggerFactory.CreateLogger<ClrAssemblyLoader>());
Handle(
HandleCil(
loader,
outPath,
skipEnums,
@ -100,10 +101,10 @@ internal sealed class Commands
using ILoggerFactory loggerFactory = CreateLoggerFactory(logLevel);
ILogger logger = CreateProtodecLogger(loggerFactory);
logger.LogInformation("Loading target assemblies...");
logger.LogInformation("Loading Il2Cpp assembly and metadata...");
Il2CppAssemblyLoader loader = new(gameAssembly, globalMetadata, unityVer, loggerFactory);
Handle(
HandleCil(
loader,
outPath,
skipEnums,
@ -115,7 +116,73 @@ internal sealed class Commands
logger);
}
private static void Handle(
/// <summary>
/// Use Lua AST backend to load Lua source files.
/// </summary>
/// <param name="targetPath">Either the path to the target lua file or a directory of lua files, all of which be parsed.</param>
/// <param name="outPath">An existing directory to output into individual files, otherwise output to a single file.</param>
/// <param name="logLevel">Logging severity level.</param>
[Command("lua")]
public void Lua(
[Argument] string targetPath,
[Argument] string outPath,
LogLevel logLevel = LogLevel.Information)
{
using ILoggerFactory loggerFactory = CreateLoggerFactory(logLevel);
ILogger logger = CreateProtodecLogger(loggerFactory);
logger.LogInformation("Loading Lua sources...");
LuaSourceLoader loader = new(targetPath, loggerFactory.CreateLogger<LuaSourceLoader>());
ProtodecContext ctx = new()
{
Logger = loggerFactory.CreateLogger<ProtodecContext>()
};
logger.LogInformation("Parsing Lua syntax trees...");
foreach (SyntaxTree ast in loader.LoadedSyntaxTrees)
{
ctx.ParseLuaSyntaxTree(ast);
}
// NOTE: I'm duplicating this code rather than refactoring because I have a lot of uncommited changes on another computer,
// therefore, so as to not give myself brain cancer, I will not touch anything that would cause git conflicts.
const string indent = " ";
if (Directory.Exists(outPath))
{
logger.LogInformation("Writing {count} Protobuf files to \"{path}\"...", ctx.Protobufs.Count, outPath);
HashSet<string> writtenFiles = [];
foreach (Protobuf protobuf in ctx.Protobufs)
{
// This workaround stops files from being overwritten in the case of a naming conflict,
// however the actual conflict will still have to be resolved manually
string fileName = protobuf.FileName;
while (!writtenFiles.Add(fileName))
{
fileName = '_' + fileName;
}
string protobufPath = Path.Join(outPath, fileName);
using StreamWriter streamWriter = new(protobufPath);
using IndentedTextWriter indentWriter = new(streamWriter, indent);
protobuf.WriteTo(indentWriter);
}
}
else
{
logger.LogInformation("Writing Protobufs as a single file to \"{path}\"...", outPath);
using StreamWriter streamWriter = new(outPath);
using IndentedTextWriter indentWriter = new(streamWriter, indent);
ctx.WriteAllTo(indentWriter);
}
}
private static void HandleCil(
CilAssemblyLoader loader,
string outPath,
bool skipEnums,