Files
gregExtractor/GUI/ViewModels/ExtractorViewModel.cs
T

192 lines
6.3 KiB
C#

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using gregExtractor.Models;
using gregExtractor.Services;
using gregExtractor.Utils;
namespace gregExtractor.GUI.ViewModels;
public sealed class ExtractorViewModel : ObservableObject
{
private readonly ExtractorService _extractorService = new();
private HookClassifier _classifier;
private string _gamePath = string.Empty;
private string _il2CppPath = string.Empty;
private bool _isLoading;
private double _progress;
public ExtractorViewModel()
{
string groupsPath = Path.Combine(Directory.GetCurrentDirectory(), "hook_groups.json");
_classifier = HookClassifier.LoadFromFile(groupsPath, new Progress<string>(AddLog));
foreach ((string groupName, HookGroupConfig config) in _classifier.Groups)
HookGroups.Add(new HookGroupViewModel(groupName, config.Description));
AutoDetectPathCommand = new RelayCommand(AutoDetectPaths);
BrowsePathCommand = new AsyncRelayCommand<string?>(BrowsePathAsync);
StartAnalysisCommand = new AsyncRelayCommand(StartAnalysisAsync, () => !IsLoading);
AutoDetectPaths();
}
public Func<string?, Task<string?>>? BrowseFolderHandler { get; set; }
public string GamePath
{
get => _gamePath;
set => SetProperty(ref _gamePath, value);
}
public string Il2CppPath
{
get => _il2CppPath;
set => SetProperty(ref _il2CppPath, value);
}
public bool IsLoading
{
get => _isLoading;
set
{
if (!SetProperty(ref _isLoading, value))
return;
StartAnalysisCommand.NotifyCanExecuteChanged();
}
}
public double Progress
{
get => _progress;
set => SetProperty(ref _progress, value);
}
public ObservableCollection<HookGroupViewModel> HookGroups { get; } = new();
public ObservableCollection<string> LogLines { get; } = new();
public int TotalHooks => HookGroups.Sum(x => x.Count);
public int GroupedHooks => HookGroups.Where(x => !x.GroupName.Equals("Uncategorized", StringComparison.OrdinalIgnoreCase)).Sum(x => x.Count);
public int UnknownHooks => HookGroups.Where(x => x.GroupName.Equals("Uncategorized", StringComparison.OrdinalIgnoreCase)).Sum(x => x.Count);
public int GroupCount => HookGroups.Count;
public IRelayCommand AutoDetectPathCommand { get; }
public IAsyncRelayCommand<string?> BrowsePathCommand { get; }
public IAsyncRelayCommand StartAnalysisCommand { get; }
private void AutoDetectPaths()
{
GamePath = SteamLocator.FindGamePath() ?? string.Empty;
Il2CppPath = SteamLocator.FindIl2CppAssembliesPath() ?? string.Empty;
if (string.IsNullOrWhiteSpace(Il2CppPath))
AddLog("⚠ Kein IL2CPP-Pfad automatisch erkannt.");
else
AddLog($"✓ IL2CPP-Pfad erkannt: {Il2CppPath}");
}
private async Task BrowsePathAsync(string? target)
{
if (BrowseFolderHandler is null)
return;
string? selected = await BrowseFolderHandler(target).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(selected))
return;
if (string.Equals(target, "GamePath", StringComparison.OrdinalIgnoreCase))
GamePath = selected;
else
Il2CppPath = selected;
}
private async Task StartAnalysisAsync()
{
if (string.IsNullOrWhiteSpace(Il2CppPath) || !Directory.Exists(Il2CppPath))
{
AddLog("✗ IL2CPP-Pfad ungültig.");
return;
}
IsLoading = true;
Progress = 0;
var progress = new Progress<string>(message =>
{
AddLog(message);
Progress = Math.Min(98, Progress + 1);
});
try
{
IReadOnlyList<HookDefinition> hooks = await _extractorService
.ExtractAsync(Il2CppPath, _classifier, progress, CancellationToken.None)
.ConfigureAwait(false);
string hooksPath = Path.Combine(Directory.GetCurrentDirectory(), "game_hooks.json");
await File.WriteAllTextAsync(hooksPath, JsonSerializer.Serialize(hooks, new JsonSerializerOptions { WriteIndented = true }), CancellationToken.None)
.ConfigureAwait(false);
string unknownPath = Path.Combine(Directory.GetCurrentDirectory(), "unknown_hooks.json");
HookDefinition[] unknown = hooks.Where(h => h.Group.Equals("Uncategorized", StringComparison.OrdinalIgnoreCase)).ToArray();
await File.WriteAllTextAsync(unknownPath, JsonSerializer.Serialize(unknown, new JsonSerializerOptions { WriteIndented = true }), CancellationToken.None)
.ConfigureAwait(false);
ApplyGroups(hooks);
Progress = 100;
AddLog($"✓ Analyse abgeschlossen. Hooks: {hooks.Count}");
}
catch (Exception exception)
{
AddLog($"✗ Analyse fehlgeschlagen: {exception.Message}");
}
finally
{
IsLoading = false;
OnPropertyChanged(nameof(TotalHooks));
OnPropertyChanged(nameof(GroupedHooks));
OnPropertyChanged(nameof(UnknownHooks));
OnPropertyChanged(nameof(GroupCount));
}
}
private void ApplyGroups(IEnumerable<HookDefinition> hooks)
{
Dictionary<string, HookDefinition[]> grouped = hooks
.GroupBy(x => x.Group)
.ToDictionary(x => x.Key, x => x.ToArray(), StringComparer.OrdinalIgnoreCase);
foreach (HookGroupViewModel group in HookGroups)
{
grouped.TryGetValue(group.GroupName, out HookDefinition[]? values);
group.ReplaceHooks(values ?? Array.Empty<HookDefinition>());
}
foreach ((string groupName, HookDefinition[] values) in grouped)
{
if (HookGroups.Any(x => x.GroupName.Equals(groupName, StringComparison.OrdinalIgnoreCase)))
continue;
var vm = new HookGroupViewModel(groupName, "Detected at runtime") { IsSelected = true };
vm.ReplaceHooks(values);
HookGroups.Add(vm);
}
}
public void AddLog(string message)
{
string line = $"[{DateTime.Now:HH:mm:ss}] {message}";
if (LogLines.Count > 1500)
LogLines.RemoveAt(0);
LogLines.Add(line);
}
}