248 lines
7.0 KiB
C#
248 lines
7.0 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 CoverageViewModel : ObservableObject
|
|
{
|
|
private readonly CoverageAnalyzerService _analyzerService = new();
|
|
|
|
private string _il2CppPath = string.Empty;
|
|
private string _gameHooksPath = Path.Combine(Directory.GetCurrentDirectory(), "game_hooks.json");
|
|
private string _frameworkSourceDirsText = CoveragePathResolver.GetDefaultSourcesText(Directory.GetCurrentDirectory());
|
|
private string _outputBasePath = Path.Combine(Directory.GetCurrentDirectory(), "coverage_report");
|
|
private bool _isAnalyzing;
|
|
private double _progress;
|
|
private int _totalHooks;
|
|
private int _coveredCount;
|
|
private int _plannedCount;
|
|
private int _uncoveredCount;
|
|
private double _coveragePercent;
|
|
|
|
public CoverageViewModel()
|
|
{
|
|
AutoDetectPathCommand = new RelayCommand(AutoDetectPath);
|
|
BrowseFolderCommand = new AsyncRelayCommand<string?>(BrowseFolderAsync);
|
|
AnalyzeCoverageCommand = new AsyncRelayCommand(AnalyzeAsync, () => !IsAnalyzing);
|
|
OpenMarkdownReportCommand = new RelayCommand(OpenMarkdownReport, () => File.Exists(CoverageAnalyzerService.GetReportPaths(OutputBasePath).MarkdownPath));
|
|
|
|
AutoDetectPath();
|
|
}
|
|
|
|
public Func<string?, Task<string?>>? BrowseFolderHandler { get; set; }
|
|
|
|
public string Il2CppPath
|
|
{
|
|
get => _il2CppPath;
|
|
set => SetProperty(ref _il2CppPath, value);
|
|
}
|
|
|
|
public string GameHooksPath
|
|
{
|
|
get => _gameHooksPath;
|
|
set => SetProperty(ref _gameHooksPath, value);
|
|
}
|
|
|
|
public string FrameworkSourceDirsText
|
|
{
|
|
get => _frameworkSourceDirsText;
|
|
set => SetProperty(ref _frameworkSourceDirsText, value);
|
|
}
|
|
|
|
public string OutputBasePath
|
|
{
|
|
get => _outputBasePath;
|
|
set
|
|
{
|
|
if (SetProperty(ref _outputBasePath, value))
|
|
OpenMarkdownReportCommand.NotifyCanExecuteChanged();
|
|
}
|
|
}
|
|
|
|
public bool IsAnalyzing
|
|
{
|
|
get => _isAnalyzing;
|
|
set
|
|
{
|
|
if (!SetProperty(ref _isAnalyzing, value))
|
|
return;
|
|
|
|
AnalyzeCoverageCommand.NotifyCanExecuteChanged();
|
|
OnPropertyChanged(nameof(IsLoading));
|
|
}
|
|
}
|
|
|
|
public bool IsLoading => IsAnalyzing;
|
|
|
|
public double Progress
|
|
{
|
|
get => _progress;
|
|
set => SetProperty(ref _progress, value);
|
|
}
|
|
|
|
public int TotalHooks
|
|
{
|
|
get => _totalHooks;
|
|
set => SetProperty(ref _totalHooks, value);
|
|
}
|
|
|
|
public int CoveredCount
|
|
{
|
|
get => _coveredCount;
|
|
set => SetProperty(ref _coveredCount, value);
|
|
}
|
|
|
|
public int PlannedCount
|
|
{
|
|
get => _plannedCount;
|
|
set => SetProperty(ref _plannedCount, value);
|
|
}
|
|
|
|
public int UncoveredCount
|
|
{
|
|
get => _uncoveredCount;
|
|
set => SetProperty(ref _uncoveredCount, value);
|
|
}
|
|
|
|
public double CoveragePercent
|
|
{
|
|
get => _coveragePercent;
|
|
set => SetProperty(ref _coveragePercent, value);
|
|
}
|
|
|
|
public ObservableCollection<HookCoverageEntry> Entries { get; } = new();
|
|
|
|
public ObservableCollection<string> LogLines { get; } = new();
|
|
|
|
public IRelayCommand AutoDetectPathCommand { get; }
|
|
|
|
public IAsyncRelayCommand<string?> BrowseFolderCommand { get; }
|
|
|
|
public IAsyncRelayCommand AnalyzeCoverageCommand { get; }
|
|
|
|
public IRelayCommand OpenMarkdownReportCommand { get; }
|
|
|
|
private void AutoDetectPath()
|
|
{
|
|
Il2CppPath = SteamLocator.FindIl2CppAssembliesPath() ?? string.Empty;
|
|
|
|
if (string.IsNullOrWhiteSpace(Il2CppPath))
|
|
Log("Auto-detect for IL2CPP path failed.");
|
|
else
|
|
Log($"Auto-detected IL2CPP path: {Il2CppPath}");
|
|
}
|
|
|
|
private async Task BrowseFolderAsync(string? target)
|
|
{
|
|
if (BrowseFolderHandler is null)
|
|
return;
|
|
|
|
string? selected = await BrowseFolderHandler(target).ConfigureAwait(false);
|
|
if (string.IsNullOrWhiteSpace(selected))
|
|
return;
|
|
|
|
switch (target)
|
|
{
|
|
case "Il2Cpp":
|
|
Il2CppPath = selected;
|
|
break;
|
|
case "SourceDirs":
|
|
FrameworkSourceDirsText = selected;
|
|
break;
|
|
case "Output":
|
|
OutputBasePath = Path.Combine(selected, "coverage_report");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private async Task AnalyzeAsync()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Il2CppPath) || !Directory.Exists(Il2CppPath))
|
|
{
|
|
Log("IL2CPP path is invalid.");
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(GameHooksPath) || !File.Exists(GameHooksPath))
|
|
{
|
|
Log("game_hooks.json path is invalid.");
|
|
return;
|
|
}
|
|
|
|
string[] sourceDirs = CoveragePathResolver.ResolveSourceDirs(FrameworkSourceDirsText, Directory.GetCurrentDirectory());
|
|
if (sourceDirs.Length == 0)
|
|
{
|
|
Log("No valid source directories found. Provide ';' separated paths.");
|
|
return;
|
|
}
|
|
|
|
IsAnalyzing = true;
|
|
Progress = 0;
|
|
Entries.Clear();
|
|
|
|
try
|
|
{
|
|
var progress = new Progress<string>(message =>
|
|
{
|
|
Log(message);
|
|
Progress = Math.Min(99, Progress + 1);
|
|
});
|
|
|
|
CoverageReport report = await Task.Run(() => _analyzerService.Analyze(new AnalyzeOptions(
|
|
Il2CppDir: Il2CppPath,
|
|
GameHooksJsonPath: GameHooksPath,
|
|
FrameworkSourceDirs: sourceDirs,
|
|
OutputBasePath: OutputBasePath,
|
|
Progress: progress)))
|
|
.ConfigureAwait(false);
|
|
|
|
TotalHooks = report.TotalHooks;
|
|
CoveredCount = report.CoveredCount;
|
|
PlannedCount = report.PlannedCount;
|
|
UncoveredCount = report.UncoveredCount;
|
|
CoveragePercent = report.CoveragePercent;
|
|
|
|
foreach (HookCoverageEntry entry in report.Entries)
|
|
Entries.Add(entry);
|
|
|
|
Progress = 100;
|
|
Log($"Coverage analysis done. Coverage: {report.CoveragePercent:F2}%");
|
|
OpenMarkdownReportCommand.NotifyCanExecuteChanged();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Log($"Coverage analysis failed: {exception.Message}");
|
|
}
|
|
finally
|
|
{
|
|
IsAnalyzing = false;
|
|
}
|
|
}
|
|
|
|
private void OpenMarkdownReport()
|
|
{
|
|
string markdownPath = CoverageAnalyzerService.GetReportPaths(OutputBasePath).MarkdownPath;
|
|
if (!File.Exists(markdownPath))
|
|
return;
|
|
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = markdownPath,
|
|
UseShellExecute = true,
|
|
});
|
|
}
|
|
|
|
private void Log(string message)
|
|
{
|
|
string line = $"[{DateTime.Now:HH:mm:ss}] {message}";
|
|
|
|
if (LogLines.Count > 1000)
|
|
LogLines.RemoveAt(0);
|
|
|
|
LogLines.Add(line);
|
|
}
|
|
}
|