Files
gregExtractor/Commands/SyncCommand.cs
T

180 lines
6.6 KiB
C#

using gregExtractor.Models;
using gregExtractor.Services;
using gregExtractor.Utils;
namespace gregExtractor.Commands;
public sealed class SyncCommand : AsyncCommand<SyncCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("--source <FRAMEWORK_SOURCE_DIR>")]
public string Source { get; init; } = string.Empty;
[CommandOption("--dry-run")]
public bool DryRun { get; init; }
[CommandOption("--git")]
public bool UseGitDiff { get; init; }
[CommandOption("--force")]
public bool Force { get; init; }
public override ValidationResult Validate()
{
return string.IsNullOrWhiteSpace(Source)
? ValidationResult.Error("--source ist erforderlich.")
: ValidationResult.Success();
}
}
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
{
AnsiConsole.Write(new FigletText("Sync").Color(Color.Yellow));
if (!settings.DryRun && !settings.Force)
{
bool confirmed = AnsiConsole.Confirm(
"⚠️ Dies schreibt direkt in deinen Framework-Quellcode!\nBackups werden angelegt. Fortfahren?",
false);
if (!confirmed)
{
AnsiConsole.MarkupLine("[yellow]Abgebrochen.[/]");
return 0;
}
}
string workingDirectory = Directory.GetCurrentDirectory();
string hooksPath = Path.Combine(workingDirectory, "game_hooks.json");
string groupsPath = Path.Combine(workingDirectory, "hook_groups.json");
if (!File.Exists(hooksPath))
{
AnsiConsole.MarkupLine($"[red]Vorheriger Zustand fehlt:[/] {Markup.Escape(hooksPath)}");
return -1;
}
HookDefinition[] oldHooks = JsonSerializer.Deserialize<HookDefinition[]>(await File.ReadAllTextAsync(hooksPath).ConfigureAwait(false))
?? Array.Empty<HookDefinition>();
HookClassifier classifier = HookClassifier.LoadFromFile(groupsPath, new Progress<string>(_ => { }));
string? il2CppPath = SteamLocator.FindIl2CppAssembliesPath();
if (string.IsNullOrWhiteSpace(il2CppPath) || !Directory.Exists(il2CppPath))
{
AnsiConsole.MarkupLine("[yellow]IL2CPP-Pfad konnte nicht automatisch gefunden werden.[/]");
il2CppPath = AnsiConsole.Ask<string>("Pfad zu Il2CppAssemblies:");
}
if (string.IsNullOrWhiteSpace(il2CppPath) || !Directory.Exists(il2CppPath))
{
AnsiConsole.MarkupLine("[red]Ungültiger IL2CPP-Pfad.[/]");
return -1;
}
var extractor = new ExtractorService();
var diffService = new DiffService();
var syncService = new FrameworkSyncService();
var logLines = new List<string>();
var progress = new Progress<string>(line => logLines.Add(line));
IReadOnlyList<HookDefinition> newHooks = Array.Empty<HookDefinition>();
HookDiff diff = new(Array.Empty<HookDefinition>(), Array.Empty<HookDefinition>(), Array.Empty<HookDefinition>());
await AnsiConsole.Status()
.Spinner(Spinner.Known.Dots)
.StartAsync("Berechne Assembly-Diff...", async _ =>
{
newHooks = await extractor.ExtractAsync(il2CppPath, classifier, progress, CancellationToken.None).ConfigureAwait(false);
diff = diffService.Diff(oldHooks.ToList(), newHooks.ToList());
})
.ConfigureAwait(false);
var summary = new Table().RoundedBorder();
summary.Title = new TableTitle("Assembly Diff");
summary.AddColumn("Typ");
summary.AddColumn("Anzahl");
summary.AddRow("+ Neue Methoden", diff.Added.Count.ToString());
summary.AddRow("~ Signaturen geändert", diff.Changed.Count.ToString());
summary.AddRow("- Methoden entfernt", diff.Removed.Count.ToString());
AnsiConsole.Write(summary);
SyncResult result = syncService.Sync(new SyncOptions(
FrameworkSourceDir: settings.Source,
Diff: diff,
AllHooks: newHooks.ToList(),
DryRun: settings.DryRun,
Progress: progress));
foreach (string line in logLines)
AnsiConsole.MarkupLine($"[grey]{Markup.Escape(line)}[/]");
foreach (string file in result.FilesWritten)
AnsiConsole.MarkupLine($"[green]✓[/] {Markup.Escape(Path.GetFileName(file))}");
foreach (string warning in result.Warnings)
AnsiConsole.MarkupLine($"[yellow]{Markup.Escape(warning)}[/]");
if (!settings.DryRun)
{
await File.WriteAllTextAsync(
hooksPath,
JsonSerializer.Serialize(newHooks, new JsonSerializerOptions { WriteIndented = true }),
CancellationToken.None)
.ConfigureAwait(false);
}
if (settings.UseGitDiff)
{
try
{
string gitOutput = await RunGitDiffStatAsync(settings.Source).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(gitOutput))
{
AnsiConsole.MarkupLine("[cyan]git diff --stat[/]");
AnsiConsole.WriteLine(gitOutput);
}
}
catch (Exception exception)
{
AnsiConsole.MarkupLine($"[yellow]Git-Diff konnte nicht gelesen werden:[/] {Markup.Escape(exception.Message)}");
}
}
return 0;
}
private static async Task<string> RunGitDiffStatAsync(string frameworkSourceDir)
{
string workingDirectory = Directory.Exists(frameworkSourceDir)
? frameworkSourceDir
: Directory.GetCurrentDirectory();
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "git",
Arguments = "diff --stat",
WorkingDirectory = workingDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
},
};
process.Start();
string output = await process.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
string error = await process.StandardError.ReadToEndAsync().ConfigureAwait(false);
await process.WaitForExitAsync().ConfigureAwait(false);
if (process.ExitCode != 0)
return string.IsNullOrWhiteSpace(error) ? output : error;
return output;
}
}