122 lines
4.6 KiB
C#
122 lines
4.6 KiB
C#
using gregExtractor.Models;
|
|
|
|
namespace gregExtractor.Services;
|
|
|
|
public sealed class HookClassifier
|
|
{
|
|
private static readonly Dictionary<string, string[]> MethodKeywordMap = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["Persistence"] = ["save", "load", "autosave"],
|
|
["Economy"] = ["coin", "money", "shop", "buy", "xp", "reputation", "checkout"],
|
|
["Networking"] = ["connect", "device", "cable", "ip", "network", "lacp"],
|
|
["Hardware"] = ["power", "repair", "broken", "server", "switch"],
|
|
["Lifecycle"] = ["pause", "resume", "day", "scene", "quit", "loadlevel"],
|
|
["VisualUI"] = ["ui", "menu", "panel", "screen", "button", "hud"],
|
|
};
|
|
|
|
private static readonly Dictionary<string, string[]> ClassKeywordMap = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["Persistence"] = ["save"],
|
|
["Economy"] = ["player", "shop", "economy"],
|
|
["Networking"] = ["network", "cable", "packet", "switch", "server"],
|
|
["Hardware"] = ["server", "rack", "item", "asset"],
|
|
["Lifecycle"] = ["loading", "pause", "time", "scene", "playermanager"],
|
|
["VisualUI"] = ["menu", "ui", "hud", "panel"],
|
|
};
|
|
|
|
public HookClassifier(IReadOnlyDictionary<string, HookGroupConfig> groups)
|
|
{
|
|
Groups = groups;
|
|
}
|
|
|
|
public IReadOnlyDictionary<string, HookGroupConfig> Groups { get; }
|
|
|
|
public static HookClassifier LoadFromFile(string jsonPath, IProgress<string>? progress = null)
|
|
{
|
|
if (!File.Exists(jsonPath))
|
|
{
|
|
progress?.Report($"hook_groups.json not found at '{jsonPath}'. Classifier starts with empty groups.");
|
|
return new HookClassifier(new Dictionary<string, HookGroupConfig>(StringComparer.OrdinalIgnoreCase));
|
|
}
|
|
|
|
try
|
|
{
|
|
string json = File.ReadAllText(jsonPath);
|
|
Dictionary<string, HookGroupConfig>? model = JsonSerializer.Deserialize<Dictionary<string, HookGroupConfig>>(
|
|
json,
|
|
new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
});
|
|
|
|
var normalized = new Dictionary<string, HookGroupConfig>(StringComparer.OrdinalIgnoreCase);
|
|
if (model != null)
|
|
{
|
|
foreach ((string key, HookGroupConfig value) in model)
|
|
{
|
|
normalized[key] = new HookGroupConfig(
|
|
value.Description ?? string.Empty,
|
|
value.Classes ?? Array.Empty<string>(),
|
|
value.Methods ?? Array.Empty<string>());
|
|
}
|
|
}
|
|
|
|
return new HookClassifier(normalized);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
progress?.Report($"Failed to parse hook_groups.json: {exception.Message}");
|
|
return new HookClassifier(new Dictionary<string, HookGroupConfig>(StringComparer.OrdinalIgnoreCase));
|
|
}
|
|
}
|
|
|
|
public string Classify(string className, string methodName)
|
|
{
|
|
// 1) Exact class + method match
|
|
foreach ((string groupName, HookGroupConfig groupConfig) in Groups)
|
|
{
|
|
bool classMatch = groupConfig.Classes.Any(c => EqualsIgnoreCase(c, className));
|
|
bool methodMatch = groupConfig.Methods.Any(m => EqualsIgnoreCase(m, methodName));
|
|
if (classMatch && methodMatch)
|
|
return groupName;
|
|
}
|
|
|
|
// 2) Class match only
|
|
foreach ((string groupName, HookGroupConfig groupConfig) in Groups)
|
|
{
|
|
if (groupConfig.Classes.Any(c => EqualsIgnoreCase(c, className)))
|
|
return groupName;
|
|
}
|
|
|
|
// 3) Method match only
|
|
foreach ((string groupName, HookGroupConfig groupConfig) in Groups)
|
|
{
|
|
if (groupConfig.Methods.Any(m => EqualsIgnoreCase(m, methodName)))
|
|
return groupName;
|
|
}
|
|
|
|
// 4) Method keyword heuristic
|
|
foreach ((string group, string[] keywords) in MethodKeywordMap)
|
|
{
|
|
if (keywords.Any(keyword => methodName.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
|
|
return group;
|
|
}
|
|
|
|
// 5) Class keyword heuristic
|
|
foreach ((string group, string[] keywords) in ClassKeywordMap)
|
|
{
|
|
if (keywords.Any(keyword => className.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
|
|
return group;
|
|
}
|
|
|
|
// 6) Fallback
|
|
return "Uncategorized";
|
|
}
|
|
|
|
private static bool EqualsIgnoreCase(string left, string right)
|
|
{
|
|
return string.Equals(left?.Trim(), right?.Trim(), StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
}
|