mirror of
https://github.com/builtbybel/FluentCleaner.git
synced 2026-09-16 14:27:02 +00:00
92 lines
4.8 KiB
C#
92 lines
4.8 KiB
C#
using FluentCleaner.Models;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace FluentCleaner.Services;
|
|
|
|
// Parses the Winapp2.ini format into CleanerEntry objects.
|
|
// The format is INI-like but with numbered multi-value keys:
|
|
// FileKey1=..., FileKey2=..., Detect, Detect1, Detect2, etc.
|
|
public class Winapp2Parser
|
|
{
|
|
private static readonly Regex RxFileKey = new(@"^FileKey\d+$", RegexOptions.IgnoreCase | RegexOptions.Compiled); // FileKey1, FileKey2 ...; number required
|
|
private static readonly Regex RxRegKey = new(@"^RegKey\d+$", RegexOptions.IgnoreCase | RegexOptions.Compiled); // RegKey1, RegKey2 ...
|
|
private static readonly Regex RxExcludeKey = new(@"^ExcludeKey\d+$", RegexOptions.IgnoreCase | RegexOptions.Compiled); // ExcludeKey1, ExcludeKey2 ...
|
|
private static readonly Regex RxDetect = new(@"^Detect\d*$", RegexOptions.IgnoreCase | RegexOptions.Compiled); // Detect or Detect1; number optional
|
|
private static readonly Regex RxDetectFile = new(@"^DetectFile\d*$", RegexOptions.IgnoreCase | RegexOptions.Compiled); // DetectFile or DetectFile1; number optional
|
|
|
|
public List<CleanerEntry> Parse(string content)
|
|
{
|
|
var entries = new List<CleanerEntry>();
|
|
CleanerEntry? current = null;
|
|
var raw = new StringBuilder(); // collects the verbatim block of the current entry for "Show source"
|
|
|
|
//Split on both \r and \n;WinUI 3 TextBox saves with \r only (not \r\n),
|
|
//so splitting on just \n would leave the entire file as a single line
|
|
foreach (var rawLine in content.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
var line = rawLine.Trim();
|
|
if (line.Length == 0 || line[0] == ';' || line[0] == '#') continue;
|
|
|
|
if (line.StartsWith("[", StringComparison.Ordinal) && line.EndsWith("]", StringComparison.Ordinal))
|
|
{
|
|
if (current is not null && IsValid(current)) { current.RawText = raw.ToString().TrimEnd(); entries.Add(current); }
|
|
|
|
raw.Clear().AppendLine(line); // new block starts at the [header]
|
|
|
|
var name = line[1..^1].Trim();
|
|
//Skip the files own header block
|
|
if (name.StartsWith("Winapp2", StringComparison.OrdinalIgnoreCase) ||
|
|
name.StartsWith("version", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
current = null;
|
|
continue;
|
|
}
|
|
|
|
//Strip the trailing " *" Winapp2 uses to mark community entries
|
|
current = new CleanerEntry { Name = name.TrimEnd('*').TrimEnd() };
|
|
continue;
|
|
}
|
|
|
|
if (current is null) continue;
|
|
|
|
raw.AppendLine(line); // keep every key line so the source view matches the ini
|
|
|
|
var eqIdx = line.IndexOf('=');
|
|
if (eqIdx < 0) continue;
|
|
|
|
var key = line[..eqIdx].Trim();
|
|
var value = line[(eqIdx + 1)..].Trim();
|
|
if (value.Length == 0) continue;
|
|
|
|
if (key.Equals("LangSecRef", StringComparison.OrdinalIgnoreCase)) { if (int.TryParse(value, out var n)) current.LangSecRef = n; }
|
|
else if (key.Equals("Section", StringComparison.OrdinalIgnoreCase)) current.Section = value;
|
|
else if (key.Equals("SpecialDetect", StringComparison.OrdinalIgnoreCase)) current.SpecialDetect = value;
|
|
else if (key.Equals("Warning", StringComparison.OrdinalIgnoreCase)) current.Warning = value;
|
|
else if (key.Equals("Default", StringComparison.OrdinalIgnoreCase)) current.Default = value.Equals("True", StringComparison.OrdinalIgnoreCase);
|
|
else if (RxDetect.IsMatch(key)) current.DetectKeys.Add(value);
|
|
else if (RxDetectFile.IsMatch(key)) current.DetectFiles.Add(value);
|
|
else if (RxFileKey.IsMatch(key)) current.FileKeys.Add(FileKeyEntry.Parse(value));
|
|
else if (RxRegKey.IsMatch(key)) current.RegKeys.Add(RegKeyEntry.Parse(value));
|
|
else if (RxExcludeKey.IsMatch(key)) current.ExcludeKeys.Add(ExcludeKeyEntry.Parse(value));
|
|
}
|
|
|
|
if (current is not null && IsValid(current)) { current.RawText = raw.ToString().TrimEnd(); entries.Add(current); }
|
|
return entries;
|
|
}
|
|
|
|
// An entry is only useful if it can be detected AND has something to clean
|
|
private static bool IsValid(CleanerEntry e) =>
|
|
(e.DetectKeys.Count > 0 || e.DetectFiles.Count > 0 || e.SpecialDetect is not null) &&
|
|
(e.FileKeys.Count > 0 || e.RegKeys.Count > 0);
|
|
|
|
public async Task<List<CleanerEntry>> ParseFileAsync(string filePath)
|
|
{
|
|
// netstandard2.0/net48 don't have File.ReadAllTextAsync;StreamReader does the same job
|
|
string content;
|
|
using (var reader = new StreamReader(filePath))
|
|
content = await reader.ReadToEndAsync();
|
|
return Parse(content);
|
|
}
|
|
}
|